经理
This commit is contained in:
parent
dae91e892e
commit
b6d8af0fa3
@ -67,6 +67,7 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
|||||||
canGrantVehicle: true,
|
canGrantVehicle: true,
|
||||||
canTransferUserCountry: false,
|
canTransferUserCountry: false,
|
||||||
contact: "+63",
|
contact: "+63",
|
||||||
|
status: "disabled",
|
||||||
});
|
});
|
||||||
await listHosts({ agency_id: 41, page: 1, page_size: 10, sort_by: "diamond", sort_direction: "desc" });
|
await listHosts({ agency_id: 41, page: 1, page_size: 10, sort_by: "diamond", sort_direction: "desc" });
|
||||||
await listCoinSellers({
|
await listCoinSellers({
|
||||||
@ -149,6 +150,7 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
|||||||
canGrantVehicle: true,
|
canGrantVehicle: true,
|
||||||
canTransferUserCountry: false,
|
canTransferUserCountry: false,
|
||||||
contact: "+63",
|
contact: "+63",
|
||||||
|
status: "disabled",
|
||||||
});
|
});
|
||||||
expect(String(hostUrl)).toContain("/api/v1/admin/hosts?");
|
expect(String(hostUrl)).toContain("/api/v1/admin/hosts?");
|
||||||
expect(String(hostUrl)).toContain("agency_id=41");
|
expect(String(hostUrl)).toContain("agency_id=41");
|
||||||
|
|||||||
@ -28,6 +28,12 @@ const emptyManagerForm = () => ({ contact: "", targetUserId: "", ...emptyPermiss
|
|||||||
const managerPermissionPayload = (form) =>
|
const managerPermissionPayload = (form) =>
|
||||||
Object.fromEntries(managerPermissionFields.map((field) => [field, form[field] !== false]));
|
Object.fromEntries(managerPermissionFields.map((field) => [field, form[field] !== false]));
|
||||||
|
|
||||||
|
const managerStatusPayload = (item, status) => ({
|
||||||
|
contact: item?.contact || "",
|
||||||
|
status,
|
||||||
|
...Object.fromEntries(managerPermissionFields.map((field) => [field, item?.[field] !== false])),
|
||||||
|
});
|
||||||
|
|
||||||
const managerFormFromItem = (item) => ({
|
const managerFormFromItem = (item) => ({
|
||||||
contact: item?.contact || "",
|
contact: item?.contact || "",
|
||||||
targetUserId: String(item?.userId || ""),
|
targetUserId: String(item?.userId || ""),
|
||||||
@ -139,6 +145,21 @@ export function useHostManagersPage() {
|
|||||||
setActiveAction("manager-edit");
|
setActiveAction("manager-edit");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleManager = async (item, enabled) => {
|
||||||
|
const nextStatus = enabled ? "active" : "disabled";
|
||||||
|
setLoadingAction(`manager-status-${item.userId}`);
|
||||||
|
try {
|
||||||
|
// 状态接口复用经理更新入口;提交当前行的权限快照,避免只改状态时把已有权限开关覆盖成后端默认值。
|
||||||
|
await updateManager(item.userId, managerStatusPayload(item, nextStatus));
|
||||||
|
showToast(enabled ? "经理已启用" : "经理已停用", "success");
|
||||||
|
await reload();
|
||||||
|
} catch (err) {
|
||||||
|
showToast(err.message || (enabled ? "启用经理失败" : "停用经理失败"), "error");
|
||||||
|
} finally {
|
||||||
|
setLoadingAction("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
abilities,
|
abilities,
|
||||||
activeAction,
|
activeAction,
|
||||||
@ -165,5 +186,6 @@ export function useHostManagersPage() {
|
|||||||
setPage,
|
setPage,
|
||||||
status,
|
status,
|
||||||
submitManager,
|
submitManager,
|
||||||
|
toggleManager,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { formatMillis } from "@/shared/utils/time.js";
|
|||||||
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
||||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||||
import { IconButton } from "@/shared/ui/IconButton.jsx";
|
import { IconButton } from "@/shared/ui/IconButton.jsx";
|
||||||
import { managerStatusFilters, statusLabels } from "@/features/host-org/constants.js";
|
import { managerStatusFilters } from "@/features/host-org/constants.js";
|
||||||
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
||||||
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
||||||
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
||||||
@ -55,7 +55,7 @@ const managerColumns = [
|
|||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
label: "状态",
|
label: "状态",
|
||||||
render: (item) => <ManagerStatus item={item} />,
|
render: (item, _index, context) => <ManagerStatusSwitch item={item} page={context?.page} />,
|
||||||
width: "minmax(90px, 0.7fr)",
|
width: "minmax(90px, 0.7fr)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -187,12 +187,14 @@ function ManagerUser({ item }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ManagerStatus({ item }) {
|
function ManagerStatusSwitch({ item, page }) {
|
||||||
const active = item.status === "active";
|
|
||||||
return (
|
return (
|
||||||
<span className={active ? styles.positive : styles.negative}>
|
<AdminSwitch
|
||||||
{statusLabels[item.status] || item.status || "-"}
|
checked={item.status === "active"}
|
||||||
</span>
|
disabled={!page?.abilities.canUpdate || page.loadingAction === `manager-status-${item.userId}`}
|
||||||
|
inputProps={{ "aria-label": item.status === "active" ? "停用经理" : "启用经理" }}
|
||||||
|
onChange={(event) => page.toggleManager(item, event.target.checked)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
103
src/features/host-org/pages/HostManagersPage.test.jsx
Normal file
103
src/features/host-org/pages/HostManagersPage.test.jsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
|
import { afterEach, expect, test, vi } from "vitest";
|
||||||
|
import { HostManagersPage } from "./HostManagersPage.jsx";
|
||||||
|
import { useHostManagersPage } from "@/features/host-org/hooks/useHostManagersPage.js";
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
toggleManager: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@/features/host-org/hooks/useHostManagersPage.js", () => ({
|
||||||
|
useHostManagersPage: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("manager status column uses a switch and toggles through the page hook", () => {
|
||||||
|
const manager = managerFixture();
|
||||||
|
vi.mocked(useHostManagersPage).mockReturnValue(pageFixture({ data: { items: [manager], page: 1, pageSize: 50, total: 1 } }));
|
||||||
|
|
||||||
|
render(<HostManagersPage />);
|
||||||
|
|
||||||
|
const statusSwitch = screen.getByRole("switch", { name: "停用经理" });
|
||||||
|
expect(statusSwitch).toBeChecked();
|
||||||
|
|
||||||
|
fireEvent.click(statusSwitch);
|
||||||
|
|
||||||
|
expect(mocks.toggleManager).toHaveBeenCalledWith(manager, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
function pageFixture(patch = {}) {
|
||||||
|
return {
|
||||||
|
abilities: {
|
||||||
|
canCreate: true,
|
||||||
|
canUpdate: true,
|
||||||
|
canView: true,
|
||||||
|
},
|
||||||
|
activeAction: "",
|
||||||
|
changeQuery: vi.fn(),
|
||||||
|
changeRegionId: vi.fn(),
|
||||||
|
changeStatus: vi.fn(),
|
||||||
|
closeAction: vi.fn(),
|
||||||
|
data: { items: [], page: 1, pageSize: 50, total: 0 },
|
||||||
|
editingManager: null,
|
||||||
|
error: null,
|
||||||
|
loading: false,
|
||||||
|
loadingAction: "",
|
||||||
|
loadingRegions: false,
|
||||||
|
managerForm: {
|
||||||
|
canAddAdmin: true,
|
||||||
|
canAddBdLeader: true,
|
||||||
|
canAddSuperadmin: true,
|
||||||
|
canBlockUser: true,
|
||||||
|
canGrantAvatarFrame: true,
|
||||||
|
canGrantBadge: true,
|
||||||
|
canGrantVehicle: true,
|
||||||
|
canTransferUserCountry: true,
|
||||||
|
canUpdateUserLevel: true,
|
||||||
|
contact: "",
|
||||||
|
targetUserId: "",
|
||||||
|
},
|
||||||
|
openManagerEdit: vi.fn(),
|
||||||
|
openManagerForm: vi.fn(),
|
||||||
|
page: 1,
|
||||||
|
query: "",
|
||||||
|
regionId: "",
|
||||||
|
regionOptions: [],
|
||||||
|
reload: vi.fn(),
|
||||||
|
resetFilters: vi.fn(),
|
||||||
|
setManagerForm: vi.fn(),
|
||||||
|
setPage: vi.fn(),
|
||||||
|
status: "",
|
||||||
|
submitManager: vi.fn(),
|
||||||
|
toggleManager: mocks.toggleManager,
|
||||||
|
...patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function managerFixture() {
|
||||||
|
return {
|
||||||
|
avatar: "",
|
||||||
|
bdLeaderCount: 3,
|
||||||
|
canAddAdmin: true,
|
||||||
|
canAddBdLeader: false,
|
||||||
|
canAddSuperadmin: true,
|
||||||
|
canBlockUser: true,
|
||||||
|
canGrantAvatarFrame: true,
|
||||||
|
canGrantBadge: true,
|
||||||
|
canGrantVehicle: true,
|
||||||
|
canTransferUserCountry: true,
|
||||||
|
canUpdateUserLevel: true,
|
||||||
|
contact: "+63 910 323 3670",
|
||||||
|
displayUserId: "167579",
|
||||||
|
lastInvitedAtMs: 1782198308000,
|
||||||
|
regionId: 1,
|
||||||
|
regionName: "欧亚美区",
|
||||||
|
status: "active",
|
||||||
|
updatedAtMs: 1782284840000,
|
||||||
|
userId: "10001",
|
||||||
|
username: "PH-MANAGER",
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -607,6 +607,7 @@ export interface UpdateManagerPayload {
|
|||||||
canTransferUserCountry?: boolean;
|
canTransferUserCountry?: boolean;
|
||||||
canUpdateUserLevel?: boolean;
|
canUpdateUserLevel?: boolean;
|
||||||
contact?: string;
|
contact?: string;
|
||||||
|
status?: "active" | "disabled";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HostProfileDto {
|
export interface HostProfileDto {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user