diff --git a/app/account/login/code/page.tsx b/app/account/login/code/page.tsx new file mode 100644 index 0000000..05f075e --- /dev/null +++ b/app/account/login/code/page.tsx @@ -0,0 +1,119 @@ +"use client" + +import { useState, useEffect } from "react" +import { useRouter } from "next/navigation" +import { Key } from "lucide-react" +import Cookies from "js-cookie" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Alert, AlertDescription } from "@/components/ui/alert" + +export default function Login() { + const [magicCode, setMagicCode] = useState("") + const [errorMessage, setErrorMessage] = useState("") + const [isLoading, setIsLoading] = useState(false) + const router = useRouter() + + useEffect(() => { + const checkStatus = async () => { + setErrorMessage("") + setIsLoading(true) + + try { + const response = await fetch('http://localhost:3001/auth/validateStageTwo', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email: Cookies.get("email"), stageTwoKey: Cookies.get("stageTwoKey") }), + }) + + const data = await response.json() + + if (!response.ok || !data.success) { + router.push("/account/login") + } + } catch (error) { + console.error("There was a problem with checking the status of your request:", error) + setErrorMessage("An unexpected error occurred. Please try again.") + } finally { + setIsLoading(false) + } + } + + checkStatus() + }, [magicCode, router]) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsLoading(true) + setErrorMessage("") + + try { + const response = await fetch('http://localhost:3001/auth/validateMagicCode', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email: Cookies.get("email"), stageTwoKey: Cookies.get("stageTwoKey"), magicCode }), + }) + + const data = await response.json() + + if (response.ok && data.success) { + Cookies.set("key", data.key) + Cookies.remove("email") + Cookies.remove("stageTwoKey") + router.push("/account/dashboard") + } else { + setErrorMessage(data.error || "An unknown error occurred.") + } + } catch (error) { + console.error("There was a problem with checking the magic code:", error) + setErrorMessage("An unexpected error occurred. Please try again.") + } finally { + setIsLoading(false) + } + } + + return ( +
+ + + Enter Your Magic Code + Check your email for the code we sent you. + + +
+
+ setMagicCode(e.target.value)} + required + /> +
+ + {errorMessage && ( + + {errorMessage} + + )} +
+
+
+
+ ) +} + diff --git a/app/account/login/page.tsx b/app/account/login/page.tsx index b6e9e65..d366aa3 100644 --- a/app/account/login/page.tsx +++ b/app/account/login/page.tsx @@ -2,49 +2,92 @@ import Link from "next/link" import { useState } from "react" -import { TextField, Button, Flex, Text, Card } from "@radix-ui/themes" +import { useRouter } from "next/navigation" import { Mail } from "lucide-react" +import Cookies from "js-cookie" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Alert, AlertDescription } from "@/components/ui/alert" export default function Login() { const [email, setEmail] = useState("") + const [errorMessage, setErrorMessage] = useState("") + const [isLoading, setIsLoading] = useState(false) + const router = useRouter() - const handleSubmit = (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() + setIsLoading(true) + setErrorMessage("") + + try { + const response = await fetch('http://localhost:3001/auth/login', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email }), + }) + + const data = await response.json() + + if (response.ok && data.success) { + Cookies.set("stageTwoKey", data.stageTwoKey) + Cookies.set("email", email) + router.push("/account/login/code") + } else { + setErrorMessage(data.error || "An unknown error occurred.") + } + } catch (error) { + console.error("There was a problem with requesting a magic code:", error) + setErrorMessage("An unexpected error occurred. Please try again.") + } finally { + setIsLoading(false) + } } return ( - - -
- - Log in to your account - - - ) => setEmail(e.target.value)} - required - > - - - - - - - I don't have an account - - -
+ {errorMessage && ( + + {errorMessage} + + )} +
+ + I don't have an account + +
+ +
-
+ ) } diff --git a/bun.lockb b/bun.lockb index cd18a27..aaa1ec4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx new file mode 100644 index 0000000..5afd41d --- /dev/null +++ b/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..cabfbfc --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/components/ui/input.tsx b/components/ui/input.tsx new file mode 100644 index 0000000..69b64fb --- /dev/null +++ b/components/ui/input.tsx @@ -0,0 +1,22 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/package.json b/package.json index 7cf42eb..20980e3 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@radix-ui/themes": "^3.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "js-cookie": "^3.0.5", "lucide-react": "^0.474.0", "next": "15.1.6", "react": "^19.0.0", @@ -22,14 +23,15 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", + "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "postcss": "^8", - "tailwindcss": "^3.4.1", "eslint": "^9", "eslint-config-next": "15.1.6", - "@eslint/eslintrc": "^3" + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" } }