From 27590f9509094cf8542c41d32c27624ce6bfd6ac Mon Sep 17 00:00:00 2001 From: Brian Nelson Date: Thu, 24 Jul 2025 09:42:57 -0400 Subject: [PATCH] venues and ui changes --- README.md | 17 +++ app/(auth)/dashboard/page.tsx | 82 ++++++------- app/(auth)/locations/page.tsx | 13 -- app/(auth)/venues/page.tsx | 13 ++ app/api/events/[eventId]/fetch/route.ts | 47 ++++++++ app/api/events/[eventId]/route.ts | 38 +++--- app/api/venues/create/route.ts | 33 +++++ app/api/venues/fetch/route.ts | 12 ++ components/EventInfoQuickView.tsx | 23 ++-- components/app-sidebar.tsx | 2 +- components/dashboard/DashboardEvents.tsx | 51 ++++++++ components/dashboard/DashboardGuestBook.tsx | 41 +++++++ components/dialogs/DialogWrapper.tsx | 30 +++++ components/events/EventDashboard.tsx | 4 +- components/events/EventInfo.tsx | 39 +++++- components/forms/CreateVenueForm.tsx | 113 ++++++++++++++++++ components/forms/EditEventForm.tsx | 93 ++++++++++++++ components/tables/LocationsTable.tsx | 65 ---------- components/tables/VenuesTable.tsx | 99 +++++++++++++++ lib/helper/fetchVenues.ts | 5 + lib/queries.ts | 30 ++++- .../migration.sql | 34 ++++++ prisma/schema.prisma | 20 ++-- types.d.ts | 17 ++- 24 files changed, 757 insertions(+), 164 deletions(-) delete mode 100644 app/(auth)/locations/page.tsx create mode 100644 app/(auth)/venues/page.tsx create mode 100644 app/api/events/[eventId]/fetch/route.ts create mode 100644 app/api/venues/create/route.ts create mode 100644 app/api/venues/fetch/route.ts create mode 100644 components/dashboard/DashboardEvents.tsx create mode 100644 components/dashboard/DashboardGuestBook.tsx create mode 100644 components/dialogs/DialogWrapper.tsx create mode 100644 components/forms/CreateVenueForm.tsx create mode 100644 components/forms/EditEventForm.tsx delete mode 100644 components/tables/LocationsTable.tsx create mode 100644 components/tables/VenuesTable.tsx create mode 100644 lib/helper/fetchVenues.ts create mode 100644 prisma/migrations/20250723173825_updated_location_name_to_venue/migration.sql diff --git a/README.md b/README.md index a0746a7..ea934d3 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,23 @@ My goal for this project is to be an all-in-one self hosted event planner for ma - Inline editing with live preview - Task list per event with due dates & completion toggle +#### 7.24.25 – Notes and Tasks +**Major Update** +- Added Venues + - Venues are significant locations which can be added to an event. +| Column | Default | Required | Type | +| --- | --- | --- | --- | +| Name | null | Yes | String | +| Address | null | Yes | String | +| City/Town | null | Yes | String | +| State | null | Yes | String | +| Postal/Area Code | null | Yes | String | +| Country | 'United States' | Yes | String | +| Phone | null | No | String | +| Email | null | No | String | +- UI changes to Dashboard to make use of Shadcn components + + ## Getting Started This app is fully deployable with Docker or runnable in development with Node. diff --git a/app/(auth)/dashboard/page.tsx b/app/(auth)/dashboard/page.tsx index 4844f25..dbf9d5c 100644 --- a/app/(auth)/dashboard/page.tsx +++ b/app/(auth)/dashboard/page.tsx @@ -1,55 +1,57 @@ -import AddFirstGuestBookEntryClient from '@/components/AddFirstGuestBookEntryClient' -import CreateEventClient from '@/components/CreateEventClient' -import EventInfoQuickView from '@/components/EventInfoQuickView' -import GuestBookQuickView from '@/components/GuestBookQuickView' +import DashboardEvents from '@/components/dashboard/DashboardEvents' +import DashboardGuestBook from '@/components/dashboard/DashboardGuestBook' import { queries } from '@/lib/queries' -import Link from 'next/link' import React from 'react' export default async function DashboardPage() { - const events = await queries.fetchEvents(); + const events = await queries.fetchQuickViewEvents(); const guestBookData = await queries.fetchGuestBookEntries({ takeOnlyRecent: 5 }); const guestBookEntries = Array.isArray(guestBookData) ? guestBookData : guestBookData.entries; return ( -
-
-
-
-

