mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 15:46:37 +08:00
oauth
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { motion } from "motion/react"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
import { AppSidebar } from "@/components/layout/sidebar"
|
||||
import { SiteHeader } from "@/components/layout/header"
|
||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"
|
||||
|
||||
import { UserProvider } from "@/contexts/user-context"
|
||||
import {motion} from "motion/react"
|
||||
import {usePathname, useRouter} from "next/navigation"
|
||||
import {useEffect, useState} from "react"
|
||||
import {AppSidebar} from "@/components/layout/sidebar"
|
||||
import {SiteHeader} from "@/components/layout/header"
|
||||
import {SidebarInset, SidebarProvider} from "@/components/ui/sidebar"
|
||||
import {LoadingPage} from "@/components/layout/loading"
|
||||
import {useUser} from "@/contexts/user-context"
|
||||
|
||||
|
||||
export default function MainLayout({
|
||||
@@ -15,40 +15,56 @@ export default function MainLayout({
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const {user, loading} = useUser()
|
||||
const [isFullWidth, setIsFullWidth] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (loading || user) return
|
||||
|
||||
const queryString = window.location.search
|
||||
const callbackUrl = queryString ? `${pathname}${queryString}` : pathname
|
||||
const loginUrl = new URL("/login", window.location.origin)
|
||||
|
||||
loginUrl.searchParams.set("callbackUrl", callbackUrl)
|
||||
sessionStorage.setItem("redirect_after_login", callbackUrl)
|
||||
router.replace(loginUrl.toString())
|
||||
}, [loading, pathname, router, user])
|
||||
|
||||
if (loading || !user) {
|
||||
return <LoadingPage text="登录状态" badgeText="Auth" />
|
||||
}
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
<SidebarProvider
|
||||
className="h-screen"
|
||||
style={
|
||||
{
|
||||
"--header-height": "60px",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<AppSidebar />
|
||||
<SidebarInset className="flex flex-col min-w-0 h-screen">
|
||||
<SiteHeader isFullWidth={isFullWidth} onToggleFullWidth={setIsFullWidth} />
|
||||
<div className="flex flex-1 flex-col bg-background overflow-y-auto overflow-x-hidden min-w-0 hide-scrollbar">
|
||||
<div className={`w-full mx-auto px-4 sm:px-6 md:px-8 lg:px-12 min-w-0 transition-all duration-300 ease-in-out ${!isFullWidth ? "max-w-[1320px]" : "max-w-full"}`}>
|
||||
<motion.div
|
||||
key={pathname}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
ease: "easeOut",
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
</UserProvider>
|
||||
<SidebarProvider
|
||||
className="h-screen"
|
||||
style={
|
||||
{
|
||||
"--header-height": "60px",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<AppSidebar />
|
||||
<SidebarInset className="flex flex-col min-w-0 h-screen">
|
||||
<SiteHeader isFullWidth={isFullWidth} onToggleFullWidth={setIsFullWidth} />
|
||||
<div className="flex flex-1 flex-col bg-background overflow-y-auto overflow-x-hidden min-w-0 hide-scrollbar">
|
||||
<div className={`w-full mx-auto px-4 sm:px-6 md:px-8 lg:px-12 min-w-0 transition-all duration-300 ease-in-out ${!isFullWidth ? "max-w-[1320px]" : "max-w-full"}`}>
|
||||
<motion.div
|
||||
key={pathname}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
ease: "easeOut",
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import Link from "next/link"
|
||||
import { Card, CardTitle, CardDescription, CardContent } from "@/components/ui/card"
|
||||
import { UserRound, Bell, Shield, Palette } from "lucide-react"
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import {Card, CardContent, CardDescription, CardTitle} from "@/components/ui/card"
|
||||
import {Bell, Loader2, Palette, Shield, UserRound} from "lucide-react"
|
||||
import {useAuth} from "@/components/providers/auth-provider"
|
||||
|
||||
/* 设置项 */
|
||||
const settingsItems = [
|
||||
@@ -28,7 +30,7 @@ const settingsItems = [
|
||||
},
|
||||
{
|
||||
title: "外观设置",
|
||||
description: "自定义界面主题和显示",
|
||||
description: "自定义界面主题 and 显示",
|
||||
icon: Palette,
|
||||
href: "/settings/appearance",
|
||||
category: "个人设置",
|
||||
@@ -36,7 +38,25 @@ const settingsItems = [
|
||||
]
|
||||
|
||||
export default function SettingsPage() {
|
||||
const groupedSettings = settingsItems.reduce((acc, item) => {
|
||||
const { user, loading } = useAuth()
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[200px]">
|
||||
<Loader2 className="size-6 animate-spin text-indigo-500" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 非管理员过滤掉安全设置
|
||||
const filteredItems = settingsItems.filter((item) => {
|
||||
if (item.href === "/settings/security") {
|
||||
return !!user?.is_admin
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
const groupedSettings = filteredItems.reduce((acc, item) => {
|
||||
if (!acc[item.category]) {
|
||||
acc[item.category] = []
|
||||
}
|
||||
@@ -46,7 +66,7 @@ export default function SettingsPage() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6 py-6">
|
||||
<div className="font-semibold">设置</div>
|
||||
<div className="font-semibold text-lg">设置</div>
|
||||
|
||||
{Object.entries(groupedSettings).map(([category, items]) => (
|
||||
<div key={category} className="space-y-4">
|
||||
|
||||
+18
-12
@@ -1,9 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { ThemeProvider } from "@/components/layout/theme-provider";
|
||||
import { CustomThemeProvider } from "@/lib/theme";
|
||||
import { BellRingProvider } from "@/contexts/bell-ring-context";
|
||||
import { NotificationSettingsProvider } from "@/contexts/notification-settings-context";
|
||||
import type {Metadata} from "next";
|
||||
import {Toaster} from "@/components/ui/sonner";
|
||||
import {ThemeProvider} from "@/components/layout/theme-provider";
|
||||
import {CustomThemeProvider} from "@/lib/theme";
|
||||
import {BellRingProvider} from "@/contexts/bell-ring-context";
|
||||
import {NotificationSettingsProvider} from "@/contexts/notification-settings-context";
|
||||
import {UserProvider} from "@/contexts/user-context";
|
||||
import {AppQueryProvider} from "@/components/providers/query-provider";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -32,12 +34,16 @@ export default function RootLayout({
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<CustomThemeProvider>
|
||||
<NotificationSettingsProvider>
|
||||
<BellRingProvider>
|
||||
{children}
|
||||
<Toaster position="top-center" />
|
||||
</BellRingProvider>
|
||||
</NotificationSettingsProvider>
|
||||
<AppQueryProvider>
|
||||
<UserProvider>
|
||||
<NotificationSettingsProvider>
|
||||
<BellRingProvider>
|
||||
{children}
|
||||
<Toaster position="top-center" />
|
||||
</BellRingProvider>
|
||||
</NotificationSettingsProvider>
|
||||
</UserProvider>
|
||||
</AppQueryProvider>
|
||||
</CustomThemeProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user