Event Info
- Nmae: {event.name}
+ Name: {event.name}
Date: {event.date ? event.date.toDateString() : 'Upcoming'}
- Location: {event.location ? event.location : 'No location yet'}
+ Location: {event.location ? event.location.name : 'No location yet'}
{daysLeft !== null && (
{daysLeft} days until this event!
diff --git a/components/forms/FormWrapper.tsx b/components/forms/FormWrapper.tsx
new file mode 100644
index 0000000..5f8de63
--- /dev/null
+++ b/components/forms/FormWrapper.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
+
+export default function FormWrapper({
+ title,
+ description,
+ form
+}: {
+ title: string,
+ description?: string,
+ form: React.ReactNode
+}) {
+ return (
+
+
+ {title}
+ {description && (
+ Enter your email below to login
+ )}
+
+
+ {form}
+
+
+ )
+}
diff --git a/components/forms/LoginForm.tsx b/components/forms/LoginForm.tsx
new file mode 100644
index 0000000..6cf624e
--- /dev/null
+++ b/components/forms/LoginForm.tsx
@@ -0,0 +1,77 @@
+'use client'
+import React, { useState } from 'react'
+import { Label } from '../ui/label'
+import { Input } from '../ui/input'
+import Link from 'next/link'
+import { Button } from '../ui/button'
+import { signIn } from 'next-auth/react'
+import { useRouter } from 'next/navigation'
+
+export default function LoginForm() {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('')
+ const [error, setError] = useState('');
+ const router = useRouter();
+
+ async function handleSubmit(e: React.FormEvent) {
+ e.preventDefault();
+
+ const result = await signIn('credentials', {
+ redirect: false,
+ email,
+ password,
+ })
+
+ console.log('[CLIENT] signIn result:', result)
+
+ if (result?.error) {
+ setError(result.error)
+ } else {
+ router.push('/dashboard')
+ }
+ }
+ return (
+
+ )
+}
diff --git a/components/forms/SignUpForm.tsx b/components/forms/SignUpForm.tsx
new file mode 100644
index 0000000..76166a4
--- /dev/null
+++ b/components/forms/SignUpForm.tsx
@@ -0,0 +1,78 @@
+'use client'
+import { useRouter } from 'next/navigation'
+import React, { useState } from 'react'
+import { Label } from '../ui/label'
+import { Input } from '../ui/input'
+import { Button } from '../ui/button'
+
+interface Props {
+ invite: {
+ token: string
+ email: string
+ role: 'COUPLE' | 'PLANNER' | 'GUEST'
+ }
+}
+
+export default function SignUpForm({ invite }: Props) {
+ const [username, setUsername] = useState('')
+ const [password, setPassword] = useState('')
+ const [error, setError] = useState('')
+ const router = useRouter()
+
+ async function handleSubmit(e: React.FormEvent) {
+ e.preventDefault()
+
+ const res = await fetch('/api/signup/from-invite', {
+ method: 'POST',
+ body: JSON.stringify({ token: invite.token, username, password }),
+ headers: { 'Content-Type': 'application/json' },
+ })
+
+ if (res.ok) {
+ router.push('/login')
+ } else {
+ const { message } = await res.json()
+ setError(message || 'Signup failed')
+ }
+ }
+ return (
+
+ )
+}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
index a2df8dc..f7caee6 100644
--- a/components/ui/button.tsx
+++ b/components/ui/button.tsx
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
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-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive hover:cursor-pointer",
{
variants: {
variant: {
diff --git a/lib/queries.ts b/lib/queries.ts
index 1069188..5930a13 100644
--- a/lib/queries.ts
+++ b/lib/queries.ts
@@ -9,8 +9,9 @@ export const queries = {
id: true,
username: true
}
- }
- }
+ },
+ location: true
+ },
})
return allEvents;
@@ -74,6 +75,7 @@ export const queries = {
todos: {
orderBy: { dueDate: 'asc' },
},
+ location: true
}
})
return event
diff --git a/prisma/migrations/20250710130603_add_location_model/migration.sql b/prisma/migrations/20250710130603_add_location_model/migration.sql
new file mode 100644
index 0000000..0253f52
--- /dev/null
+++ b/prisma/migrations/20250710130603_add_location_model/migration.sql
@@ -0,0 +1,27 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `location` on the `Event` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE "Event" DROP COLUMN "location",
+ADD COLUMN "locationid" TEXT;
+
+-- CreateTable
+CREATE TABLE "Location" (
+ "id" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "address" TEXT NOT NULL,
+ "city" TEXT NOT NULL,
+ "state" TEXT NOT NULL,
+ "postalCode" TEXT NOT NULL,
+ "country" TEXT NOT NULL DEFAULT 'United States',
+ "phone" TEXT,
+ "email" TEXT,
+
+ CONSTRAINT "Location_pkey" PRIMARY KEY ("id")
+);
+
+-- AddForeignKey
+ALTER TABLE "Event" ADD CONSTRAINT "Event_locationid_fkey" FOREIGN KEY ("locationid") REFERENCES "Location"("id") ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 000420f..ed23dd7 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -28,16 +28,31 @@ model Event {
id String @id @default(cuid())
name String
date DateTime?
- location String?
+ location Location? @relation(fields: [locationid], references: [id])
+ locationid String?
creator User @relation("EventCreator", fields: [creatorId], references: [id])
creatorId String
guests Guest[]
eventGuests EventGuest[]
notes String?
- todos EventTodo[]
+ todos EventTodo[]
createdAt DateTime @default(now())
}
+model Location {
+ id String @id @default(cuid())
+ name String
+ address String
+ city String
+ state String
+ postalCode String
+ country String @default("United States")
+ phone String?
+ email String?
+
+ Event Event[]
+}
+
model Guest {
id String @id @default(cuid())
event Event @relation(fields: [eventId], references: [id])
diff --git a/types.d.ts b/types.d.ts
index 21a0668..c9cb674 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -15,7 +15,7 @@ interface EventProps {
id: string
name: string
date?: Date | null
- location?: string | null
+ location?: EventLocation | null
creator: { id: string; username: string; }
createdAt: Date; date: Date | null;
creatorId: string;
@@ -44,7 +44,7 @@ interface EventData {
id: string
name: string
date: Date | null
- location: string | null
+ location: EventLocation | null
creatorId: string
createdAt: string
creator: Creator
@@ -71,4 +71,15 @@ type User = {
name?: string
username: string
role: 'COUPLE' | 'PLANNER' | 'GUEST'
+}
+
+interface EventLocation {
+ id: string
+ name: string
+ address: string
+ city: string
+ state: string
+ country: string
+ phone: string | null
+ email: string | null
}
\ No newline at end of file