feat: api client, auth store, login, authed shell, data-table + CRUD kit

This commit is contained in:
2026-07-07 22:15:08 +08:00
parent 95a3dee29f
commit eb617ce436
23 changed files with 1002 additions and 352 deletions
+68
View File
@@ -0,0 +1,68 @@
import { useState } from 'react'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog'
interface ConfirmDialogProps {
trigger: React.ReactNode
title: string
description?: string
confirmLabel?: string
destructive?: boolean
onConfirm: () => Promise<void> | void
}
export function ConfirmDialog({
trigger,
title,
description,
confirmLabel = '确认',
destructive = false,
onConfirm,
}: ConfirmDialogProps) {
const [busy, setBusy] = useState(false)
return (
<AlertDialog>
<AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
{description && <AlertDialogDescription>{description}</AlertDialogDescription>}
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction
disabled={busy}
className={destructive ? 'bg-destructive text-white hover:bg-destructive/90' : undefined}
onClick={async (event) => {
event.preventDefault()
setBusy(true)
try {
await onConfirm()
// close via Escape-equivalent: AlertDialogAction default closes,
// but we prevented it — dispatch close by clicking cancel sibling.
;(event.target as HTMLElement)
.closest('[role="alertdialog"]')
?.querySelector<HTMLButtonElement>('[data-slot="alert-dialog-cancel"]')
?.click()
} finally {
setBusy(false)
}
}}
>
{busy ? '处理中…' : confirmLabel}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}