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>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Suspense } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -10,41 +9,42 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { LoginForm } from "./login-form";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "登录",
|
||||
};
|
||||
|
||||
const hasKeycloak = !!process.env.AUTH_KEYCLOAK_ISSUER;
|
||||
|
||||
function LoginPageContent() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const callbackUrl = searchParams.get("callbackUrl");
|
||||
const error = searchParams.get("error");
|
||||
export default async function LoginPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||
}) {
|
||||
const sp = await searchParams;
|
||||
const callbackUrl = typeof sp.callbackUrl === "string" ? sp.callbackUrl : null;
|
||||
const error = typeof sp.error === "string" ? sp.error : null;
|
||||
|
||||
useEffect(() => {
|
||||
// If not an OAuth callback, redirect to home with login modal open
|
||||
if (!callbackUrl && !error) {
|
||||
router.replace("/?login=1");
|
||||
}
|
||||
}, [callbackUrl, error, router]);
|
||||
if (!callbackUrl && !error) {
|
||||
redirect("/?login=1");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100 p-4">
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-background via-background to-muted/40 p-4">
|
||||
<Dialog defaultOpen>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-center">登录</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<LoginForm hasKeycloak={hasKeycloak} />
|
||||
{error && (
|
||||
<p className="rounded-md border border-destructive/30 bg-destructive/10 p-2 text-center text-xs text-destructive">
|
||||
登录过程中出现错误,请重试
|
||||
</p>
|
||||
)}
|
||||
<Suspense fallback={<div className="text-sm text-muted-foreground">加载中...</div>}>
|
||||
<LoginForm hasKeycloak={hasKeycloak} callbackUrl={callbackUrl ?? undefined} />
|
||||
</Suspense>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<Suspense fallback={<div className="flex min-h-screen items-center justify-center">加载中...</div>}>
|
||||
<LoginPageContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user