diff --git a/bun.lockb b/bun.lockb index d2920a4..7a3b3f5 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 3f94dad..7b4afd5 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "react-dom": "^19.1.0", "react-hook-form": "^7.58.1", "react-resizable-panels": "^3.0.3", - "recharts": "^3.0.0", + "recharts": "^3.0.1", "sonner": "^2.0.5", "tailwind-merge": "^3.3.1", "vaul": "^1.1.2", @@ -81,7 +81,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", - "@tailwindcss/postcss": "^4.1.10", + "@tailwindcss/postcss": "^4.1.11", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/node": "^20.19.1", @@ -96,7 +96,7 @@ "postcss": "^8.5.6", "prettier": "^3.6.1", "prettier-plugin-tailwindcss": "^0.6.13", - "tailwindcss": "^4.1.10", + "tailwindcss": "^4.1.11", "tw-animate-css": "^1.3.4", "typescript": "^5.8.3", "typescript-eslint": "^8.35.0" diff --git a/public/icons/auth/apple.svg b/public/icons/auth/apple.svg new file mode 100644 index 0000000..381f84a --- /dev/null +++ b/public/icons/auth/apple.svg @@ -0,0 +1,19 @@ + + + + + apple [#173] + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/icons/auth/microsoft.svg b/public/icons/auth/microsoft.svg new file mode 100644 index 0000000..89ff5b9 --- /dev/null +++ b/public/icons/auth/microsoft.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/components/default/auth/buttons/client/index.tsx b/src/components/default/auth/buttons/client/index.tsx new file mode 100644 index 0000000..d13fac5 --- /dev/null +++ b/src/components/default/auth/buttons/client/index.tsx @@ -0,0 +1,3 @@ +export { SignInWithApple } from './sign-in-with-apple'; +export { SignInWithMicrosoft } from './sign-in-with-microsoft'; +export { SignInButton } from './sign-in'; diff --git a/src/components/default/auth/buttons/client/SignInWithApple.tsx b/src/components/default/auth/buttons/client/sign-in-with-apple.tsx similarity index 94% rename from src/components/default/auth/buttons/client/SignInWithApple.tsx rename to src/components/default/auth/buttons/client/sign-in-with-apple.tsx index 3cff053..afea7c1 100644 --- a/src/components/default/auth/buttons/client/SignInWithApple.tsx +++ b/src/components/default/auth/buttons/client/sign-in-with-apple.tsx @@ -1,5 +1,5 @@ 'use client'; -import { signInWithApple, getProfile, updateProfile } from '@/lib/queries'; +import { signInWithApple } from '@/lib/queries'; import { StatusMessage, SubmitButton } from '@/components/default/forms'; import { useAuth } from '@/lib/hooks/context'; import { useRouter } from 'next/navigation'; @@ -85,7 +85,7 @@ export const SignInWithApple = ({ width={imageProps.width} height={imageProps.height} /> -

Sign In with Apple

+

Sign In with Apple

{statusMessage && } diff --git a/src/components/default/auth/buttons/client/sign-in-with-microsoft.tsx b/src/components/default/auth/buttons/client/sign-in-with-microsoft.tsx new file mode 100644 index 0000000..b109238 --- /dev/null +++ b/src/components/default/auth/buttons/client/sign-in-with-microsoft.tsx @@ -0,0 +1,94 @@ +'use client'; +import { signInWithMicrosoft } from '@/lib/queries'; +import { StatusMessage, SubmitButton } from '@/components/default/forms'; +import { useAuth } from '@/lib/hooks/context'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import Image from 'next/image'; +import { type buttonVariants } from '@/components/ui'; +import { type ComponentProps } from 'react'; +import { type VariantProps } from 'class-variance-authority'; +import { useSupabaseClient } from '@/utils/supabase'; + +type ButtonProps = ComponentProps<'button'> & VariantProps; +type ImageProps = { + src: string; + alt: string; + className?: string; + width?: number; + height?: number; +} +type FormProps = ComponentProps<'form'>; +type TextProps = ComponentProps<'p'>; +type SignInWithMicrosoftProps = { + buttonProps?: ButtonProps; + imageProps?: ImageProps; + formProps?: FormProps; + textProps?: TextProps; +}; + +export const SignInWithMicrosoft = ({ + buttonProps = { + className: 'w-full cursor-pointer', + type: 'submit', + }, + imageProps = { + src: '/icons/auth/microsoft.svg', + alt: 'Apple', + className: '', + width: 24, + height: 24, + }, + formProps = { + className: 'my-4', + }, + textProps = { + className: 'text-[1.0rem]', + }, +} : SignInWithMicrosoftProps) => { + const router = useRouter(); + const { loading, refreshUser } = useAuth(); + const [statusMessage, setStatusMessage] = useState(''); + const [ isLoading, setIsLoading ] = useState(false); + const supabase = useSupabaseClient(); + + const handleSignInWithMicrosoft = async (e: React.FormEvent) => { + e.preventDefault(); + try { + if (!supabase) throw new Error('Supabase client not found'); + setStatusMessage(''); + setIsLoading(true); + const result = await signInWithMicrosoft(supabase); + if (result.data.url) window.location.href = result.data.url; + else setStatusMessage(`There was a problem signing in with Apple!`); + } catch (error) { + setStatusMessage(`Error signing in: ${error as string}`); + } finally { + setIsLoading(false); + await refreshUser(); + router.push(''); + } + }; + + return ( +
+ +
+ {imageProps.alt} +

Sign In with Apple

+
+
+ {statusMessage && } + + ); +}; diff --git a/src/components/default/auth/buttons/client/SignIn.tsx b/src/components/default/auth/buttons/client/sign-in.tsx similarity index 100% rename from src/components/default/auth/buttons/client/SignIn.tsx rename to src/components/default/auth/buttons/client/sign-in.tsx diff --git a/src/components/default/auth/cards/client/forgot-password.tsx b/src/components/default/auth/cards/client/forgot-password.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/default/auth/cards/client/profile.tsx b/src/components/default/auth/cards/client/profile.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/default/auth/cards/client/sign-in.tsx b/src/components/default/auth/cards/client/sign-in.tsx new file mode 100755 index 0000000..83fb989 --- /dev/null +++ b/src/components/default/auth/cards/client/sign-in.tsx @@ -0,0 +1,126 @@ +'use client'; + +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import Link from 'next/link'; +import { signIn, signUp } from '@/lib/queries'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useAuth } from '@/lib/hooks/context'; +import { useSupabaseClient } from '@/utils/supabase'; +import { StatusMessage, SubmitButton } from '@/components/default/forms'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, + Input, + Separator, + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from '@/components/ui'; +import { + SignInWithApple, + SignInWithMicrosoft +} from '@/components/default/auth/buttons/client'; + +const signInFormSchema = z.object({ + email: z.string().email({ + message: 'Please enter a valid email address.' + }), + password: z.string().min(8, { + message: 'Password must be at least 8 characters.' + }), +}); + +const signUpformSchema = z + .object({ + name: z.string().min(2, { + message: 'Name must be at least 2 characters.', + }), + email: z.string().email({ + message: 'Please enter a valid email address.', + }), + password: z.string().min(8, { + message: 'Password must be at least 8 characters.', + }), + confirmPassword: z.string().min(8, { + message: 'Password must be at least 8 characters.', + }), + }) + .refine((data) => data.password === data.confirmPassword, { + message: 'Passwords do not match!', + path: ['confirmPassword'], + }); + +export const SignInCard = () => { + const router = useRouter(); + const { isAuthenticated, loading, refreshUser } = useAuth(); + const [statusMessage, setStatusMessage] = useState(''); + const supabase = useSupabaseClient(); + + const signInForm = useForm>({ + resolver: zodResolver(signInFormSchema), + defaultValues: { + email: '', + password: '', + }, + }); + + const signUpForm = useForm>({ + resolver: zodResolver(signUpformSchema), + defaultValues: { + name: '', + email: '', + password: '', + confirmPassword: '', + }, + }); + + useEffect(() => { + if (isAuthenticated) router.push('/'); + }, [isAuthenticated, router]); + + const handleSignIn = async (values: z.infer) => { + try { + setStatusMessage(''); + const formData = new FormData(); + formData.append('email', values.email); + formData.append('password', values.password); + if (!supabase) throw new Error('Supabase client not found'); + const result = await signIn(supabase, formData); + if (result.error) throw new Error(result.error.message); + else if (result.data) { + await refreshUser(); + signInForm.reset(); + router.push(''); + } + } catch (error) { + setStatusMessage(`Error signing in: ${error as string}`); + } + }; + + const handleSignUp = async (values: z.infer) => { + try { + + } catch { + + } + }; + + return ( + + ); + +}; + diff --git a/src/components/default/forms/index.tsx b/src/components/default/forms/index.tsx index e1e1b64..1fc702a 100644 --- a/src/components/default/forms/index.tsx +++ b/src/components/default/forms/index.tsx @@ -1,2 +1,2 @@ -export { StatusMessage } from './StatusMessage'; -export { SubmitButton } from './SubmitButton'; +export { StatusMessage } from './status-message'; +export { SubmitButton } from './submit-button'; diff --git a/src/components/default/forms/StatusMessage.tsx b/src/components/default/forms/status-message.tsx similarity index 100% rename from src/components/default/forms/StatusMessage.tsx rename to src/components/default/forms/status-message.tsx diff --git a/src/components/default/forms/SubmitButton.tsx b/src/components/default/forms/submit-button.tsx similarity index 100% rename from src/components/default/forms/SubmitButton.tsx rename to src/components/default/forms/submit-button.tsx