Your Events

- + <> + +
+ + + {/*
+
+
+

Your Events

+ +
+ {!events.length && <>You don't have any events yet. Create your first event.} +
+ {events.map((item) => ( + + ))} +
- {!events.length && <>You don't have any events yet. Create your first event.} -
- {events.map((item) => ( - +
+ + View all + +
+
+
+
+

Guest Book

+ + View All + +
+
+ {!guestBookEntries.length && } + {guestBookEntries.map(entry => ( + ))}
-
-
- - View all - -
+
*/}
-
-
-

Guest Book

- - View All - -
-
- {!guestBookEntries.length && } - {guestBookEntries.map(entry => ( - - ))} -
-
-
+ ) } diff --git a/app/(auth)/locations/page.tsx b/app/(auth)/locations/page.tsx deleted file mode 100644 index da1f94b..0000000 --- a/app/(auth)/locations/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import LocationsTable from '@/components/tables/LocationsTable' -import { queries } from '@/lib/queries' -import React from 'react' - -export default async function LocationsPage() { - const eventLocations = await queries.fetchAllLocations() - - return ( -
- -
- ) -} diff --git a/app/(auth)/venues/page.tsx b/app/(auth)/venues/page.tsx new file mode 100644 index 0000000..e877560 --- /dev/null +++ b/app/(auth)/venues/page.tsx @@ -0,0 +1,13 @@ +import VenuesTable from '@/components/tables/VenuesTable' +import { queries } from '@/lib/queries' +import React from 'react' + +export default async function LocationsPage() { + const venues = await queries.fetchAllLocations() + + return ( +
+ +
+ ) +} diff --git a/app/api/events/[eventId]/fetch/route.ts b/app/api/events/[eventId]/fetch/route.ts new file mode 100644 index 0000000..6a6a329 --- /dev/null +++ b/app/api/events/[eventId]/fetch/route.ts @@ -0,0 +1,47 @@ +import { NextRequest, NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getServerSession } from 'next-auth' +import { authOptions } from '@/app/api/auth/[...nextauth]/route' + +export async function GET( + req: NextRequest, + { params }: { params: { eventId: string } } +) { + const session = await getServerSession(authOptions) + + if (!session?.user) { + return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }) + } + + try { + const event = await prisma.event.findUnique({ + where: { id: params.eventId }, + include: { + creator: { + select: { id: true, name: true, email: true, role: true } + }, + venue: true, + todos: { + orderBy: [ + { complete: 'asc' }, + { dueDate: 'asc' } + ] + }, + eventGuests: { + include: { + guestBookEntry: true + } + } + } + }) + + if (!event) { + return NextResponse.json({ message: 'Event not found' }, { status: 404 }) + } + + return NextResponse.json(event) + } catch (err) { + console.error(err) + return NextResponse.json({ message: 'Error fetching event' }, { status: 500 }) + } +} diff --git a/app/api/events/[eventId]/route.ts b/app/api/events/[eventId]/route.ts index cf85bda..458b608 100644 --- a/app/api/events/[eventId]/route.ts +++ b/app/api/events/[eventId]/route.ts @@ -1,27 +1,29 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { mutations } from '@/lib/mutations'; -import { getServerSession } from 'next-auth'; -import { authOptions } from '../../auth/[...nextauth]/route'; +import { NextRequest, NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getServerSession } from 'next-auth' +import { authOptions } from '@/app/api/auth/[...nextauth]/route' export async function PATCH(req: NextRequest, { params }: { params: { eventId: string } }) { - const session = await getServerSession(authOptions); + const session = await getServerSession(authOptions) if (!session?.user) { - return new NextResponse('Unauthorized', { status: 401 }); + return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }) } - const eventId = params.eventId; - const body = await req.json(); + const body = await req.json() try { - const updated = await mutations.updateEvent(eventId, { - name: body.name, - date: body.date, - location: body.location, - notes: body.notes, - }); - return NextResponse.json(updated); - } catch (error) { - console.error('[PATCH EVENT]', error); - return new NextResponse('Failed to update event', { status: 500 }); + const updated = await prisma.event.update({ + where: { id: params.eventId }, + data: { + name: body.name, + date: body.date ? new Date(body.date) : undefined, + venueId: body.venueId || null, + }, + }) + + return NextResponse.json(updated) + } catch (err) { + console.error(err) + return NextResponse.json({ message: 'Error updating event' }, { status: 500 }) } } diff --git a/app/api/venues/create/route.ts b/app/api/venues/create/route.ts new file mode 100644 index 0000000..c330ffb --- /dev/null +++ b/app/api/venues/create/route.ts @@ -0,0 +1,33 @@ +import { NextRequest, NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getServerSession } from 'next-auth' +import { authOptions } from '@/app/api/auth/[...nextauth]/route' + +export async function POST(req: NextRequest) { + const session = await getServerSession(authOptions) + if (!session?.user) { + return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }) + } + + const body = await req.json() + + try { + const venue = await prisma.venue.create({ + data: { + name: body.name, + address: body.address, + city: body.city, + state: body.state, + postalCode: body.postalCode, + country: body.country, + phone: body.phone || undefined, + email: body.email || undefined + } + }) + + return NextResponse.json(venue) + } catch (err) { + console.error(err) + return NextResponse.json({ message: 'Error creating venue' }, { status: 500 }) + } +} diff --git a/app/api/venues/fetch/route.ts b/app/api/venues/fetch/route.ts new file mode 100644 index 0000000..9827622 --- /dev/null +++ b/app/api/venues/fetch/route.ts @@ -0,0 +1,12 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' + +export async function GET() { + try { + const venues = await prisma.venue.findMany() + return NextResponse.json(venues) + } catch (error) { + console.error('Failed to fetch venues:', error) + return new NextResponse('Failed to fetch venues', { status: 500 }) + } +} diff --git a/components/EventInfoQuickView.tsx b/components/EventInfoQuickView.tsx index 83c31d5..075e420 100644 --- a/components/EventInfoQuickView.tsx +++ b/components/EventInfoQuickView.tsx @@ -1,15 +1,22 @@ import Link from 'next/link' import React from 'react' +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from './ui/card' -export default function EventInfoQuickView(props: EventProps) { +export default function EventInfoQuickView(props: QucikEventProps) { return ( - -
-

