migrate frontend

This commit is contained in:
ryan
2026-06-18 18:05:00 +08:00
parent 31b114b0cc
commit e7b8993181
27 changed files with 1836 additions and 383 deletions
@@ -1,7 +1,10 @@
"use client"
import {useState} from "react"
import {useEffect} from "react"
import {zodResolver} from "@hookform/resolvers/zod"
import {Loader2} from "lucide-react"
import {useForm} from "react-hook-form"
import {z} from "zod"
import {
AlertDialog,
@@ -16,6 +19,12 @@ import {Button} from "@/components/ui/button"
import {Input} from "@/components/ui/input"
import {Label} from "@/components/ui/label"
const cleanupSchema = z.object({
keepCount: z.number().int().min(3, "最少保留 3 个历史快照"),
})
type CleanupFormValues = z.infer<typeof cleanupSchema>
interface CleanupDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
@@ -29,17 +38,20 @@ export function CleanupDialog({
onConfirm,
loading,
}: CleanupDialogProps) {
const [keepCount, setKeepCount] = useState(10)
const [error, setError] = useState<string | null>(null)
const form = useForm<CleanupFormValues>({
resolver: zodResolver(cleanupSchema),
defaultValues: { keepCount: 10 },
})
const handleConfirm = () => {
if (keepCount < 3) {
setError("最少保留 3 个历史快照")
return
useEffect(() => {
if (open) {
form.reset({ keepCount: 10 })
}
setError(null)
onConfirm(keepCount)
}
}, [form, open])
const handleConfirm = form.handleSubmit((values) => {
onConfirm(values.keepCount)
})
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
@@ -57,17 +69,18 @@ export function CleanupDialog({
id="keepCount"
type="number"
min={3}
value={keepCount}
onChange={(e) => setKeepCount(Number.parseInt(e.target.value, 10) || 3)}
disabled={loading}
{...form.register("keepCount", { valueAsNumber: true })}
/>
<p className="text-xs text-muted-foreground">默认为 10 个,最少需保留 3 个。</p>
{error ? <p className="text-xs text-destructive">{error}</p> : null}
{form.formState.errors.keepCount ? (
<p className="text-xs text-destructive">{form.formState.errors.keepCount.message}</p>
) : null}
</div>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading}>取消</AlertDialogCancel>
<Button variant="destructive" onClick={handleConfirm} disabled={loading}>
<Button variant="destructive" onClick={() => void handleConfirm()} disabled={loading}>
{loading ? (
<>
<Loader2 className="size-4 animate-spin mr-1" />
@@ -81,4 +94,4 @@ export function CleanupDialog({
</AlertDialogContent>
</AlertDialog>
)
}
}