经理
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,
|
||||
canTransferUserCountry: false,
|
||||
contact: "+63",
|
||||
status: "disabled",
|
||||
});
|
||||
await listHosts({ agency_id: 41, page: 1, page_size: 10, sort_by: "diamond", sort_direction: "desc" });
|
||||
await listCoinSellers({
|
||||
@ -149,6 +150,7 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
||||
canGrantVehicle: true,
|
||||
canTransferUserCountry: false,
|
||||
contact: "+63",
|
||||
status: "disabled",
|
||||
});
|
||||
expect(String(hostUrl)).toContain("/api/v1/admin/hosts?");
|
||||
expect(String(hostUrl)).toContain("agency_id=41");
|
||||
|
||||
@ -28,6 +28,12 @@ const emptyManagerForm = () => ({ contact: "", targetUserId: "", ...emptyPermiss
|
||||
const managerPermissionPayload = (form) =>
|
||||
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) => ({
|
||||
contact: item?.contact || "",
|
||||
targetUserId: String(item?.userId || ""),
|
||||
@ -139,6 +145,21 @@ export function useHostManagersPage() {
|
||||
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 {
|
||||
abilities,
|
||||
activeAction,
|
||||
@ -165,5 +186,6 @@ export function useHostManagersPage() {
|
||||
setPage,
|
||||
status,
|
||||
submitManager,
|
||||
toggleManager,
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
||||
import { DataState } from "@/shared/ui/DataState.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 { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
||||
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
||||
@ -55,7 +55,7 @@ const managerColumns = [
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
render: (item) => <ManagerStatus item={item} />,
|
||||
render: (item, _index, context) => <ManagerStatusSwitch item={item} page={context?.page} />,
|
||||
width: "minmax(90px, 0.7fr)",
|
||||
},
|
||||
{
|
||||
@ -187,12 +187,14 @@ function ManagerUser({ item }) {
|
||||
);
|
||||
}
|
||||
|
||||
function ManagerStatus({ item }) {
|
||||
const active = item.status === "active";
|
||||
function ManagerStatusSwitch({ item, page }) {
|
||||
return (
|
||||
<span className={active ? styles.positive : styles.negative}>
|
||||
{statusLabels[item.status] || item.status || "-"}
|
||||
</span>
|
||||
<AdminSwitch
|
||||
checked={item.status === "active"}
|
||||
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;
|
||||
canUpdateUserLevel?: boolean;
|
||||
contact?: string;
|
||||
status?: "active" | "disabled";
|
||||
}
|
||||
|
||||
export interface HostProfileDto {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user