即构游戏以及bd相关修复
This commit is contained in:
parent
a95330857c
commit
c08f9e7a83
@ -881,6 +881,28 @@
|
|||||||
"x-permissions": ["agency:status"]
|
"x-permissions": ["agency:status"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/agencies/{agency_id}/status": {
|
||||||
|
"patch": {
|
||||||
|
"operationId": "setAgencyStatus",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/components/responses/EmptyResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "agency_id",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"x-permission": "agency:status",
|
||||||
|
"x-permissions": ["agency:status"]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/agencies/{agency_id}/delete": {
|
"/admin/agencies/{agency_id}/delete": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "deleteAgency",
|
"operationId": "deleteAgency",
|
||||||
@ -5757,6 +5779,9 @@
|
|||||||
"agencyId": {
|
"agencyId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"activeHostCount": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"createdAtMs": {
|
"createdAtMs": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -5849,6 +5874,9 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["userId"],
|
"required": ["userId"],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"agencyCount": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"createdAtMs": {
|
"createdAtMs": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -60,6 +60,7 @@ const writableBridgeAdapterTypes = new Set([
|
|||||||
"baishun_v1",
|
"baishun_v1",
|
||||||
"vivagames_v1",
|
"vivagames_v1",
|
||||||
"reyou_v1",
|
"reyou_v1",
|
||||||
|
"zgame_v1",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const emptyCatalog = { items: [], pageSize: 50 };
|
const emptyCatalog = { items: [], pageSize: 50 };
|
||||||
|
|||||||
@ -51,6 +51,7 @@ const adapterTypeOptions = [
|
|||||||
["baishun_v1", "百顺 BAISHUN V1"],
|
["baishun_v1", "百顺 BAISHUN V1"],
|
||||||
["vivagames_v1", "VIVAGAMES V1"],
|
["vivagames_v1", "VIVAGAMES V1"],
|
||||||
["reyou_v1", "热游 Reyou V1"],
|
["reyou_v1", "热游 Reyou V1"],
|
||||||
|
["zgame_v1", "ZGame V1"],
|
||||||
];
|
];
|
||||||
|
|
||||||
const baseColumns = [
|
const baseColumns = [
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export { listRegions } from "@/shared/api/regions";
|
|||||||
import type {
|
import type {
|
||||||
AgencyDto,
|
AgencyDto,
|
||||||
AgencyJoinEnabledPayload,
|
AgencyJoinEnabledPayload,
|
||||||
|
AgencyStatusPayload,
|
||||||
ApiList,
|
ApiList,
|
||||||
ApiPage,
|
ApiPage,
|
||||||
BDLeaderPositionAliasPayload,
|
BDLeaderPositionAliasPayload,
|
||||||
@ -382,6 +383,17 @@ export function deleteAgency(agencyId: EntityId, payload: HostCommandPayload): P
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setAgencyStatus(agencyId: EntityId, payload: AgencyStatusPayload): Promise<AgencyDto> {
|
||||||
|
const endpoint = API_ENDPOINTS.setAgencyStatus;
|
||||||
|
return apiRequest<AgencyDto, AgencyStatusPayload>(
|
||||||
|
apiEndpointPath(API_OPERATIONS.setAgencyStatus, { agency_id: agencyId }),
|
||||||
|
{
|
||||||
|
body: payload,
|
||||||
|
method: endpoint.method,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function setAgencyJoinEnabled(agencyId: EntityId, payload: AgencyJoinEnabledPayload): Promise<AgencyDto> {
|
export function setAgencyJoinEnabled(agencyId: EntityId, payload: AgencyJoinEnabledPayload): Promise<AgencyDto> {
|
||||||
const endpoint = API_ENDPOINTS.setAgencyJoinEnabled;
|
const endpoint = API_ENDPOINTS.setAgencyJoinEnabled;
|
||||||
return apiRequest<AgencyDto, AgencyJoinEnabledPayload>(
|
return apiRequest<AgencyDto, AgencyJoinEnabledPayload>(
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { parseForm } from "@/shared/forms/validation";
|
|||||||
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
||||||
import { useConfirm } from "@/shared/ui/ConfirmProvider.jsx";
|
import { useConfirm } from "@/shared/ui/ConfirmProvider.jsx";
|
||||||
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
||||||
import { closeAgency, createAgency, deleteAgency, listAgencies, setAgencyJoinEnabled } from "@/features/host-org/api";
|
import { createAgency, deleteAgency, listAgencies, setAgencyJoinEnabled, setAgencyStatus } from "@/features/host-org/api";
|
||||||
import { useRegionOptions } from "@/shared/hooks/useRegionOptions.js";
|
import { useRegionOptions } from "@/shared/hooks/useRegionOptions.js";
|
||||||
import { emptyUserIdentityFilter, userIdentityFilterParams } from "@/shared/ui/userIdentityFilter.js";
|
import { emptyUserIdentityFilter, userIdentityFilterParams } from "@/shared/ui/userIdentityFilter.js";
|
||||||
import { useAgencyAbilities } from "@/features/host-org/permissions.js";
|
import { useAgencyAbilities } from "@/features/host-org/permissions.js";
|
||||||
@ -146,20 +146,32 @@ export function useHostAgenciesPage() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeAgencyFromList = async (agency) => {
|
const toggleAgencyStatusFromList = async (agency, enabled) => {
|
||||||
if (!abilities.canStatus || !agency?.agencyId || agency.status !== "active") {
|
if (!abilities.canStatus || !agency?.agencyId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await runAction(`agency-status-${agency.agencyId}`, "Agency 已关闭", async () => {
|
const nextStatus = enabled ? "active" : "closed";
|
||||||
const data = await closeAgency(agency.agencyId, {
|
if (agency.status === nextStatus || agency.status === "deleted") {
|
||||||
commandId: makeCommandId("agency-close"),
|
return;
|
||||||
reason: "close agency from list switch",
|
}
|
||||||
|
await runAction(`agency-status-${agency.agencyId}`, enabled ? "Agency 已启用" : "Agency 已关闭", async () => {
|
||||||
|
const data = await setAgencyStatus(agency.agencyId, {
|
||||||
|
commandId: makeCommandId("agency-status"),
|
||||||
|
reason: enabled ? "open agency from list switch" : "close agency from list switch",
|
||||||
|
status: nextStatus,
|
||||||
});
|
});
|
||||||
patchAgencyRow(agency.agencyId, agencyActionPatch(data, { status: "closed", joinEnabled: false }));
|
patchAgencyRow(
|
||||||
|
agency.agencyId,
|
||||||
|
agencyActionPatch(data, enabled ? { status: nextStatus } : { status: nextStatus, joinEnabled: false }),
|
||||||
|
);
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeAgencyFromList = async (agency) => {
|
||||||
|
await toggleAgencyStatusFromList(agency, false);
|
||||||
|
};
|
||||||
|
|
||||||
const toggleAgencyJoinEnabledFromList = async (agency, joinEnabled) => {
|
const toggleAgencyJoinEnabledFromList = async (agency, joinEnabled) => {
|
||||||
if (!abilities.canStatus || !agency?.agencyId || agency.status !== "active") {
|
if (!abilities.canStatus || !agency?.agencyId || agency.status !== "active") {
|
||||||
return;
|
return;
|
||||||
@ -248,6 +260,7 @@ export function useHostAgenciesPage() {
|
|||||||
status,
|
status,
|
||||||
submitAgency,
|
submitAgency,
|
||||||
changeSort,
|
changeSort,
|
||||||
|
toggleAgencyStatusFromList,
|
||||||
toggleAgencyJoinEnabledFromList,
|
toggleAgencyJoinEnabledFromList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -258,7 +271,7 @@ function makeCommandId(prefix) {
|
|||||||
|
|
||||||
function agencyActionPatch(data, fallback = {}) {
|
function agencyActionPatch(data, fallback = {}) {
|
||||||
const patch = { ...fallback };
|
const patch = { ...fallback };
|
||||||
for (const key of ["status", "joinEnabled", "updatedAtMs"]) {
|
for (const key of ["activeHostCount", "status", "joinEnabled", "updatedAtMs"]) {
|
||||||
if (data?.[key] !== undefined) {
|
if (data?.[key] !== undefined) {
|
||||||
patch[key] = data[key];
|
patch[key] = data[key];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
||||||
import { listHosts } from "@/features/host-org/api";
|
import { listHosts } from "@/features/host-org/api";
|
||||||
import { useRegionOptions } from "@/shared/hooks/useRegionOptions.js";
|
import { useRegionOptions } from "@/shared/hooks/useRegionOptions.js";
|
||||||
@ -11,10 +12,12 @@ const emptyData = { items: [], page: 1, pageSize, total: 0 };
|
|||||||
export function useHostHostsPage() {
|
export function useHostHostsPage() {
|
||||||
const abilities = useHostAbilities();
|
const abilities = useHostAbilities();
|
||||||
const { loadingRegions, regionOptions } = useRegionOptions();
|
const { loadingRegions, regionOptions } = useRegionOptions();
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const agencyIdParam = searchParams.get("agency_id") || searchParams.get("agencyId") || "";
|
||||||
const [userFilter, setUserFilter] = useState(emptyUserIdentityFilter);
|
const [userFilter, setUserFilter] = useState(emptyUserIdentityFilter);
|
||||||
const [status, setStatus] = useState("");
|
const [status, setStatus] = useState("");
|
||||||
const [regionId, setRegionId] = useState("");
|
const [regionId, setRegionId] = useState("");
|
||||||
const [agencyId, setAgencyId] = useState("");
|
const [agencyId, setAgencyId] = useState(() => agencyIdParam);
|
||||||
const [sortBy, setSortBy] = useState("");
|
const [sortBy, setSortBy] = useState("");
|
||||||
const [sortDirection, setSortDirection] = useState("");
|
const [sortDirection, setSortDirection] = useState("");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
@ -43,6 +46,14 @@ export function useHostHostsPage() {
|
|||||||
queryKey: ["host-org", "hosts", filters, page],
|
queryKey: ["host-org", "hosts", filters, page],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (agencyId === agencyIdParam) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAgencyId(agencyIdParam);
|
||||||
|
setPage(1);
|
||||||
|
}, [agencyId, agencyIdParam]);
|
||||||
|
|
||||||
const changeUserFilter = (value) => {
|
const changeUserFilter = (value) => {
|
||||||
setUserFilter(value);
|
setUserFilter(value);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
@ -58,9 +69,23 @@ export function useHostHostsPage() {
|
|||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const syncAgencyIdSearchParam = (value) => {
|
||||||
|
const nextSearchParams = new URLSearchParams(searchParams);
|
||||||
|
const normalizedValue = String(value || "").trim();
|
||||||
|
if (normalizedValue) {
|
||||||
|
nextSearchParams.set("agency_id", normalizedValue);
|
||||||
|
nextSearchParams.delete("agencyId");
|
||||||
|
} else {
|
||||||
|
nextSearchParams.delete("agency_id");
|
||||||
|
nextSearchParams.delete("agencyId");
|
||||||
|
}
|
||||||
|
setSearchParams(nextSearchParams, { replace: true });
|
||||||
|
};
|
||||||
|
|
||||||
const changeAgencyId = (value) => {
|
const changeAgencyId = (value) => {
|
||||||
setAgencyId(value);
|
setAgencyId(value);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
syncAgencyIdSearchParam(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeSort = (field) => {
|
const changeSort = (field) => {
|
||||||
@ -77,6 +102,7 @@ export function useHostHostsPage() {
|
|||||||
setSortBy("");
|
setSortBy("");
|
||||||
setSortDirection("");
|
setSortDirection("");
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
syncAgencyIdSearchParam("");
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -234,6 +234,29 @@
|
|||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hostCountButton {
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 44px;
|
||||||
|
min-height: 28px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--primary);
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-weight: 760;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostCountButton:hover,
|
||||||
|
.hostCountButton:focus-visible {
|
||||||
|
color: var(--primary-strong);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.inlineAliasText {
|
.inlineAliasText {
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import Add from "@mui/icons-material/Add";
|
|||||||
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
|
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
|
||||||
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import { formatMillis } from "@/shared/utils/time.js";
|
import { formatMillis } from "@/shared/utils/time.js";
|
||||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||||
import { agencyStatusFilters } from "@/features/host-org/constants.js";
|
import { agencyStatusFilters } from "@/features/host-org/constants.js";
|
||||||
@ -29,6 +30,12 @@ const agencyColumns = [
|
|||||||
render: (item) => <AgencyOwner item={item} />,
|
render: (item) => <AgencyOwner item={item} />,
|
||||||
width: "minmax(220px, 1.2fr)",
|
width: "minmax(220px, 1.2fr)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "activeHostCount",
|
||||||
|
label: "子 Host",
|
||||||
|
render: (item, _index, context) => <AgencyHostCount item={item} onOpen={context?.openAgencyHosts} />,
|
||||||
|
width: "minmax(110px, 0.65fr)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "salaryWallet",
|
key: "salaryWallet",
|
||||||
label: "工资钱包",
|
label: "工资钱包",
|
||||||
@ -84,9 +91,16 @@ const agencyColumns = [
|
|||||||
|
|
||||||
export function HostAgenciesPage() {
|
export function HostAgenciesPage() {
|
||||||
const page = useHostAgenciesPage();
|
const page = useHostAgenciesPage();
|
||||||
|
const navigate = useNavigate();
|
||||||
const createDisabled = !page.abilities.canCreate;
|
const createDisabled = !page.abilities.canCreate;
|
||||||
const items = page.data.items || [];
|
const items = page.data.items || [];
|
||||||
const total = page.data.total || 0;
|
const total = page.data.total || 0;
|
||||||
|
const openAgencyHosts = (agency) => {
|
||||||
|
if (!agency?.agencyId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigate(`/host/hosts?agency_id=${encodeURIComponent(String(agency.agencyId))}`);
|
||||||
|
};
|
||||||
const toolbarActions = [
|
const toolbarActions = [
|
||||||
page.abilities.canCreate
|
page.abilities.canCreate
|
||||||
? { icon: <Add fontSize="small" />, label: "创建 Agency", onClick: page.openAgencyForm, variant: "primary" }
|
? { icon: <Add fontSize="small" />, label: "创建 Agency", onClick: page.openAgencyForm, variant: "primary" }
|
||||||
@ -158,9 +172,9 @@ export function HostAgenciesPage() {
|
|||||||
<div className={styles.listBlock}>
|
<div className={styles.listBlock}>
|
||||||
<HostOrgTable
|
<HostOrgTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
context={{ page, regionOptions: page.regionOptions }}
|
context={{ openAgencyHosts, page, regionOptions: page.regionOptions }}
|
||||||
items={items}
|
items={items}
|
||||||
minWidth="1340px"
|
minWidth="1450px"
|
||||||
pagination={
|
pagination={
|
||||||
total > 0
|
total > 0
|
||||||
? {
|
? {
|
||||||
@ -251,18 +265,28 @@ function ParentBD({ item }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AgencyHostCount({ item, onOpen }) {
|
||||||
|
const count = Number(item.activeHostCount ?? item.hostCount ?? 0);
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
aria-label={`查看 Agency ${item.agencyId} 的 Host 列表`}
|
||||||
|
className={styles.hostCountButton}
|
||||||
|
type="button"
|
||||||
|
onClick={() => onOpen?.(item)}
|
||||||
|
>
|
||||||
|
{count.toLocaleString("zh-CN")}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function AgencyStatusSwitch({ item, page }) {
|
function AgencyStatusSwitch({ item, page }) {
|
||||||
const active = item.status === "active";
|
const active = item.status === "active";
|
||||||
return (
|
return (
|
||||||
<AdminSwitch
|
<AdminSwitch
|
||||||
checked={active}
|
checked={active}
|
||||||
disabled={!active || !page?.abilities.canStatus || page.loadingAction === `agency-status-${item.agencyId}`}
|
disabled={!page?.abilities.canStatus || page.loadingAction === `agency-status-${item.agencyId}`}
|
||||||
inputProps={{ "aria-label": active ? "关闭 Agency" : "Agency 已关闭" }}
|
inputProps={{ "aria-label": active ? "关闭 Agency" : "启用 Agency" }}
|
||||||
onChange={(event) => {
|
onChange={(event) => page.toggleAgencyStatusFromList(item, event.target.checked)}
|
||||||
if (!event.target.checked) {
|
|
||||||
page.closeAgencyFromList(item);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,12 @@ const bdColumns = [
|
|||||||
render: (item) => <BDUser item={item} />,
|
render: (item) => <BDUser item={item} />,
|
||||||
width: "minmax(220px, 1.25fr)",
|
width: "minmax(220px, 1.25fr)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "agencyCount",
|
||||||
|
label: "子 Agency",
|
||||||
|
render: (item) => <span className={styles.inlineValue}>{formatCount(item.agencyCount)}</span>,
|
||||||
|
width: "minmax(110px, 0.65fr)",
|
||||||
|
},
|
||||||
{ key: "role", label: "角色", width: "minmax(100px, 0.65fr)" },
|
{ key: "role", label: "角色", width: "minmax(100px, 0.65fr)" },
|
||||||
{
|
{
|
||||||
key: "salaryWallet",
|
key: "salaryWallet",
|
||||||
@ -151,7 +157,7 @@ export function HostBdsPage() {
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
context={{ page, regionOptions: page.regionOptions }}
|
context={{ page, regionOptions: page.regionOptions }}
|
||||||
items={items}
|
items={items}
|
||||||
minWidth="1440px"
|
minWidth="1550px"
|
||||||
pagination={
|
pagination={
|
||||||
total > 0
|
total > 0
|
||||||
? {
|
? {
|
||||||
@ -271,3 +277,7 @@ function BDActions({ item, page }) {
|
|||||||
function bdUserLabel(item) {
|
function bdUserLabel(item) {
|
||||||
return item.displayUserId || item.username || item.userId;
|
return item.displayUserId || item.username || item.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCount(value) {
|
||||||
|
return Number(value || 0).toLocaleString("zh-CN");
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { fireEvent, render, screen } from "@testing-library/react";
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
import { afterEach, expect, test, vi } from "vitest";
|
import { afterEach, expect, test, vi } from "vitest";
|
||||||
|
import { MemoryRouter, useLocation } from "react-router-dom";
|
||||||
import { HostAgenciesPage } from "./HostAgenciesPage.jsx";
|
import { HostAgenciesPage } from "./HostAgenciesPage.jsx";
|
||||||
import { HostBdLeadersPage } from "./HostBdLeadersPage.jsx";
|
import { HostBdLeadersPage } from "./HostBdLeadersPage.jsx";
|
||||||
import { HostBdsPage } from "./HostBdsPage.jsx";
|
import { HostBdsPage } from "./HostBdsPage.jsx";
|
||||||
@ -28,12 +29,37 @@ test("agency list salary wallet header requests salary wallet sorting", () => {
|
|||||||
const changeSort = vi.fn();
|
const changeSort = vi.fn();
|
||||||
vi.mocked(useHostAgenciesPage).mockReturnValue(agencyPageFixture({ changeSort }));
|
vi.mocked(useHostAgenciesPage).mockReturnValue(agencyPageFixture({ changeSort }));
|
||||||
|
|
||||||
render(<HostAgenciesPage />);
|
renderWithRouter(<HostAgenciesPage />);
|
||||||
fireEvent.click(screen.getByLabelText("按工资钱包降序排序"));
|
fireEvent.click(screen.getByLabelText("按工资钱包降序排序"));
|
||||||
|
|
||||||
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("agency host count opens filtered host list", () => {
|
||||||
|
vi.mocked(useHostAgenciesPage).mockReturnValue(agencyPageFixture());
|
||||||
|
|
||||||
|
const location = renderWithRouter(<HostAgenciesPage />);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "查看 Agency 9001 的 Host 列表" }));
|
||||||
|
|
||||||
|
expect(location.current()).toBe("/host/hosts?agency_id=9001");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("closed agency status switch can reopen agency", () => {
|
||||||
|
const toggleAgencyStatusFromList = vi.fn();
|
||||||
|
vi.mocked(useHostAgenciesPage).mockReturnValue(
|
||||||
|
agencyPageFixture({
|
||||||
|
abilities: { canCreate: false, canDelete: false, canSalaryAdjust: false, canStatus: true },
|
||||||
|
data: { items: [{ ...agencyFixture(), joinEnabled: false, status: "closed" }], page: 1, pageSize: 50, total: 1 },
|
||||||
|
toggleAgencyStatusFromList,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
renderWithRouter(<HostAgenciesPage />);
|
||||||
|
fireEvent.click(screen.getByRole("switch", { name: "启用 Agency" }));
|
||||||
|
|
||||||
|
expect(toggleAgencyStatusFromList).toHaveBeenCalledWith(expect.objectContaining({ agencyId: "9001" }), true);
|
||||||
|
});
|
||||||
|
|
||||||
test("bd leader list salary wallet header requests salary wallet sorting", () => {
|
test("bd leader list salary wallet header requests salary wallet sorting", () => {
|
||||||
const changeSort = vi.fn();
|
const changeSort = vi.fn();
|
||||||
vi.mocked(useHostBdsPage).mockReturnValue(bdPageFixture({ changeSort, profileType: "leader" }));
|
vi.mocked(useHostBdsPage).mockReturnValue(bdPageFixture({ changeSort, profileType: "leader" }));
|
||||||
@ -54,6 +80,20 @@ test("bd list salary wallet header requests salary wallet sorting", () => {
|
|||||||
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("bd list renders child agency count", () => {
|
||||||
|
vi.mocked(useHostBdsPage).mockReturnValue(
|
||||||
|
bdPageFixture({
|
||||||
|
data: { items: [{ ...bdFixture(), agencyCount: 7 }], page: 1, pageSize: 50, total: 1 },
|
||||||
|
profileType: "bd",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<HostBdsPage />);
|
||||||
|
|
||||||
|
expect(screen.getByText("子 Agency")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("7")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test("host list salary wallet header requests salary wallet sorting", () => {
|
test("host list salary wallet header requests salary wallet sorting", () => {
|
||||||
const changeSort = vi.fn();
|
const changeSort = vi.fn();
|
||||||
vi.mocked(useHostHostsPage).mockReturnValue(hostPageFixture({ changeSort }));
|
vi.mocked(useHostHostsPage).mockReturnValue(hostPageFixture({ changeSort }));
|
||||||
@ -96,6 +136,7 @@ function agencyPageFixture(patch = {}) {
|
|||||||
sortDirection: "",
|
sortDirection: "",
|
||||||
status: "",
|
status: "",
|
||||||
submitAgency: vi.fn(),
|
submitAgency: vi.fn(),
|
||||||
|
toggleAgencyStatusFromList: vi.fn(),
|
||||||
toggleAgencyJoinEnabledFromList: vi.fn(),
|
toggleAgencyJoinEnabledFromList: vi.fn(),
|
||||||
...patch,
|
...patch,
|
||||||
};
|
};
|
||||||
@ -184,6 +225,7 @@ function hostPageFixture(patch = {}) {
|
|||||||
|
|
||||||
function agencyFixture() {
|
function agencyFixture() {
|
||||||
return {
|
return {
|
||||||
|
activeHostCount: 12,
|
||||||
agencyId: "9001",
|
agencyId: "9001",
|
||||||
joinEnabled: true,
|
joinEnabled: true,
|
||||||
name: "Agency One",
|
name: "Agency One",
|
||||||
@ -200,6 +242,7 @@ function agencyFixture() {
|
|||||||
|
|
||||||
function bdFixture() {
|
function bdFixture() {
|
||||||
return {
|
return {
|
||||||
|
agencyCount: 3,
|
||||||
createdAtMs: 1760000000000,
|
createdAtMs: 1760000000000,
|
||||||
displayUserId: "160002",
|
displayUserId: "160002",
|
||||||
parentLeaderUserId: "3001",
|
parentLeaderUserId: "3001",
|
||||||
@ -229,3 +272,19 @@ function hostFixture() {
|
|||||||
username: "host_user",
|
username: "host_user",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderWithRouter(ui) {
|
||||||
|
const location = { value: "" };
|
||||||
|
function LocationProbe() {
|
||||||
|
const currentLocation = useLocation();
|
||||||
|
location.value = `${currentLocation.pathname}${currentLocation.search}`;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
render(
|
||||||
|
<MemoryRouter initialEntries={["/host/agencies"]}>
|
||||||
|
{ui}
|
||||||
|
<LocationProbe />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
return { current: () => location.value };
|
||||||
|
}
|
||||||
|
|||||||
@ -235,6 +235,7 @@ export const API_OPERATIONS = {
|
|||||||
revokeResourceGrant: "revokeResourceGrant",
|
revokeResourceGrant: "revokeResourceGrant",
|
||||||
search: "search",
|
search: "search",
|
||||||
setAgencyJoinEnabled: "setAgencyJoinEnabled",
|
setAgencyJoinEnabled: "setAgencyJoinEnabled",
|
||||||
|
setAgencyStatus: "setAgencyStatus",
|
||||||
setBDLeaderStatus: "setBDLeaderStatus",
|
setBDLeaderStatus: "setBDLeaderStatus",
|
||||||
setBDStatus: "setBDStatus",
|
setBDStatus: "setBDStatus",
|
||||||
setCoinSellerStatus: "setCoinSellerStatus",
|
setCoinSellerStatus: "setCoinSellerStatus",
|
||||||
@ -1850,6 +1851,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
|||||||
permission: "agency:status",
|
permission: "agency:status",
|
||||||
permissions: ["agency:status"]
|
permissions: ["agency:status"]
|
||||||
},
|
},
|
||||||
|
setAgencyStatus: {
|
||||||
|
method: "PATCH",
|
||||||
|
operationId: API_OPERATIONS.setAgencyStatus,
|
||||||
|
path: "/v1/admin/agencies/{agency_id}/status",
|
||||||
|
permission: "agency:status",
|
||||||
|
permissions: ["agency:status"]
|
||||||
|
},
|
||||||
setBDLeaderStatus: {
|
setBDLeaderStatus: {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
operationId: API_OPERATIONS.setBDLeaderStatus,
|
operationId: API_OPERATIONS.setBDLeaderStatus,
|
||||||
|
|||||||
32
src/shared/api/generated/schema.d.ts
vendored
32
src/shared/api/generated/schema.d.ts
vendored
@ -708,6 +708,22 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/admin/agencies/{agency_id}/status": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch: operations["setAgencyStatus"];
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/admin/agencies/{agency_id}/delete": {
|
"/admin/agencies/{agency_id}/delete": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@ -3479,6 +3495,7 @@ export interface components {
|
|||||||
};
|
};
|
||||||
Agency: {
|
Agency: {
|
||||||
agencyId: number;
|
agencyId: number;
|
||||||
|
activeHostCount?: number;
|
||||||
createdAtMs?: number;
|
createdAtMs?: number;
|
||||||
createdByUserId?: number;
|
createdByUserId?: number;
|
||||||
joinEnabled?: boolean;
|
joinEnabled?: boolean;
|
||||||
@ -3510,6 +3527,7 @@ export interface components {
|
|||||||
targetUserId: string;
|
targetUserId: string;
|
||||||
};
|
};
|
||||||
BDProfile: {
|
BDProfile: {
|
||||||
|
agencyCount?: number;
|
||||||
createdAtMs?: number;
|
createdAtMs?: number;
|
||||||
createdByUserId?: number;
|
createdByUserId?: number;
|
||||||
parentLeaderUserId?: string;
|
parentLeaderUserId?: string;
|
||||||
@ -4706,6 +4724,20 @@ export interface operations {
|
|||||||
200: components["responses"]["EmptyResponse"];
|
200: components["responses"]["EmptyResponse"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
setAgencyStatus: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
agency_id: number;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
200: components["responses"]["EmptyResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
deleteAgency: {
|
deleteAgency: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|||||||
@ -568,6 +568,7 @@ export interface AppVersionPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface BDProfileDto {
|
export interface BDProfileDto {
|
||||||
|
agencyCount?: number;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
createdAtMs?: number;
|
createdAtMs?: number;
|
||||||
createdByAvatar?: string;
|
createdByAvatar?: string;
|
||||||
@ -594,6 +595,7 @@ export interface BDProfileDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AgencyDto {
|
export interface AgencyDto {
|
||||||
|
activeHostCount?: number;
|
||||||
agencyId: number;
|
agencyId: number;
|
||||||
createdAtMs?: number;
|
createdAtMs?: number;
|
||||||
createdByUserId?: number;
|
createdByUserId?: number;
|
||||||
@ -1488,6 +1490,10 @@ export interface AgencyJoinEnabledPayload extends HostCommandPayload {
|
|||||||
joinEnabled: boolean;
|
joinEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AgencyStatusPayload extends HostCommandPayload {
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CountryDto {
|
export interface CountryDto {
|
||||||
countryCode: string;
|
countryCode: string;
|
||||||
countryDisplayName?: string;
|
countryDisplayName?: string;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user