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 @@
+
+
+
\ 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 (
+
+ );
+};
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