撤销资源组
This commit is contained in:
parent
b6d8af0fa3
commit
bc9484c60d
@ -2857,6 +2857,28 @@
|
|||||||
"x-permissions": ["resource-grant:create"]
|
"x-permissions": ["resource-grant:create"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/resource-grants/{grant_id}/revoke": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "revokeResourceGrant",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "grant_id",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/components/responses/EmptyResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-permission": "resource-grant:revoke",
|
||||||
|
"x-permissions": ["resource-grant:revoke"]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/resource-grants/resource": {
|
"/admin/resource-grants/resource": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "grantResource",
|
"operationId": "grantResource",
|
||||||
|
|||||||
@ -120,6 +120,7 @@ export const PERMISSIONS = {
|
|||||||
resourceGroupUpdate: "resource-group:update",
|
resourceGroupUpdate: "resource-group:update",
|
||||||
resourceGrantView: "resource-grant:view",
|
resourceGrantView: "resource-grant:view",
|
||||||
resourceGrantCreate: "resource-grant:create",
|
resourceGrantCreate: "resource-grant:create",
|
||||||
|
resourceGrantRevoke: "resource-grant:revoke",
|
||||||
giftView: "gift:view",
|
giftView: "gift:view",
|
||||||
giftCreate: "gift:create",
|
giftCreate: "gift:create",
|
||||||
giftUpdate: "gift:update",
|
giftUpdate: "gift:update",
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import {
|
|||||||
listResourceShopPurchaseOrders,
|
listResourceShopPurchaseOrders,
|
||||||
listResources,
|
listResources,
|
||||||
lookupResourceGrantTarget,
|
lookupResourceGrantTarget,
|
||||||
|
revokeResourceGrant,
|
||||||
updateGift,
|
updateGift,
|
||||||
updateGiftTypes,
|
updateGiftTypes,
|
||||||
updateResource,
|
updateResource,
|
||||||
@ -171,6 +172,7 @@ test("resource APIs use generated admin paths", async () => {
|
|||||||
reason: "manual",
|
reason: "manual",
|
||||||
targetUserId: "1001",
|
targetUserId: "1001",
|
||||||
});
|
});
|
||||||
|
await revokeResourceGrant("rgr_test");
|
||||||
|
|
||||||
const [listUrl, listInit] = vi.mocked(fetch).mock.calls[0];
|
const [listUrl, listInit] = vi.mocked(fetch).mock.calls[0];
|
||||||
const [createUrl, createInit] = vi.mocked(fetch).mock.calls[1];
|
const [createUrl, createInit] = vi.mocked(fetch).mock.calls[1];
|
||||||
@ -191,6 +193,7 @@ test("resource APIs use generated admin paths", async () => {
|
|||||||
const [grantTargetUrl, grantTargetInit] = vi.mocked(fetch).mock.calls[16];
|
const [grantTargetUrl, grantTargetInit] = vi.mocked(fetch).mock.calls[16];
|
||||||
const [grantResourceUrl, grantResourceInit] = vi.mocked(fetch).mock.calls[17];
|
const [grantResourceUrl, grantResourceInit] = vi.mocked(fetch).mock.calls[17];
|
||||||
const [grantGroupUrl, grantGroupInit] = vi.mocked(fetch).mock.calls[18];
|
const [grantGroupUrl, grantGroupInit] = vi.mocked(fetch).mock.calls[18];
|
||||||
|
const [revokeGrantUrl, revokeGrantInit] = vi.mocked(fetch).mock.calls[19];
|
||||||
|
|
||||||
expect(String(listUrl)).toContain("/api/v1/admin/resources?");
|
expect(String(listUrl)).toContain("/api/v1/admin/resources?");
|
||||||
expect(String(listUrl)).toContain("resource_type=gift");
|
expect(String(listUrl)).toContain("resource_type=gift");
|
||||||
@ -262,6 +265,8 @@ test("resource APIs use generated admin paths", async () => {
|
|||||||
expect(String(grantGroupUrl)).toContain("/api/v1/admin/resource-grants/group");
|
expect(String(grantGroupUrl)).toContain("/api/v1/admin/resource-grants/group");
|
||||||
expect(grantGroupInit?.method).toBe("POST");
|
expect(grantGroupInit?.method).toBe("POST");
|
||||||
expect(JSON.parse(String(grantGroupInit?.body))).toMatchObject({ groupId: 22, targetUserId: "1001" });
|
expect(JSON.parse(String(grantGroupInit?.body))).toMatchObject({ groupId: 22, targetUserId: "1001" });
|
||||||
|
expect(String(revokeGrantUrl)).toContain("/api/v1/admin/resource-grants/rgr_test/revoke");
|
||||||
|
expect(revokeGrantInit?.method).toBe("POST");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("resource identity auto grant config APIs use generated admin paths", async () => {
|
test("resource identity auto grant config APIs use generated admin paths", async () => {
|
||||||
|
|||||||
@ -278,6 +278,9 @@ export interface ResourceGrantDto {
|
|||||||
items?: ResourceGrantItemDto[];
|
items?: ResourceGrantItemDto[];
|
||||||
operatorUserId?: number;
|
operatorUserId?: number;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
|
revokedAtMs?: number;
|
||||||
|
revokedByUserId?: string;
|
||||||
|
revokeReason?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
targetUser?: ResourceGrantUserDto;
|
targetUser?: ResourceGrantUserDto;
|
||||||
targetUserId?: string;
|
targetUserId?: string;
|
||||||
@ -620,6 +623,13 @@ export function grantResourceGroup(payload: GrantResourceGroupPayload): Promise<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function revokeResourceGrant(grantId: string): Promise<ResourceGrantDto> {
|
||||||
|
const endpoint = API_ENDPOINTS.revokeResourceGrant;
|
||||||
|
return apiRequest<ResourceGrantDto>(apiEndpointPath(API_OPERATIONS.revokeResourceGrant, { grant_id: grantId }), {
|
||||||
|
method: endpoint.method,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
type RawResourceIdentityAutoGrantItem = ResourceIdentityAutoGrantItemDto & {
|
type RawResourceIdentityAutoGrantItem = ResourceIdentityAutoGrantItemDto & {
|
||||||
created_at_ms?: unknown;
|
created_at_ms?: unknown;
|
||||||
identity_type?: unknown;
|
identity_type?: unknown;
|
||||||
|
|||||||
@ -33,10 +33,12 @@ export const cpRelationTypeLabels = Object.fromEntries(cpRelationTypeOptions);
|
|||||||
export const resourceGrantStatusFilters = [
|
export const resourceGrantStatusFilters = [
|
||||||
["", "全部状态"],
|
["", "全部状态"],
|
||||||
["succeeded", "成功"],
|
["succeeded", "成功"],
|
||||||
|
["revoked", "已撤销"],
|
||||||
];
|
];
|
||||||
|
|
||||||
export const resourceGrantStatusLabels = {
|
export const resourceGrantStatusLabels = {
|
||||||
succeeded: "成功",
|
succeeded: "成功",
|
||||||
|
revoked: "已撤销",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resourceGrantSubjectLabels = {
|
export const resourceGrantSubjectLabels = {
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import {
|
|||||||
listResourceShopPurchaseOrders,
|
listResourceShopPurchaseOrders,
|
||||||
listResources,
|
listResources,
|
||||||
lookupResourceGrantTarget,
|
lookupResourceGrantTarget,
|
||||||
|
revokeResourceGrant,
|
||||||
updateGift,
|
updateGift,
|
||||||
updateGiftTypes,
|
updateGiftTypes,
|
||||||
updateResource,
|
updateResource,
|
||||||
@ -1539,6 +1540,7 @@ export function useEmojiPackListPage() {
|
|||||||
|
|
||||||
export function useResourceGrantListPage() {
|
export function useResourceGrantListPage() {
|
||||||
const abilities = useResourceAbilities();
|
const abilities = useResourceAbilities();
|
||||||
|
const confirm = useConfirm();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [status, setStatus] = useState("");
|
const [status, setStatus] = useState("");
|
||||||
@ -1644,6 +1646,36 @@ export function useResourceGrantListPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const revokeGrant = async (grant) => {
|
||||||
|
if (
|
||||||
|
!abilities.canRevokeGrant ||
|
||||||
|
!grant?.grantId ||
|
||||||
|
grant.status !== "succeeded" ||
|
||||||
|
grant.grantSubjectType !== "resource_group"
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const accepted = await confirm({
|
||||||
|
title: "撤销资源组赠送",
|
||||||
|
message: "撤销后会扣回金币并移除资源组内装扮。该操作不可自动恢复。",
|
||||||
|
confirmText: "撤销",
|
||||||
|
tone: "danger",
|
||||||
|
});
|
||||||
|
if (!accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoadingAction(`grant-revoke-${grant.grantId}`);
|
||||||
|
try {
|
||||||
|
await revokeResourceGrant(grant.grantId);
|
||||||
|
showToast("资源组赠送已撤销", "success");
|
||||||
|
await result.reload();
|
||||||
|
} catch (err) {
|
||||||
|
showToast(err.message || "撤销失败", "error");
|
||||||
|
} finally {
|
||||||
|
setLoadingAction("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const resetFilters = () => {
|
const resetFilters = () => {
|
||||||
setQuery("");
|
setQuery("");
|
||||||
setStatus("");
|
setStatus("");
|
||||||
@ -1665,6 +1697,7 @@ export function useResourceGrantListPage() {
|
|||||||
optionsLoading,
|
optionsLoading,
|
||||||
resourceOptions,
|
resourceOptions,
|
||||||
resetFilters,
|
resetFilters,
|
||||||
|
revokeGrant,
|
||||||
setForm,
|
setForm,
|
||||||
status,
|
status,
|
||||||
submitGrant,
|
submitGrant,
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
import Add from "@mui/icons-material/Add";
|
import Add from "@mui/icons-material/Add";
|
||||||
|
import UndoOutlined from "@mui/icons-material/UndoOutlined";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import { AdminFormDialog, AdminFormFieldGrid, AdminFormSection } from "@/shared/ui/AdminFormDialog.jsx";
|
import { AdminFormDialog, AdminFormFieldGrid, AdminFormSection } from "@/shared/ui/AdminFormDialog.jsx";
|
||||||
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
|
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
|
||||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||||
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
||||||
import { AdminActionIconButton, AdminListBody, AdminListPage, AdminListToolbar } from "@/shared/ui/AdminListLayout.jsx";
|
import {
|
||||||
|
AdminActionIconButton,
|
||||||
|
AdminListBody,
|
||||||
|
AdminListPage,
|
||||||
|
AdminListToolbar,
|
||||||
|
AdminRowActions,
|
||||||
|
} from "@/shared/ui/AdminListLayout.jsx";
|
||||||
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
||||||
import { formatMillis } from "@/shared/utils/time.js";
|
import { formatMillis } from "@/shared/utils/time.js";
|
||||||
import { GiftResourceSelectField } from "@/features/resources/components/GiftResourceSelectDrawer.jsx";
|
import { GiftResourceSelectField } from "@/features/resources/components/GiftResourceSelectDrawer.jsx";
|
||||||
@ -63,7 +70,7 @@ const grantColumns = [
|
|||||||
label: "状态",
|
label: "状态",
|
||||||
width: "minmax(100px, 0.55fr)",
|
width: "minmax(100px, 0.55fr)",
|
||||||
render: (grant) => (
|
render: (grant) => (
|
||||||
<span className={`status-badge status-badge--${grant.status || "stopped"}`}>
|
<span className={`status-badge status-badge--${grantStatusTone(grant.status)}`}>
|
||||||
<span className="status-point" />
|
<span className="status-point" />
|
||||||
{resourceGrantStatusLabels[grant.status] || grant.status || "-"}
|
{resourceGrantStatusLabels[grant.status] || grant.status || "-"}
|
||||||
</span>
|
</span>
|
||||||
@ -87,30 +94,38 @@ export function ResourceGrantListPage() {
|
|||||||
const page = useResourceGrantListPage();
|
const page = useResourceGrantListPage();
|
||||||
const items = page.data.items || [];
|
const items = page.data.items || [];
|
||||||
const total = page.data.total || 0;
|
const total = page.data.total || 0;
|
||||||
const columns = grantColumns.map((column) => {
|
const columns = [
|
||||||
if (column.key === "target") {
|
...grantColumns.map((column) => {
|
||||||
return {
|
if (column.key === "target") {
|
||||||
...column,
|
return {
|
||||||
filter: createTextColumnFilter({
|
...column,
|
||||||
placeholder: "搜索用户 ID",
|
filter: createTextColumnFilter({
|
||||||
value: page.query,
|
placeholder: "搜索用户 ID",
|
||||||
onChange: page.changeQuery,
|
value: page.query,
|
||||||
}),
|
onChange: page.changeQuery,
|
||||||
};
|
}),
|
||||||
}
|
};
|
||||||
if (column.key === "status") {
|
}
|
||||||
return {
|
if (column.key === "status") {
|
||||||
...column,
|
return {
|
||||||
filter: createOptionsColumnFilter({
|
...column,
|
||||||
options: resourceGrantStatusFilters,
|
filter: createOptionsColumnFilter({
|
||||||
placeholder: "搜索状态",
|
options: resourceGrantStatusFilters,
|
||||||
value: page.status,
|
placeholder: "搜索状态",
|
||||||
onChange: page.changeStatus,
|
value: page.status,
|
||||||
}),
|
onChange: page.changeStatus,
|
||||||
};
|
}),
|
||||||
}
|
};
|
||||||
return column;
|
}
|
||||||
});
|
return column;
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: "actions",
|
||||||
|
label: "操作",
|
||||||
|
width: "minmax(88px, 0.45fr)",
|
||||||
|
render: (grant) => <ResourceGrantActions grant={grant} page={page} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminListPage>
|
<AdminListPage>
|
||||||
@ -128,7 +143,7 @@ export function ResourceGrantListPage() {
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
items={items}
|
items={items}
|
||||||
minWidth="1370px"
|
minWidth="1460px"
|
||||||
pagination={
|
pagination={
|
||||||
total > 0
|
total > 0
|
||||||
? {
|
? {
|
||||||
@ -161,6 +176,28 @@ export function ResourceGrantListPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ResourceGrantActions({ grant, page }) {
|
||||||
|
const canRevoke =
|
||||||
|
page.abilities.canRevokeGrant && grant?.status === "succeeded" && grant?.grantSubjectType === "resource_group";
|
||||||
|
if (!canRevoke) {
|
||||||
|
return <span className={styles.meta}>-</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<AdminRowActions>
|
||||||
|
<AdminActionIconButton
|
||||||
|
aria-label="撤销"
|
||||||
|
disabled={page.loadingAction === `grant-revoke-${grant.grantId}`}
|
||||||
|
label="撤销"
|
||||||
|
sx={dangerActionSx}
|
||||||
|
tooltip="撤销资源组赠送"
|
||||||
|
onClick={() => page.revokeGrant(grant)}
|
||||||
|
>
|
||||||
|
<UndoOutlined fontSize="small" />
|
||||||
|
</AdminActionIconButton>
|
||||||
|
</AdminRowActions>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ResourceGrantDialog({
|
function ResourceGrantDialog({
|
||||||
disabled,
|
disabled,
|
||||||
form,
|
form,
|
||||||
@ -281,6 +318,29 @@ function ResourceGrantDialog({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function grantStatusTone(status) {
|
||||||
|
if (status === "revoked") {
|
||||||
|
return "stopped";
|
||||||
|
}
|
||||||
|
if (status === "failed") {
|
||||||
|
return "danger";
|
||||||
|
}
|
||||||
|
if (status === "pending") {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
return status || "stopped";
|
||||||
|
}
|
||||||
|
|
||||||
|
const dangerActionSx = {
|
||||||
|
borderColor: "var(--danger-border)",
|
||||||
|
color: "var(--danger)",
|
||||||
|
"&:hover": {
|
||||||
|
borderColor: "var(--danger-border-strong)",
|
||||||
|
backgroundColor: "var(--danger-surface)",
|
||||||
|
color: "var(--danger)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function GrantItems({ grant }) {
|
function GrantItems({ grant }) {
|
||||||
const items = grant.items || [];
|
const items = grant.items || [];
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
|
|||||||
124
src/features/resources/pages/ResourceGrantListPage.test.jsx
Normal file
124
src/features/resources/pages/ResourceGrantListPage.test.jsx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
|
import { afterEach, expect, test, vi } from "vitest";
|
||||||
|
import { ResourceGrantListPage } from "./ResourceGrantListPage.jsx";
|
||||||
|
import { useResourceGrantListPage } from "@/features/resources/hooks/useResourcePages.js";
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
revokeGrant: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@/features/resources/hooks/useResourcePages.js", () => ({
|
||||||
|
useResourceGrantListPage: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("resource grant list shows revoke only for succeeded resource group grants", () => {
|
||||||
|
const eligible = grantFixture({
|
||||||
|
grantId: "rgr_group_ok",
|
||||||
|
grantSubjectType: "resource_group",
|
||||||
|
status: "succeeded",
|
||||||
|
});
|
||||||
|
vi.mocked(useResourceGrantListPage).mockReturnValue(
|
||||||
|
pageFixture({
|
||||||
|
data: {
|
||||||
|
items: [
|
||||||
|
eligible,
|
||||||
|
grantFixture({ grantId: "rgr_resource_ok", grantSubjectType: "resource", status: "succeeded" }),
|
||||||
|
grantFixture({ grantId: "rgr_group_revoked", grantSubjectType: "resource_group", status: "revoked" }),
|
||||||
|
],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 50,
|
||||||
|
total: 3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<ResourceGrantListPage />);
|
||||||
|
|
||||||
|
expect(screen.getByText("已撤销")).toBeInTheDocument();
|
||||||
|
const revokeButtons = screen.getAllByLabelText("撤销");
|
||||||
|
expect(revokeButtons).toHaveLength(1);
|
||||||
|
|
||||||
|
fireEvent.click(revokeButtons[0]);
|
||||||
|
|
||||||
|
expect(mocks.revokeGrant).toHaveBeenCalledWith(eligible);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("resource grant list hides revoke without permission", () => {
|
||||||
|
vi.mocked(useResourceGrantListPage).mockReturnValue(
|
||||||
|
pageFixture({
|
||||||
|
abilities: { canCreateGrant: false, canRevokeGrant: false },
|
||||||
|
data: {
|
||||||
|
items: [grantFixture({ grantSubjectType: "resource_group", status: "succeeded" })],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 50,
|
||||||
|
total: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<ResourceGrantListPage />);
|
||||||
|
|
||||||
|
expect(screen.queryByLabelText("撤销")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
function pageFixture(patch = {}) {
|
||||||
|
return {
|
||||||
|
abilities: {
|
||||||
|
canCreateGrant: true,
|
||||||
|
canRevokeGrant: true,
|
||||||
|
},
|
||||||
|
activeAction: "",
|
||||||
|
changeQuery: vi.fn(),
|
||||||
|
changeStatus: vi.fn(),
|
||||||
|
closeAction: vi.fn(),
|
||||||
|
data: { items: [], page: 1, pageSize: 50, total: 0 },
|
||||||
|
error: null,
|
||||||
|
form: {
|
||||||
|
durationDays: "0",
|
||||||
|
giftIds: [],
|
||||||
|
groupId: "",
|
||||||
|
reason: "",
|
||||||
|
resourceIds: [],
|
||||||
|
subjectType: "resource",
|
||||||
|
targetUserId: "",
|
||||||
|
},
|
||||||
|
giftOptions: [],
|
||||||
|
giftTypeOptions: [],
|
||||||
|
groupOptions: [],
|
||||||
|
loading: false,
|
||||||
|
loadingAction: "",
|
||||||
|
openCreateGrant: vi.fn(),
|
||||||
|
optionsLoading: false,
|
||||||
|
page: 1,
|
||||||
|
query: "",
|
||||||
|
reload: vi.fn(),
|
||||||
|
resetFilters: vi.fn(),
|
||||||
|
resourceOptions: [],
|
||||||
|
revokeGrant: mocks.revokeGrant,
|
||||||
|
setForm: vi.fn(),
|
||||||
|
setPage: vi.fn(),
|
||||||
|
status: "",
|
||||||
|
submitGrant: vi.fn(),
|
||||||
|
...patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function grantFixture(patch = {}) {
|
||||||
|
return {
|
||||||
|
commandId: "resource-group-grant-test",
|
||||||
|
createdAtMs: 1782287714973,
|
||||||
|
grantId: "rgr_test",
|
||||||
|
grantSubjectId: "22",
|
||||||
|
grantSubjectType: "resource_group",
|
||||||
|
items: [{ grantItemId: 1, quantity: 100, resourceId: 0 }],
|
||||||
|
operatorUserId: 7,
|
||||||
|
reason: "manual",
|
||||||
|
status: "succeeded",
|
||||||
|
targetUserId: "318705991371722752",
|
||||||
|
...patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ export function useResourceAbilities() {
|
|||||||
canCreateGift: can(PERMISSIONS.giftCreate),
|
canCreateGift: can(PERMISSIONS.giftCreate),
|
||||||
canCreateGrant: can(PERMISSIONS.resourceGrantCreate),
|
canCreateGrant: can(PERMISSIONS.resourceGrantCreate),
|
||||||
canCreateGroup: can(PERMISSIONS.resourceGroupCreate),
|
canCreateGroup: can(PERMISSIONS.resourceGroupCreate),
|
||||||
|
canRevokeGrant: can(PERMISSIONS.resourceGrantRevoke),
|
||||||
canCreateEmojiPack: can(PERMISSIONS.emojiPackCreate),
|
canCreateEmojiPack: can(PERMISSIONS.emojiPackCreate),
|
||||||
canDeleteGift: can(PERMISSIONS.giftDelete),
|
canDeleteGift: can(PERMISSIONS.giftDelete),
|
||||||
canStatusGift: can(PERMISSIONS.giftStatus),
|
canStatusGift: can(PERMISSIONS.giftStatus),
|
||||||
|
|||||||
@ -221,6 +221,7 @@ export const API_OPERATIONS = {
|
|||||||
retryRedPacketRefund: "retryRedPacketRefund",
|
retryRedPacketRefund: "retryRedPacketRefund",
|
||||||
retryRoomRpsSettlement: "retryRoomRpsSettlement",
|
retryRoomRpsSettlement: "retryRoomRpsSettlement",
|
||||||
retryRoomTurnoverRewardSettlement: "retryRoomTurnoverRewardSettlement",
|
retryRoomTurnoverRewardSettlement: "retryRoomTurnoverRewardSettlement",
|
||||||
|
revokeResourceGrant: "revokeResourceGrant",
|
||||||
search: "search",
|
search: "search",
|
||||||
setAgencyJoinEnabled: "setAgencyJoinEnabled",
|
setAgencyJoinEnabled: "setAgencyJoinEnabled",
|
||||||
setBDLeaderStatus: "setBDLeaderStatus",
|
setBDLeaderStatus: "setBDLeaderStatus",
|
||||||
@ -1739,6 +1740,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
|||||||
permission: "room-turnover-reward:retry",
|
permission: "room-turnover-reward:retry",
|
||||||
permissions: ["room-turnover-reward:retry"]
|
permissions: ["room-turnover-reward:retry"]
|
||||||
},
|
},
|
||||||
|
revokeResourceGrant: {
|
||||||
|
method: "POST",
|
||||||
|
operationId: API_OPERATIONS.revokeResourceGrant,
|
||||||
|
path: "/v1/admin/resource-grants/{grant_id}/revoke",
|
||||||
|
permission: "resource-grant:revoke",
|
||||||
|
permissions: ["resource-grant:revoke"]
|
||||||
|
},
|
||||||
search: {
|
search: {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
operationId: API_OPERATIONS.search,
|
operationId: API_OPERATIONS.search,
|
||||||
|
|||||||
30
src/shared/api/generated/schema.d.ts
vendored
30
src/shared/api/generated/schema.d.ts
vendored
@ -1988,6 +1988,22 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/admin/resource-grants/{grant_id}/revoke": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post: operations["revokeResourceGrant"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/admin/resource-grants/resource": {
|
"/admin/resource-grants/resource": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@ -6079,6 +6095,20 @@ export interface operations {
|
|||||||
200: components["responses"]["EmptyResponse"];
|
200: components["responses"]["EmptyResponse"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
revokeResourceGrant: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
grant_id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
200: components["responses"]["EmptyResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
grantResource: {
|
grantResource: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user