fix(admin): remove redundant confirmations and repair code access
Independent Staging Quality Gate / validate (push) Failing after 12m38s
Independent Staging Quality Gate / publish (push) Has been skipped

This commit is contained in:
Jesse_Chen
2026-08-16 17:55:15 +08:00
parent f12a43ad1d
commit 945d61fa1c
21 changed files with 314 additions and 413 deletions
@@ -22,7 +22,6 @@ import {
import dayjs from "dayjs";
import { useState } from "react";
import { ConfirmActionModal } from "@/components/admin/confirm-action-modal";
import {
formatAdminDate,
ResourceTable,
@@ -72,52 +71,54 @@ export default function CodesPage() {
const { mutateAsync: updateCode, mutation: updateMutation } =
useUpdate<CodeRecord>();
const [createOpen, setCreateOpen] = useState(false);
const [pendingCreate, setPendingCreate] = useState<CreateValues | null>(null);
const [editRecord, setEditRecord] = useState<CodeRecord | null>(null);
const [pendingEdit, setPendingEdit] = useState<EditValues | null>(null);
const [generated, setGenerated] = useState<CodeRecord[]>([]);
const [revokeRecord, setRevokeRecord] = useState<CodeRecord | null>(null);
const [revoking, setRevoking] = useState(false);
const [revokingId, setRevokingId] = useState<string | null>(null);
const [createForm] = Form.useForm<CreateValues>();
const [editForm] = Form.useForm<EditValues>();
const writable = Boolean(
identity?.permissions.includes("billing.adjustments.write"),
);
const writable = Boolean(identity?.permissions.includes("admin.access"));
async function submitCreate() {
if (!pendingCreate) return;
const result = await createCodes({
resource: "codes",
values: {
credits: pendingCreate.credits,
count: pendingCreate.count,
expiresAt: pendingCreate.expiresAt?.toISOString() ?? null,
note: pendingCreate.note?.trim() || null,
},
successNotification: false,
});
setGenerated(result.data.generated);
setPendingCreate(null);
setCreateOpen(false);
createForm.resetFields();
async function submitCreate(values: CreateValues) {
try {
const result = await createCodes({
resource: "codes",
values: {
credits: values.credits,
count: values.count,
expiresAt: values.expiresAt?.toISOString() ?? null,
note: values.note?.trim() || null,
},
successNotification: false,
});
setGenerated(result.data.generated);
setCreateOpen(false);
createForm.resetFields();
} catch (error) {
message.error(error instanceof Error ? error.message : "生成兑换码失败");
}
}
async function submitEdit() {
if (!editRecord || !pendingEdit) return;
await updateCode({
resource: "codes",
id: editRecord.id,
values: {
note: pendingEdit.note?.trim() || null,
expiresAt: pendingEdit.expiresAt?.toISOString() ?? null,
},
});
setPendingEdit(null);
setEditRecord(null);
async function submitEdit(values: EditValues) {
if (!editRecord) return;
try {
await updateCode({
resource: "codes",
id: editRecord.id,
values: {
note: values.note?.trim() || null,
expiresAt: values.expiresAt?.toISOString() ?? null,
},
successNotification: false,
});
setEditRecord(null);
message.success("兑换码已保存");
} catch (error) {
message.error(error instanceof Error ? error.message : "保存兑换码失败");
}
}
async function revoke(record: CodeRecord) {
setRevoking(true);
setRevokingId(record.id);
try {
await adminRequestJson(`/api/admin/codes/${record.id}`, {
method: "DELETE",
@@ -125,11 +126,10 @@ export default function CodesPage() {
});
await invalidate({ resource: "codes", invalidates: ["list"] });
message.success("兑换码已撤销");
setRevokeRecord(null);
} catch (error) {
message.error(error instanceof Error ? error.message : "撤销失败");
} finally {
setRevoking(false);
setRevokingId(null);
}
}
@@ -175,7 +175,6 @@ export default function CodesPage() {
<Button
size="small"
onClick={() => {
setPendingEdit(null);
setEditRecord(record);
editForm.setFieldsValue({
note: record.note ?? undefined,
@@ -188,8 +187,8 @@ export default function CodesPage() {
<Button
danger
size="small"
loading={revoking && revokeRecord?.id === record.id}
onClick={() => setRevokeRecord(record)}
loading={revokingId === record.id}
onClick={() => void revoke(record)}
>
</Button>
@@ -216,10 +215,7 @@ export default function CodesPage() {
writable ? (
<Button
type="primary"
onClick={() => {
setPendingCreate(null);
setCreateOpen(true);
}}
onClick={() => setCreateOpen(true)}
>
</Button>
@@ -230,10 +226,7 @@ export default function CodesPage() {
<Modal
title="批量生成兑换码"
open={createOpen}
onCancel={() => {
setPendingCreate(null);
setCreateOpen(false);
}}
onCancel={() => setCreateOpen(false)}
footer={null}
destroyOnHidden
>
@@ -241,7 +234,7 @@ export default function CodesPage() {
form={createForm}
layout="vertical"
initialValues={{ credits: 10, count: 1 }}
onFinish={setPendingCreate}
onFinish={(values) => void submitCreate(values)}
>
<Form.Item
name="credits"
@@ -289,14 +282,15 @@ export default function CodesPage() {
<Modal
title="编辑未兑换码"
open={Boolean(editRecord)}
onCancel={() => {
setPendingEdit(null);
setEditRecord(null);
}}
onCancel={() => setEditRecord(null)}
footer={null}
destroyOnHidden
>
<Form form={editForm} layout="vertical" onFinish={setPendingEdit}>
<Form
form={editForm}
layout="vertical"
onFinish={(values) => void submitEdit(values)}
>
<Form.Item name="expiresAt" label="到期时间">
<DatePicker showTime style={{ width: "100%" }} />
</Form.Item>
@@ -313,31 +307,6 @@ export default function CodesPage() {
</Button>
</Form>
</Modal>
<ConfirmActionModal
open={Boolean(pendingCreate)}
title="生成兑换码"
okText="确认生成"
confirmLoading={createMutation.isPending}
onCancel={() => setPendingCreate(null)}
onConfirm={submitCreate}
/>
<ConfirmActionModal
open={Boolean(pendingEdit)}
title="编辑未兑换码"
okText="确认保存"
confirmLoading={updateMutation.isPending}
onCancel={() => setPendingEdit(null)}
onConfirm={submitEdit}
/>
<ConfirmActionModal
open={Boolean(revokeRecord)}
title="撤销兑换码"
okText="确认撤销"
danger
confirmLoading={revoking}
onCancel={() => setRevokeRecord(null)}
onConfirm={() => revoke(revokeRecord!)}
/>
</>
);
}