import Add from "@mui/icons-material/Add";
import Inventory2Outlined from "@mui/icons-material/Inventory2Outlined";
import UndoOutlined from "@mui/icons-material/UndoOutlined";
import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField";
import { AdminFormDialog, AdminFormFieldGrid, AdminFormSection } from "@/shared/ui/AdminFormDialog.jsx";
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
import { DataState } from "@/shared/ui/DataState.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx";
import {
AdminActionIconButton,
AdminListBody,
AdminListPage,
AdminListToolbar,
AdminRowActions,
} from "@/shared/ui/AdminListLayout.jsx";
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
import { formatMillis } from "@/shared/utils/time.js";
import { GiftResourceSelectField } from "@/features/resources/components/GiftResourceSelectDrawer.jsx";
import {
resourceGroupAssetLabels,
resourceGrantStatusFilters,
resourceGrantStatusLabels,
resourceGrantSubjectLabels,
resourceTypeLabels,
} from "@/features/resources/constants.js";
import { useResourceGrantListPage } from "@/features/resources/hooks/useResourcePages.js";
import styles from "@/features/resources/resources.module.css";
const grantColumns = [
{
key: "grant",
label: "来源",
width: "minmax(220px, 1fr)",
render: (grant) => (
{grantSourceLabel(grant.grantSource)}
),
},
{
key: "target",
label: "用户信息",
width: "minmax(240px, 1.1fr)",
render: (grant) => ,
},
{
key: "operator",
label: "操作人",
width: "minmax(190px, 0.9fr)",
render: (grant) => ,
},
{
key: "subject",
label: "发放方式",
width: "minmax(170px, 0.75fr)",
render: (grant) => ,
},
{
key: "items",
label: "发放内容",
width: "minmax(320px, 1.5fr)",
render: (grant) => ,
},
{
key: "status",
label: "状态",
width: "minmax(100px, 0.55fr)",
render: (grant) => (
{resourceGrantStatusLabels[grant.status] || grant.status || "-"}
),
},
{
key: "reason",
label: "原因",
width: "minmax(190px, 0.95fr)",
render: (grant) => ,
},
{
key: "time",
label: "创建时间",
width: "minmax(170px, 0.9fr)",
render: (grant) => formatMillis(grant.createdAtMs),
},
];
export function ResourceGrantListPage() {
const page = useResourceGrantListPage();
const items = page.data.items || [];
const total = page.data.total || 0;
const columns = [
...grantColumns.map((column) => {
if (column.key === "target") {
return {
...column,
filter: createTextColumnFilter({
placeholder: "搜索用户 ID",
value: page.query,
onChange: page.changeQuery,
}),
};
}
if (column.key === "status") {
return {
...column,
filter: createOptionsColumnFilter({
options: resourceGrantStatusFilters,
placeholder: "搜索状态",
value: page.status,
onChange: page.changeStatus,
}),
};
}
return column;
}),
{
key: "actions",
label: "操作",
width: "minmax(88px, 0.45fr)",
render: (grant) => ,
},
];
return (
) : null
}
/>
0
? {
page: page.page,
pageSize: page.data.pageSize || 50,
total,
onPageChange: page.setPage,
}
: undefined
}
rowKey={(grant) => grant.grantId}
/>
);
}
function ResourceGrantActions({ grant, page }) {
const canRevoke =
page.abilities.canRevokeGrant && grant?.status === "succeeded" && grant?.grantSubjectType === "resource_group";
if (!canRevoke) {
return -;
}
return (
page.revokeGrant(grant)}
>
);
}
function ResourceGrantDialog({
disabled,
form,
giftOptions,
giftTypeOptions,
groupOptions,
loading,
onClose,
onSubmit,
open,
optionsLoading,
resourceOptions,
setForm,
}) {
const submitDisabled = disabled || loading || optionsLoading;
return (
setForm({ ...form, targetUserId: event.target.value })}
/>
setForm({
...form,
groupId: "",
resourceId: "",
resourceIds: [],
subjectType: event.target.value,
})
}
>
{form.subjectType === "resource" ? (
<>
setForm({ ...form, resourceId: resourceIds[0] || "", resourceIds })
}
/>
setForm({ ...form, quantity: event.target.value })}
/>
setForm({ ...form, durationDays: event.target.value })}
/>
>
) : (
setForm({ ...form, groupId: event.target.value })}
>
{groupOptions.map((group) => (
))}
)}
setForm({ ...form, reason: event.target.value })}
/>
);
}
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 }) {
const items = grant.items || [];
if (!items.length) {
return "-";
}
return (
{items.map((item) => (
))}
);
}
function GrantItemMedia({ item }) {
const snapshot = parseGrantItemSnapshot(item.resourceSnapshotJson);
const walletAssetType = snapshotValue(snapshot, "walletAssetType", "wallet_asset_type", "WalletAssetType");
if (walletAssetType) {
const label = resourceGroupAssetLabels[walletAssetType] || walletAssetType;
const amount = snapshotValue(snapshot, "walletAssetAmount", "wallet_asset_amount", "WalletAssetAmount", "quantity");
return {`${label} ${formatNumber(amount || item.quantity)}`};
}
const label = grantItemLabel(item, snapshot);
const imageUrl = grantItemImageUrl(snapshot);
return (
{imageUrl ?
: }
{label}
);
}
function GrantSubject({ grant }) {
const subjectLabel = resourceGrantSubjectLabels[grant.grantSubjectType] || grant.grantSubjectType || "发放";
const sourceLabel = grantSourceLabel(grant.grantSource);
return (
{sourceLabel === "-" ? subjectLabel : `${subjectLabel} · ${sourceLabel}`}
{subjectMeta(grant)}
);
}
function GrantReason({ grant }) {
const label = reasonLabel(grant.reason);
const raw = String(grant.reason || "").trim();
return (
{label}
{raw && raw !== label ? {raw} : null}
);
}
function GrantTargetUser({ grant }) {
const user = grant.targetUser || grant.target_user || grant.user || {};
const userId = user.userId || user.user_id || grant.targetUserId;
return (
);
}
function GrantOperator({ grant }) {
const operator = grant.operator || {};
const source = operator.source || grant.grantSource || "";
const operatorUserId = operator.userId || grant.operatorUserId;
if (source === "manager_center") {
return (
);
}
return "-";
}
const grantSourceLabels = {
achievement: "成就奖励",
admin: "后台发放",
agency_opening: "Agency 开通",
cp_weekly_rank: "CP 周榜奖励",
cumulative_recharge_reward: "累充奖励",
first_recharge_reward: "首充奖励",
game_robot_init: "机器人初始化",
growth_level: "成长等级奖励",
manager_center: "经理中心",
registration_reward: "注册奖励",
resource_shop: "道具商店",
room_rocket: "房间火箭",
seven_day_checkin: "七日签到",
weekly_star: "周星奖励",
wheel_reward: "转盘奖励",
};
const grantReasonLabels = {
...grantSourceLabels,
growth_level_registration_badge: "注册成长等级徽章",
"resource shop purchase": "道具商店购买",
};
function grantSourceLabel(source) {
const key = String(source || "").trim();
return grantSourceLabels[key] || key || "-";
}
function reasonLabel(reason) {
const key = String(reason || "").trim();
return grantReasonLabels[key] || key || "-";
}
function subjectMeta(grant) {
const subjectLabel = resourceGrantSubjectLabels[grant.grantSubjectType] || grant.grantSubjectType || "对象";
return grant.grantSubjectId ? `${subjectLabel} ID ${grant.grantSubjectId}` : "-";
}
function grantItemLabel(item, snapshot = parseGrantItemSnapshot(item.resourceSnapshotJson)) {
const walletAssetType = snapshotValue(snapshot, "walletAssetType", "wallet_asset_type", "WalletAssetType");
if (walletAssetType) {
const label = resourceGroupAssetLabels[walletAssetType] || walletAssetType;
const amount = snapshotValue(snapshot, "walletAssetAmount", "wallet_asset_amount", "WalletAssetAmount", "quantity");
return `${label} ${formatNumber(amount || item.quantity)}`;
}
const resourceName = snapshotValue(snapshot, "name", "Name", "resourceName", "resource_name") || `资源 ${item.resourceId || "-"}`;
const resourceType = snapshotValue(snapshot, "resourceType", "resource_type", "ResourceType");
const typeLabel = resourceTypeLabels[resourceType] || resourceType || "资源";
const quantity = Number(item.quantity || 0);
const parts = [`${resourceName} x${formatNumber(quantity)}`, typeLabel];
if (item.durationMs) {
parts.push(formatDuration(item.durationMs));
}
return parts.join(" · ");
}
function grantItemImageUrl(snapshot) {
return imageURL(
snapshotValue(
snapshot,
"previewUrl",
"preview_url",
"PreviewURL",
"assetUrl",
"asset_url",
"AssetURL",
"animationUrl",
"animation_url",
"AnimationURL",
),
);
}
function parseGrantItemSnapshot(value) {
if (!value || typeof value !== "string") {
return {};
}
try {
const parsed = JSON.parse(value);
return parsed && typeof parsed === "object" ? parsed : {};
} catch {
return {};
}
}
function snapshotValue(snapshot, ...keys) {
for (const key of keys) {
const value = snapshot?.[key];
if (value !== undefined && value !== null && value !== "") {
return value;
}
}
return "";
}
function formatNumber(value) {
return Number(value || 0).toLocaleString("zh-CN");
}
function imageURL(value) {
const url = String(value || "").trim();
return /\.(avif|gif|jpe?g|png|webp)(\?|#|$)/i.test(url) ? url : "";
}
function formatDuration(durationMs) {
const days = Number(durationMs || 0) / (24 * 60 * 60 * 1000);
return Number.isInteger(days) ? `${days}天` : `${days.toFixed(1)}天`;
}