217 lines
7.5 KiB
JavaScript
217 lines
7.5 KiB
JavaScript
import { DataState } from "@/shared/ui/DataState.jsx";
|
|
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
|
import { AdminListBody, AdminListPage } from "@/shared/ui/AdminListLayout.jsx";
|
|
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
|
|
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
|
import { formatMillis } from "@/shared/utils/time.js";
|
|
import { SevenDayCheckInConfigDrawer } from "@/features/seven-day-checkin/components/SevenDayCheckInConfigDrawer.jsx";
|
|
import { SevenDayCheckInConfigSummary } from "@/features/seven-day-checkin/components/SevenDayCheckInConfigSummary.jsx";
|
|
import { useSevenDayCheckInPage } from "@/features/seven-day-checkin/hooks/useSevenDayCheckInPage.js";
|
|
import styles from "@/features/seven-day-checkin/seven-day-checkin.module.css";
|
|
|
|
const claimStatusOptions = [
|
|
["pending", "发放中"],
|
|
["granted", "已发放"],
|
|
["failed", "发放失败"],
|
|
];
|
|
|
|
const dayOptions = Array.from({ length: 7 }, (_, index) => [String(index + 1), `第 ${index + 1} 天`]);
|
|
|
|
const columnsBase = [
|
|
{
|
|
key: "user",
|
|
label: "用户信息",
|
|
width: "minmax(260px, 1.15fr)",
|
|
render: (claim) => <ClaimUser claim={claim} />,
|
|
},
|
|
{
|
|
key: "cycle",
|
|
label: "签到周期",
|
|
width: "minmax(180px, 0.8fr)",
|
|
render: (claim) => (
|
|
<div className={styles.stack}>
|
|
<span>第 {claim.cycleNo || 0} 轮</span>
|
|
<span className={styles.meta}>{claim.checkinDay || "-"}</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "dayIndex",
|
|
label: "签到天数",
|
|
width: "minmax(120px, 0.55fr)",
|
|
render: (claim) => `第 ${claim.dayIndex || 0} 天`,
|
|
},
|
|
{
|
|
key: "reward",
|
|
label: "奖励资源组",
|
|
width: "minmax(180px, 0.85fr)",
|
|
render: (claim, _index, context) => (
|
|
<ResourceGroupLabel groups={context.resourceGroups} id={claim.resourceGroupId} />
|
|
),
|
|
},
|
|
{
|
|
key: "status",
|
|
label: "状态",
|
|
width: "minmax(120px, 0.55fr)",
|
|
render: (claim) => <ClaimStatus status={claim.status} />,
|
|
},
|
|
{
|
|
key: "time",
|
|
label: "签到时间",
|
|
width: "minmax(180px, 0.85fr)",
|
|
render: (claim) => formatMillis(claim.signedAtMs || claim.createdAtMs),
|
|
},
|
|
{
|
|
key: "grant",
|
|
label: "钱包发放",
|
|
width: "minmax(220px, 1fr)",
|
|
render: (claim) => (
|
|
<div className={styles.stack}>
|
|
<span>{claim.walletGrantId || "-"}</span>
|
|
<span className={styles.meta}>{claim.walletCommandId || "-"}</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "claim",
|
|
label: "签到记录",
|
|
width: "minmax(240px, 1fr)",
|
|
render: (claim) => (
|
|
<div className={styles.stack}>
|
|
<span>{claim.claimId}</span>
|
|
<span className={styles.meta}>{claim.commandId || "-"}</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "failure",
|
|
label: "失败原因",
|
|
width: "minmax(180px, 0.85fr)",
|
|
render: (claim) => claim.failureReason || "-",
|
|
},
|
|
];
|
|
|
|
export function SevenDayCheckInPage() {
|
|
const page = useSevenDayCheckInPage();
|
|
const total = page.claims.total || 0;
|
|
const columns = columnsBase.map((column) => {
|
|
if (column.key === "user") {
|
|
return {
|
|
...column,
|
|
filter: createTextColumnFilter({
|
|
placeholder: "搜索用户 ID、短号、名称",
|
|
value: page.query,
|
|
onChange: page.changeQuery,
|
|
}),
|
|
};
|
|
}
|
|
if (column.key === "status") {
|
|
return {
|
|
...column,
|
|
filter: createOptionsColumnFilter({
|
|
options: [["", "全部状态"], ...claimStatusOptions],
|
|
placeholder: "搜索状态",
|
|
value: page.status,
|
|
onChange: page.changeStatus,
|
|
}),
|
|
};
|
|
}
|
|
if (column.key === "dayIndex") {
|
|
return {
|
|
...column,
|
|
filter: createOptionsColumnFilter({
|
|
options: [["", "全部天数"], ...dayOptions],
|
|
placeholder: "搜索天数",
|
|
value: page.dayIndex,
|
|
onChange: page.changeDayIndex,
|
|
}),
|
|
};
|
|
}
|
|
if (column.key === "cycle") {
|
|
return {
|
|
...column,
|
|
filter: createTextColumnFilter({
|
|
placeholder: "YYYY-MM-DD",
|
|
value: page.checkinDay,
|
|
onChange: page.changeCheckinDay,
|
|
}),
|
|
};
|
|
}
|
|
return column;
|
|
});
|
|
|
|
return (
|
|
<AdminListPage>
|
|
<SevenDayCheckInConfigSummary
|
|
canUpdate={page.abilities.canUpdate}
|
|
config={page.config}
|
|
configLoading={page.configLoading}
|
|
resourceGroups={page.resourceGroups}
|
|
onEdit={page.openConfigDrawer}
|
|
onRefresh={page.reloadConfig}
|
|
/>
|
|
<DataState error={page.claimsError} loading={page.claimsLoading} onRetry={page.reloadClaims}>
|
|
<AdminListBody>
|
|
<DataTable
|
|
columns={columns}
|
|
context={{ resourceGroups: page.resourceGroups }}
|
|
items={page.claims.items || []}
|
|
minWidth="1540px"
|
|
pagination={
|
|
total > 0
|
|
? {
|
|
page: page.page,
|
|
pageSize: page.claims.pageSize || 50,
|
|
total,
|
|
onPageChange: page.setPage,
|
|
}
|
|
: undefined
|
|
}
|
|
rowKey={(claim) => claim.claimId}
|
|
/>
|
|
</AdminListBody>
|
|
</DataState>
|
|
<SevenDayCheckInConfigDrawer
|
|
abilities={page.abilities}
|
|
configLoading={page.configLoading}
|
|
configSaving={page.configSaving}
|
|
form={page.form}
|
|
open={page.configDrawerOpen}
|
|
resourceGroups={page.resourceGroups}
|
|
setForm={page.setForm}
|
|
onClose={page.closeConfigDrawer}
|
|
onSubmit={page.submitConfig}
|
|
/>
|
|
</AdminListPage>
|
|
);
|
|
}
|
|
|
|
function ClaimUser({ claim }) {
|
|
const user = claim.user || {};
|
|
return <AdminUserIdentity openInAppUserDetail user={{ ...user, userId: user.userId || claim.userId }} />;
|
|
}
|
|
|
|
function ResourceGroupLabel({ groups, id }) {
|
|
const group = groups.find((item) => Number(item.groupId) === Number(id));
|
|
return (
|
|
<div className={styles.stack}>
|
|
<span>{group?.name || group?.groupCode || (id ? `#${id}` : "-")}</span>
|
|
<span className={styles.meta}>资源组</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ClaimStatus({ status }) {
|
|
const tone = status === "granted" ? "running" : status === "failed" ? "danger" : "warning";
|
|
return (
|
|
<span className={`status-badge status-badge--${tone}`}>
|
|
<span className="status-point" />
|
|
{claimStatusLabel(status)}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
function claimStatusLabel(status) {
|
|
return claimStatusOptions.find(([value]) => value === status)?.[1] || status || "-";
|
|
}
|