Ran prettier. Moved Header to layout file
This commit is contained in:
parent
f5e3cb6234
commit
939fd796ee
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
|
||||
* for Docker builds.
|
||||
*/
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
|
||||
* for Docker builds.
|
||||
*/
|
||||
import './src/env.js';
|
||||
import { withSentryConfig } from '@sentry/nextjs';
|
||||
|
||||
|
@ -1,35 +1,35 @@
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { createClient } from '@/utils/supabase/server'
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
|
||||
// Creating a handler to a GET request to route /auth/confirm
|
||||
export const GET = async (request: NextRequest) => {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token_hash = searchParams.get('token_hash')
|
||||
const type = searchParams.get('type') as EmailOtpType | null
|
||||
const next = '/account'
|
||||
const { searchParams } = new URL(request.url);
|
||||
const token_hash = searchParams.get('token_hash');
|
||||
const type = searchParams.get('type') as EmailOtpType | null;
|
||||
const next = '/account';
|
||||
|
||||
// Create redirect link without the secret token
|
||||
const redirectTo = request.nextUrl.clone()
|
||||
redirectTo.pathname = next
|
||||
redirectTo.searchParams.delete('token_hash')
|
||||
redirectTo.searchParams.delete('type')
|
||||
const redirectTo = request.nextUrl.clone();
|
||||
redirectTo.pathname = next;
|
||||
redirectTo.searchParams.delete('token_hash');
|
||||
redirectTo.searchParams.delete('type');
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = await createClient()
|
||||
const supabase = await createClient();
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
});
|
||||
if (!error) {
|
||||
redirectTo.searchParams.delete('next')
|
||||
return NextResponse.redirect(redirectTo)
|
||||
redirectTo.searchParams.delete('next');
|
||||
return NextResponse.redirect(redirectTo);
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
redirectTo.pathname = '/error'
|
||||
return NextResponse.redirect(redirectTo)
|
||||
}
|
||||
redirectTo.pathname = '/error';
|
||||
return NextResponse.redirect(redirectTo);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
const ErrorPage = () => {
|
||||
return <p>Sorry, something went wrong</p>
|
||||
}
|
||||
return <p>Sorry, something went wrong</p>;
|
||||
};
|
||||
export default ErrorPage;
|
||||
|
@ -3,6 +3,9 @@ import { GeistSans } from 'geist/font/sans';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ThemeProvider } from '@/components/context/Theme';
|
||||
import { TVModeProvider } from '@/components/context/TVMode';
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
import LoginForm from '@/components/auth/LoginForm';
|
||||
import Header from '@/components/defaults/Header';
|
||||
|
||||
import { type Metadata } from 'next';
|
||||
export const metadata: Metadata = {
|
||||
@ -28,20 +31,14 @@ export const metadata: Metadata = {
|
||||
],
|
||||
};
|
||||
|
||||
const RootLayout = ({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) => {
|
||||
const RootLayout = async ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||
return (
|
||||
<html
|
||||
lang='en'
|
||||
className={`${GeistSans.variable}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body
|
||||
className={cn(
|
||||
'min-h-screen bg-background font-sans antialiased'
|
||||
)}
|
||||
>
|
||||
<body className={cn('min-h-screen bg-background font-sans antialiased')}>
|
||||
<ThemeProvider
|
||||
attribute='class'
|
||||
defaultTheme='system'
|
||||
@ -49,7 +46,10 @@ const RootLayout = ({
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<TVModeProvider>
|
||||
{children}
|
||||
<main className='min-h-screen'>
|
||||
<Header />
|
||||
{children}
|
||||
</main>
|
||||
</TVModeProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
@ -1,6 +1,5 @@
|
||||
'use server'
|
||||
'use server';
|
||||
import LoginForm from '@/components/auth/LoginForm';
|
||||
import Header from '@/components/defaults/Header';
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
|
||||
const HomePage = async () => {
|
||||
@ -8,21 +7,15 @@ const HomePage = async () => {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
if (!session) {
|
||||
return (
|
||||
<main className="min-h-screen">
|
||||
<Header />
|
||||
<div className="flex flex-col items-center justify-center md:min-h-[80vh]">
|
||||
<LoginForm />
|
||||
</div>
|
||||
</main>
|
||||
<div className='flex flex-col items-center justify-center md:min-h-[70vh]'>
|
||||
<LoginForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<main>
|
||||
<Header />
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<h1>Hello, {session.user.email}</h1>
|
||||
</div>
|
||||
</main>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
<h1>Hello, {session.user.email}</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default HomePage;
|
||||
|
@ -1,4 +1,4 @@
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
@ -13,6 +13,7 @@ import {
|
||||
import { createClient } from '@/utils/supabase/client';
|
||||
import type { Session } from '@supabase/supabase-js';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
const AvatarDropdown = () => {
|
||||
const supabase = createClient();
|
||||
@ -23,7 +24,9 @@ const AvatarDropdown = () => {
|
||||
// Function to fetch the session
|
||||
async function fetchSession() {
|
||||
try {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
setSession(session);
|
||||
} catch (error) {
|
||||
console.error('Error fetching session:', error);
|
||||
@ -33,17 +36,16 @@ const AvatarDropdown = () => {
|
||||
}
|
||||
|
||||
// Call the function
|
||||
fetchSession()
|
||||
.catch((error) => {
|
||||
console.error('Error fetching session:', error);
|
||||
});
|
||||
fetchSession().catch((error) => {
|
||||
console.error('Error fetching session:', error);
|
||||
});
|
||||
|
||||
// Set up auth state change listener
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange(
|
||||
(_event, session) => {
|
||||
setSession(session);
|
||||
}
|
||||
);
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange((_event, session) => {
|
||||
setSession(session);
|
||||
});
|
||||
|
||||
// Clean up the subscription when component unmounts
|
||||
return () => {
|
||||
@ -54,11 +56,12 @@ const AvatarDropdown = () => {
|
||||
// Handle sign out
|
||||
const handleSignOut = async () => {
|
||||
await supabase.auth.signOut();
|
||||
redirect('/');
|
||||
};
|
||||
|
||||
// Show nothing while loading
|
||||
if (loading) {
|
||||
return <div className="animate-pulse h-8 w-8 rounded-full bg-gray-300" />;
|
||||
return <div className='animate-pulse h-8 w-8 rounded-full bg-gray-300' />;
|
||||
}
|
||||
|
||||
// If no session, return empty div
|
||||
@ -67,14 +70,16 @@ const AvatarDropdown = () => {
|
||||
}
|
||||
|
||||
// Get user details
|
||||
const pfp = session.user?.user_metadata?.avatar_url as string ??
|
||||
session.user?.user_metadata?.picture as string ??
|
||||
'/images/default_user_pfp.png';
|
||||
|
||||
const name: string = session.user?.user_metadata?.full_name as string ??
|
||||
session.user?.user_metadata?.name as string ??
|
||||
session.user?.email as string ??
|
||||
'Profile' as string;
|
||||
const pfp =
|
||||
(session.user?.user_metadata?.avatar_url as string) ??
|
||||
(session.user?.user_metadata?.picture as string) ??
|
||||
'/images/default_user_pfp.png';
|
||||
|
||||
const name: string =
|
||||
(session.user?.user_metadata?.full_name as string) ??
|
||||
(session.user?.user_metadata?.name as string) ??
|
||||
(session.user?.email as string) ??
|
||||
('Profile' as string);
|
||||
|
||||
// If no session, return empty div
|
||||
if (!session) return <div />;
|
||||
@ -84,7 +89,7 @@ const AvatarDropdown = () => {
|
||||
<DropdownMenuTrigger>
|
||||
<Image
|
||||
src={pfp}
|
||||
alt="User profile"
|
||||
alt='User profile'
|
||||
width={35}
|
||||
height={35}
|
||||
className='rounded-full border-2 border-white m-auto mr-1 md:mr-2
|
||||
@ -103,5 +108,5 @@ const AvatarDropdown = () => {
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default AvatarDropdown;
|
||||
|
@ -1,4 +1,4 @@
|
||||
'use client'
|
||||
'use client';
|
||||
import { login, signup } from '@/server/actions/auth';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
@ -22,20 +22,20 @@ import {
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
} from '@/components/ui/card';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import MicrosoftSignIn from '@/components/auth/microsoft/SignIn';
|
||||
import AppleSignIn from './apple/SignIn';
|
||||
|
||||
const formSchema = z.object({
|
||||
fullName: z.string().optional(),
|
||||
email: z.string().email("Must be a valid email!"),
|
||||
password: z.string().min(8, "Must be at least 8 characters!"),
|
||||
email: z.string().email('Must be a valid email!'),
|
||||
password: z.string().min(8, 'Must be at least 8 characters!'),
|
||||
});
|
||||
|
||||
const LoginForm = () => {
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
@ -68,21 +68,17 @@ const LoginForm = () => {
|
||||
>
|
||||
<CardHeader className='flex items-center justify-center'>
|
||||
<CardTitle>Sign In</CardTitle>
|
||||
<CardDescription>
|
||||
Log in or create an account.
|
||||
</CardDescription>
|
||||
<CardDescription>Log in or create an account.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form ref={formRef} className="space-y-4">
|
||||
<form ref={formRef} className='space-y-4'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="fullName"
|
||||
name='fullName'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel htmlFor='fullName'>
|
||||
Full Name
|
||||
</FormLabel>
|
||||
<FormLabel htmlFor='fullName'>Full Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input id='fullName' type='text' {...field} />
|
||||
</FormControl>
|
||||
@ -95,12 +91,10 @@ const LoginForm = () => {
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel htmlFor='email'>
|
||||
Email
|
||||
</FormLabel>
|
||||
<FormLabel htmlFor='email'>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input id='email' type='email' {...field} required />
|
||||
</FormControl>
|
||||
@ -110,12 +104,10 @@ const LoginForm = () => {
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
name='password'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Password
|
||||
</FormLabel>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input id='password' type='password' {...field} required />
|
||||
</FormControl>
|
||||
@ -123,12 +115,12 @@ const LoginForm = () => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex gap-4 justify-center items-center">
|
||||
<div className='flex gap-4 justify-center items-center'>
|
||||
<Button
|
||||
type='button'
|
||||
className='w-5/12 font-semibold bg-gradient-to-r
|
||||
from-primary via-primary to-primarydark
|
||||
hover:from-primary hover:via-primarydark hover:to-primarydark'
|
||||
hover:from-primary hover:via-primarydark hover:to-primarydark'
|
||||
onClick={handleLogin}
|
||||
>
|
||||
Log in
|
||||
@ -137,7 +129,7 @@ const LoginForm = () => {
|
||||
type='button'
|
||||
className='w-5/12 font-semibold bg-gradient-to-l
|
||||
from-primary via-primary to-primarydark
|
||||
hover:from-primary hover:via-primarydark hover:to-primarydark'
|
||||
hover:from-primary hover:via-primarydark hover:to-primarydark'
|
||||
onClick={handleSignup}
|
||||
>
|
||||
Sign up
|
||||
@ -149,13 +141,11 @@ const LoginForm = () => {
|
||||
<CardFooter className='flex flex-col items-center justify-center w-full'>
|
||||
<div className='flex flex-row items-center justify-between w-5/6'>
|
||||
<Separator className='w-5/12 h-0.5 rounded-3xl' />
|
||||
<p className='text-center text-muted-foreground font-semibold'>
|
||||
or
|
||||
</p>
|
||||
<p className='text-center text-muted-foreground font-semibold'>or</p>
|
||||
<Separator className='w-5/12 h-0.5 rounded-3xl' />
|
||||
</div>
|
||||
<div className='m-1'>
|
||||
< MicrosoftSignIn />
|
||||
<MicrosoftSignIn />
|
||||
<div className='my-2'>
|
||||
<AppleSignIn />
|
||||
</div>
|
||||
|
@ -11,12 +11,11 @@ const AppleSignIn = () => {
|
||||
<Image
|
||||
src='/images/apple_black.svg'
|
||||
alt='Apple Logo'
|
||||
width={16} height={16}
|
||||
width={16}
|
||||
height={16}
|
||||
className='invert dark:invert-0'
|
||||
/>
|
||||
<p className='font-semibold dark:text-slate-950'>
|
||||
Sign in with Apple
|
||||
</p>
|
||||
<p className='font-semibold dark:text-slate-950'>Sign in with Apple</p>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@ -11,7 +11,8 @@ const MicrosoftSignIn = () => {
|
||||
<Image
|
||||
src='/images/microsoft_logo.png'
|
||||
alt='Microsoft Logo'
|
||||
width={20} height={20}
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
<p className='font-semibold dark:text-slate-950'>
|
||||
Sign in with Microsoft
|
||||
|
@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
import type { ReactNode } from 'react';
|
||||
//import { useSession } from 'next-auth/react';
|
||||
import type { Session } from '@supabase/supabase-js';
|
||||
import { createClient } from '@/utils/supabase/client';
|
||||
|
||||
interface TVModeContextProps {
|
||||
tvMode: boolean;
|
||||
@ -35,8 +36,45 @@ export const useTVMode = () => {
|
||||
|
||||
export const TVToggle = () => {
|
||||
const { tvMode, toggleTVMode } = useTVMode();
|
||||
//const { data: session } = useSession();
|
||||
//if (!session) return <div />;
|
||||
const supabase = createClient();
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Function to fetch the session
|
||||
async function fetchSession() {
|
||||
try {
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
setSession(session);
|
||||
} catch (error) {
|
||||
console.error('Error fetching session:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the function
|
||||
fetchSession().catch((error) => {
|
||||
console.error('Error fetching session:', error);
|
||||
});
|
||||
|
||||
// Set up auth state change listener
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange((_event, session) => {
|
||||
setSession(session);
|
||||
});
|
||||
|
||||
// Clean up the subscription when component unmounts
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [supabase]);
|
||||
|
||||
if (loading || !session) return <div />;
|
||||
|
||||
return (
|
||||
<button onClick={toggleTVMode} className='mr-4 mt-1'>
|
||||
{tvMode ? (
|
||||
|
@ -11,7 +11,7 @@ const Header = () => {
|
||||
<div className='w-full flex flex-row items-end justify-end'>
|
||||
<div className='flex flex-row my-auto items-center pt-2 pr-0 sm:pr-8 sm:pt-4'>
|
||||
<AvatarDropdown />
|
||||
<div className='mb-0.5'>
|
||||
<div className='mb-0.5 ml-2'>
|
||||
<TVToggle />
|
||||
</div>
|
||||
<ThemeToggle />
|
||||
@ -27,7 +27,7 @@ const Header = () => {
|
||||
pt-2 pr-0 sm:pt-4 sm:pr-8'
|
||||
>
|
||||
<AvatarDropdown />
|
||||
<div className='mb-0.5'>
|
||||
<div className='mb-0.5 ml-2'>
|
||||
<TVToggle />
|
||||
</div>
|
||||
<ThemeToggle />
|
||||
|
@ -1,9 +1,9 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||
import * as React from 'react';
|
||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||
@ -12,13 +12,13 @@ const Avatar = React.forwardRef<
|
||||
<AvatarPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||
className
|
||||
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Avatar.displayName = AvatarPrimitive.Root.displayName
|
||||
));
|
||||
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||
@ -26,11 +26,11 @@ const AvatarImage = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Image
|
||||
ref={ref}
|
||||
className={cn("aspect-square h-full w-full", className)}
|
||||
className={cn('aspect-square h-full w-full', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
||||
));
|
||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
||||
|
||||
const AvatarFallback = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||
@ -39,12 +39,12 @@ const AvatarFallback = React.forwardRef<
|
||||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||
className
|
||||
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
||||
));
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
||||
export { Avatar, AvatarImage, AvatarFallback };
|
||||
|
@ -1,57 +1,57 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
outline:
|
||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
);
|
||||
},
|
||||
);
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button, buttonVariants }
|
||||
export { Button, buttonVariants };
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react"
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
@ -9,13 +9,13 @@ const Card = React.forwardRef<
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-xl border bg-card text-card-foreground shadow",
|
||||
className
|
||||
'rounded-xl border bg-card text-card-foreground shadow',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Card.displayName = "Card"
|
||||
));
|
||||
Card.displayName = 'Card';
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
@ -23,11 +23,11 @@ const CardHeader = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardHeader.displayName = "CardHeader"
|
||||
));
|
||||
CardHeader.displayName = 'CardHeader';
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
@ -35,11 +35,11 @@ const CardTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||
className={cn('font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardTitle.displayName = "CardTitle"
|
||||
));
|
||||
CardTitle.displayName = 'CardTitle';
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
@ -47,19 +47,19 @@ const CardDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardDescription.displayName = "CardDescription"
|
||||
));
|
||||
CardDescription.displayName = 'CardDescription';
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
))
|
||||
CardContent.displayName = "CardContent"
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
));
|
||||
CardContent.displayName = 'CardContent';
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
@ -67,10 +67,17 @@ const CardFooter = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
className={cn('flex items-center p-6 pt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardFooter.displayName = "CardFooter"
|
||||
));
|
||||
CardFooter.displayName = 'CardFooter';
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
};
|
||||
|
@ -1,44 +1,44 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||
import * as React from 'react';
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||
import { Check, ChevronRight, Circle } from 'lucide-react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
className
|
||||
'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto" />
|
||||
<ChevronRight className='ml-auto' />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
DropdownMenuPrimitive.SubTrigger.displayName
|
||||
DropdownMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
@ -47,14 +47,14 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
||||
className
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
));
|
||||
DropdownMenuSubContent.displayName =
|
||||
DropdownMenuPrimitive.SubContent.displayName
|
||||
DropdownMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
@ -65,33 +65,33 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
||||
className
|
||||
'z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||
));
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
className
|
||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||
));
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
@ -100,22 +100,22 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
<Check className='h-4 w-4' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
));
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
@ -124,38 +124,38 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="h-2 w-2 fill-current" />
|
||||
<Circle className='h-2 w-2 fill-current' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
))
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-semibold",
|
||||
inset && "pl-8",
|
||||
className
|
||||
'px-2 py-1.5 text-sm font-semibold',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||
));
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
@ -163,11 +163,11 @@ const DropdownMenuSeparator = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
const DropdownMenuShortcut = ({
|
||||
className,
|
||||
@ -175,12 +175,12 @@ const DropdownMenuShortcut = ({
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
||||
className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
||||
);
|
||||
};
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
@ -198,4 +198,4 @@ export {
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
}
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import * as React from 'react';
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
@ -10,27 +10,27 @@ import {
|
||||
type ControllerProps,
|
||||
type FieldPath,
|
||||
type FieldValues,
|
||||
} from "react-hook-form"
|
||||
} from 'react-hook-form';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Label } from '@/components/ui/label';
|
||||
|
||||
const Form = FormProvider
|
||||
const Form = FormProvider;
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> = {
|
||||
name: TName
|
||||
}
|
||||
name: TName;
|
||||
};
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue
|
||||
)
|
||||
{} as FormFieldContextValue,
|
||||
);
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>({
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
@ -38,21 +38,21 @@ const FormField = <
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext)
|
||||
const itemContext = React.useContext(FormItemContext)
|
||||
const { getFieldState, formState } = useFormContext()
|
||||
const fieldContext = React.useContext(FormFieldContext);
|
||||
const itemContext = React.useContext(FormItemContext);
|
||||
const { getFieldState, formState } = useFormContext();
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState)
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>")
|
||||
throw new Error('useFormField should be used within <FormField>');
|
||||
}
|
||||
|
||||
const { id } = itemContext
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
@ -61,53 +61,54 @@ const useFormField = () => {
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string
|
||||
}
|
||||
id: string;
|
||||
};
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue
|
||||
)
|
||||
{} as FormItemContextValue,
|
||||
);
|
||||
|
||||
const FormItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const id = React.useId()
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||
<div ref={ref} className={cn('space-y-2', className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
)
|
||||
})
|
||||
FormItem.displayName = "FormItem"
|
||||
);
|
||||
});
|
||||
FormItem.displayName = 'FormItem';
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField()
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={cn(error && "text-destructive", className)}
|
||||
className={cn(error && 'text-destructive', className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormLabel.displayName = "FormLabel"
|
||||
);
|
||||
});
|
||||
FormLabel.displayName = 'FormLabel';
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
>(({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||
const { error, formItemId, formDescriptionId, formMessageId } =
|
||||
useFormField();
|
||||
|
||||
return (
|
||||
<Slot
|
||||
@ -121,50 +122,50 @@ const FormControl = React.forwardRef<
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormControl.displayName = "FormControl"
|
||||
);
|
||||
});
|
||||
FormControl.displayName = 'FormControl';
|
||||
|
||||
const FormDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { formDescriptionId } = useFormField()
|
||||
const { formDescriptionId } = useFormField();
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={cn("text-[0.8rem] text-muted-foreground", className)}
|
||||
className={cn('text-[0.8rem] text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormDescription.displayName = "FormDescription"
|
||||
);
|
||||
});
|
||||
FormDescription.displayName = 'FormDescription';
|
||||
|
||||
const FormMessage = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
const { error, formMessageId } = useFormField()
|
||||
const body = error ? String(error?.message ?? "") : children
|
||||
const { error, formMessageId } = useFormField();
|
||||
const body = error ? String(error?.message ?? '') : children;
|
||||
|
||||
if (!body) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
||||
className={cn('text-[0.8rem] font-medium text-destructive', className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
FormMessage.displayName = "FormMessage"
|
||||
);
|
||||
});
|
||||
FormMessage.displayName = 'FormMessage';
|
||||
|
||||
export {
|
||||
useFormField,
|
||||
@ -175,4 +176,4 @@ export {
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormField,
|
||||
}
|
||||
};
|
||||
|
@ -1,22 +1,22 @@
|
||||
import * as React from "react"
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Input.displayName = "Input"
|
||||
);
|
||||
},
|
||||
);
|
||||
Input.displayName = 'Input';
|
||||
|
||||
export { Input }
|
||||
export { Input };
|
||||
|
@ -1,14 +1,14 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import * as React from 'react';
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
)
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
);
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
@ -20,7 +20,7 @@ const Label = React.forwardRef<
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Label.displayName = LabelPrimitive.Root.displayName
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
|
||||
export { Label }
|
||||
export { Label };
|
||||
|
@ -1,9 +1,9 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
import * as React from 'react';
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
@ -11,38 +11,38 @@ const ScrollArea = React.forwardRef<
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
className={cn('relative overflow-hidden', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
<ScrollAreaPrimitive.Viewport className='h-full w-full rounded-[inherit]'>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
));
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
>(({ className, orientation = 'vertical', ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none select-none transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||
className
|
||||
'flex touch-none select-none transition-colors',
|
||||
orientation === 'vertical' &&
|
||||
'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
||||
orientation === 'horizontal' &&
|
||||
'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className='relative flex-1 rounded-full bg-border' />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
));
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
export { ScrollArea, ScrollBar };
|
||||
|
@ -1,31 +1,31 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||
import * as React from 'react';
|
||||
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Separator = React.forwardRef<
|
||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||
>(
|
||||
(
|
||||
{ className, orientation = "horizontal", decorative = true, ...props },
|
||||
ref
|
||||
{ className, orientation = 'horizontal', decorative = true, ...props },
|
||||
ref,
|
||||
) => (
|
||||
<SeparatorPrimitive.Root
|
||||
ref={ref}
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"shrink-0 bg-border",
|
||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
||||
className
|
||||
'shrink-0 bg-border',
|
||||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
Separator.displayName = SeparatorPrimitive.Root.displayName
|
||||
),
|
||||
);
|
||||
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||
|
||||
export { Separator }
|
||||
export { Separator };
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import { updateSession } from '@/utils/supabase/middleware'
|
||||
import { type NextRequest } from 'next/server';
|
||||
import { updateSession } from '@/utils/supabase/middleware';
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
// update user's auth session
|
||||
return await updateSession(request)
|
||||
return await updateSession(request);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
@ -17,4 +17,4 @@ export const config = {
|
||||
*/
|
||||
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
||||
],
|
||||
}
|
||||
};
|
||||
|
@ -1,32 +1,33 @@
|
||||
'use server'
|
||||
'use server';
|
||||
|
||||
import 'server-only';
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { createClient } from '@/utils/supabase/server'
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
|
||||
export const login = async (formData: FormData) => {
|
||||
const supabase = await createClient()
|
||||
const supabase = await createClient();
|
||||
|
||||
// type-casting here for convenience
|
||||
// in practice, you should validate your inputs
|
||||
const data = {
|
||||
email: formData.get('email') as string,
|
||||
password: formData.get('password') as string,
|
||||
}
|
||||
};
|
||||
|
||||
const { error } = await supabase.auth.signInWithPassword(data)
|
||||
const { error } = await supabase.auth.signInWithPassword(data);
|
||||
|
||||
if (error) {
|
||||
redirect('/error')
|
||||
redirect('/error');
|
||||
}
|
||||
|
||||
revalidatePath('/', 'layout')
|
||||
revalidatePath('/', 'layout');
|
||||
redirect('/');
|
||||
};
|
||||
|
||||
export const signup = async (formData: FormData) => {
|
||||
const supabase = await createClient()
|
||||
const supabase = await createClient();
|
||||
|
||||
// type-casting here for convenience
|
||||
// in practice, you should validate your inputs
|
||||
@ -34,13 +35,14 @@ export const signup = async (formData: FormData) => {
|
||||
fullName: formData.get('fullName') as string,
|
||||
email: formData.get('email') as string,
|
||||
password: formData.get('password') as string,
|
||||
}
|
||||
};
|
||||
|
||||
const { error } = await supabase.auth.signUp(data)
|
||||
const { error } = await supabase.auth.signUp(data);
|
||||
|
||||
if (error) {
|
||||
redirect('/error')
|
||||
redirect('/error');
|
||||
}
|
||||
|
||||
revalidatePath('/', 'layout')
|
||||
revalidatePath('/', 'layout');
|
||||
redirect('/');
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { createBrowserClient } from '@supabase/ssr'
|
||||
import { createBrowserClient } from '@supabase/ssr';
|
||||
|
||||
export const createClient = () => {
|
||||
return createBrowserClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
||||
)
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
);
|
||||
};
|
||||
|
@ -1,31 +1,33 @@
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { NextResponse, type NextRequest } from 'next/server'
|
||||
import { createServerClient } from '@supabase/ssr';
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
|
||||
export async function updateSession(request: NextRequest) {
|
||||
let supabaseResponse = NextResponse.next({
|
||||
request,
|
||||
})
|
||||
});
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
getAll() {
|
||||
return request.cookies.getAll()
|
||||
return request.cookies.getAll();
|
||||
},
|
||||
setAll(cookiesToSet) {
|
||||
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
request.cookies.set(name, value),
|
||||
);
|
||||
supabaseResponse = NextResponse.next({
|
||||
request,
|
||||
})
|
||||
});
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
supabaseResponse.cookies.set(name, value, options)
|
||||
)
|
||||
supabaseResponse.cookies.set(name, value, options),
|
||||
);
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
// refreshing the auth token
|
||||
await supabase.auth.getUser()
|
||||
return supabaseResponse
|
||||
await supabase.auth.getUser();
|
||||
return supabaseResponse;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { cookies } from 'next/headers'
|
||||
import { createServerClient } from '@supabase/ssr';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
export async function createClient() {
|
||||
const cookieStore = await cookies()
|
||||
const cookieStore = await cookies();
|
||||
|
||||
// Create a server's supabase client with newly configured cookie,
|
||||
// which could be used to maintain user's session
|
||||
@ -12,13 +12,13 @@ export async function createClient() {
|
||||
{
|
||||
cookies: {
|
||||
getAll() {
|
||||
return cookieStore.getAll()
|
||||
return cookieStore.getAll();
|
||||
},
|
||||
setAll(cookiesToSet) {
|
||||
try {
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
cookieStore.set(name, value, options)
|
||||
)
|
||||
cookieStore.set(name, value, options),
|
||||
);
|
||||
} catch {
|
||||
// The `setAll` method was called from a Server Component.
|
||||
// This can be ignored if you have middleware refreshing
|
||||
@ -26,6 +26,6 @@ export async function createClient() {
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -2,72 +2,69 @@ import { type Config } from 'tailwindcss';
|
||||
import { fontFamily } from 'tailwindcss/defaultTheme';
|
||||
|
||||
export default {
|
||||
darkMode: ['class'],
|
||||
content: ['./src/**/*.tsx'],
|
||||
darkMode: ['class'],
|
||||
content: ['./src/**/*.tsx'],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: [
|
||||
'var(--font-geist-sans)',
|
||||
...fontFamily.sans
|
||||
]
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))'
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
primarydark: {
|
||||
DEFAULT: 'hsl(var(--primary-dark))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
primarylight: {
|
||||
DEFAULT: 'hsl(var(--primary-light))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))'
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))'
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))'
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
}
|
||||
}
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['var(--font-geist-sans)', ...fontFamily.sans],
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
primarydark: {
|
||||
DEFAULT: 'hsl(var(--primary-dark))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
primarylight: {
|
||||
DEFAULT: 'hsl(var(--primary-light))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
plugins: [require('tailwindcss-animate')],
|
||||
} satisfies Config;
|
||||
|
Loading…
x
Reference in New Issue
Block a user