Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions app/(admin)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { AppSidebar } from "@/components/app-sidebar";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import AuthorizedApolloWrapper from "@/providers/use-apollo.rsc";
import ProtectedRoute from "@/providers/use-protected-route";
import { unstable_ViewTransition as ViewTransition } from "react";

export default function AdminLayout({
Expand All @@ -8,20 +10,24 @@ export default function AdminLayout({
children: React.ReactNode;
}>) {
return (
<SidebarProvider
style={{
"--sidebar-width": "calc(var(--spacing) * 72)",
"--header-height": "calc(var(--spacing) * 12)",
} as React.CSSProperties}
>
<AppSidebar variant="inset" />
<SidebarInset>
<ViewTransition>
<div suppressHydrationWarning>
{children}
</div>
</ViewTransition>
</SidebarInset>
</SidebarProvider>
<ProtectedRoute>
<AuthorizedApolloWrapper>
<SidebarProvider
style={{
"--sidebar-width": "calc(var(--spacing) * 72)",
"--header-height": "calc(var(--spacing) * 12)",
} as React.CSSProperties}
>
<AppSidebar variant="inset" />
<SidebarInset>
<ViewTransition>
<div suppressHydrationWarning>
{children}
</div>
</ViewTransition>
</SidebarInset>
</SidebarProvider>
</AuthorizedApolloWrapper>
</ProtectedRoute>
);
}
10 changes: 10 additions & 0 deletions app/forbidden.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ForbiddenLayout from "@/components/forbidden-layout/page";
import type { Metadata } from "next";

export const metadata: Metadata = {
title: "權限不足",
};

export default function ForbiddenPage() {
return <ForbiddenLayout />;
}
13 changes: 5 additions & 8 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Toaster } from "@/components/ui/sonner";
import { getAuthToken } from "@/lib/auth";
import { ApolloWrapper } from "@/providers/use-apollo";
import { ProgressProvider } from "@/providers/use-progress-provider";
import { PreloadResources } from "./preload-resources";

Expand All @@ -18,7 +16,10 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: { template: "%s | 管理介面 | 資料庫練功坊", default: "管理介面 | 資料庫練功坊" },
title: {
template: "%s | 管理介面 | 資料庫練功坊",
default: "管理介面 | 資料庫練功坊",
},
description: "管理資料庫練功坊的題目、使用者、做題記錄等。",
};

Expand All @@ -29,8 +30,6 @@ export default async function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const token = await getAuthToken();

return (
<html lang="zh-hant-tw">
<head>
Expand All @@ -48,9 +47,7 @@ export default async function RootLayout({
font-sans antialiased
`}
>
<ApolloWrapper token={token}>
<ProgressProvider delay={500}>{children}</ProgressProvider>
</ApolloWrapper>
<ProgressProvider delay={500}>{children}</ProgressProvider>
<Toaster />
</body>
</html>
Expand Down
23 changes: 2 additions & 21 deletions app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import { LoginForm } from "@/components/login-form";
import { Logo } from "@/components/logo";
import { redirectIfAuthenticated } from "@/lib/auth.rsc";
import type { Metadata } from "next";
import Link from "next/link";

interface LoginPageProps {
searchParams: Promise<{
error?: string;
error_description?: string;
message?: string;
redirect?: string;
}>;
}

export const metadata: Metadata = {
title: "登入",
};

export default async function LoginPage({ searchParams }: LoginPageProps) {
// Redirect if already authenticated
await redirectIfAuthenticated();

const params = await searchParams;

export default function LoginPage() {
return (
<div
className={`
Expand All @@ -46,11 +31,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
</div>
Database Playground
</Link>
<LoginForm
error={params.error}
errorDescription={params.error_description}
message={params.message}
/>
<LoginForm />
</div>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions app/unauthorized.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from "next/navigation";

export default async function UnauthorizedPage() {
redirect("/login");
}
11 changes: 9 additions & 2 deletions components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ const buildNavbar = (
title: "題庫管理",
url: "/questions",
icon: LibrarySquare,
isActive: pathname.startsWith("/questions") || pathname.startsWith("/database"),
isActive: pathname.startsWith("/questions")
|| pathname.startsWith("/database"),
items: [
{
title: "題庫",
Expand Down Expand Up @@ -167,7 +168,13 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
{data.navMain.map((group) => <NavMain key={group.group} items={group.items} groupLabel={group.group} />)}
{data.navMain.map((group) => (
<NavMain
key={group.group}
items={group.items}
groupLabel={group.group}
/>
))}
<NavSecondary items={data.navSecondary} className="mt-auto" />
</SidebarContent>
<SidebarFooter>
Expand Down
18 changes: 8 additions & 10 deletions app/forbidden/page.tsx → components/forbidden-layout/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { Logo } from "@/components/logo";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { redirectIfAuthenticated } from "@/lib/auth.rsc";
import AuthorizedApolloWrapper from "@/providers/use-apollo.rsc";
import { AlertTriangle } from "lucide-react";
import Link from "next/link";
import { Suspense } from "react";
import { UserInfo } from "./user-info";

import type { Metadata } from "next";
export const metadata: Metadata = {
title: "權限不足",
};

export default async function ForbiddenPage() {
await redirectIfAuthenticated();

export default async function ForbiddenLayout() {
return (
<div
className={`
Expand Down Expand Up @@ -52,7 +46,11 @@ export default async function ForbiddenPage() {
<CardFooter
className={`justify-center text-center text-xs text-muted-foreground`}
>
<UserInfo />
<Suspense>
<AuthorizedApolloWrapper>
<UserInfo />
</AuthorizedApolloWrapper>
</Suspense>
</CardFooter>
</Card>
</div>
Expand Down
File renamed without changes.
107 changes: 0 additions & 107 deletions components/login-form.tsx

This file was deleted.

49 changes: 49 additions & 0 deletions components/login-form/error-alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import { AlertCircle } from "lucide-react";
import { useSearchParams } from "next/navigation";
import { Alert, AlertDescription } from "../ui/alert";

export function ErrorAlert() {
const searchParams = useSearchParams();
const error = searchParams.get("error");
const errorDescription = searchParams.get("error_description");

if (!error || !errorDescription) {
return null;
}

return (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
{getErrorMessage(error, errorDescription)}
</AlertDescription>
</Alert>
);
}

function getErrorMessage(error: string, description?: string | null): string {
if (description) return description;

switch (error) {
case "invalid_request":
return "登入請求無效,請重試。";
case "unauthorized":
return "您沒有權限存取此應用程式。";
case "access_denied":
return "登入已取消或拒絕。";
case "server_error":
return "伺服器發生錯誤,請稍後再試。";
case "temporarily_unavailable":
return "服務暫時無法使用,請稍後再試。";
case "auth_error":
return "認證過程中發生錯誤,請重新登入。";
case "logout_failed":
return "登出時發生錯誤,但您的本地工作階段已清除。";
case "forbidden":
return "您沒有權限存取此應用程式。";
default:
return "登入時發生未知錯誤,請重試。";
}
}
Loading