From 98bc1ac115efc299095c2821442aae33ad5a055b Mon Sep 17 00:00:00 2001 From: Daniel Adu-Gyan <26229521+danieladugyan@users.noreply.github.com> Date: Wed, 21 Aug 2024 23:25:37 +0200 Subject: [PATCH] Change booking system - Replace calendar with table - Implement booking deletion --- src/database/prisma/schema.prisma | 1 + src/database/schema.zmodel | 1 + src/lib/components/ConfirmDialog.svelte | 102 ++++++++++++++++++++ src/lib/utils/client/member.ts | 7 +- src/routes/(app)/booking/+page.server.ts | 50 +++++++++- src/routes/(app)/booking/+page.svelte | 97 ++++++++++++++++++- src/routes/(app)/booking/Calendar.svelte | 76 --------------- src/routes/(app)/booking/admin/+page.svelte | 74 ++++++++++++-- src/translations/en.json | 8 ++ src/translations/sv.json | 8 ++ 10 files changed, 332 insertions(+), 92 deletions(-) create mode 100644 src/lib/components/ConfirmDialog.svelte delete mode 100644 src/routes/(app)/booking/Calendar.svelte diff --git a/src/database/prisma/schema.prisma b/src/database/prisma/schema.prisma index eb4a9acdf..46255c1c8 100644 --- a/src/database/prisma/schema.prisma +++ b/src/database/prisma/schema.prisma @@ -218,6 +218,7 @@ model Bookable { /// @@allow('read', has(auth().policies, 'booking_request:read')) /// @@allow('update', has(auth().policies, 'booking_request:update')) /// @@allow('delete', has(auth().policies, 'booking_request:delete')) +/// @@allow('delete', auth().memberId == bookerId) /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model BookingRequest { id String @id() @default(dbgenerated("gen_random_uuid()")) @db.Uuid() diff --git a/src/database/schema.zmodel b/src/database/schema.zmodel index 2aa25b458..45b6d6dec 100644 --- a/src/database/schema.zmodel +++ b/src/database/schema.zmodel @@ -212,6 +212,7 @@ model BookingRequest { @@allow("read", has(auth().policies, "booking_request:read")) @@allow("update", has(auth().policies, "booking_request:update")) @@allow("delete", has(auth().policies, "booking_request:delete")) + @@allow("delete", auth().memberId == bookerId) @@map("booking_requests") } diff --git a/src/lib/components/ConfirmDialog.svelte b/src/lib/components/ConfirmDialog.svelte new file mode 100644 index 000000000..bdb803958 --- /dev/null +++ b/src/lib/components/ConfirmDialog.svelte @@ -0,0 +1,102 @@ + + + + + + + diff --git a/src/lib/utils/client/member.ts b/src/lib/utils/client/member.ts index 4ba412189..3212eb631 100644 --- a/src/lib/utils/client/member.ts +++ b/src/lib/utils/client/member.ts @@ -6,8 +6,11 @@ import type { Position, } from "@prisma/client"; export type MemberNames = Pick; -export const getFullName = (member: MemberNames) => { - if (member.nickname) { +type Options = { + hideNickname?: boolean; +}; +export const getFullName = (member: MemberNames, options: Options = {}) => { + if (member.nickname && !options.hideNickname) { if (member.firstName && member.lastName) return `${member.firstName} "${member.nickname}" ${member.lastName}`; return `"${member.nickname}"`; diff --git a/src/routes/(app)/booking/+page.server.ts b/src/routes/(app)/booking/+page.server.ts index fbcdc6f46..e8a79cada 100644 --- a/src/routes/(app)/booking/+page.server.ts +++ b/src/routes/(app)/booking/+page.server.ts @@ -1,10 +1,56 @@ -export const load = async ({ locals }) => { - const { prisma } = locals; +import apiNames from "$lib/utils/apiNames"; +import * as m from "$paraglide/messages"; +import { isAuthorized } from "$lib/utils/authorization"; +import { redirect } from "$lib/utils/redirect"; +import { error } from "@sveltejs/kit"; + +export const load = async (event) => { + const { prisma, user } = event.locals; const bookingRequests = await prisma.bookingRequest.findMany({ + where: { + bookerId: user.memberId, + }, include: { bookables: true, }, }); + if (bookingRequests.length === 0) { + const isAdmin = isAuthorized(apiNames.BOOKINGS.UPDATE, user); + if (isAdmin) { + return redirect( + "/booking/admin", + { + message: m.booking_noBookings(), + type: "info", + }, + event, + ); + } + redirect( + "/booking/create", + { + message: m.booking_noBookings(), + type: "info", + }, + event, + ); + } + return { bookingRequests }; }; + +export const actions = { + delete: async ({ request, locals }) => { + const { prisma } = locals; + const formData = await request.formData(); + const id = formData.get("id"); + if (id && typeof id === "string") { + await prisma.bookingRequest.delete({ + where: { id }, + }); + } else { + error(422, "Invalid booking request id"); + } + }, +}; diff --git a/src/routes/(app)/booking/+page.svelte b/src/routes/(app)/booking/+page.svelte index 4b3980d30..a36099e7d 100644 --- a/src/routes/(app)/booking/+page.svelte +++ b/src/routes/(app)/booking/+page.svelte @@ -1,11 +1,104 @@ - +
+ + {m.booking_createBooking()} + + + {#if isAuthorized(apiNames.BOOKINGS.UPDATE, $page.data.user)} + + {m.booking_manageBookings()} + + {/if} +
+ +
+ + + + + + + + + + + + + {#each data.bookingRequests as bookingRequest (bookingRequest.id)} + + + + + + + + + {/each} + +
{m.booking_booking()}{m.booking_from()}{m.booking_until()}{m.booking_event()}{m.booking_status()} +
+ {#each bookingRequest.bookables.map((a) => a.name) as bookable} +

{bookable}

+ {/each} +
{dayjs(bookingRequest.start).format("YYYY-MM-DD HH:MM")}{dayjs(bookingRequest.end).format("YYYY-MM-DD HH:MM")}{bookingRequest.event} + + +
+ + +
+
+
+ + +

+ {#if selectedBooking} + + {@html m.booking_deleteMyAreYouSure({ + bookables: selectedBooking?.bookables + .map(({ name }) => name) + .join(", "), + })} + {/if} +

+
diff --git a/src/routes/(app)/booking/Calendar.svelte b/src/routes/(app)/booking/Calendar.svelte deleted file mode 100644 index 356f8e8f0..000000000 --- a/src/routes/(app)/booking/Calendar.svelte +++ /dev/null @@ -1,76 +0,0 @@ - - -
diff --git a/src/routes/(app)/booking/admin/+page.svelte b/src/routes/(app)/booking/admin/+page.svelte index c0f43e0f1..6010e9893 100644 --- a/src/routes/(app)/booking/admin/+page.svelte +++ b/src/routes/(app)/booking/admin/+page.svelte @@ -6,15 +6,25 @@ import MemberAvatar from "$lib/components/socials/MemberAvatar.svelte"; import * as m from "$paraglide/messages"; import SetPageTitle from "$lib/components/nav/SetPageTitle.svelte"; + import ConfirmDialog from "$lib/components/ConfirmDialog.svelte"; export let data; + + let deleteModal: HTMLDialogElement; + let selectedBooking: (typeof data.bookingRequests)[number] | undefined = + undefined; - - - {m.booking_goBack()} - +
@@ -51,7 +61,7 @@ {getFullName(bookingRequest.booker)} + >{getFullName(bookingRequest.booker, { hideNickname: true })} {/if} @@ -66,7 +76,7 @@ + +
+ + + @@ -96,3 +129,24 @@
+ + +

+ {#if selectedBooking} + + {@html m.booking_deleteAreYouSure({ + name: selectedBooking?.booker?.firstName ?? "", + bookables: selectedBooking?.bookables + .map(({ name }) => name) + .join(", "), + })} + {/if} +

+
diff --git a/src/translations/en.json b/src/translations/en.json index d78b07360..26e4d1c1b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3,6 +3,8 @@ "no": "No", "yes": "Yes", "save": "Save", + "cancel": "Cancel", + "ok": "Ok", "errors_notImplemented": "Not implemented yet", "errors_missingPermissions": "You do not have permission, have you logged in? Required policy:", "home": "Home", @@ -265,6 +267,7 @@ "committees_stopEditing": "Stop editing", "booking_createBooking": "Create booking", "booking_manageBookings": "Manage bookings", + "booking_noBookings": "No bookings found!", "booking_subscribe": "Subscribe", "booking_today": "Today", "booking_month": "Month", @@ -287,6 +290,11 @@ "booking_requestSent": "Booking request sent!", "booking_goBack": "Go back", "booking_create": "Create", + "booking_edit": "Edit", + "booking_delete": "Delete", + "booking_deleteTitle": "Delete booking", + "booking_deleteAreYouSure": "Are you sure you want to delete {name}'s request to book: {bookables}?", + "booking_deleteMyAreYouSure": "Are you sure you want to delete your request to book: {bookables}?", "notifications_notificationsRead": "Notifications read", "notifications_notificationsRemoved": "Notifications removed", "notifications_notificationRemoved": "Notification removed", diff --git a/src/translations/sv.json b/src/translations/sv.json index 928773ae4..1d10da75a 100644 --- a/src/translations/sv.json +++ b/src/translations/sv.json @@ -3,6 +3,8 @@ "no": "Nej", "yes": "Ja", "save": "Spara", + "cancel": "Avbryt", + "ok": "Ok", "errors_notImplemented": "Inte implementerat än", "errors_missingPermissions": "Du har inte access, har du loggat in? Policy som krävs:", "home": "Hem", @@ -267,6 +269,7 @@ "committees_stopEditing": "Sluta redigera", "booking_createBooking": "Skapa bokning", "booking_manageBookings": "Hantera bokningar", + "booking_noBookings": "Du har inga bokningar!", "booking_subscribe": "Prenumerera", "booking_today": "Idag", "booking_month": "Månad", @@ -289,6 +292,11 @@ "booking_requestSent": "Bokningsförfrågan skickad!", "booking_goBack": "Tillbaka", "booking_create": "Skapa", + "booking_edit": "Redigera", + "booking_delete": "Ta bort", + "booking_deleteTitle": "Ta bort bokning", + "booking_deleteAreYouSure": "Är du säker på att du vill ta bort {name}s bokning av: {bookables}?", + "booking_deleteMyAreYouSure": "Är du säker på att du vill ta bort din bokning av: {bookables}?", "notifications_notificationsRead": "Notiser lästa", "notifications_notificationsRemoved": "Notiser borttagna", "notifications_notificationRemoved": "Notis borttagen",