{props.name}

-

Date: {props.date ? props.date.toDateString() : 'null'}

-

Location: {props.location ? props.location.name : 'null'}

-

Created By: {props.creator.username}

-
+ + + + {props.name} + + +

Date: {props.date ? props.date.toDateString() : 'null'}

+

Location: {props.venue ? props.venue.name : 'null'}

+
+ +

Created By: {props.creator.username}

+
+
) } diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index eb4ea2d..6912bb8 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -48,7 +48,7 @@ const data = { }, { title: "Locations", - url: "/locations", + url: "/venues", icon: IconBuildingArch, }, ], diff --git a/components/dashboard/DashboardEvents.tsx b/components/dashboard/DashboardEvents.tsx new file mode 100644 index 0000000..3d9aa8c --- /dev/null +++ b/components/dashboard/DashboardEvents.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '../ui/card' +import { Button } from '../ui/button' +import Link from 'next/link' +import EventInfoQuickView from '../EventInfoQuickView' + +interface EventsProps { + events: { + id: string + name: string + date?: Date | null + creator: { + id: string, + username: string + }, + venue?: { + name: string + } | null + }[] +} + +export default function DashboardEvents({events}: EventsProps) { + return ( + + +
+ + Your Events + + +
+
+ +
+ {events.map((item) => ( + + ))} +
+
+ +
+ View all +
+
+
+ ) +} diff --git a/components/dashboard/DashboardGuestBook.tsx b/components/dashboard/DashboardGuestBook.tsx new file mode 100644 index 0000000..6cc7873 --- /dev/null +++ b/components/dashboard/DashboardGuestBook.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { Card, CardContent, CardHeader, CardTitle } from '../ui/card' +import Link from 'next/link' +import AddFirstGuestBookEntryClient from '../AddFirstGuestBookEntryClient' +import GuestBookQuickView from '../GuestBookQuickView' + +interface GuestBookEntryProps { + guestBookEntries: { + id: string + fName: string + lName: string + email?: string | null + phone?: string | null + address?: string | null + notes?: string | null + side: string + congratulated?: boolean | null + createdAt: Date + }[] +} + +export default function DashboardGuestBook(guestBookEntries: GuestBookEntryProps) { + return ( + + +
+ + Guest Book + + View All +
+
+ + {!guestBookEntries.guestBookEntries.length && } + {guestBookEntries.guestBookEntries.map(entry => ( + + ))} + +
+ ) +} diff --git a/components/dialogs/DialogWrapper.tsx b/components/dialogs/DialogWrapper.tsx new file mode 100644 index 0000000..afa6db0 --- /dev/null +++ b/components/dialogs/DialogWrapper.tsx @@ -0,0 +1,30 @@ +'use client' +import React from 'react' +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog' + +export default function DialogWrapper({ + title, + description, + form, + open, + onOpenChange, + +}: { + title: string, + description?: string, + form: React.ReactNode, + open: boolean, + onOpenChange: (open: boolean) => void +}) { + return ( + + e.preventDefault()}> + + {title} + {description} + +
{form}
+
+
+ ) +} diff --git a/components/events/EventDashboard.tsx b/components/events/EventDashboard.tsx index f6b3049..b5d172b 100644 --- a/components/events/EventDashboard.tsx +++ b/components/events/EventDashboard.tsx @@ -13,7 +13,7 @@ interface Props { export default function EventDashboard({ event }: Props) { const [todos, setTodos] = useState(event.todos) - async function refreshTodos() { + async function refreshTodos() { try { const data = await fetchEventTodos(event.id) setTodos(data) @@ -22,8 +22,6 @@ export default function EventDashboard({ event }: Props) { } } - console.log(todos) - return (
diff --git a/components/events/EventInfo.tsx b/components/events/EventInfo.tsx index 2638767..eda16db 100644 --- a/components/events/EventInfo.tsx +++ b/components/events/EventInfo.tsx @@ -4,6 +4,8 @@ import { Card, CardContent } from '../ui/card' import { getDaysUntilEvent } from '@/lib/helper/getDaysUntilEvent' import { Button } from '../ui/button' import EventNotesEditor from '../EventNotesEditor' +import DialogWrapper from '../dialogs/DialogWrapper' +import EditEventForm from '../forms/EditEventForm' interface EventProps { event: EventData @@ -11,6 +13,7 @@ interface EventProps { export default function EventInfo({ event }: EventProps) { const [daysLeft, setDaysLeft] = useState(null) + const [isDialogOpen, setIsDialogOpen] = useState(false) useEffect(() => { if (event.date) { @@ -19,6 +22,19 @@ export default function EventInfo({ event }: EventProps) { } }, [event.date]) + async function refreshEventData(eventId: string) { + try { + const res = await fetch(`/api/events/${eventId}/fetch`) + if (!res.ok) throw new Error('Failed to fetch event data') + const data = await res.json() + return data + } catch (err) { + console.error('Failed to refresh event data:', err) + return null + } + } + + return (
@@ -26,13 +42,18 @@ export default function EventInfo({ event }: EventProps) {

Event Info

Name: {event.name}

Date: {event.date ? event.date.toDateString() : 'Upcoming'}

-

Location: {event.location ? event.location.name : 'No location yet'}

+

Venue: {event.venue ? event.venue.name : 'No location yet'}

{daysLeft !== null && (

{daysLeft} days until this event!

)} - +
@@ -45,6 +66,20 @@ export default function EventInfo({ event }: EventProps) { /> + + { + await refreshEventData(event.id) + setIsDialogOpen(false) + }} + /> + } + />
) } diff --git a/components/forms/CreateVenueForm.tsx b/components/forms/CreateVenueForm.tsx new file mode 100644 index 0000000..3402f70 --- /dev/null +++ b/components/forms/CreateVenueForm.tsx @@ -0,0 +1,113 @@ +'use client' + +import React, { useState } from 'react' +import { Input } from '../ui/input' +import { Button } from '../ui/button' +import { Label } from '../ui/label' +import { toast } from 'sonner' + +interface CreateVenueFormProps { + onSuccess?: () => void +} + +export default function CreateVenueForm({ onSuccess }: CreateVenueFormProps) { + const [formData, setFormData] = useState({ + name: '', + address: '', + city: '', + state: '', + postalCode: '', + country: 'United States', + phone: '', + email: '' + }) + + const [loading, setLoading] = useState(false) + + function handleChange(e: React.ChangeEvent) { + const { name, value } = e.target + setFormData(prev => ({ ...prev, [name]: value })) + } + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + + if (!formData.name || !formData.address || !formData.city || !formData.state || !formData.postalCode) { + toast.error('Please fill in all required fields') + return + } + + setLoading(true) + try { + const res = await fetch('/api/venues/create', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(formData) + }) + + if (!res.ok) throw new Error('Failed to create venue') + + toast.success('Venue created!') + setFormData({ + name: '', + address: '', + city: '', + state: '', + postalCode: '', + country: 'United States', + phone: '', + email: '' + }) + + if (onSuccess) onSuccess() + } catch (err) { + console.error(err) + toast.error('Something went wrong') + } finally { + setLoading(false) + } + } + + return ( +
+ {[ + { label: 'Name', name: 'name', required: true }, + { label: 'Address', name: 'address', required: true }, + { label: 'City', name: 'city', required: true }, + { label: 'State', name: 'state', required: true }, + { label: 'Postal Code', name: 'postalCode', required: true }, + { label: 'Country', name: 'country', required: false }, + { label: 'Phone', name: 'phone', required: false }, + { label: 'Email', name: 'email', required: false } + ].map(field => ( +
+ + +
+ ))} + {/*
+ + +
*/} + +
+ ) +} diff --git a/components/forms/EditEventForm.tsx b/components/forms/EditEventForm.tsx new file mode 100644 index 0000000..ddcc109 --- /dev/null +++ b/components/forms/EditEventForm.tsx @@ -0,0 +1,93 @@ +'use client' + +import React, { useState, useEffect } from 'react' +import { Input } from '../ui/input' +import { Label } from '../ui/label' +import { Button } from '../ui/button' +import { toast } from 'sonner' + +interface EditEventFormProps { + event: EventData + onSuccess?: () => void +} + +export default function EditEventForm({ event, onSuccess }: EditEventFormProps) { + const [formData, setFormData] = useState({ + name: event.name, + date: event.date?.toISOString().substring(0, 10) || '', + venueId: event.venue?.id || '' + }) + + const [venues, setVenues] = useState<{ id: string; name: string }[]>([]) + const [loading, setLoading] = useState(false) + + useEffect(() => { + async function fetchVenues() { + const res = await fetch('/api/venues/fetch') + const data = await res.json() + setVenues(data) + } + fetchVenues() + }, []) + + function handleChange(e: React.ChangeEvent) { + const { name, value } = e.target + setFormData(prev => ({ ...prev, [name]: value })) + } + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + setLoading(true) + + try { + const res = await fetch(`/api/events/${event.id}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(formData) + }) + + if (!res.ok) throw new Error('Failed to update event') + toast.success('Event updated!') + + if (onSuccess) onSuccess() + } catch (err) { + console.error(err) + toast.error('Something went wrong') + } finally { + setLoading(false) + } + } + + return ( +
+
+ + +
+
+ + +
+
+ + +
+ +
+ ) +} diff --git a/components/tables/LocationsTable.tsx b/components/tables/LocationsTable.tsx deleted file mode 100644 index 36cf61a..0000000 --- a/components/tables/LocationsTable.tsx +++ /dev/null @@ -1,65 +0,0 @@ -'use client' - -import { ColumnDef } from '@tanstack/react-table' -import { DataTable } from './DataTable' - -interface LocationRow { - id: string - name: string - address: string - city: string - state: string - postalCode: string - country: string - phone?: string | null - email?: string | null -} - -interface Props { - eventLocations: LocationRow[] -} - -const columns: ColumnDef[] = [ - { - accessorKey: 'name', - header: 'Name' - }, - { - accessorKey: 'address', - header: 'Address' - }, - { - accessorKey: 'city', - header: 'City' - }, - { - accessorKey: 'state', - header: 'State' - }, - { - accessorKey: 'postalCode', - header: 'Postal Code' - }, - { - accessorKey: 'country', - header: 'Country' - }, - { - accessorKey: 'phone', - header: 'Phone', - cell: ({ row }) => row.original.phone || '—' - }, - { - accessorKey: 'email', - header: 'Email', - cell: ({ row }) => row.original.email || '—' - } -] - -export default function LocationsTable({ eventLocations }: Props) { - return ( -
- -
- ) -} diff --git a/components/tables/VenuesTable.tsx b/components/tables/VenuesTable.tsx new file mode 100644 index 0000000..e8e5689 --- /dev/null +++ b/components/tables/VenuesTable.tsx @@ -0,0 +1,99 @@ +'use client' + +import { ColumnDef } from '@tanstack/react-table' +import { DataTable } from './DataTable' +import { Button } from '../ui/button' +import DialogWrapper from '../dialogs/DialogWrapper' +import CreateVenueForm from '../forms/CreateVenueForm' +import { useState } from 'react' +import { fetchVenuesClient } from '@/lib/helper/fetchVenues' + +interface LocationRow { + id: string + name: string + address: string + city: string + state: string + postalCode: string + country: string + phone?: string | null + email?: string | null +} + +interface Props { + eventLocations: LocationRow[] +} + +const columns: ColumnDef[] = [ + { + accessorKey: 'name', + header: 'Name' + }, + { + accessorKey: 'address', + header: 'Address' + }, + { + accessorKey: 'city', + header: 'City' + }, + { + accessorKey: 'state', + header: 'State' + }, + { + accessorKey: 'postalCode', + header: 'Postal Code' + }, + { + accessorKey: 'country', + header: 'Country' + }, + { + accessorKey: 'phone', + header: 'Phone', + cell: ({ row }) => row.original.phone || '—' + }, + { + accessorKey: 'email', + header: 'Email', + cell: ({ row }) => row.original.email || '—' + } +] + +export default function VenuesTable({ eventLocations }: Props) { + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [venues, setVenues] = useState(eventLocations) + + async function refreshVenues() { + try { + const updated = await fetchVenuesClient() + setVenues(updated) + } catch (err) { + console.error('Failed to refresh venues:', err) + } + } + + return ( +
+ + + { + await refreshVenues() + setIsDialogOpen(false) + }} + />} + /> +
+ ) +} diff --git a/lib/helper/fetchVenues.ts b/lib/helper/fetchVenues.ts new file mode 100644 index 0000000..e98baca --- /dev/null +++ b/lib/helper/fetchVenues.ts @@ -0,0 +1,5 @@ +export async function fetchVenuesClient() { + const res = await fetch('/api/venues/fetch', { cache: 'no-store' }) // ensure no stale cache + if (!res.ok) throw new Error('Failed to fetch venues') + return res.json() +} \ No newline at end of file diff --git a/lib/queries.ts b/lib/queries.ts index d25c318..99437e2 100644 --- a/lib/queries.ts +++ b/lib/queries.ts @@ -10,13 +10,37 @@ export const queries = { username: true } }, - location: true + venue: true }, }) return allEvents; }, + async fetchQuickViewEvents() { + const events = await prisma.event.findMany({ + take: 3, + select: { + id: true, + name: true, + date: true, + creator: { + select: { + id: true, + username: true, + } + }, + venue: { + select: { + name: true, + }, + }, + } + }) + + return events + }, + async fetchEventGuests(eventId: string) { return await prisma.eventGuest.findMany({ where: { eventId }, @@ -78,7 +102,7 @@ export const queries = { { dueDate: 'asc' }, ], }, - location: true + venue: true } }) return event @@ -136,7 +160,7 @@ export const queries = { }, async fetchAllLocations() { - return await prisma.location.findMany() + return await prisma.venue.findMany() }, } \ No newline at end of file diff --git a/prisma/migrations/20250723173825_updated_location_name_to_venue/migration.sql b/prisma/migrations/20250723173825_updated_location_name_to_venue/migration.sql new file mode 100644 index 0000000..5e23d69 --- /dev/null +++ b/prisma/migrations/20250723173825_updated_location_name_to_venue/migration.sql @@ -0,0 +1,34 @@ +/* + Warnings: + + - You are about to drop the column `locationid` on the `Event` table. All the data in the column will be lost. + - You are about to drop the `Location` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "Event" DROP CONSTRAINT "Event_locationid_fkey"; + +-- AlterTable +ALTER TABLE "Event" DROP COLUMN "locationid", +ADD COLUMN "venueId" TEXT; + +-- DropTable +DROP TABLE "Location"; + +-- CreateTable +CREATE TABLE "Venue" ( + "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 "Venue_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Event" ADD CONSTRAINT "Event_venueId_fkey" FOREIGN KEY ("venueId") REFERENCES "Venue"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8740779..2e0237c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -30,8 +30,8 @@ model Event { id String @id @default(cuid()) name String date DateTime? - location Location? @relation(fields: [locationid], references: [id]) - locationid String? + venue Venue? @relation(fields: [venueId], references: [id]) + venueId String? creator User @relation("EventCreator", fields: [creatorId], references: [id]) creatorId String guests Guest[] @@ -43,7 +43,7 @@ model Event { FileUpload FileUpload[] } -model Location { +model Venue { id String @id @default(cuid()) name String address String @@ -53,17 +53,19 @@ model Location { country String @default("United States") phone String? email String? + createdAt DateTime @default(now()) Event Event[] } model Guest { - id String @id @default(cuid()) - event Event @relation(fields: [eventId], references: [id]) - eventId String - name String - email String? - rsvp RsvpStatus @default(PENDING) + id String @id @default(cuid()) + event Event @relation(fields: [eventId], references: [id]) + eventId String + name String + email String? + rsvp RsvpStatus @default(PENDING) + // attended RsvpStatus @default(PENDING) } enum RsvpStatus { diff --git a/types.d.ts b/types.d.ts index c9cb674..cc86500 100644 --- a/types.d.ts +++ b/types.d.ts @@ -22,6 +22,19 @@ interface EventProps { key: string; } +interface QucikEventProps { + id: string + name: string + date?: Date | null + creator: { + id: string + username: string + }, + venue?: { + name: string + } | null +} + interface Creator { id: string email: string @@ -44,7 +57,7 @@ interface EventData { id: string name: string date: Date | null - location: EventLocation | null + venue: Venue | null creatorId: string createdAt: string creator: Creator @@ -73,7 +86,7 @@ type User = { role: 'COUPLE' | 'PLANNER' | 'GUEST' } -interface EventLocation { +interface Venue { id: string name: string address: string