mirror of
https://github.com/vernu/textbee.git
synced 2026-05-18 05:14:49 -04:00
ui(web): dark mode support
This commit is contained in:
@@ -58,7 +58,7 @@ export default function RequestPasswordResetForm() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100'>
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100 dark:bg-muted'>
|
||||
<Card className='w-[400px] shadow-lg'>
|
||||
<CardHeader className='space-y-1'>
|
||||
<CardTitle className='text-2xl font-bold text-center'>
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function ResetPasswordForm({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100'>
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100 dark:bg-muted'>
|
||||
<Card className='w-[400px] shadow-lg'>
|
||||
<CardHeader className='space-y-1'>
|
||||
<CardTitle className='text-2xl font-bold text-center'>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Routes } from '@/config/routes'
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100'>
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100 dark:bg-muted'>
|
||||
<Card className='w-[400px] shadow-lg'>
|
||||
<CardHeader className='space-y-1'>
|
||||
<CardTitle className='text-2xl font-bold text-center'>
|
||||
@@ -34,7 +34,7 @@ export default function LoginPage() {
|
||||
<span className='w-full border-t' />
|
||||
</div>
|
||||
<div className='relative flex justify-center text-xs uppercase'>
|
||||
<span className='bg-background px-2 text-muted-foreground'>
|
||||
<span className='bg-background dark:bg-muted px-2 text-muted-foreground'>
|
||||
Or
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Routes } from '@/config/routes'
|
||||
|
||||
export default function RegisterPage() {
|
||||
return (
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100'>
|
||||
<div className='flex items-center justify-center min-h-screen bg-gray-100 dark:bg-muted'>
|
||||
<Card className='w-[450px] shadow-lg'>
|
||||
<CardHeader className='space-y-1'>
|
||||
<CardTitle className='text-2xl font-bold text-center'>
|
||||
@@ -28,6 +28,16 @@ export default function RegisterPage() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<RegisterForm />
|
||||
<div className='relative mt-4'>
|
||||
<div className='absolute inset-0 flex items-center'>
|
||||
<span className='w-full border-t' />
|
||||
</div>
|
||||
<div className='relative flex justify-center text-xs uppercase'>
|
||||
<span className='bg-background dark:bg-muted px-2 text-muted-foreground'>
|
||||
Or
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-4 flex justify-center'>
|
||||
<LoginWithGoogle />
|
||||
</div>
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function GenerateApiKey() {
|
||||
>
|
||||
{isGeneratingApiKey ? (
|
||||
<div className='flex justify-center items-center h-full'>
|
||||
<Spinner size='sm' className='text-white' />
|
||||
<Spinner size='sm' className='text-white dark:text-black' />
|
||||
</div>
|
||||
) : (
|
||||
'Generate API Key'
|
||||
@@ -111,7 +111,7 @@ export default function GenerateApiKey() {
|
||||
</DialogHeader>
|
||||
|
||||
<div className='space-y-6'>
|
||||
<div className='flex justify-center p-4 bg-muted rounded-lg '>
|
||||
<div className='flex justify-center p-4 bg-muted dark:bg-white rounded-lg '>
|
||||
{generatedApiKey?.data && (
|
||||
<QRCode value={generatedApiKey?.data} size={120} />
|
||||
)}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ApiEndpoints } from '@/config/api'
|
||||
import { useEffect } from 'react'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { Routes } from '@/config/routes'
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
|
||||
export default function LayoutWrapper({ session, children }) {
|
||||
const router = useRouter()
|
||||
@@ -35,15 +36,17 @@ export default function LayoutWrapper({ session, children }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SessionProvider session={session}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<GoogleOAuthProvider
|
||||
clientId={process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID}
|
||||
>
|
||||
{children}
|
||||
</GoogleOAuthProvider>
|
||||
</QueryClientProvider>
|
||||
</SessionProvider>
|
||||
<ThemeProvider attribute='class' defaultTheme='system' enableSystem>
|
||||
<SessionProvider session={session}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<GoogleOAuthProvider
|
||||
clientId={process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID}
|
||||
>
|
||||
{children}
|
||||
</GoogleOAuthProvider>
|
||||
</QueryClientProvider>
|
||||
</SessionProvider>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export default async function RootLayout({ children }: PropsWithChildren) {
|
||||
<>
|
||||
<LayoutWrapper session={session}>
|
||||
<AppHeader />
|
||||
{children}
|
||||
<main className='min-h-[80vh]'>{children}</main>
|
||||
</LayoutWrapper>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../../components/ui/tabs'
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from '../../../components/ui/tabs'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||
import { dark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
||||
|
||||
const codeSnippets = [
|
||||
{
|
||||
@@ -56,10 +64,10 @@ print(response.json())`,
|
||||
|
||||
export default function CodeSnippetSection() {
|
||||
return (
|
||||
<section className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl bg-gray-50'>
|
||||
<section className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl bg-gray-50 dark:bg-muted rounded-2xl my-12'>
|
||||
<div className='mx-auto max-w-[58rem]'>
|
||||
<h3 className='text-3xl font-bold mb-8'>Code Snippet</h3>
|
||||
<div className='bg-white p-6 rounded-xl shadow-sm'>
|
||||
<div className='bg-white dark:bg-black p-6 rounded-xl shadow-sm'>
|
||||
<Tabs defaultValue={codeSnippets[0].tech} className='w-full'>
|
||||
<TabsList className='grid w-full grid-cols-3'>
|
||||
{codeSnippets.map((snippet) => {
|
||||
@@ -76,6 +84,7 @@ export default function CodeSnippetSection() {
|
||||
<SyntaxHighlighter
|
||||
language={snippet.language}
|
||||
showLineNumbers={snippet.language !== 'bash'}
|
||||
style={dark}
|
||||
// className='min-h-[200px]'
|
||||
>
|
||||
{snippet.snippet}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Link from 'next/link'
|
||||
|
||||
export default function CustomizationSection() {
|
||||
return (
|
||||
<section className='py-24 bg-gradient-to-b from-blue-50 to-white'>
|
||||
<section className='py-24 bg-gradient-to-b from-blue-50 to-white dark:from-blue-950 dark:to-muted'>
|
||||
<div className='container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl'>
|
||||
<div className='mx-auto max-w-3xl text-center mb-12'>
|
||||
<h2 className='text-4xl font-bold mb-4 text-blue-600'>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function DownloadAppSection() {
|
||||
return (
|
||||
<section className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl'>
|
||||
<div className='mx-auto max-w-[58rem] text-center'>
|
||||
<div className='rounded-xl bg-gradient-to-r from-blue-50 to-indigo-50 p-8'>
|
||||
<div className='rounded-2xl bg-gradient-to-r from-blue-50 to-indigo-50 p-8 dark:from-blue-950 dark:to-muted'>
|
||||
<div className='mx-auto max-w-sm'>
|
||||
<Image
|
||||
alt='App preview'
|
||||
@@ -24,7 +24,7 @@ export default function DownloadAppSection() {
|
||||
Gateway.
|
||||
</p>
|
||||
<Link href={Routes.downloadAndroidApp} prefetch={false}>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600'>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600 text-white'>
|
||||
Download App
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function FeaturesSection() {
|
||||
<Card className='flex flex-col items-center justify-center p-6 text-center'>
|
||||
<Send className='h-12 w-12 mb-4 text-blue-500' />
|
||||
<h3 className='font-bold'>Send SMS</h3>
|
||||
<p className='text-sm text-gray-500'>
|
||||
<p className='text-sm '>
|
||||
Send SMS to any number from your dashboard or via REST API
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
@@ -6,7 +6,7 @@ import Link from 'next/link'
|
||||
|
||||
export default function HeroSection() {
|
||||
return (
|
||||
<section className='relative overflow-hidden bg-gradient-to-b from-blue-50 to-white py-16 sm:py-24'>
|
||||
<section className='relative overflow-hidden bg-gradient-to-b from-blue-50 to-white dark:from-blue-950 dark:to-muted py-16 sm:py-24'>
|
||||
<div className='absolute inset-0 bg-[url(/grid.svg)] bg-center [mask-image:linear-gradient(180deg,white,rgba(255,255,255,0))]'></div>
|
||||
<div className='container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl relative'>
|
||||
<div className='grid gap-8 lg:grid-cols-2 lg:gap-16'>
|
||||
@@ -26,7 +26,7 @@ export default function HeroSection() {
|
||||
</div>
|
||||
<div className='flex flex-cdol gap-4 flex-row'>
|
||||
<Link href={Routes.register} prefetch={false}>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600' size='lg'>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600 dark:text-white' size='lg'>
|
||||
Get Started
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function HowItWorksSection() {
|
||||
return (
|
||||
<section
|
||||
id='how-it-works'
|
||||
className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl bg-gray-50'
|
||||
className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl bg-gray-50 dark:bg-muted rounded-2xl'
|
||||
>
|
||||
<div className='mx-auto max-w-[58rem]'>
|
||||
<h2 className='text-3xl font-bold text-center mb-8'>How It Works</h2>
|
||||
|
||||
@@ -3,48 +3,54 @@ import { MessageSquarePlus, Moon } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ExternalLinks } from '@/config/external-links'
|
||||
import { Routes } from '@/config/routes'
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
import ThemeToggle from '@/components/shared/theme-toggle'
|
||||
|
||||
export default function LandingPageHeader() {
|
||||
return (
|
||||
<header className='sticky top-0 z-50 w-full border-b bg-white/95 backdrop-blur supports-[backdrop-filter]:bg-white/60'>
|
||||
<div className='container flex h-14 items-center justify-between px-2'>
|
||||
<Link
|
||||
className='flex items-center space-x-2'
|
||||
href={Routes.landingPage}
|
||||
>
|
||||
<MessageSquarePlus className='h-6 w-6 text-blue-500' />
|
||||
<span className='font-bold'>
|
||||
Text<span className='text-blue-500'>Bee</span>
|
||||
</span>
|
||||
</Link>
|
||||
<nav className='flex items-center space-x-4'>
|
||||
{/* <Button variant='ghost' size='icon'>
|
||||
<ThemeProvider attribute='class' defaultTheme='system'>
|
||||
<header className='sticky top-0 z-50 w-full border-b bg-white/95 dark:bg-[#1A2752] backdrop-blur supports-[backdrop-filter]:bg-white/60 dark:bg-muted/95'>
|
||||
<div className='container flex h-14 items-center justify-between px-2'>
|
||||
<Link
|
||||
className='flex items-center space-x-2'
|
||||
href={Routes.landingPage}
|
||||
>
|
||||
<MessageSquarePlus className='h-6 w-6 text-blue-500' />
|
||||
<span className='font-bold'>
|
||||
Text<span className='text-blue-500'>Bee</span>
|
||||
</span>
|
||||
</Link>
|
||||
<nav className='flex items-center space-x-4'>
|
||||
<ThemeToggle />
|
||||
|
||||
{/* <Button variant='ghost' size='icon'>
|
||||
<Moon className='h-4 w-4' />
|
||||
<span className='sr-only'>Toggle theme</span>
|
||||
</Button> */}
|
||||
<Link
|
||||
className='text-sm font-medium hover:text-blue-500'
|
||||
href={ExternalLinks.github}
|
||||
>
|
||||
Github
|
||||
</Link>
|
||||
<Link
|
||||
className='text-sm font-medium hover:text-blue-500'
|
||||
href={ExternalLinks.github}
|
||||
>
|
||||
Github
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className='text-sm font-medium hover:text-blue-500'
|
||||
href={Routes.dashboard}
|
||||
>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600 rounded-full'>
|
||||
Go to Dashboard
|
||||
</Button>
|
||||
</Link>
|
||||
{/* <Link
|
||||
<Link
|
||||
className='text-sm font-medium hover:text-blue-500'
|
||||
href={Routes.dashboard}
|
||||
>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600 dark:text-white rounded-full'>
|
||||
Go to Dashboard
|
||||
</Button>
|
||||
</Link>
|
||||
{/* <Link
|
||||
className='text-sm font-medium hover:text-blue-500'
|
||||
href='/register'
|
||||
>
|
||||
Register
|
||||
</Link> */}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function SupportProjectSection() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl bg-gray-50'>
|
||||
<section className='container mx-auto py-24 px-4 sm:px-6 lg:px-8 max-w-7xl bg-gray-50 dark:bg-muted rounded-2xl my-12'>
|
||||
<div className='mx-auto max-w-[58rem] text-center'>
|
||||
<h2 className='text-3xl font-bold mb-4'>Support The Project</h2>
|
||||
<p className='text-gray-500 mb-8'>
|
||||
@@ -64,7 +64,7 @@ export default function SupportProjectSection() {
|
||||
</p>
|
||||
<div className='flex flex-col sm:flex-row justify-center gap-4'>
|
||||
<Link href={ExternalLinks.patreon} prefetch={false} target='_blank'>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600'>
|
||||
<Button className='bg-blue-500 hover:bg-blue-600 text-white'>
|
||||
<Heart className='mr-2 h-4 w-4' /> Become a Patron
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -84,7 +84,7 @@ export default function SupportProjectSection() {
|
||||
{cryptoWallets.map((wallet, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='flex items-center justify-between p-4 rounded-lg bg-gray-100'
|
||||
className='flex items-center justify-between p-4 rounded-lg bg-gray-100 dark:bg-muted'
|
||||
>
|
||||
<div>
|
||||
<h4 className='font-semibold'>{wallet.name}</h4>
|
||||
|
||||
@@ -9,6 +9,9 @@ import { Session } from 'next-auth'
|
||||
import { getServerSession } from 'next-auth'
|
||||
import { headers } from 'next/dist/client/components/headers'
|
||||
import { authOptions } from '@/lib/auth'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import prismaClient from '@/lib/prismaClient'
|
||||
import { userAgent } from 'next/server'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'textbee.dev - Free and Open-Source SMS Gateway',
|
||||
@@ -59,8 +62,46 @@ export const metadata: Metadata = {
|
||||
metadataBase: new URL('https://textbee.dev'),
|
||||
}
|
||||
|
||||
const trackPageView = async ({
|
||||
headerList,
|
||||
session,
|
||||
}: {
|
||||
headerList: Headers
|
||||
session: Session | null
|
||||
}) => {
|
||||
const { ua } = userAgent({
|
||||
headers: headerList,
|
||||
})
|
||||
|
||||
const url = headerList.get('x-current-url')
|
||||
const ip = headerList.get('x-forwarded-for')
|
||||
|
||||
const referer = headerList.get('referer')
|
||||
|
||||
const res = await prismaClient.pageView.create({
|
||||
data: {
|
||||
url,
|
||||
// @ts-ignore
|
||||
user: session?.user?.id,
|
||||
userAgent: ua,
|
||||
ip,
|
||||
referer,
|
||||
},
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children }: PropsWithChildren) {
|
||||
const session: Session | null = await getServerSession(authOptions as any)
|
||||
|
||||
const headerList = headers()
|
||||
|
||||
trackPageView({ headerList, session })
|
||||
.catch(console.error)
|
||||
.then((res) => {
|
||||
// console.log(res)
|
||||
})
|
||||
|
||||
return (
|
||||
<html lang='en'>
|
||||
<body>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
|
||||
import { Menu, LogOut, LayoutDashboard, MessageSquarePlus } from 'lucide-react'
|
||||
import { signOut, useSession } from 'next-auth/react'
|
||||
import { Routes } from '@/config/routes'
|
||||
import ThemeToggle from './theme-toggle'
|
||||
|
||||
export default function AppHeader() {
|
||||
const session = useSession()
|
||||
@@ -44,7 +45,9 @@ export default function AppHeader() {
|
||||
{session.data?.user?.name?.charAt(0)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className='hidden md:block'>{session.data?.user?.name}</div>
|
||||
<div className='hidden md:block'>
|
||||
{session.data?.user?.name?.split(' ')[0]}
|
||||
</div>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className='w-56' align='end' forceMount>
|
||||
@@ -122,7 +125,7 @@ export default function AppHeader() {
|
||||
<Button
|
||||
asChild
|
||||
color='primary'
|
||||
className='bg-blue-500 hover:bg-blue-600 rounded-full'
|
||||
className='bg-blue-500 hover:bg-blue-600 text-white rounded-full'
|
||||
>
|
||||
<Link href={Routes.register}>Get started</Link>
|
||||
</Button>
|
||||
@@ -148,7 +151,8 @@ export default function AppHeader() {
|
||||
</Link>
|
||||
</div>
|
||||
<div className='flex flex-1 items-center justify-end space-x-2'>
|
||||
<nav className='flex items-center space-x-2'>
|
||||
<nav className='flex items-center space-x-6'>
|
||||
<ThemeToggle />
|
||||
{isAuthenticated ? (
|
||||
<AuthenticatedMenu />
|
||||
) : (
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function SupportButton() {
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
className='fixed bottom-4 right-4 shadow-lg bg-blue-500 hover:bg-blue-600 rounded-full'
|
||||
className='fixed bottom-4 right-4 shadow-lg bg-blue-500 hover:bg-blue-600 dark:text-white rounded-full'
|
||||
size='sm'
|
||||
>
|
||||
<MessageSquarePlus className='h-5 w-5 mr-1' />
|
||||
|
||||
@@ -4,7 +4,7 @@ import { MessageSquarePlus } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className='border-t py-6 bg-gray-50'>
|
||||
<footer className='border-t py-6 bg-gray-50 dark:bg-muted'>
|
||||
<div className='container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row'>
|
||||
<div className='flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0'>
|
||||
<MessageSquarePlus className='h-6 w-6 text-blue-500' />
|
||||
|
||||
41
web/components/shared/theme-toggle.tsx
Normal file
41
web/components/shared/theme-toggle.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
'use client'
|
||||
|
||||
import { useTheme } from 'next-themes'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Sun, Moon, Laptop } from 'lucide-react'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='ghost' size='icon'>
|
||||
<Sun className='h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0' />
|
||||
<Moon className='absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100' />
|
||||
<span className='sr-only'>Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end'>
|
||||
<DropdownMenuItem onClick={() => setTheme('light')}>
|
||||
<Sun className='mr-2 h-4 w-4' />
|
||||
<span>Light</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('dark')}>
|
||||
<Moon className='mr-2 h-4 w-4' />
|
||||
<span>Dark</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('system')}>
|
||||
<Laptop className='mr-2 h-4 w-4' />
|
||||
<span>System</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -40,7 +40,13 @@ export async function middleware(request: NextRequest) {
|
||||
const response = NextResponse.next()
|
||||
response.headers.set('x-pathname', pathname)
|
||||
|
||||
return response
|
||||
request.headers?.set('x-current-url', request.nextUrl?.href ?? '')
|
||||
|
||||
return NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"lucide-react": "^0.453.0",
|
||||
"next": "14.1.0",
|
||||
"next-auth": "^4.24.10",
|
||||
"next-themes": "^0.4.3",
|
||||
"nodemailer": "^6.9.16",
|
||||
"prisma": "^5.22.0",
|
||||
"react": "^18.2.0",
|
||||
|
||||
14
web/pnpm-lock.yaml
generated
14
web/pnpm-lock.yaml
generated
@@ -80,6 +80,9 @@ importers:
|
||||
next-auth:
|
||||
specifier: ^4.24.10
|
||||
version: 4.24.10(next@14.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(nodemailer@6.9.16)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
next-themes:
|
||||
specifier: ^0.4.3
|
||||
version: 0.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
nodemailer:
|
||||
specifier: ^6.9.16
|
||||
version: 6.9.16
|
||||
@@ -1780,6 +1783,12 @@ packages:
|
||||
nodemailer:
|
||||
optional: true
|
||||
|
||||
next-themes@0.4.3:
|
||||
resolution: {integrity: sha512-nG84VPkTdUHR2YeD89YchvV4I9RbiMAql3GiLEQlPvq1ioaqPaIReK+yMRdg/zgiXws620qS1rU30TiWmmG9lA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
next@14.1.0:
|
||||
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
|
||||
engines: {node: '>=18.17.0'}
|
||||
@@ -4212,6 +4221,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
nodemailer: 6.9.16
|
||||
|
||||
next-themes@0.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
next@14.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@next/env': 14.1.0
|
||||
|
||||
@@ -30,25 +30,25 @@
|
||||
--radius: 0.5rem
|
||||
}
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--card: 224 71.4% 4.1%;
|
||||
--card-foreground: 210 20% 98%;
|
||||
--popover: 224 71.4% 4.1%;
|
||||
--popover-foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--secondary: 215 27.9% 16.9%;
|
||||
--secondary-foreground: 210 20% 98%;
|
||||
--muted: 215 27.9% 16.9%;
|
||||
--muted-foreground: 217.9 10.6% 64.9%;
|
||||
--accent: 215 27.9% 16.9%;
|
||||
--accent-foreground: 210 20% 98%;
|
||||
--background: 0 0% 15%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 0 0% 15%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 0 0% 15%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 0 0% 15%;
|
||||
--secondary: 0 0% 25%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 25%;
|
||||
--muted-foreground: 0 0% 65%;
|
||||
--accent: 0 0% 25%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
--border: 0 0% 25%;
|
||||
--input: 0 0% 25%;
|
||||
--ring: 0 0% 83%;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
|
||||
Reference in New Issue
Block a user