feat(admin): add audited Refine staging console
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
AuditOutlined,
|
||||
GiftOutlined,
|
||||
MessageOutlined,
|
||||
TeamOutlined,
|
||||
TransactionOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Authenticated, Refine } from "@refinedev/core";
|
||||
import { ErrorComponent, ThemedLayout, useNotificationProvider } from "@refinedev/antd";
|
||||
import routerProvider from "@refinedev/nextjs-router";
|
||||
import { App as AntdApp, ConfigProvider, Spin, theme } from "antd";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import {
|
||||
adminAccessControlProvider,
|
||||
adminAuthProvider,
|
||||
adminDataProvider,
|
||||
} from "@/lib/admin/providers";
|
||||
|
||||
export function AdminApp({ children }: { children: ReactNode }) {
|
||||
const notificationProvider = useNotificationProvider();
|
||||
return (
|
||||
<ConfigProvider theme={{ algorithm: theme.darkAlgorithm, token: { colorPrimary: "#c8a96b" } }}>
|
||||
<AntdApp>
|
||||
<Refine
|
||||
routerProvider={routerProvider}
|
||||
dataProvider={adminDataProvider}
|
||||
authProvider={adminAuthProvider}
|
||||
accessControlProvider={adminAccessControlProvider}
|
||||
notificationProvider={notificationProvider}
|
||||
resources={[
|
||||
{ name: "codes", list: "/admin/codes", meta: { label: "兑换码", icon: <GiftOutlined /> } },
|
||||
{ name: "users", list: "/admin/users", meta: { label: "用户资料", icon: <TeamOutlined /> } },
|
||||
{ name: "credit-transactions", list: "/admin/credit-transactions", meta: { label: "积分流水", icon: <TransactionOutlined /> } },
|
||||
{ name: "consultations", list: "/admin/consultations", meta: { label: "咨询请求", icon: <MessageOutlined /> } },
|
||||
{ name: "audit-logs", list: "/admin/audit-logs", meta: { label: "审计日志", icon: <AuditOutlined /> } },
|
||||
]}
|
||||
options={{
|
||||
syncWithLocation: true,
|
||||
warnWhenUnsavedChanges: true,
|
||||
title: { text: "Jyotisha 后台" },
|
||||
}}
|
||||
>
|
||||
<Authenticated
|
||||
key="admin-authenticated"
|
||||
loading={<div className="admin-loading"><Spin size="large" /><span>正在验证后台权限</span></div>}
|
||||
>
|
||||
<ThemedLayout>{children}</ThemedLayout>
|
||||
</Authenticated>
|
||||
</Refine>
|
||||
</AntdApp>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export { ErrorComponent as AdminErrorComponent };
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
|
||||
import type { BaseRecord } from "@refinedev/core";
|
||||
import { List, useTable } from "@refinedev/antd";
|
||||
import { Alert, Empty, Form, Input, Select, Space, Table, type TableColumnsType } from "antd";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export type ResourceFilterOption = { label: string; value: string };
|
||||
|
||||
export function ResourceTable<T extends BaseRecord>({
|
||||
resource,
|
||||
title,
|
||||
columns,
|
||||
statusOptions,
|
||||
extra,
|
||||
}: {
|
||||
resource: string;
|
||||
title: string;
|
||||
columns: TableColumnsType<T>;
|
||||
statusOptions?: ResourceFilterOption[];
|
||||
extra?: ReactNode;
|
||||
}) {
|
||||
const { tableProps, searchFormProps, tableQuery } = useTable<T, { message: string; statusCode: number }, { q?: string; status?: string }>({
|
||||
resource,
|
||||
syncWithLocation: true,
|
||||
pagination: { pageSize: 20 },
|
||||
onSearch(values) {
|
||||
return [
|
||||
{ field: "q", operator: "contains", value: values.q },
|
||||
{ field: "status", operator: "eq", value: values.status },
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
const error = tableQuery.error;
|
||||
return (
|
||||
<List title={title} headerButtons={extra}>
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
<Form {...searchFormProps} layout="inline">
|
||||
<Form.Item name="q"><Input.Search allowClear placeholder="搜索" /></Form.Item>
|
||||
{statusOptions && (
|
||||
<Form.Item name="status">
|
||||
<Select allowClear placeholder="状态" options={statusOptions} style={{ minWidth: 160 }} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
{error && <Alert type="error" showIcon message="读取失败" description={error.message} />}
|
||||
<Table<T>
|
||||
{...tableProps}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
locale={{ emptyText: <Empty description="暂无数据" /> }}
|
||||
scroll={{ x: "max-content" }}
|
||||
/>
|
||||
</Space>
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
export function formatAdminDate(value: string | null | undefined) {
|
||||
return value ? new Intl.DateTimeFormat("zh-CN", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
}).format(new Date(value)) : "—";
|
||||
}
|
||||
Reference in New Issue
Block a user