feat(ledger): add bookkeeping workbench and transaction views
- Add transactions_dirty table and zero-tolerance cleansing action - Implement bookkeeping workbench (/bookkeeping) with searchable Combobox channel selector and card suffix / account identifier - Implement transaction ledger (/transactions) with date groupings, multi-currency metrics, and filters - Update AGENTS.md guidelines for Base UI Select and Combobox bindings
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import * as React from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useActionState } from "react";
|
||||
import * as React from "react"
|
||||
import Link from "next/link"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useActionState } from "react"
|
||||
import {
|
||||
ArrowLeftIcon,
|
||||
CheckCircle2Icon,
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
MailIcon,
|
||||
SparklesIcon,
|
||||
UserIcon,
|
||||
} from "lucide-react";
|
||||
} from "lucide-react"
|
||||
|
||||
import { registerAction, type RegisterState } from "@/lib/actions/auth";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { registerAction, type RegisterState } from "@/lib/actions/auth"
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -25,45 +25,45 @@ import {
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { FluxentLogo } from "@/components/fluxent-logo";
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
} from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { FluxentLogo } from "@/components/fluxent-logo"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
|
||||
const initialState: RegisterState = {};
|
||||
const initialState: RegisterState = {}
|
||||
|
||||
export function RegisterForm() {
|
||||
const router = useRouter();
|
||||
const router = useRouter()
|
||||
const [state, formAction, isPending] = useActionState(
|
||||
registerAction,
|
||||
initialState
|
||||
);
|
||||
)
|
||||
|
||||
const [password, setPassword] = React.useState("");
|
||||
const [confirmPassword, setConfirmPassword] = React.useState("");
|
||||
const [password, setPassword] = React.useState("")
|
||||
const [confirmPassword, setConfirmPassword] = React.useState("")
|
||||
|
||||
// 当注册成功时,优雅倒计时或自动跳转
|
||||
React.useEffect(() => {
|
||||
if (state?.success) {
|
||||
const timer = setTimeout(() => {
|
||||
router.push("/login?registered=1");
|
||||
}, 1500);
|
||||
return () => clearTimeout(timer);
|
||||
router.push("/login?registered=1")
|
||||
}, 1500)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [state?.success, router]);
|
||||
}, [state?.success, router])
|
||||
|
||||
// 密码复杂度提示计算
|
||||
const hasMinLen = password.length >= 8;
|
||||
const hasMinLen = password.length >= 8
|
||||
const passwordsMatch = Boolean(
|
||||
password && confirmPassword && password === confirmPassword
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-svh flex-col justify-between overflow-hidden bg-background">
|
||||
{/* Decorative ambient gradients */}
|
||||
<div className="pointer-events-none absolute -top-40 -left-40 size-[32rem] rounded-full bg-primary/5 blur-3xl" />
|
||||
<div className="pointer-events-none absolute -bottom-40 -right-40 size-[34rem] rounded-full bg-primary/5 blur-3xl" />
|
||||
<div className="pointer-events-none absolute -right-40 -bottom-40 size-[34rem] rounded-full bg-primary/5 blur-3xl" />
|
||||
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(120,119,198,0.08),rgba(255,255,255,0))]" />
|
||||
|
||||
{/* Header */}
|
||||
@@ -300,7 +300,7 @@ export function RegisterForm() {
|
||||
>
|
||||
<ArrowLeftIcon
|
||||
data-icon="inline-start"
|
||||
className="inline size-3 mr-0.5"
|
||||
className="mr-0.5 inline size-3"
|
||||
/>
|
||||
返回直接登录
|
||||
</Link>
|
||||
@@ -312,8 +312,10 @@ export function RegisterForm() {
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="relative z-10 flex h-12 items-center justify-center border-t border-border/40 px-6 text-center text-[11px] text-muted-foreground">
|
||||
<span>© {new Date().getFullYear()} Fluxent Financial. 保留所有权利。</span>
|
||||
<span>
|
||||
© {new Date().getFullYear()} Fluxent Financial. 保留所有权利。
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user