Files
root 694b02e848 frontend: rebuild bookmark page with drag-and-drop, search, and theme system
- bookmark management with dnd-kit reordering, bulk edit, search,
  category filter/rename, and meta auto-fetch
- migrate /bookmarks → /dashboard/bookmarks under (main) layout
- homepage redesign with category grid, /-key search, dock tooltips
- theme toggle + use-theme, sonner toasts, alert-dialog/skeleton,
  visual refresh of auth pages

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 22:53:17 +00:00

98 lines
2.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { Suspense } from "react";
import { useState } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import { signIn } from "next-auth/react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
function BindForm() {
const searchParams = useSearchParams();
const router = useRouter();
const keycloakId = searchParams.get("keycloakId") || "";
const keycloakEmail = searchParams.get("email") || "";
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError("");
const res = await fetch("/api/proxy/auth/bind-keycloak", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username,
password,
keycloakId,
keycloakEmail,
}),
});
if (!res.ok) {
const data = await res.json();
setError(data.error || "绑定失败");
return;
}
const data = await res.json();
if (data.bound) {
await signIn("keycloak", { callbackUrl: "/dashboard" });
}
}
return (
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-center"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
Keycloak {keycloakEmail || keycloakId}
</p>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<Label htmlFor="username"></Label>
<Input
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
</div>
<div>
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
<Button type="submit" className="w-full">
</Button>
</form>
</CardContent>
</Card>
);
}
export default function BindAccountPage() {
return (
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-background via-background to-muted/40 p-4">
<Suspense fallback={<div className="text-sm text-muted-foreground">...</div>}>
<BindForm />
</Suspense>
</div>
);
}