diff --git a/src/lib/components/wishlist/ReservationButton.svelte b/src/lib/components/wishlist/ReservationButton.svelte index 2e4cca5..6742fe6 100644 --- a/src/lib/components/wishlist/ReservationButton.svelte +++ b/src/lib/components/wishlist/ReservationButton.svelte @@ -7,12 +7,25 @@ itemId: string; isReserved: boolean; reserverName?: string | null; + reservationUserId?: string | null; + currentUserId?: string | null; } - let { itemId, isReserved, reserverName }: Props = $props(); + let { itemId, isReserved, reserverName, reservationUserId, currentUserId }: Props = $props(); let showReserveForm = $state(false); let name = $state(''); + let showCancelConfirmation = $state(false); + + const canCancel = $derived(() => { + if (!isReserved) return false; + if (reservationUserId) { + return currentUserId === reservationUserId; + } + return true; + }); + + const isAnonymousReservation = $derived(!reservationUserId); {#if isReserved} @@ -23,12 +36,52 @@ by {reserverName} {/if} -
- - -
+ {#if canCancel()} + {#if showCancelConfirmation} +
+

+ Cancel this reservation? +

+
+
{ + return async ({ update }) => { + showCancelConfirmation = false; + await update(); + }; + }}> + + +
+ +
+
+ {:else if isAnonymousReservation} + + {:else} +
+ + +
+ {/if} + {/if} {:else if showReserveForm}
items.id, { onDelete: 'cascade' }), + userId: text('user_id').references(() => users.id, { onDelete: 'set null' }), reserverName: text('reserver_name'), createdAt: timestamp('created_at').defaultNow().notNull() }); @@ -125,6 +126,10 @@ export const reservationsRelations = relations(reservations, ({ one }) => ({ item: one(items, { fields: [reservations.itemId], references: [items.id] + }), + user: one(users, { + fields: [reservations.userId], + references: [users.id] }) })); @@ -154,7 +159,8 @@ export const savedWishlistsRelations = relations(savedWishlists, ({ one }) => ({ export const usersRelations = relations(users, ({ many }) => ({ wishlists: many(wishlists), - savedWishlists: many(savedWishlists) + savedWishlists: many(savedWishlists), + reservations: many(reservations) })); export type User = typeof users.$inferSelect; diff --git a/src/routes/wishlist/[token]/+page.server.ts b/src/routes/wishlist/[token]/+page.server.ts index ae726a6..0c77ca2 100644 --- a/src/routes/wishlist/[token]/+page.server.ts +++ b/src/routes/wishlist/[token]/+page.server.ts @@ -43,12 +43,13 @@ export const load: PageServerLoad = async ({ params, locals }) => { isSaved, isClaimed, savedWishlistId, - isAuthenticated: !!session?.user + isAuthenticated: !!session?.user, + currentUserId: session?.user?.id || null }; }; export const actions: Actions = { - reserve: async ({ request }) => { + reserve: async ({ request, locals }) => { const formData = await request.formData(); const itemId = formData.get('itemId') as string; const reserverName = formData.get('reserverName') as string; @@ -57,6 +58,8 @@ export const actions: Actions = { return { success: false, error: 'Item ID is required' }; } + const session = await locals.auth(); + const existingReservation = await db.query.reservations.findFirst({ where: eq(reservations.itemId, itemId) }); @@ -68,6 +71,7 @@ export const actions: Actions = { await db.transaction(async (tx) => { await tx.insert(reservations).values({ itemId, + userId: session?.user?.id || null, reserverName: reserverName?.trim() || null }); @@ -80,7 +84,7 @@ export const actions: Actions = { return { success: true }; }, - unreserve: async ({ request }) => { + unreserve: async ({ request, locals }) => { const formData = await request.formData(); const itemId = formData.get('itemId') as string; @@ -88,6 +92,25 @@ export const actions: Actions = { return { success: false, error: 'Item ID is required' }; } + const session = await locals.auth(); + + const reservation = await db.query.reservations.findFirst({ + where: eq(reservations.itemId, itemId) + }); + + if (!reservation) { + return { success: false, error: 'Reservation not found' }; + } + + if (reservation.userId) { + if (!session?.user?.id || session.user.id !== reservation.userId) { + return { + success: false, + error: 'You can only cancel your own reservations' + }; + } + } + await db.transaction(async (tx) => { await tx.delete(reservations).where(eq(reservations.itemId, itemId)); diff --git a/src/routes/wishlist/[token]/+page.svelte b/src/routes/wishlist/[token]/+page.svelte index da74e6a..1c639c3 100644 --- a/src/routes/wishlist/[token]/+page.svelte +++ b/src/routes/wishlist/[token]/+page.svelte @@ -115,6 +115,8 @@ itemId={item.id} isReserved={item.isReserved} reserverName={item.reservations?.[0]?.reserverName} + reservationUserId={item.reservations?.[0]?.userId} + currentUserId={data.currentUserId} /> {/each}