From 40857d1542d093a2aae76dd69d5b43a27444bd70 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Wed, 25 Dec 2024 22:33:08 +0100 Subject: [PATCH 01/30] feat(frontend): better reservation state --- .../EventCreateReservation.spec.ts | 6 +- .../reservation/EventShowReservation.spec.ts | 94 ++++++++----- .../reservation/EventShowReservation.vue | 125 ++++++------------ .../reservation/PlannedReservationForm.vue | 3 +- .../ReservationGeneralInfo.spec.ts | 11 +- .../reservation/WalkInReservationForm.vue | 3 +- .../admin/event/AdminEventRTInfo.spec.ts | 3 +- .../analytics/bucketize-reservations.spec.ts | 4 + .../reservations/useReservationPermissions.ts | 48 ++++++- .../reservations/useReservations.ts | 40 ++++-- packages/frontend/src/helpers/floor.spec.ts | 4 +- .../planned-to-queued-reservation.spec.ts | 9 +- .../queued-to-planned-reservation.ts | 3 +- packages/frontend/src/i18n/de/index.ts | 1 + packages/frontend/src/i18n/en-gb/index.ts | 1 + packages/types/src/base-reservation.ts | 13 ++ packages/types/src/queued-reservation.ts | 1 + packages/types/src/walk-in-reservation.ts | 7 +- 18 files changed, 237 insertions(+), 139 deletions(-) diff --git a/packages/frontend/src/components/Event/reservation/EventCreateReservation.spec.ts b/packages/frontend/src/components/Event/reservation/EventCreateReservation.spec.ts index 889a7a17..93b182aa 100644 --- a/packages/frontend/src/components/Event/reservation/EventCreateReservation.spec.ts +++ b/packages/frontend/src/components/Event/reservation/EventCreateReservation.spec.ts @@ -7,9 +7,9 @@ import type { import type { EventCreateReservationProps } from "./EventCreateReservation.vue"; import EventCreateReservation from "./EventCreateReservation.vue"; import { renderComponent, t, getLocaleForTest } from "../../../../test-helpers/render-component"; +import { ReservationState, ReservationStatus, ReservationType } from "@firetable/types"; import { getDefaultTimezone, hourFromTimestamp } from "src/helpers/date-utils"; import { ONE_HOUR } from "src/constants"; -import { ReservationStatus, ReservationType } from "@firetable/types"; import { describe, expect, it, beforeEach } from "vitest"; import { userEvent } from "@vitest/browser/context"; import { addHours, format } from "date-fns"; @@ -47,6 +47,7 @@ describe("EventCreateReservation", () => { ); return { type: ReservationType.WALK_IN, + state: ReservationState.ARRIVED, guestName: "", numberOfGuests: 2, guestContact: "", @@ -65,6 +66,7 @@ describe("EventCreateReservation", () => { function generateUpdateState(): Omit { return { type: ReservationType.WALK_IN, + state: ReservationState.ARRIVED, guestName: "John Doe", numberOfGuests: 4, guestContact: "+43666666666", @@ -345,6 +347,7 @@ describe("EventCreateReservation", () => { function generateInitialPlannedState(): Omit { return { type: ReservationType.PLANNED, + state: ReservationState.PENDING, guestName: "", numberOfGuests: 2, guestContact: "", @@ -366,6 +369,7 @@ describe("EventCreateReservation", () => { function generateUpdatePlannedState(): Omit { return { type: ReservationType.PLANNED, + state: ReservationState.PENDING, guestName: "Charlie", numberOfGuests: 5, guestContact: "+432313213", diff --git a/packages/frontend/src/components/Event/reservation/EventShowReservation.spec.ts b/packages/frontend/src/components/Event/reservation/EventShowReservation.spec.ts index a5fa6032..0e582fec 100644 --- a/packages/frontend/src/components/Event/reservation/EventShowReservation.spec.ts +++ b/packages/frontend/src/components/Event/reservation/EventShowReservation.spec.ts @@ -3,7 +3,13 @@ import type { EventShowReservationProps } from "./EventShowReservation.vue"; import type { GuestSummary } from "src/stores/guests-store"; import EventShowReservation from "./EventShowReservation.vue"; import { renderComponent, t } from "../../../../test-helpers/render-component"; -import { ReservationStatus, ReservationType, Role, UserCapability } from "@firetable/types"; +import { + ReservationState, + ReservationStatus, + ReservationType, + Role, + UserCapability, +} from "@firetable/types"; import { beforeEach, describe, expect, it } from "vitest"; import { userEvent } from "@vitest/browser/context"; import { nextTick } from "vue"; @@ -36,6 +42,7 @@ describe("EventShowReservation", () => { reservationConfirmed: false, waitingForResponse: false, type: ReservationType.PLANNED, + state: ReservationState.PENDING, consumption: 0, time: "22:00", status: ReservationStatus.ACTIVE, @@ -61,8 +68,8 @@ describe("EventShowReservation", () => { }; }); - describe("toggle states", () => { - it('renders the "Waiting for Response" toggle when conditions are met', async () => { + describe("reservation state select", () => { + it("renders the state select when conditions are met", async () => { const screen = renderComponent(EventShowReservation, props, { piniaStoreOptions: { initialState: { @@ -76,17 +83,12 @@ describe("EventShowReservation", () => { }, }); - const toggle = screen.getByText(t("EventShowReservation.waitingForResponse")); - await expect.element(toggle).toBeVisible(); - - await userEvent.click(toggle); - - expect(screen.emitted().waitingForResponse).toBeTruthy(); - expect(screen.emitted().waitingForResponse[0]).toEqual([true]); + const select = screen.getByRole("combobox", { name: "Reservation state" }); + await expect.element(select).toHaveValue(t("EventShowReservation.pendingLabel")); }); - it('does not render the "Waiting for Response" toggle when reservation is confirmed', async () => { - reservation.reservationConfirmed = true; + it("does not render the state select when reservation is cancelled", async () => { + reservation.cancelled = true; const screen = renderComponent(EventShowReservation, props, { piniaStoreOptions: { @@ -101,13 +103,10 @@ describe("EventShowReservation", () => { }, }); - const label = screen.getByText(t("EventShowReservation.waitingForResponse")); - await expect.element(label).not.toBeInTheDocument(); + await expect.element(screen.getByRole("combobox")).not.toBeInTheDocument(); }); - it('does not render the "Waiting for Response" toggle when guest has arrived', async () => { - reservation.arrived = true; - + it("emits correct events when state changes to WAITING_FOR_RESPONSE", async () => { const screen = renderComponent(EventShowReservation, props, { piniaStoreOptions: { initialState: { @@ -115,20 +114,26 @@ describe("EventShowReservation", () => { user: { id: "user1", role: Role.PROPERTY_OWNER, - capabilities: { - [UserCapability.CAN_CONFIRM_RESERVATION]: true, - }, }, }, }, }, }); - const label = screen.getByText(t("EventShowReservation.waitingForResponse")); - await expect.element(label).not.toBeInTheDocument(); + const select = screen.getByRole("combobox"); + await userEvent.click(select); + + const waitingOption = screen.getByText(t("EventShowReservation.waitingForResponse")); + await userEvent.click(waitingOption); + + expect(screen.emitted().waitingForResponse).toBeTruthy(); + expect(screen.emitted().waitingForResponse[0]).toEqual([true]); + expect(screen.emitted().stateChange[0]).toEqual([ + ReservationState.WAITING_FOR_RESPONSE, + ]); }); - it('renders the "Reservation Confirmed" toggle when conditions are met', async () => { + it("emits correct events when state changes to CONFIRMED", async () => { const screen = renderComponent(EventShowReservation, props, { piniaStoreOptions: { initialState: { @@ -142,16 +147,20 @@ describe("EventShowReservation", () => { }, }); - const toggle = screen.getByText(t("EventShowReservation.reservationConfirmedLabel")); - await expect.element(toggle).toBeVisible(); + const select = screen.getByRole("combobox"); + await userEvent.click(select); - await userEvent.click(toggle); + const confirmedOption = screen.getByText( + t("EventShowReservation.reservationConfirmedLabel"), + ); + await userEvent.click(confirmedOption); expect(screen.emitted().reservationConfirmed).toBeTruthy(); expect(screen.emitted().reservationConfirmed[0]).toEqual([true]); + expect(screen.emitted().stateChange[0]).toEqual([ReservationState.CONFIRMED]); }); - it('renders the "Guest Arrived" toggle when conditions are met', async () => { + it("emits correct events when state changes to ARRIVED", async () => { const screen = renderComponent(EventShowReservation, props, { piniaStoreOptions: { initialState: { @@ -165,13 +174,38 @@ describe("EventShowReservation", () => { }, }); - const toggle = screen.getByText(t("EventShowReservation.reservationGuestArrivedLabel")); - await expect.element(toggle).toBeVisible(); + await userEvent.click(screen.getByRole("combobox")); - await userEvent.click(toggle); + const arrivedOption = screen.getByText( + t("EventShowReservation.reservationGuestArrivedLabel"), + ); + await userEvent.click(arrivedOption); expect(screen.emitted().arrived).toBeTruthy(); expect(screen.emitted().arrived[0]).toEqual([true]); + expect(screen.emitted().stateChange[0]).toEqual([ReservationState.ARRIVED]); + }); + + it("shows correct initial state based on reservation props", async () => { + reservation.arrived = true; + + const screen = renderComponent(EventShowReservation, props, { + piniaStoreOptions: { + initialState: { + auth: { + user: { + id: "user1", + role: Role.PROPERTY_OWNER, + }, + }, + }, + }, + }); + + const select = screen.getByRole("combobox", { name: "Reservation state" }); + await expect + .element(select) + .toHaveValue(t("EventShowReservation.reservationGuestArrivedLabel")); }); }); diff --git a/packages/frontend/src/components/Event/reservation/EventShowReservation.vue b/packages/frontend/src/components/Event/reservation/EventShowReservation.vue index a33df6ea..50ebb9d4 100644 --- a/packages/frontend/src/components/Event/reservation/EventShowReservation.vue +++ b/packages/frontend/src/components/Event/reservation/EventShowReservation.vue @@ -1,8 +1,8 @@ @@ -91,76 +93,25 @@ function onWaitingForResponse(): void { permissionsStore.canConfirmReservation " > - - - - - - - - - - - {{ t("EventShowReservation.reservationGuestArrivedLabel") }} - - - - - - + - - import type { AppUser, PlannedReservation, User } from "@firetable/types"; import { isTimeWithinEventDuration } from "./reservation-form-utils"; -import { ReservationStatus, ReservationType } from "@firetable/types"; +import { ReservationState, ReservationStatus, ReservationType } from "@firetable/types"; import { computed, ref, watch, useTemplateRef } from "vue"; import { useI18n } from "vue-i18n"; import { QForm } from "quasar"; @@ -38,6 +38,7 @@ function generateInitialState() { ? { ...props.reservationData } : { type: ReservationType.PLANNED as const, + state: ReservationState.PENDING, guestName: "", numberOfGuests: 2, guestContact: "", diff --git a/packages/frontend/src/components/Event/reservation/ReservationGeneralInfo.spec.ts b/packages/frontend/src/components/Event/reservation/ReservationGeneralInfo.spec.ts index 1fd53194..0638141d 100644 --- a/packages/frontend/src/components/Event/reservation/ReservationGeneralInfo.spec.ts +++ b/packages/frontend/src/components/Event/reservation/ReservationGeneralInfo.spec.ts @@ -1,8 +1,14 @@ import type { QueuedReservationDoc, UserCapabilities, WalkInReservation } from "@firetable/types"; import type { RenderResult } from "vitest-browser-vue"; import ReservationGeneralInfo from "./ReservationGeneralInfo.vue"; -import { renderComponent, getLocaleForTest } from "../../../../test-helpers/render-component"; -import { ReservationStatus, ReservationType, Role, UserCapability } from "@firetable/types"; +import { getLocaleForTest, renderComponent } from "../../../../test-helpers/render-component"; +import { + ReservationState, + ReservationStatus, + ReservationType, + Role, + UserCapability, +} from "@firetable/types"; import { formatEventDate, getDefaultTimezone } from "src/helpers/date-utils"; import { describe, expect, it } from "vitest"; @@ -241,6 +247,7 @@ describe("ReservationGeneralInfo", () => { arrived: true, tableLabel: "Table 1", floorId: "floor1", + state: ReservationState.ARRIVED, }; const screen = renderComponent( diff --git a/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue b/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue index a0e71b41..3edbd422 100644 --- a/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue +++ b/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue @@ -1,7 +1,7 @@ + + diff --git a/packages/frontend/src/helpers/date-utils.ts b/packages/frontend/src/helpers/date-utils.ts index 0f360812..dd7a9468 100644 --- a/packages/frontend/src/helpers/date-utils.ts +++ b/packages/frontend/src/helpers/date-utils.ts @@ -1,9 +1,12 @@ import type { FirestoreTimestamp } from "@firetable/types"; -import { isNumber } from "es-toolkit/compat"; +import { isNumber, memoize } from "es-toolkit/compat"; -export const timezones = Intl.supportedValuesOf("timeZone").sort(function (a, b) { - return a.localeCompare(b, undefined, { sensitivity: "base" }); +export const timezones = memoize(function () { + return Intl.supportedValuesOf("timeZone").sort(function (a, b) { + return a.localeCompare(b, undefined, { sensitivity: "base" }); + }); }); + export const UTC = "UTC"; /** diff --git a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts index b3bb9c76..2c655407 100644 --- a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts +++ b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts @@ -8,14 +8,17 @@ import { page, userEvent } from "@vitest/browser/context"; import { nextTick } from "vue"; const { updateOrganisationSettingsMock, updatePropertySettingsMock } = vi.hoisted(() => ({ - tryCatchLoadingWrapperSpy: vi.fn(), updateOrganisationSettingsMock: vi.fn(), updatePropertySettingsMock: vi.fn(), })); -vi.mock("@firetable/backend", () => ({ +vi.mock("../../backend-proxy", () => ({ updateOrganisationSettings: updateOrganisationSettingsMock, updatePropertySettings: updatePropertySettingsMock, + fetchOrganisationById: vi.fn(), + fetchOrganisationsForAdmin: vi.fn(), + fetchPropertiesForAdmin: vi.fn(), + propertiesCollection: vi.fn(), })); describe("PageAdminOrganisationSettings.vue", () => { @@ -97,8 +100,8 @@ describe("PageAdminOrganisationSettings.vue", () => { await expect.element(propertyOneSection).toBeInTheDocument(); await expect.element(propertyTwoSection).toBeInTheDocument(); - const propertyOneSelect = propertyOneSection.getByRole("combobox"); - const propertyTwoSelect = propertyTwoSection.getByRole("combobox"); + const propertyOneSelect = propertyOneSection.getByLabelText("Property timezone"); + const propertyTwoSelect = propertyTwoSection.getByLabelText("Property timezone"); await expect.element(propertyOneSelect).toHaveValue("Europe/Vienna"); await expect.element(propertyTwoSelect).toHaveValue("Europe/Athens"); @@ -112,8 +115,10 @@ describe("PageAdminOrganisationSettings.vue", () => { await nextTick(); - const timezoneSelect = propertySection.getByRole("combobox"); + const timezoneSelect = propertySection.getByLabelText("Property timezone"); await userEvent.click(timezoneSelect); + const search = screen.getByRole("textbox", { name: "Search timezones" }); + await userEvent.fill(search, "Vilnius"); const vilniusOption = screen.getByText("Europe/Vilnius"); await userEvent.click(vilniusOption); diff --git a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue index dbbdee51..22ef882f 100644 --- a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue +++ b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue @@ -1,16 +1,17 @@ @@ -246,11 +264,12 @@ onMounted(initPropertySettings); :title="property.name" > - From ca1b88e3b63be7339c382ca09308bcf2e43b35b7 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 17:42:27 +0100 Subject: [PATCH 04/30] chore(frontend): create hasChanged composable and use it in settings page --- .../src/composables/useHasChanged.spec.ts | 252 ++++++++++++++++++ .../frontend/src/composables/useHasChanged.ts | 31 +++ .../Admin/PageAdminOrganisationSettings.vue | 82 +++--- .../frontend/src/stores/properties-store.ts | 24 +- 4 files changed, 348 insertions(+), 41 deletions(-) create mode 100644 packages/frontend/src/composables/useHasChanged.spec.ts create mode 100644 packages/frontend/src/composables/useHasChanged.ts diff --git a/packages/frontend/src/composables/useHasChanged.spec.ts b/packages/frontend/src/composables/useHasChanged.spec.ts new file mode 100644 index 00000000..7ec708fd --- /dev/null +++ b/packages/frontend/src/composables/useHasChanged.spec.ts @@ -0,0 +1,252 @@ +import { useHasChanged } from "./useHasChanged"; +import { describe, it, expect } from "vitest"; +import { ref, reactive, computed } from "vue"; + +describe("useHasChanged", () => { + it("returns false initially when values are equal", () => { + const source = ref({ name: "test" }); + const { hasChanged } = useHasChanged(source); + expect(hasChanged.value).toBe(false); + }); + + it("detects changes in primitive values", () => { + const source = ref({ count: 1 }); + const { hasChanged } = useHasChanged(source); + + source.value.count = 2; + expect(hasChanged.value).toBe(true); + }); + + it("detects changes in nested objects", () => { + const source = reactive({ user: { name: "John", age: 30 } }); + const { hasChanged } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + source.user.age = 31; + expect(hasChanged.value).toBe(true); + }); + + it("detects changes in deeply nested objects", () => { + const source = reactive({ + level1: { + level2: { + level3: { + value: "initial", + }, + }, + }, + }); + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + // Modify a deeply nested value + source.level1.level2.level3.value = "changed"; + expect(hasChanged.value).toBe(true); + + reset(); + expect(hasChanged.value).toBe(false); + }); + + it("handles objects with circular references", () => { + const source = reactive>({ name: "Circular" }); + source.self = source; + + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + source.name = "Changed"; + expect(hasChanged.value).toBe(true); + + reset(); + expect(hasChanged.value).toBe(false); + }); + + it("handles arrays correctly", () => { + const source = ref({ items: [1, 2, 3] }); + const { hasChanged } = useHasChanged(source); + + source.value.items.push(4); + expect(hasChanged.value).toBe(true); + }); + + it("resets state correctly", () => { + const source = ref({ count: 1 }); + const { hasChanged, reset } = useHasChanged(source); + + source.value.count = 2; + expect(hasChanged.value).toBe(true); + + reset(); + expect(hasChanged.value).toBe(false); + + source.value.count = 3; + expect(hasChanged.value).toBe(true); + }); + + it("handles null and undefined values", () => { + const source = ref>({ value: null }); + const { hasChanged } = useHasChanged(source); + + source.value.value = undefined; + expect(hasChanged.value).toBe(true); + + source.value.value = null; + expect(hasChanged.value).toBe(false); + }); + + it("compares values not references", () => { + const source = reactive({ user: { name: "John" } }); + const { hasChanged } = useHasChanged(source); + + source.user = { name: "John" }; + expect(hasChanged.value).toBe(false); + }); + + it("works with computed values", () => { + const baseValue = ref({ count: 1 }); + const source = computed(() => ({ + doubled: baseValue.value.count * 2, + })); + const { hasChanged } = useHasChanged(source); + + baseValue.value.count = 2; + expect(hasChanged.value).toBe(true); + }); + + it("handles objects with Date and RegExp types correctly", () => { + const source = ref({ + date: new Date("2023-01-01"), + regex: /test/i, + }); + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + source.value.date = new Date("2024-01-01"); + expect(hasChanged.value).toBe(true); + + reset(); + expect(hasChanged.value).toBe(false); + + source.value.regex = /changed/i; + expect(hasChanged.value).toBe(true); + }); + + it("handles objects with Symbol properties correctly", () => { + const sym = Symbol("unique"); + const source = ref>({ + [sym]: "symbolValue", + regularProp: "value", + }); + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + source.value[sym] = "newSymbolValue"; + expect(hasChanged.value).toBe(true); + + reset(); + expect(hasChanged.value).toBe(false); + + const newSym = Symbol("another"); + source.value[newSym] = "anotherValue"; + expect(hasChanged.value).toBe(true); + }); + + it("handles Map and Set objects correctly", () => { + const source = ref({ + map: new Map([["key1", "value1"]]), + set: new Set([1, 2, 3]), + }); + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + // Modify Map + source.value.map.set("key2", "value2"); + expect(hasChanged.value).toBe(true); + + // Reset and modify Set + reset(); + expect(hasChanged.value).toBe(false); + + source.value.set.add(4); + expect(hasChanged.value).toBe(true); + }); + + it("handles proxied objects correctly", () => { + const base = { name: "ProxyTest", details: { age: 25 } }; + const source = reactive(base); + const proxiedSource = new Proxy(source, { + get(target, prop, receiver) { + return Reflect.get(target, prop, receiver); + }, + set(target, prop, value, receiver) { + return Reflect.set(target, prop, value, receiver); + }, + }); + + const { hasChanged } = useHasChanged(proxiedSource); + + expect(hasChanged.value).toBe(false); + + proxiedSource.details.age = 26; + expect(hasChanged.value).toBe(true); + }); + + it("handles large objects efficiently", () => { + const largeObject: Record = {}; + for (let i = 0; i < 1000; i++) { + largeObject[`key${i}`] = `value${i}`; + } + const source = ref(largeObject); + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + source.value.key999 = "newValue999"; + expect(hasChanged.value).toBe(true); + + reset(); + expect(hasChanged.value).toBe(false); + }); + + it("handles immutable data structures correctly", () => { + const source = ref({ count: 1 }); + const { hasChanged } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + // Replace the entire object + source.value = { count: 1 }; + // No actual change + expect(hasChanged.value).toBe(false); + + // Change the value + source.value = { count: 2 }; + expect(hasChanged.value).toBe(true); + }); + + it("detects changes in computed properties with dependencies", () => { + const base = ref({ count: 1, multiplier: 2 }); + const source = computed(() => ({ + result: base.value.count * base.value.multiplier, + })); + const { hasChanged, reset } = useHasChanged(source); + + expect(hasChanged.value).toBe(false); + + // Change a dependency + base.value.count = 3; + expect(hasChanged.value).toBe(true); + + // Reset and change another dependency + reset(); + expect(hasChanged.value).toBe(false); + + base.value.multiplier = 4; + expect(hasChanged.value).toBe(true); + }); +}); diff --git a/packages/frontend/src/composables/useHasChanged.ts b/packages/frontend/src/composables/useHasChanged.ts new file mode 100644 index 00000000..dc3e23ae --- /dev/null +++ b/packages/frontend/src/composables/useHasChanged.ts @@ -0,0 +1,31 @@ +import type { Ref } from "vue"; +import { ref, watch, isRef } from "vue"; +import { isEqual, cloneDeep } from "es-toolkit"; + +type UseHasChangedReturnType = { + hasChanged: Ref; + reset: () => void; +}; + +export function useHasChanged(source: Ref | T): UseHasChangedReturnType { + const hasChanged = ref(false); + let original = cloneDeep(isRef(source) ? source.value : source); + + watch( + () => (isRef(source) ? source.value : source), + function (newVal) { + hasChanged.value = !isEqual(newVal, original); + }, + { deep: true, flush: "sync" }, + ); + + function reset(): void { + original = cloneDeep(isRef(source) ? source.value : source); + hasChanged.value = false; + } + + return { + hasChanged, + reset, + }; +} diff --git a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue index 22ef882f..7861c77f 100644 --- a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue +++ b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue @@ -5,6 +5,7 @@ import { usePropertiesStore } from "src/stores/properties-store"; import { computed, onMounted, ref } from "vue"; import { tryCatchLoadingWrapper } from "src/helpers/ui-helpers"; import { useDialog } from "src/composables/useDialog"; +import { useHasChanged } from "src/composables/useHasChanged"; import SettingsSection from "src/components/admin/organisation-settings/SettingsSection.vue"; import FTDialogTimezoneSelector from "src/components/FTDialogTimezoneSelector.vue"; @@ -12,6 +13,7 @@ import FTTitle from "src/components/FTTitle.vue"; import AppCardSection from "src/components/AppCardSection.vue"; import FTBtn from "src/components/FTBtn.vue"; import FTBottomDialog from "src/components/FTBottomDialog.vue"; +import { cloneDeep, isEqual } from "es-toolkit"; export interface PageAdminOrganisationSettingsProps { organisationId: string; @@ -57,30 +59,25 @@ type EditableSettings = { properties: Record; }; const editableSettings = ref({ - organisation: JSON.parse(JSON.stringify(organisationSettings.value)), + organisation: cloneDeep(organisationSettings.value), properties: {}, }); -const aspectRatioOptions = ["1", "16:9"]; +const { hasChanged: organisationHasChanged, reset: resetOrganisationChangedTracking } = + useHasChanged(computed(() => editableSettings.value.organisation)); -const hasSettingsChanged = computed(function () { - // Check if organisation settings changed - const organisationChanged = - JSON.stringify(editableSettings.value.organisation) !== - JSON.stringify(organisationSettings.value); - - // Check if any property settings changed - const propertiesChanged = properties.value.some(function (property) { - const currentSettings = propertiesStore.getPropertySettingsById(property.id); - const editedSettings = editableSettings.value.properties[property.id]; - return JSON.stringify(currentSettings) !== JSON.stringify(editedSettings); - }); +const { hasChanged: propertiesHaveChanged, reset: resetPropertiesChangedTracking } = useHasChanged( + computed(() => editableSettings.value.properties), +); - return organisationChanged || propertiesChanged; -}); +const hasChanged = computed(() => organisationHasChanged.value || propertiesHaveChanged.value); + +const aspectRatioOptions = ["1", "16:9"]; function reset(): void { - editableSettings.value.organisation = JSON.parse(JSON.stringify(organisationSettings.value)); + editableSettings.value.organisation = cloneDeep(organisationSettings.value); + resetOrganisationChangedTracking(); + initPropertySettings(); } @@ -93,6 +90,25 @@ function initPropertySettings(): void { markGuestAsLateAfterMinutes: propertySettings.markGuestAsLateAfterMinutes, }; }); + + resetPropertiesChangedTracking(); +} + +async function synchroniseNewOrganisationSettings(): Promise { + await updateOrganisationSettings(props.organisationId, editableSettings.value.organisation); + propertiesStore.setOrganisationSettings( + props.organisationId, + editableSettings.value.organisation, + ); +} + +async function synchroniseNewPropertySettings(propertyId: string): Promise { + await updatePropertySettings( + props.organisationId, + propertyId, + editableSettings.value.properties[propertyId], + ); + propertiesStore.setPropertySettings(propertyId, editableSettings.value.properties[propertyId]); } async function saveSettings(): Promise { @@ -101,17 +117,8 @@ async function saveSettings(): Promise { const savePromises: Promise[] = []; // Check if organisation settings changed and need to be saved - const organisationChanged = - JSON.stringify(editableSettings.value.organisation) !== - JSON.stringify(organisationSettings.value); - - if (organisationChanged) { - savePromises.push( - updateOrganisationSettings( - props.organisationId, - editableSettings.value.organisation, - ), - ); + if (organisationHasChanged.value) { + savePromises.push(synchroniseNewOrganisationSettings()); } // Check which properties had their settings changed @@ -119,15 +126,15 @@ async function saveSettings(): Promise { const currentSettings = propertiesStore.getPropertySettingsById(property.id); const editedSettings = editableSettings.value.properties[property.id]; - if (JSON.stringify(currentSettings) !== JSON.stringify(editedSettings)) { - savePromises.push( - updatePropertySettings(props.organisationId, property.id, editedSettings), - ); + if (!isEqual(currentSettings, editedSettings)) { + savePromises.push(synchroniseNewPropertySettings(property.id)); } }); - // Wait for all updates to complete await Promise.all(savePromises); + + resetOrganisationChangedTracking(); + resetPropertiesChangedTracking(); }, }); } @@ -157,17 +164,12 @@ onMounted(initPropertySettings); Save - + Reset diff --git a/packages/frontend/src/stores/properties-store.ts b/packages/frontend/src/stores/properties-store.ts index fa4363b2..ec796f0e 100644 --- a/packages/frontend/src/stores/properties-store.ts +++ b/packages/frontend/src/stores/properties-store.ts @@ -15,13 +15,14 @@ import { propertiesCollection, } from "../backend-proxy"; import { Role } from "@firetable/types"; -import { merge } from "es-toolkit"; +import { merge, cloneDeep } from "es-toolkit"; import { defineStore } from "pinia"; import { createQuery, useFirestoreCollection } from "src/composables/useFirestore"; import { documentId, query, where } from "firebase/firestore"; import { ref, watch } from "vue"; import { matchesProperty } from "es-toolkit/compat"; import { getDefaultTimezone } from "src/helpers/date-utils"; +import { AppLogger } from "src/logger/FTLogger"; const DEFAULT_PROPERTY_SETTINGS = { timezone: getDefaultTimezone(), @@ -195,6 +196,25 @@ export const usePropertiesStore = defineStore("properties", function () { await promise.value; } + function setOrganisationSettings(organisationId: string, settings: OrganisationSettings): void { + const orgIndex = organisations.value.findIndex(matchesProperty("id", organisationId)); + + if (orgIndex === -1) { + AppLogger.warn(`Organisation with ID ${organisationId} not found in the store.`); + } else { + organisations.value[orgIndex].settings = cloneDeep(settings); + } + } + + function setPropertySettings(propertyId: string, settings: PropertySettings): void { + const propIndex = properties.value.findIndex(matchesProperty("id", propertyId)); + if (propIndex === -1) { + AppLogger.warn(`Property with ID ${propertyId} not found in the store.`); + } else { + properties.value[propIndex].settings = cloneDeep(settings); + } + } + return { unsubs, properties, @@ -212,6 +232,8 @@ export const usePropertiesStore = defineStore("properties", function () { getPropertyNameById, initAdminProperties, initOrganisations, + setOrganisationSettings, + setPropertySettings, cleanup, }; }); From b4163694aac0e69ff981c38349e564f0a33f75e7 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 19:21:11 +0100 Subject: [PATCH 05/30] chore: lockfile maintenance --- pnpm-lock.yaml | 75 ++------------------------------------------------ 1 file changed, 3 insertions(+), 72 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4a7b9ea..3044b020 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2013,10 +2013,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.18.1': - resolution: {integrity: sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.18.2': resolution: {integrity: sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2028,33 +2024,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.18.1': - resolution: {integrity: sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.18.2': resolution: {integrity: sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.18.1': - resolution: {integrity: sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/typescript-estree@8.18.2': resolution: {integrity: sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.18.1': - resolution: {integrity: sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.18.2': resolution: {integrity: sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2062,10 +2041,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.18.1': - resolution: {integrity: sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.18.2': resolution: {integrity: sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4358,9 +4333,6 @@ packages: magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.30.14: - resolution: {integrity: sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==} - magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -8492,11 +8464,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.18.1': - dependencies: - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/visitor-keys': 8.18.1 - '@typescript-eslint/scope-manager@8.18.2': dependencies: '@typescript-eslint/types': 8.18.2 @@ -8513,24 +8480,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.18.1': {} - '@typescript-eslint/types@8.18.2': {} - '@typescript-eslint/typescript-estree@8.18.1(typescript@5.7.2)': - dependencies: - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/visitor-keys': 8.18.1 - debug: 4.4.0 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.7.2) - typescript: 5.7.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.18.2(typescript@5.7.2)': dependencies: '@typescript-eslint/types': 8.18.2 @@ -8545,17 +8496,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.18.1(eslint@9.17.0)(typescript@5.7.2)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.17.0) - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) - eslint: 9.17.0 - typescript: 5.7.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.18.2(eslint@9.17.0)(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.17.0) @@ -8567,11 +8507,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.18.1': - dependencies: - '@typescript-eslint/types': 8.18.1 - eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.18.2': dependencies: '@typescript-eslint/types': 8.18.2 @@ -8732,7 +8667,7 @@ snapshots: '@vue/compiler-ssr': 3.5.13 '@vue/shared': 3.5.13 estree-walker: 2.0.2 - magic-string: 0.30.14 + magic-string: 0.30.17 postcss: 8.4.49 source-map-js: 1.2.1 @@ -9923,8 +9858,8 @@ snapshots: eslint-plugin-import-x@4.6.1(eslint@9.17.0)(typescript@5.7.2): dependencies: '@types/doctrine': 0.0.9 - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/utils': 8.18.1(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.18.2 + '@typescript-eslint/utils': 8.18.2(eslint@9.17.0)(typescript@5.7.2) debug: 4.4.0 doctrine: 3.0.0 enhanced-resolve: 5.17.1 @@ -11401,10 +11336,6 @@ snapshots: dependencies: sourcemap-codec: 1.4.8 - magic-string@0.30.14: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 From 440ddba8d3081f7f174a147eb46b34b9c114103f Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 20:39:51 +0100 Subject: [PATCH 06/30] chore: up vue-18n fixes superfluous warnings --- packages/frontend/package.json | 2 +- pnpm-lock.yaml | 38 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index e5d61bcc..32908162 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -55,7 +55,7 @@ "vue": "3.5.13", "vue-draggable-plus": "0.6.0", "vue-echarts": "7.0.3", - "vue-i18n": "11.0.0", + "vue-i18n": "11.0.1", "vue-router": "4.5.0", "vuefire": "3.2.1", "workbox-build": "7.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3044b020..bf6b8acf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,8 +191,8 @@ importers: specifier: 7.0.3 version: 7.0.3(@vue/runtime-core@3.5.13)(echarts@5.5.1)(vue@3.5.13(typescript@5.7.2)) vue-i18n: - specifier: 11.0.0 - version: 11.0.0(vue@3.5.13(typescript@5.7.2)) + specifier: 11.0.1 + version: 11.0.1(vue@3.5.13(typescript@5.7.2)) vue-router: specifier: 4.5.0 version: 4.5.0(vue@3.5.13(typescript@5.7.2)) @@ -1469,16 +1469,16 @@ packages: peerDependencies: '@types/node': '>=18' - '@intlify/core-base@11.0.0': - resolution: {integrity: sha512-sbtC0hVOEr4ZAYSffQoj362inaKpb4sjgtLHVGt4tbfP+YjnHO6eUDlHgAIEESch+Kffmsc7Z+l1l2sV9oJZQQ==} + '@intlify/core-base@11.0.1': + resolution: {integrity: sha512-NAmhw1l/llM0HZRpagR/ChJTNymW4ll6/4EDSJML5c8L5Hl/+k6UyF8EIgE6DeHpfheQujkSRngauViHqq6jJQ==} engines: {node: '>= 16'} - '@intlify/message-compiler@11.0.0': - resolution: {integrity: sha512-teYbQMdZisn19KsKj/jvRIFN72Lyxd1VY69TLhhXd9wDgZMlyEVLLLdLb0wDLfbBP8uSda9vKmxYh+q8Xa5sZQ==} + '@intlify/message-compiler@11.0.1': + resolution: {integrity: sha512-5RFH8x+Mn3mbjcHXnb6KCXGiczBdiQkWkv99iiA0JpKrNuTAQeW59Pjq/uObMB0eR0shnKYGTkIJxum+DbL3sw==} engines: {node: '>= 16'} - '@intlify/shared@11.0.0': - resolution: {integrity: sha512-MLKIyFz37qYF1G5UQZMwGoJyp8DD3rtcXDplRHZdSOmbfOYbD4g9kJZ6qCsm1Ru3vee6CTnJcbO4LV1JeIDGbA==} + '@intlify/shared@11.0.1': + resolution: {integrity: sha512-lH164+aDDptHZ3dBDbIhRa1dOPQUp+83iugpc+1upTOWCnwyC1PVis6rSWNMMJ8VQxvtHQB9JMib48K55y0PvQ==} engines: {node: '>= 16'} '@isaacs/cliui@8.0.2': @@ -6202,8 +6202,8 @@ packages: peerDependencies: eslint: '>=6.0.0' - vue-i18n@11.0.0: - resolution: {integrity: sha512-U7U0IoyN2z8aaWbnRwtU2MRDxRSYKK3VKwgMGp9a3BETMuFBOIp5VlZLpuIG/A19+c5OaOx+88pjbM5wXelfqA==} + vue-i18n@11.0.1: + resolution: {integrity: sha512-pWAT8CusK8q9/EpN7V3oxwHwxWm6+Kp2PeTZmRGvdZTkUzMQDpbbmHp0TwQ8xw04XKm23cr6B4GL72y3W8Yekg==} engines: {node: '>= 16'} peerDependencies: vue: ^3.0.0 @@ -7881,17 +7881,17 @@ snapshots: dependencies: '@types/node': 22.10.2 - '@intlify/core-base@11.0.0': + '@intlify/core-base@11.0.1': dependencies: - '@intlify/message-compiler': 11.0.0 - '@intlify/shared': 11.0.0 + '@intlify/message-compiler': 11.0.1 + '@intlify/shared': 11.0.1 - '@intlify/message-compiler@11.0.0': + '@intlify/message-compiler@11.0.1': dependencies: - '@intlify/shared': 11.0.0 + '@intlify/shared': 11.0.1 source-map-js: 1.2.1 - '@intlify/shared@11.0.0': {} + '@intlify/shared@11.0.1': {} '@isaacs/cliui@8.0.2': dependencies: @@ -13279,10 +13279,10 @@ snapshots: transitivePeerDependencies: - supports-color - vue-i18n@11.0.0(vue@3.5.13(typescript@5.7.2)): + vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.2)): dependencies: - '@intlify/core-base': 11.0.0 - '@intlify/shared': 11.0.0 + '@intlify/core-base': 11.0.1 + '@intlify/shared': 11.0.1 '@vue/devtools-api': 6.6.4 vue: 3.5.13(typescript@5.7.2) From 61b1c394fc75366e34fa4bcc893589787b9a6a45 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 21:54:08 +0100 Subject: [PATCH 07/30] chore(frontend): rework settings --- .../src/app-event-handlers/guest-data.ts | 2 +- .../frontend/src/components/AppDrawer.spec.ts | 4 +- .../frontend/src/components/AppDrawer.vue | 22 +- .../Event/EventQueuedReservations.vue | 6 +- .../reservations/useReservations.ts | 7 +- packages/frontend/src/helpers/floor.ts | 17 +- .../src/pages/Admin/PageAdminEvent.vue | 6 +- .../src/pages/Admin/PageAdminEvents.vue | 5 +- .../Admin/PageAdminOrganisationSettings.vue | 295 ------------------ .../pages/Admin/PageAdminOrganisations.vue | 5 - .../src/pages/Admin/PageAdminProperties.vue | 7 + ...c.ts => PageAdminPropertySettings.spec.ts} | 63 ++-- .../pages/Admin/PageAdminPropertySettings.vue | 216 +++++++++++++ packages/frontend/src/pages/PageEvents.vue | 12 +- packages/frontend/src/router/routes.ts | 6 +- .../src/stores/properties-store.spec.ts | 41 ++- .../frontend/src/stores/properties-store.ts | 31 +- packages/types/src/organisation.ts | 32 -- packages/types/src/property.ts | 39 ++- packages/types/src/utils.ts | 10 +- 20 files changed, 385 insertions(+), 441 deletions(-) delete mode 100644 packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue rename packages/frontend/src/pages/Admin/{PageAdminOrganisationSettings.spec.ts => PageAdminPropertySettings.spec.ts} (67%) create mode 100644 packages/frontend/src/pages/Admin/PageAdminPropertySettings.vue diff --git a/packages/frontend/src/app-event-handlers/guest-data.ts b/packages/frontend/src/app-event-handlers/guest-data.ts index a4678603..e2b53d8c 100644 --- a/packages/frontend/src/app-event-handlers/guest-data.ts +++ b/packages/frontend/src/app-event-handlers/guest-data.ts @@ -70,7 +70,7 @@ async function handleGuestDataForReservation( ): Promise { const propertiesStore = usePropertiesStore(); const guestsStore = useGuestsStore(); - const settings = propertiesStore.getOrganisationSettingsById(eventOwner.organisationId); + const settings = propertiesStore.getPropertySettingsById(eventOwner.propertyId); if (!settings.guest.collectGuestData) { return; diff --git a/packages/frontend/src/components/AppDrawer.spec.ts b/packages/frontend/src/components/AppDrawer.spec.ts index 5f85f7ef..c291c67a 100644 --- a/packages/frontend/src/components/AppDrawer.spec.ts +++ b/packages/frontend/src/components/AppDrawer.spec.ts @@ -160,8 +160,8 @@ describe("AppDrawer", () => { - Manage Users - Guestbook - Manage Properties - - Settings - Report an Issue + - Settings - Logout `, }, @@ -177,8 +177,8 @@ describe("AppDrawer", () => { - Manage Analytics - Manage Users - Guestbook - - Settings - Report an Issue + - Settings - Logout `, }, diff --git a/packages/frontend/src/components/AppDrawer.vue b/packages/frontend/src/components/AppDrawer.vue index 3824871a..3bad9be5 100644 --- a/packages/frontend/src/components/AppDrawer.vue +++ b/packages/frontend/src/components/AppDrawer.vue @@ -106,6 +106,21 @@ const digitalDrinkCardsLink = computed(function () { }); }); +const propertySettingsLink = computed(function () { + if (isAdmin.value) { + return; + } + const role = nonNullableUser.value.role; + + return buildExpandableLink({ + label: t("AppDrawer.links.settings"), + icon: "cog-wheel", + routeName: "adminPropertySettings", + isVisible: role === Role.PROPERTY_OWNER || role === Role.MANAGER, + childIcon: "home", + }); +}); + const links = computed<(GuardedLink | LinkWithChildren)[]>(function () { const role = nonNullableUser.value.role; const organisationId = nonNullableUser.value.organisationId; @@ -146,18 +161,13 @@ const links = computed<(GuardedLink | LinkWithChildren)[]>(function () { label: t("AppDrawer.links.manageProperties"), isVisible: role === Role.PROPERTY_OWNER, }, - { - icon: "cog-wheel", - route: { name: "adminOrganisationSettings", params: { organisationId } }, - label: t("AppDrawer.links.settings"), - isVisible: role === Role.PROPERTY_OWNER || role === Role.MANAGER, - }, { icon: "bug", route: { name: "reportIssue", params: { organisationId } }, label: t("AppDrawer.links.reportIssue"), isVisible: !isAdmin.value, }, + propertySettingsLink.value, ]; return allLinks.filter(function (link): link is NonNullable { diff --git a/packages/frontend/src/components/Event/EventQueuedReservations.vue b/packages/frontend/src/components/Event/EventQueuedReservations.vue index 3386dd4a..3d91ee18 100644 --- a/packages/frontend/src/components/Event/EventQueuedReservations.vue +++ b/packages/frontend/src/components/Event/EventQueuedReservations.vue @@ -46,9 +46,7 @@ const { t } = useI18n(); const { createDialog } = useDialog(); const eventsStore = useEventsStore(); const propertiesStore = usePropertiesStore(); -const settings = computed(function () { - return propertiesStore.getOrganisationSettingsById(eventOwner.organisationId); -}); + const propertiesSettings = computed(function () { return propertiesStore.getPropertySettingsById(eventOwner.propertyId); }); @@ -66,7 +64,7 @@ function addNewQueuedReservation(): void { currentUser: nonNullableUser.value, users, eventStartTimestamp: eventData.date, - eventDurationInHours: settings.value.event.eventDurationInHours, + eventDurationInHours: propertiesSettings.value.event.eventDurationInHours, onlyPlanned: true, }, listeners: { diff --git a/packages/frontend/src/composables/reservations/useReservations.ts b/packages/frontend/src/composables/reservations/useReservations.ts index 6a5718b7..7deda08e 100644 --- a/packages/frontend/src/composables/reservations/useReservations.ts +++ b/packages/frontend/src/composables/reservations/useReservations.ts @@ -92,9 +92,6 @@ export function useReservations( const propertiesStore = usePropertiesStore(); const guestsStore = useGuestsStore(); - const settings = computed(function () { - return propertiesStore.getOrganisationSettingsById(eventOwner.organisationId); - }); const propertySettings = computed(function () { return propertiesStore.getPropertySettingsById(eventOwner.propertyId); }); @@ -161,7 +158,7 @@ export function useReservations( table.setVIPStatus(true); } - const fill = determineTableColor(reservation, settings.value.event); + const fill = determineTableColor(reservation, propertySettings.value.event); if (fill) { table.setFill(fill); } @@ -391,7 +388,7 @@ export function useReservations( ? { ...reservation, id: reservation.id } : void 0, eventStartTimestamp, - eventDurationInHours: settings.value.event.eventDurationInHours, + eventDurationInHours: propertySettings.value.event.eventDurationInHours, }, listeners: { async create(reservationData: Omit) { diff --git a/packages/frontend/src/helpers/floor.ts b/packages/frontend/src/helpers/floor.ts index 3c381171..1aeeb0a0 100644 --- a/packages/frontend/src/helpers/floor.ts +++ b/packages/frontend/src/helpers/floor.ts @@ -1,14 +1,13 @@ -import type { OrganisationSettings, Reservation } from "@firetable/types"; +import type { Reservation } from "@firetable/types"; import { isPlannedReservation } from "@firetable/types"; -type ColorPalette = Pick< - OrganisationSettings["event"], - | "reservationArrivedColor" - | "reservationCancelledColor" - | "reservationConfirmedColor" - | "reservationPendingColor" - | "reservationWaitingForResponseColor" ->; +type ColorPalette = { + reservationArrivedColor: string; + reservationCancelledColor: string; + reservationConfirmedColor: string; + reservationPendingColor: string; + reservationWaitingForResponseColor: string; +}; export function determineTableColor( reservation: Reservation | undefined, diff --git a/packages/frontend/src/pages/Admin/PageAdminEvent.vue b/packages/frontend/src/pages/Admin/PageAdminEvent.vue index e94857d3..b3216119 100644 --- a/packages/frontend/src/pages/Admin/PageAdminEvent.vue +++ b/packages/frontend/src/pages/Admin/PageAdminEvent.vue @@ -62,9 +62,7 @@ const propertiesStore = usePropertiesStore(); const tab = useLocalStorage("admin-event-active-tab", "info"); const reservationsTab = useLocalStorage("admin-event-guests-active-tab", "arrivedReservations"); -const settings = computed(function () { - return propertiesStore.getOrganisationSettingsById(props.organisationId); -}); + const propertySettings = computed(function () { return propertiesStore.getPropertySettingsById(props.propertyId); }); @@ -137,7 +135,7 @@ async function saveFloor(floor: FloorEditor): Promise { function isEventFinished(eventTime: number): boolean { const eventFinishedLimit = new Date(eventTime); eventFinishedLimit.setHours( - eventFinishedLimit.getHours() + settings.value.event.eventDurationInHours, + eventFinishedLimit.getHours() + propertySettings.value.event.eventDurationInHours, ); const currentTime = new Date().getTime(); return currentTime > eventFinishedLimit.getTime(); diff --git a/packages/frontend/src/pages/Admin/PageAdminEvents.vue b/packages/frontend/src/pages/Admin/PageAdminEvents.vue index d56daa6e..ef4d9148 100644 --- a/packages/frontend/src/pages/Admin/PageAdminEvents.vue +++ b/packages/frontend/src/pages/Admin/PageAdminEvents.vue @@ -47,9 +47,6 @@ const isAnyLoading = computed(function () { return isFloorsLoading.value || isLoadingEvents.value; }); -const settings = computed(function () { - return propertiesStore.getOrganisationSettingsById(organisationId); -}); const subscriptionSettings = computed(function () { return propertiesStore.getOrganisationSubscriptionSettingsById(organisationId); }); @@ -151,7 +148,7 @@ function showEventForm(event?: EventDoc): void { organisationId, propertyName: propertiesStore.getPropertyNameById(propertyId), event, - eventStartHours: settings.value.event.eventStartTime24HFormat, + eventStartHours: propertySettings.value.event.eventStartTime24HFormat, propertyTimezone: propertySettings.value.timezone, }, listeners: { diff --git a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue b/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue deleted file mode 100644 index 7861c77f..00000000 --- a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.vue +++ /dev/null @@ -1,295 +0,0 @@ - - - diff --git a/packages/frontend/src/pages/Admin/PageAdminOrganisations.vue b/packages/frontend/src/pages/Admin/PageAdminOrganisations.vue index aa1640ea..bc073c95 100644 --- a/packages/frontend/src/pages/Admin/PageAdminOrganisations.vue +++ b/packages/frontend/src/pages/Admin/PageAdminOrganisations.vue @@ -41,11 +41,6 @@ function createLinks(organisationId: string): Link[] { icon: "home", route: { name: "adminProperties", params }, }, - { - label: t("AppDrawer.links.settings"), - icon: "cog-wheel", - route: { name: "adminOrganisationSettings", params }, - }, { label: "Go to Organisation Page", icon: "link", diff --git a/packages/frontend/src/pages/Admin/PageAdminProperties.vue b/packages/frontend/src/pages/Admin/PageAdminProperties.vue index 3d93ee3a..55bdfb17 100644 --- a/packages/frontend/src/pages/Admin/PageAdminProperties.vue +++ b/packages/frontend/src/pages/Admin/PageAdminProperties.vue @@ -162,6 +162,13 @@ function createLinks(propertyId: string): Link[] { route: { name: "adminPropertyDrinkCards", params }, visible: permissionStore.canSeeInventory, }, + + { + label: t("AppDrawer.links.settings"), + icon: "cog-wheel", + route: { name: "adminPropertySettings", params }, + visible: true, + }, ].filter(function (link) { return link.visible; }); diff --git a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts b/packages/frontend/src/pages/Admin/PageAdminPropertySettings.spec.ts similarity index 67% rename from packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts rename to packages/frontend/src/pages/Admin/PageAdminPropertySettings.spec.ts index 2c655407..f10ab889 100644 --- a/packages/frontend/src/pages/Admin/PageAdminOrganisationSettings.spec.ts +++ b/packages/frontend/src/pages/Admin/PageAdminPropertySettings.spec.ts @@ -1,7 +1,7 @@ import type { RenderResult } from "vitest-browser-vue"; import type { OrganisationDoc, PropertyDoc } from "@firetable/types"; -import type { PageAdminOrganisationSettingsProps } from "./PageAdminOrganisationSettings.vue"; -import PageAdminOrganisationSettings from "./PageAdminOrganisationSettings.vue"; +import type { PageAdminPropertySettingsProps } from "./PageAdminPropertySettings.vue"; +import PageAdminPropertySettings from "./PageAdminPropertySettings.vue"; import { renderComponent } from "../../../test-helpers/render-component"; import { describe, it, expect, beforeEach, vi } from "vitest"; import { page, userEvent } from "@vitest/browser/context"; @@ -21,7 +21,7 @@ vi.mock("../../backend-proxy", () => ({ propertiesCollection: vi.fn(), })); -describe("PageAdminOrganisationSettings.vue", () => { +describe("PageAdminPropertySettings.vue", () => { let propertiesData: PropertyDoc[]; let organisationData: OrganisationDoc; @@ -30,22 +30,9 @@ describe("PageAdminOrganisationSettings.vue", () => { id: "org1", name: "Test Organisation", settings: { - event: { - eventStartTime24HFormat: "22:00", - eventDurationInHours: 10, - eventCardAspectRatio: "16:9", - reservationArrivedColor: "#1a7722", - reservationConfirmedColor: "#6247aa", - reservationCancelledColor: "#ff9f43", - reservationPendingColor: "#2ab7ca", - reservationWaitingForResponseColor: "#b5a22c", - }, property: { propertyCardAspectRatio: "1", }, - guest: { - collectGuestData: false, - }, }, } as OrganisationDoc; @@ -57,6 +44,19 @@ describe("PageAdminOrganisationSettings.vue", () => { settings: { timezone: "Europe/Vienna", markGuestAsLateAfterMinutes: 15, + event: { + eventStartTime24HFormat: "22:00", + eventDurationInHours: 10, + eventCardAspectRatio: "16:9", + reservationArrivedColor: "#1a7722", + reservationConfirmedColor: "#6247aa", + reservationCancelledColor: "#ff9f43", + reservationPendingColor: "#2ab7ca", + reservationWaitingForResponseColor: "#b5a22c", + }, + guest: { + collectGuestData: false, + }, }, }, { @@ -66,15 +66,28 @@ describe("PageAdminOrganisationSettings.vue", () => { settings: { timezone: "Europe/Athens", markGuestAsLateAfterMinutes: 30, + event: { + eventStartTime24HFormat: "22:00", + eventDurationInHours: 10, + eventCardAspectRatio: "16:9", + reservationArrivedColor: "#1a7722", + reservationConfirmedColor: "#6247aa", + reservationCancelledColor: "#ff9f43", + reservationPendingColor: "#2ab7ca", + reservationWaitingForResponseColor: "#b5a22c", + }, + guest: { + collectGuestData: false, + }, }, }, ] as PropertyDoc[]; }); async function render( - props = { organisationId: "org1" }, - ): Promise> { - const screen = renderComponent(PageAdminOrganisationSettings, props, { + props = { organisationId: "org1", propertyId: "property1" }, + ): Promise> { + const screen = renderComponent(PageAdminPropertySettings, props, { piniaStoreOptions: { initialState: { properties: { @@ -94,17 +107,13 @@ describe("PageAdminOrganisationSettings.vue", () => { it("renders all properties with their timezone settings", async () => { const screen = await render(); - const propertyOneSection = screen.getByLabelText("Property One settings card"); - const propertyTwoSection = screen.getByLabelText("Property Two settings card"); + const propertySection = screen.getByLabelText("Property One settings card"); - await expect.element(propertyOneSection).toBeInTheDocument(); - await expect.element(propertyTwoSection).toBeInTheDocument(); + await expect.element(propertySection).toBeInTheDocument(); - const propertyOneSelect = propertyOneSection.getByLabelText("Property timezone"); - const propertyTwoSelect = propertyTwoSection.getByLabelText("Property timezone"); + const propertySelect = propertySection.getByLabelText("Property timezone"); - await expect.element(propertyOneSelect).toHaveValue("Europe/Vienna"); - await expect.element(propertyTwoSelect).toHaveValue("Europe/Athens"); + await expect.element(propertySelect).toHaveValue("Europe/Vienna"); }); it("detects changes in property timezone settings", async () => { diff --git a/packages/frontend/src/pages/Admin/PageAdminPropertySettings.vue b/packages/frontend/src/pages/Admin/PageAdminPropertySettings.vue new file mode 100644 index 00000000..6453fc58 --- /dev/null +++ b/packages/frontend/src/pages/Admin/PageAdminPropertySettings.vue @@ -0,0 +1,216 @@ + + + diff --git a/packages/frontend/src/pages/PageEvents.vue b/packages/frontend/src/pages/PageEvents.vue index c11991a3..51213d2a 100644 --- a/packages/frontend/src/pages/PageEvents.vue +++ b/packages/frontend/src/pages/PageEvents.vue @@ -29,22 +29,22 @@ const eventOwner: EventOwner = { id: "", }; -const settings = computed(function () { - return propertiesStore.getOrganisationSettingsById(props.organisationId); -}); - const propertySettings = computed(function () { return propertiesStore.getPropertySettingsById(props.propertyId); }); const cardsAspectRatio = computed(function () { - return parseAspectRatio(settings.value.event.eventCardAspectRatio); + return parseAspectRatio(propertySettings.value.event.eventCardAspectRatio); }); const { data: events, pending: isLoading } = useFirestoreCollection( createQuery( eventsCollection(eventOwner), - where("date", ">=", Date.now() - ONE_HOUR * settings.value.event.eventDurationInHours), + where( + "date", + ">=", + Date.now() - ONE_HOUR * propertySettings.value.event.eventDurationInHours, + ), where("propertyId", "==", props.propertyId), orderBy("date"), limit(10), diff --git a/packages/frontend/src/router/routes.ts b/packages/frontend/src/router/routes.ts index 9d4ab189..6b088689 100644 --- a/packages/frontend/src/router/routes.ts +++ b/packages/frontend/src/router/routes.ts @@ -128,13 +128,13 @@ export const routes: RouteRecordRaw[] = [ }, }, { - path: "/admin/organisations/:organisationId/settings", - name: "adminOrganisationSettings", + path: "/admin/organisations/:organisationId/:propertyId/settings", + name: "adminPropertySettings", meta: { requiresAuth: true, allowedRoles: [AdminRole.ADMIN, Role.PROPERTY_OWNER, Role.MANAGER], }, - component: () => import("src/pages/Admin/PageAdminOrganisationSettings.vue"), + component: () => import("src/pages/Admin/PageAdminPropertySettings.vue"), props: true, }, { diff --git a/packages/frontend/src/stores/properties-store.spec.ts b/packages/frontend/src/stores/properties-store.spec.ts index 647283e9..78ca5a66 100644 --- a/packages/frontend/src/stores/properties-store.spec.ts +++ b/packages/frontend/src/stores/properties-store.spec.ts @@ -113,8 +113,22 @@ describe("properties-store.ts", () => { const settings = store.getPropertySettingsById("property1"); expect(settings).toEqual({ - timezone: "Europe/London", + event: { + eventCardAspectRatio: "16:9", + eventDurationInHours: 10, + eventStartTime24HFormat: "22:00", + reservationArrivedColor: "#1a7722", + reservationCancelledColor: "#ff9f43", + reservationConfirmedColor: "#6247aa", + reservationPendingColor: "#2ab7ca", + reservationWaitingForResponseColor: "#b5a22c", + }, + guest: { + collectGuestData: false, + globalGuestTags: [], + }, markGuestAsLateAfterMinutes: 10, + timezone: expect.any(String), }); }); @@ -126,6 +140,20 @@ describe("properties-store.ts", () => { const settings = store.getPropertySettingsById("property1"); expect(settings).toEqual({ timezone: expect.any(String), + event: { + eventCardAspectRatio: "16:9", + eventDurationInHours: 10, + eventStartTime24HFormat: "22:00", + reservationArrivedColor: "#1a7722", + reservationCancelledColor: "#ff9f43", + reservationConfirmedColor: "#6247aa", + reservationPendingColor: "#2ab7ca", + reservationWaitingForResponseColor: "#b5a22c", + }, + guest: { + collectGuestData: false, + globalGuestTags: [], + }, markGuestAsLateAfterMinutes: 10, }); }); @@ -146,18 +174,15 @@ describe("properties-store.ts", () => { const organisation = createTestOrganisation({ settings: { ...DEFAULT_ORGANISATION_SETTINGS, - event: { - ...DEFAULT_ORGANISATION_SETTINGS.event, - eventDurationInHours: 12, + property: { + propertyCardAspectRatio: "16:9", }, }, }); store.organisations = [organisation]; const settings = store.getOrganisationSettingsById("org1"); - expect(settings.event.eventDurationInHours).toBe(12); - expect(settings.event.eventStartTime24HFormat).toBe("22:00"); - expect(settings.property.propertyCardAspectRatio).toBe("1"); + expect(settings.property.propertyCardAspectRatio).toBe("16:9"); }); it("returns default settings when organisation has no settings", () => { @@ -168,9 +193,7 @@ describe("properties-store.ts", () => { const settings = store.getOrganisationSettingsById("org1"); expect(settings).toEqual( expect.objectContaining({ - event: expect.any(Object), property: expect.any(Object), - guest: expect.any(Object), }), ); }); diff --git a/packages/frontend/src/stores/properties-store.ts b/packages/frontend/src/stores/properties-store.ts index ec796f0e..fe73ba4c 100644 --- a/packages/frontend/src/stores/properties-store.ts +++ b/packages/frontend/src/stores/properties-store.ts @@ -24,12 +24,13 @@ import { matchesProperty } from "es-toolkit/compat"; import { getDefaultTimezone } from "src/helpers/date-utils"; import { AppLogger } from "src/logger/FTLogger"; -const DEFAULT_PROPERTY_SETTINGS = { - timezone: getDefaultTimezone(), - markGuestAsLateAfterMinutes: 0, +export const DEFAULT_ORGANISATION_SETTINGS: OrganisationSettings = { + property: { + propertyCardAspectRatio: "1", + }, }; -export const DEFAULT_ORGANISATION_SETTINGS: DeepRequired = { +const DEFAULT_PROPERTY_SETTINGS: DeepRequired = { event: { eventStartTime24HFormat: "22:00", eventDurationInHours: 10, @@ -40,13 +41,12 @@ export const DEFAULT_ORGANISATION_SETTINGS: DeepRequired = reservationPendingColor: "#2ab7ca", reservationWaitingForResponseColor: "#b5a22c", }, - property: { - propertyCardAspectRatio: "1", - }, guest: { collectGuestData: false, globalGuestTags: [], }, + timezone: getDefaultTimezone(), + markGuestAsLateAfterMinutes: 0, }; export const DEFAULT_SUBSCRIPTION_SETTINGS = { @@ -97,12 +97,14 @@ export const usePropertiesStore = defineStore("properties", function () { return merge(DEFAULT_SUBSCRIPTION_SETTINGS, settings); } - function getOrganisationSettingsById(organisationId: string): OrganisationSettings { + function getOrganisationSettingsById( + organisationId: string, + ): DeepRequired { const settings = getOrganisationById(organisationId)?.settings ?? {}; return merge(DEFAULT_ORGANISATION_SETTINGS, settings); } - function getPropertySettingsById(propertyId: string): PropertySettings { + function getPropertySettingsById(propertyId: string): DeepRequired { const property = properties.value.find(matchesProperty("id", propertyId)); if (!property) { throw new Error("No property found for the given property id"); @@ -196,16 +198,6 @@ export const usePropertiesStore = defineStore("properties", function () { await promise.value; } - function setOrganisationSettings(organisationId: string, settings: OrganisationSettings): void { - const orgIndex = organisations.value.findIndex(matchesProperty("id", organisationId)); - - if (orgIndex === -1) { - AppLogger.warn(`Organisation with ID ${organisationId} not found in the store.`); - } else { - organisations.value[orgIndex].settings = cloneDeep(settings); - } - } - function setPropertySettings(propertyId: string, settings: PropertySettings): void { const propIndex = properties.value.findIndex(matchesProperty("id", propertyId)); if (propIndex === -1) { @@ -232,7 +224,6 @@ export const usePropertiesStore = defineStore("properties", function () { getPropertyNameById, initAdminProperties, initOrganisations, - setOrganisationSettings, setPropertySettings, cleanup, }; diff --git a/packages/types/src/organisation.ts b/packages/types/src/organisation.ts index 5f596ece..76aa2b4e 100644 --- a/packages/types/src/organisation.ts +++ b/packages/types/src/organisation.ts @@ -58,36 +58,4 @@ export interface OrganisationSettings { */ propertyCardAspectRatio: AspectRatio; }; - event: { - /** - * Aspect ratio for event cards in the UI - */ - eventCardAspectRatio: AspectRatio; - /** - * Default start time for new events in 24h format - */ - eventStartTime24HFormat: string; - /** - * Default duration for new events in hours - */ - eventDurationInHours: number; - /** - * Color codes for different reservation states - */ - reservationArrivedColor: string; - reservationConfirmedColor: string; - reservationCancelledColor: string; - reservationPendingColor: string; - reservationWaitingForResponseColor: string; - }; - guest: { - /** - * Whether to collect the guest information - */ - collectGuestData: boolean; - /** - * Organisation-wide tags that can be applied to guests - */ - globalGuestTags?: string[]; - }; } diff --git a/packages/types/src/property.ts b/packages/types/src/property.ts index 0505424f..d2ff2ceb 100644 --- a/packages/types/src/property.ts +++ b/packages/types/src/property.ts @@ -1,3 +1,5 @@ +import type { AspectRatio } from "./organisation.js"; + export interface ImageUploadData { dataUrl: string; type: string; @@ -20,14 +22,47 @@ export interface PropertySettings { * IANA timezone identifier for the property location * Used for date/time calculations */ - timezone: string; + timezone?: string; /** * After configured amount of minutes the guest will be marked as late * on the floor plan * Value is in minutes */ - markGuestAsLateAfterMinutes: number; + markGuestAsLateAfterMinutes?: number; + + event?: Partial<{ + /** + * Aspect ratio for event cards in the UI + */ + eventCardAspectRatio: AspectRatio; + /** + * Default start time for new events in 24h format + */ + eventStartTime24HFormat: string; + /** + * Default duration for new events in hours + */ + eventDurationInHours: number; + /** + * Color codes for different reservation states + */ + reservationArrivedColor: string; + reservationConfirmedColor: string; + reservationCancelledColor: string; + reservationPendingColor: string; + reservationWaitingForResponseColor: string; + }>; + guest?: Partial<{ + /** + * Whether to collect the guest information + */ + collectGuestData: boolean; + /** + * Organisation-wide tags that can be applied to guests + */ + globalGuestTags?: string[]; + }>; } /** diff --git a/packages/types/src/utils.ts b/packages/types/src/utils.ts index e50f7ed8..e96d01b3 100644 --- a/packages/types/src/utils.ts +++ b/packages/types/src/utils.ts @@ -2,13 +2,9 @@ export type NumberTuple = [number, number]; export type AnyFunction = (...args: any[]) => any; export type VoidFunction = (...args: any[]) => void; -export type DeepRequired = { - [P in keyof T]-?: T[P] extends object - ? T[P] extends AnyFunction - ? T[P] - : DeepRequired - : T[P]; -}; +export type DeepRequired = Required<{ + [K in keyof T]: T[K] extends Required ? T[K] : DeepRequired; +}>; export type DeepPartial = { [P in keyof T]?: T[P] extends Record From 0a438e1a0e92016b14d43ab3287d4766e159b5e8 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 22:02:32 +0100 Subject: [PATCH 08/30] chore(floor-creator): remove cheerio from devdeps --- packages/floor-creator/package.json | 1 - pnpm-lock.yaml | 126 ++-------------------------- 2 files changed, 5 insertions(+), 122 deletions(-) diff --git a/packages/floor-creator/package.json b/packages/floor-creator/package.json index 79d41f16..525b104b 100644 --- a/packages/floor-creator/package.json +++ b/packages/floor-creator/package.json @@ -26,7 +26,6 @@ "@types/hammerjs": "2.0.46", "@types/node": "22.10.2", "@vitest/browser": "catalog:", - "cheerio": "1.0.0", "vitest": "catalog:" }, "keywords": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf6b8acf..df2ef261 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,9 +118,6 @@ importers: '@vitest/browser': specifier: 'catalog:' version: https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) - cheerio: - specifier: 1.0.0 - version: 1.0.0 vitest: specifier: 'catalog:' version: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) @@ -2537,13 +2534,6 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - cheerio-select@2.1.0: - resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - - cheerio@1.0.0: - resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} - engines: {node: '>=18.17'} - chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2776,13 +2766,6 @@ packages: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} - - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2957,19 +2940,6 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -3041,9 +3011,6 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - encoding-sniffer@0.2.0: - resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} - encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} @@ -3701,9 +3668,6 @@ packages: engines: {node: ^14.13.1 || >=16.0.0} hasBin: true - htmlparser2@9.1.0: - resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} - http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -4751,12 +4715,6 @@ packages: parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} - parse5-htmlparser2-tree-adapter@7.0.0: - resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} - - parse5-parser-stream@7.1.2: - resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - parse5@5.1.1: resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} @@ -5948,10 +5906,6 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - undici@6.20.0: - resolution: {integrity: sha512-AITZfPuxubm31Sx0vr8bteSalEbs9wQb/BOBi9FPlD9Qpd6HxZ4Q0+hI742jBhkPb4RT2v5MQzaW5VhRVyj+9A==} - engines: {node: '>=18.17'} - unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -9132,29 +9086,6 @@ snapshots: check-error@2.1.1: {} - cheerio-select@2.1.0: - dependencies: - boolbase: 1.0.0 - css-select: 5.1.0 - css-what: 6.1.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.1.0 - - cheerio@1.0.0: - dependencies: - cheerio-select: 2.1.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - domutils: 3.1.0 - encoding-sniffer: 0.2.0 - htmlparser2: 9.1.0 - parse5: 7.1.2 - parse5-htmlparser2-tree-adapter: 7.0.0 - parse5-parser-stream: 7.1.2 - undici: 6.20.0 - whatwg-mimetype: 4.0.0 - chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -9411,16 +9342,6 @@ snapshots: crypto-random-string@2.0.0: {} - css-select@5.1.0: - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 5.0.3 - domutils: 3.1.0 - nth-check: 2.1.1 - - css-what@6.1.0: {} - cssesc@3.0.0: {} cssstyle@4.1.0: @@ -9560,24 +9481,6 @@ snapshots: dom-accessibility-api@0.5.16: {} - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.1.0: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dot-case@3.0.4: dependencies: no-case: 3.0.4 @@ -9646,11 +9549,6 @@ snapshots: encodeurl@2.0.0: {} - encoding-sniffer@0.2.0: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding: 3.1.1 - encoding@0.1.13: dependencies: iconv-lite: 0.6.3 @@ -10680,13 +10578,6 @@ snapshots: relateurl: 0.2.7 terser: 5.34.1 - htmlparser2@9.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.1.0 - entities: 4.5.0 - http-cache-semantics@4.1.1: optional: true @@ -10736,6 +10627,7 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + optional: true idb@7.1.1: {} @@ -11793,15 +11685,6 @@ snapshots: dependencies: parse5: 6.0.1 - parse5-htmlparser2-tree-adapter@7.0.0: - dependencies: - domhandler: 5.0.3 - parse5: 7.1.2 - - parse5-parser-stream@7.1.2: - dependencies: - parse5: 7.1.2 - parse5@5.1.1: {} parse5@6.0.1: {} @@ -11809,6 +11692,7 @@ snapshots: parse5@7.1.2: dependencies: entities: 4.5.0 + optional: true parseurl@1.3.3: {} @@ -13034,8 +12918,6 @@ snapshots: undici-types@6.20.0: {} - undici@6.20.0: {} - unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-emoji-modifier-base@1.0.0: {} @@ -13347,10 +13229,12 @@ snapshots: whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 + optional: true whatwg-fetch@3.6.20: {} - whatwg-mimetype@4.0.0: {} + whatwg-mimetype@4.0.0: + optional: true whatwg-url@14.0.0: dependencies: From 52698ded934996266313befefdf4bfd250339c56 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 22:11:54 +0100 Subject: [PATCH 09/30] chore(floor-creator): remove obsolete code in floor-creator Fabric has fixed a bug and we don't need the workaround anymore --- packages/floor-creator/src/CanvasHistory.ts | 8 ++------ packages/floor-creator/src/FloorViewer.ts | 12 ------------ packages/floor-creator/src/TouchManager.ts | 1 - 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/packages/floor-creator/src/CanvasHistory.ts b/packages/floor-creator/src/CanvasHistory.ts index 0feda3e6..29de576a 100644 --- a/packages/floor-creator/src/CanvasHistory.ts +++ b/packages/floor-creator/src/CanvasHistory.ts @@ -2,7 +2,7 @@ import type { FloorEditor } from "./FloorEditor.js"; import type { FabricObject, TFiller } from "fabric"; import { Group } from "fabric"; import { EventEmitter } from "@posva/event-emitter"; -import { isEqual, once } from "es-toolkit"; +import { delay, isEqual, once } from "es-toolkit"; export interface HistoryState { width: number; @@ -240,9 +240,7 @@ export class CanvasHistory extends EventEmitter { }); // Additional wait to ensure all object events have fired - await new Promise((resolve) => { - setTimeout(resolve, 0); - }); + await delay(0); } finally { this.isHistoryProcessing = false; this.emit("stateChange"); @@ -253,11 +251,9 @@ export class CanvasHistory extends EventEmitter { const obj1 = JSON.parse(json1); const obj2 = JSON.parse(json2); - // Normalize the objects by removing irrelevant properties const objects1 = CanvasHistory.normalize(obj1.objects); const objects2 = CanvasHistory.normalize(obj2.objects); - // Compare the normalized objects return isEqual(objects1, objects2); } } diff --git a/packages/floor-creator/src/FloorViewer.ts b/packages/floor-creator/src/FloorViewer.ts index a16c86ba..d491d7a4 100644 --- a/packages/floor-creator/src/FloorViewer.ts +++ b/packages/floor-creator/src/FloorViewer.ts @@ -54,18 +54,6 @@ export class FloorViewer extends Floor { this.eventManager.destroy(); this.zoomManager.destroy(); this.touchManager.destroy(); - // This is an ugly hack to work around the fact that fabric does not clear the mouseDownTimeout - // during dispose. This causes an exception to be thrown if the canvas is disposed while the mouse - // is down. Reported this issue https://github.com/fabricjs/fabric.js/issues/10248 - // @ts-expect-error -- private property - // eslint-disable-next-line no-underscore-dangle -- fabric naming - const mouseDownTimeout = this.canvas._willAddMouseDown; - if (mouseDownTimeout) { - clearTimeout(mouseDownTimeout); - // @ts-expect-error -- private property - // eslint-disable-next-line no-underscore-dangle -- fabric naming - this.canvas._willAddMouseDown = 0; - } await this.canvas.dispose(); } diff --git a/packages/floor-creator/src/TouchManager.ts b/packages/floor-creator/src/TouchManager.ts index ce31a12c..725e1c64 100644 --- a/packages/floor-creator/src/TouchManager.ts +++ b/packages/floor-creator/src/TouchManager.ts @@ -31,7 +31,6 @@ export class TouchManager { const pinch = new Pinch({ enable: true, direction: DIRECTION_ALL }); const pan = new Pan({ direction: DIRECTION_ALL, threshold: 50 }); - // Add the recognizers to the hammerManager instance this.hammerManager.add([pinch, pan, doubleTap]); this.hammerManager.on("pinch", this.onPinch); From 0df8efcfc153d39a70e5f9d98053868e48e971f7 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Thu, 26 Dec 2024 22:40:54 +0100 Subject: [PATCH 10/30] chore: reduce instances of deprecated punycode --- package.json | 3 ++- pnpm-lock.yaml | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6b1cb03b..0eec2ce7 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "packageManager": "pnpm@9.15.1", "pnpm": { "overrides": { - "jsdom": "25.0.1" + "jsdom": "25.0.1", + "uri-js": "npm:uri-js-replace" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df2ef261..34c01b34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,7 @@ catalogs: overrides: jsdom: 25.0.1 + uri-js: npm:uri-js-replace importers: @@ -5968,8 +5969,8 @@ packages: resolution: {integrity: sha512-wgxdSBWv3x/YpMzsWz5G4p4ec7JWD0HCl8W6bmNB6E5Gwo+1ym5oN4hiXpLf0mPySVEJEIsYlkshnplkg2OP9A==} engines: {node: '>=14'} - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} url-join@0.0.1: resolution: {integrity: sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw==} @@ -8743,7 +8744,7 @@ snapshots: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 - uri-js: 4.4.1 + uri-js: uri-js-replace@1.0.1 ajv@8.17.1: dependencies: @@ -12987,9 +12988,7 @@ snapshots: transitivePeerDependencies: - encoding - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 + uri-js-replace@1.0.1: {} url-join@0.0.1: {} From 9908371796182a7060729b50776cc75e7c8d9e90 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 12:34:18 +0100 Subject: [PATCH 11/30] chore: up deps --- .../reservation/PlannedReservationForm.vue | 2 +- .../analytics/useReservationsAnalytics.ts | 1 + .../reservations/useReservationPermissions.ts | 1 + .../reservations/useTableOperations.ts | 1 + .../frontend/src/composables/useAdminEvent.ts | 1 + .../frontend/src/composables/useDialog.ts | 4 +- .../frontend/src/composables/useEvents.ts | 1 + .../src/composables/useFloorEditor.ts | 1 + .../src/composables/useGuestsForEvent.ts | 1 + packages/functions/package.json | 4 +- pnpm-lock.yaml | 166 +++++++++--------- pnpm-workspace.yaml | 10 +- 12 files changed, 101 insertions(+), 92 deletions(-) diff --git a/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue b/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue index c0d7fc18..2b6cf129 100644 --- a/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue +++ b/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue @@ -33,7 +33,7 @@ const props = defineProps(); const { t } = useI18n(); // Move initial state logic into a function -function generateInitialState() { +function generateInitialState(): Omit { return props.mode === "update" && props.reservationData ? { ...props.reservationData } : { diff --git a/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts b/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts index 1f0fef56..a608845d 100644 --- a/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts +++ b/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts @@ -25,6 +25,7 @@ const ENG_DAYS_OF_WEEK = [ "Saturday", ]; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useReservationsAnalytics( property: Ref, organisationId: string, diff --git a/packages/frontend/src/composables/reservations/useReservationPermissions.ts b/packages/frontend/src/composables/reservations/useReservationPermissions.ts index 66239675..862bc469 100644 --- a/packages/frontend/src/composables/reservations/useReservationPermissions.ts +++ b/packages/frontend/src/composables/reservations/useReservationPermissions.ts @@ -6,6 +6,7 @@ import { useAuthStore } from "src/stores/auth-store"; import { computed } from "vue"; import { usePermissionsStore } from "src/stores/permissions-store"; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useReservationPermissions(reservation: Ref) { const authStore = useAuthStore(); const permissionsStore = usePermissionsStore(); diff --git a/packages/frontend/src/composables/reservations/useTableOperations.ts b/packages/frontend/src/composables/reservations/useTableOperations.ts index 6fd98b70..6d5f94aa 100644 --- a/packages/frontend/src/composables/reservations/useTableOperations.ts +++ b/packages/frontend/src/composables/reservations/useTableOperations.ts @@ -41,6 +41,7 @@ export type TableOperation = | ReservationLinkOperation | ReservationTransferOperation; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useTableOperations() { const quasar = useQuasar(); diff --git a/packages/frontend/src/composables/useAdminEvent.ts b/packages/frontend/src/composables/useAdminEvent.ts index d62ab648..7b15352b 100644 --- a/packages/frontend/src/composables/useAdminEvent.ts +++ b/packages/frontend/src/composables/useAdminEvent.ts @@ -26,6 +26,7 @@ import { decompressFloorDoc } from "src/helpers/compress-floor-doc"; import { property } from "es-toolkit/compat"; import { AppLogger } from "src/logger/FTLogger.js"; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useAdminEvent(eventOwner: EventOwner) { const router = useRouter(); diff --git a/packages/frontend/src/composables/useDialog.ts b/packages/frontend/src/composables/useDialog.ts index 63c85d03..ab6fbd42 100644 --- a/packages/frontend/src/composables/useDialog.ts +++ b/packages/frontend/src/composables/useDialog.ts @@ -1,6 +1,7 @@ import type { DialogChainObject } from "quasar"; import type { Component } from "vue"; import type { ComponentProps } from "vue-component-type-helpers"; +import type { AnyFunction } from "@firetable/types"; import { useQuasar } from "quasar"; type CreateOptions = { @@ -9,11 +10,12 @@ type CreateOptions = { component: C; maximized?: boolean; componentPropsObject?: ComponentProps; - listeners?: Record any>; + listeners?: Record; title?: string; }; }; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useDialog() { const quasar = useQuasar(); diff --git a/packages/frontend/src/composables/useEvents.ts b/packages/frontend/src/composables/useEvents.ts index 7ba1ab61..0d0a8f98 100644 --- a/packages/frontend/src/composables/useEvents.ts +++ b/packages/frontend/src/composables/useEvents.ts @@ -26,6 +26,7 @@ const EVENTS_PER_PAGE = 50; * @property isLoading - A ref that indicates the loading state. It's initially set to `true` * and becomes `false` after the first fetch completes. It remains unchanged for subsequent fetches. */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useEvents(eventOwner: EventOwner) { const events = ref([]); const lastFetchedDoc = ref(); diff --git a/packages/frontend/src/composables/useFloorEditor.ts b/packages/frontend/src/composables/useFloorEditor.ts index 88b0c336..ad43379e 100644 --- a/packages/frontend/src/composables/useFloorEditor.ts +++ b/packages/frontend/src/composables/useFloorEditor.ts @@ -10,6 +10,7 @@ import { isNumber } from "es-toolkit/compat"; export const TABLE_EL_TO_ADD = [FloorElementTypes.RECT_TABLE, FloorElementTypes.ROUND_TABLE]; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useFloorEditor(containerRef: ShallowRef) { const floorInstance = shallowRef(); const selectedElement = ref(); diff --git a/packages/frontend/src/composables/useGuestsForEvent.ts b/packages/frontend/src/composables/useGuestsForEvent.ts index 2074e98b..8073fcde 100644 --- a/packages/frontend/src/composables/useGuestsForEvent.ts +++ b/packages/frontend/src/composables/useGuestsForEvent.ts @@ -7,6 +7,7 @@ import { useGuestsStore } from "src/stores/guests-store"; import { hashString } from "src/helpers/hash-string"; import { uniq } from "es-toolkit"; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- pretty verbose export function useGuestsForEvent(eventOwner: EventOwner, reservations: Ref) { const guestsStore = useGuestsStore(); diff --git a/packages/functions/package.json b/packages/functions/package.json index 8843456a..dbb95da7 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -17,13 +17,13 @@ "@google-cloud/functions-framework": "3.4.4", "firebase-admin": "13.0.2", "firebase-functions": "6.2.0", - "es-toolkit": "1.30.1" + "es-toolkit": "1.31.0" }, "devDependencies": { "@faker-js/faker": "9.3.0", "@firebase/app-types": "0.9.3", "@firebase/rules-unit-testing": "4.0.1", - "vitest": "https://pkg.pr.new/vitest@61b3016" + "vitest": "https://pkg.pr.new/vitest@bfea79f" }, "private": true } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34c01b34..34d20b75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,22 +10,22 @@ catalogs: specifier: 1.0.2 version: 1.0.2 '@vitest/browser': - specifier: https://pkg.pr.new/@vitest/browser@61b3016 + specifier: https://pkg.pr.new/@vitest/browser@bfea79f version: 3.0.0-beta.3 '@vitest/coverage-v8': - specifier: https://pkg.pr.new/@vitest/coverage-v8@61b3016 + specifier: https://pkg.pr.new/@vitest/coverage-v8@bfea79f version: 3.0.0-beta.3 '@vitest/ui': - specifier: https://pkg.pr.new/@vitest/ui@61b3016 + specifier: https://pkg.pr.new/@vitest/ui@bfea79f version: 3.0.0-beta.3 es-toolkit: - specifier: 1.30.1 - version: 1.30.1 + specifier: 1.31.0 + version: 1.31.0 firebase: specifier: 11.1.0 version: 11.1.0 vitest: - specifier: https://pkg.pr.new/vitest@61b3016 + specifier: https://pkg.pr.new/vitest@bfea79f version: 3.0.0-beta.3 overrides: @@ -86,7 +86,7 @@ importers: dependencies: es-toolkit: specifier: 'catalog:' - version: 1.30.1 + version: 1.31.0 firebase: specifier: 'catalog:' version: 11.1.0 @@ -102,7 +102,7 @@ importers: version: 1.0.2 es-toolkit: specifier: 'catalog:' - version: 1.30.1 + version: 1.31.0 fabric: specifier: 6.5.3 version: 6.5.3(encoding@0.1.13) @@ -118,10 +118,10 @@ importers: version: 22.10.2 '@vitest/browser': specifier: 'catalog:' - version: https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) + version: https://pkg.pr.new/@vitest/browser@bfea79f(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) vitest: specifier: 'catalog:' - version: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + version: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) packages/frontend: dependencies: @@ -154,7 +154,7 @@ importers: version: 5.5.1 es-toolkit: specifier: 'catalog:' - version: 1.30.1 + version: 1.31.0 fflate: specifier: 0.8.2 version: 0.8.2 @@ -227,13 +227,13 @@ importers: version: 5.2.1(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vue@3.5.13(typescript@5.7.2)) '@vitest/browser': specifier: 'catalog:' - version: https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) + version: https://pkg.pr.new/@vitest/browser@bfea79f(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) '@vitest/coverage-v8': specifier: 'catalog:' - version: https://pkg.pr.new/@vitest/coverage-v8@61b3016(@vitest/browser@3.0.0-beta.3)(vitest@3.0.0-beta.3) + version: https://pkg.pr.new/@vitest/coverage-v8@bfea79f(@vitest/browser@3.0.0-beta.3)(vitest@3.0.0-beta.3) '@vitest/ui': specifier: 'catalog:' - version: https://pkg.pr.new/@vitest/ui@61b3016(vitest@3.0.0-beta.3) + version: https://pkg.pr.new/@vitest/ui@bfea79f(vitest@3.0.0-beta.3) '@vue/test-utils': specifier: 2.4.6 version: 2.4.6 @@ -242,7 +242,7 @@ importers: version: 5.1.4(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1)) vitest: specifier: 'catalog:' - version: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + version: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) vitest-browser-vue: specifier: 0.0.1 version: 0.0.1(@vitest/browser@3.0.0-beta.3)(vitest@3.0.0-beta.3)(vue@3.5.13(typescript@5.7.2)) @@ -259,8 +259,8 @@ importers: specifier: 3.4.4 version: 3.4.4 es-toolkit: - specifier: 1.30.1 - version: 1.30.1 + specifier: 1.31.0 + version: 1.31.0 firebase-admin: specifier: 13.0.2 version: 13.0.2(encoding@0.1.13) @@ -278,8 +278,8 @@ importers: specifier: 4.0.1 version: 4.0.1(firebase@11.1.0) vitest: - specifier: https://pkg.pr.new/vitest@61b3016 - version: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + specifier: https://pkg.pr.new/vitest@bfea79f + version: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) packages/types: dependencies: @@ -2050,8 +2050,8 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/browser@https://pkg.pr.new/@vitest/browser@61b3016': - resolution: {tarball: https://pkg.pr.new/@vitest/browser@61b3016} + '@vitest/browser@https://pkg.pr.new/@vitest/browser@bfea79f': + resolution: {tarball: https://pkg.pr.new/@vitest/browser@bfea79f} version: 3.0.0-beta.3 peerDependencies: playwright: '*' @@ -2066,8 +2066,8 @@ packages: webdriverio: optional: true - '@vitest/coverage-v8@https://pkg.pr.new/@vitest/coverage-v8@61b3016': - resolution: {tarball: https://pkg.pr.new/@vitest/coverage-v8@61b3016} + '@vitest/coverage-v8@https://pkg.pr.new/@vitest/coverage-v8@bfea79f': + resolution: {tarball: https://pkg.pr.new/@vitest/coverage-v8@bfea79f} version: 3.0.0-beta.3 peerDependencies: '@vitest/browser': 3.0.0-beta.3 @@ -2076,12 +2076,12 @@ packages: '@vitest/browser': optional: true - '@vitest/expect@https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/expect@https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 - '@vitest/mocker@https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/mocker@https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 peerDependencies: msw: ^2.4.9 @@ -2095,20 +2095,20 @@ packages: '@vitest/pretty-format@3.0.0-beta.3': resolution: {integrity: sha512-d3+IaliPJLndUB/eQ4XP4OEHWhMDUJGhGc8XyLfi803Ad62nkTLC1bwFz80bNghEfKi+sz/oSkvQmW/3lQeCdA==} - '@vitest/pretty-format@https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/pretty-format@https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 - '@vitest/runner@https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/runner@https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 - '@vitest/snapshot@https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/snapshot@https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 - '@vitest/spy@https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/spy@https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 '@vitest/ui@3.0.0-beta.3': @@ -2116,8 +2116,8 @@ packages: peerDependencies: vitest: 3.0.0-beta.3 - '@vitest/ui@https://pkg.pr.new/@vitest/ui@61b3016': - resolution: {tarball: https://pkg.pr.new/@vitest/ui@61b3016} + '@vitest/ui@https://pkg.pr.new/@vitest/ui@bfea79f': + resolution: {tarball: https://pkg.pr.new/@vitest/ui@bfea79f} version: 3.0.0-beta.3 peerDependencies: vitest: 3.0.0-beta.3 @@ -2125,8 +2125,8 @@ packages: '@vitest/utils@3.0.0-beta.3': resolution: {integrity: sha512-InEoZvpkcUTmg0J08/Phm6MLkQwWpf3RCJoqVuXiHK7OkUYW/EDoT0qfzL4MGo1PWq+UqIlMw93eAkkTf3RMvQ==} - '@vitest/utils@https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca': - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + '@vitest/utils@https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 '@volar/language-core@2.4.11': @@ -3067,8 +3067,8 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - es-toolkit@1.30.1: - resolution: {integrity: sha512-ZXflqanzH8BpHkDhFa10bBf6ONDCe84EPUm7SSICGzuuROSluT2ynTPtwn9PcRelMtorCRozSknI/U0MNYp0Uw==} + es-toolkit@1.31.0: + resolution: {integrity: sha512-vwS0lv/tzjM2/t4aZZRAgN9I9TP0MSkWuvt6By+hEXfG/uLs8yg2S1/ayRXH/x3pinbLgVJYT+eppueg3cM6tg==} esbuild@0.24.0: resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} @@ -6016,8 +6016,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite-node@https://pkg.pr.new/vitest-dev/vitest/vite-node@61b30162ae32e34d4dd6ad219a7cd877aab21cca: - resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/vite-node@61b30162ae32e34d4dd6ad219a7cd877aab21cca} + vite-node@https://pkg.pr.new/vitest-dev/vitest/vite-node@bfea79f54edc1d513597ecdc45a6e3f86c7bd351: + resolution: {tarball: https://pkg.pr.new/vitest-dev/vitest/vite-node@bfea79f54edc1d513597ecdc45a6e3f86c7bd351} version: 3.0.0-beta.3 engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -6078,8 +6078,8 @@ packages: vitest: ^2.1.0-beta.4 vue: ^3.0.0 - vitest@https://pkg.pr.new/vitest@61b3016: - resolution: {tarball: https://pkg.pr.new/vitest@61b3016} + vitest@https://pkg.pr.new/vitest@bfea79f: + resolution: {tarball: https://pkg.pr.new/vitest@bfea79f} version: 3.0.0-beta.3 engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -8472,17 +8472,17 @@ snapshots: vite: 6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) vue: 3.5.13(typescript@5.7.2) - '@vitest/browser@https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3)': + '@vitest/browser@https://pkg.pr.new/@vitest/browser@bfea79f(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) - '@vitest/mocker': https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@61b30162ae32e34d4dd6ad219a7cd877aab21cca(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1)) - '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/mocker': https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@bfea79f54edc1d513597ecdc45a6e3f86c7bd351(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1)) + '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.2)(typescript@5.7.2) sirv: 3.0.0 tinyrainbow: 1.2.0 - vitest: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + vitest: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) ws: 8.18.0 optionalDependencies: playwright: 1.49.1 @@ -8493,7 +8493,7 @@ snapshots: - utf-8-validate - vite - '@vitest/coverage-v8@https://pkg.pr.new/@vitest/coverage-v8@61b3016(@vitest/browser@3.0.0-beta.3)(vitest@3.0.0-beta.3)': + '@vitest/coverage-v8@https://pkg.pr.new/@vitest/coverage-v8@bfea79f(@vitest/browser@3.0.0-beta.3)(vitest@3.0.0-beta.3)': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -8507,22 +8507,22 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + vitest: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) optionalDependencies: - '@vitest/browser': https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) + '@vitest/browser': https://pkg.pr.new/@vitest/browser@bfea79f(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) transitivePeerDependencies: - supports-color - '@vitest/expect@https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@61b30162ae32e34d4dd6ad219a7cd877aab21cca': + '@vitest/expect@https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': dependencies: - '@vitest/spy': https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@61b30162ae32e34d4dd6ad219a7cd877aab21cca - '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/spy': https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 + '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@61b30162ae32e34d4dd6ad219a7cd877aab21cca(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))': + '@vitest/mocker@https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@bfea79f54edc1d513597ecdc45a6e3f86c7bd351(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))': dependencies: - '@vitest/spy': https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/spy': https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: @@ -8534,22 +8534,22 @@ snapshots: tinyrainbow: 1.2.0 optional: true - '@vitest/pretty-format@https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@61b30162ae32e34d4dd6ad219a7cd877aab21cca': + '@vitest/pretty-format@https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@61b30162ae32e34d4dd6ad219a7cd877aab21cca': + '@vitest/runner@https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': dependencies: - '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 pathe: 1.1.2 - '@vitest/snapshot@https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@61b30162ae32e34d4dd6ad219a7cd877aab21cca': + '@vitest/snapshot@https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': dependencies: - '@vitest/pretty-format': https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/pretty-format': https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 magic-string: 0.30.17 pathe: 1.1.2 - '@vitest/spy@https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@61b30162ae32e34d4dd6ad219a7cd877aab21cca': + '@vitest/spy@https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': dependencies: tinyspy: 3.0.2 @@ -8562,19 +8562,19 @@ snapshots: sirv: 3.0.0 tinyglobby: 0.2.10 tinyrainbow: 1.2.0 - vitest: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + vitest: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) optional: true - '@vitest/ui@https://pkg.pr.new/@vitest/ui@61b3016(vitest@3.0.0-beta.3)': + '@vitest/ui@https://pkg.pr.new/@vitest/ui@bfea79f(vitest@3.0.0-beta.3)': dependencies: - '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 fflate: 0.8.2 flatted: 3.3.2 pathe: 1.1.2 sirv: 3.0.0 tinyglobby: 0.2.10 tinyrainbow: 1.2.0 - vitest: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + vitest: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) '@vitest/utils@3.0.0-beta.3': dependencies: @@ -8583,9 +8583,9 @@ snapshots: tinyrainbow: 1.2.0 optional: true - '@vitest/utils@https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca': + '@vitest/utils@https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351': dependencies: - '@vitest/pretty-format': https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/pretty-format': https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 loupe: 3.1.2 tinyrainbow: 1.2.0 @@ -9651,7 +9651,7 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - es-toolkit@1.30.1: {} + es-toolkit@1.31.0: {} esbuild@0.24.0: optionalDependencies: @@ -13028,7 +13028,7 @@ snapshots: vary@1.1.2: {} - vite-node@https://pkg.pr.new/vitest-dev/vitest/vite-node@61b30162ae32e34d4dd6ad219a7cd877aab21cca(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1): + vite-node@https://pkg.pr.new/vitest-dev/vitest/vite-node@bfea79f54edc1d513597ecdc45a6e3f86c7bd351(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1): dependencies: cac: 6.7.14 debug: 4.4.0 @@ -13075,20 +13075,20 @@ snapshots: vitest-browser-vue@0.0.1(@vitest/browser@3.0.0-beta.3)(vitest@3.0.0-beta.3)(vue@3.5.13(typescript@5.7.2)): dependencies: - '@vitest/browser': https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) + '@vitest/browser': https://pkg.pr.new/@vitest/browser@bfea79f(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) '@vue/test-utils': 2.4.6 - vitest: https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + vitest: https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) vue: 3.5.13(typescript@5.7.2) - vitest@https://pkg.pr.new/vitest@61b3016(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1): + vitest@https://pkg.pr.new/vitest@bfea79f(@types/node@22.10.2)(@vitest/browser@3.0.0-beta.3)(@vitest/ui@3.0.0-beta.3)(jsdom@25.0.1(canvas@2.11.2(encoding@0.1.13)))(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1): dependencies: - '@vitest/expect': https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@61b30162ae32e34d4dd6ad219a7cd877aab21cca - '@vitest/mocker': https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@61b30162ae32e34d4dd6ad219a7cd877aab21cca(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1)) - '@vitest/pretty-format': https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@61b30162ae32e34d4dd6ad219a7cd877aab21cca - '@vitest/runner': https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@61b30162ae32e34d4dd6ad219a7cd877aab21cca - '@vitest/snapshot': https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@61b30162ae32e34d4dd6ad219a7cd877aab21cca - '@vitest/spy': https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@61b30162ae32e34d4dd6ad219a7cd877aab21cca - '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@61b30162ae32e34d4dd6ad219a7cd877aab21cca + '@vitest/expect': https://pkg.pr.new/vitest-dev/vitest/@vitest/expect@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 + '@vitest/mocker': https://pkg.pr.new/vitest-dev/vitest/@vitest/mocker@bfea79f54edc1d513597ecdc45a6e3f86c7bd351(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1)) + '@vitest/pretty-format': https://pkg.pr.new/vitest-dev/vitest/@vitest/pretty-format@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 + '@vitest/runner': https://pkg.pr.new/vitest-dev/vitest/@vitest/runner@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 + '@vitest/snapshot': https://pkg.pr.new/vitest-dev/vitest/@vitest/snapshot@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 + '@vitest/spy': https://pkg.pr.new/vitest-dev/vitest/@vitest/spy@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 + '@vitest/utils': https://pkg.pr.new/vitest-dev/vitest/@vitest/utils@bfea79f54edc1d513597ecdc45a6e3f86c7bd351 chai: 5.1.2 debug: 4.4.0 expect-type: 1.1.0 @@ -13100,11 +13100,11 @@ snapshots: tinypool: 1.0.2 tinyrainbow: 1.2.0 vite: 6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) - vite-node: https://pkg.pr.new/vitest-dev/vitest/vite-node@61b30162ae32e34d4dd6ad219a7cd877aab21cca(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) + vite-node: https://pkg.pr.new/vitest-dev/vitest/vite-node@bfea79f54edc1d513597ecdc45a6e3f86c7bd351(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.2 - '@vitest/browser': https://pkg.pr.new/@vitest/browser@61b3016(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) + '@vitest/browser': https://pkg.pr.new/@vitest/browser@bfea79f(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.2)(sass-embedded@1.81.0)(sass@1.79.4)(terser@5.34.1)(yaml@2.5.1))(vitest@3.0.0-beta.3) '@vitest/ui': 3.0.0-beta.3(vitest@3.0.0-beta.3) jsdom: 25.0.1(canvas@2.11.2(encoding@0.1.13)) transitivePeerDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 42cf42de..22d57283 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,10 +1,10 @@ packages: - 'packages/*' catalog: - vitest: https://pkg.pr.new/vitest@61b3016 - '@vitest/browser': https://pkg.pr.new/@vitest/browser@61b3016 - '@vitest/ui': https://pkg.pr.new/@vitest/ui@61b3016 - '@vitest/coverage-v8': https://pkg.pr.new/@vitest/coverage-v8@61b3016 + vitest: https://pkg.pr.new/vitest@bfea79f + '@vitest/browser': https://pkg.pr.new/@vitest/browser@bfea79f + '@vitest/ui': https://pkg.pr.new/@vitest/ui@bfea79f + '@vitest/coverage-v8': https://pkg.pr.new/@vitest/coverage-v8@bfea79f firebase: 11.1.0 - es-toolkit: 1.30.1 + es-toolkit: 1.31.0 '@posva/event-emitter': 1.0.2 From 5da090c871286f9d2897690423a7ef56e31d0fe0 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 14:55:16 +0100 Subject: [PATCH 12/30] chore(frontend): remove some redundant comments --- .../Event/reservation/PlannedReservationForm.vue | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue b/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue index 2b6cf129..8ab52565 100644 --- a/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue +++ b/packages/frontend/src/components/Event/reservation/PlannedReservationForm.vue @@ -32,7 +32,6 @@ const socials = ["Whatsapp", "SMS", "Instagram", "Facebook", "Phone"].map(functi const props = defineProps(); const { t } = useI18n(); -// Move initial state logic into a function function generateInitialState(): Omit { return props.mode === "update" && props.reservationData ? { ...props.reservationData } @@ -54,7 +53,6 @@ function generateInitialState(): Omit>(generateInitialState()); const reservationForm = useTemplateRef("reservationForm"); @@ -79,10 +77,7 @@ const reservedByLabel = computed(function () { : t(`EventCreateReservation.reservedByLabel`); }); -// Watcher that resets state.reservedBy when selectionType changes watch(selectionType, function (newVal) { - // When selectionType changes to 'social', reset reservedBy to the first social option - // When changing to 'user', reset reservedBy to the first user option state.value.reservedBy = newVal === "social" ? socials[0] : formattedUsers.value[0]; }); @@ -95,11 +90,8 @@ function capitalizeGuestName(): void { } function reset(): void { - // Get fresh initial state const freshInitialState = generateInitialState(); - // Reset to initial state state.value = { ...freshInitialState }; - // Reset selection type based on fresh initial state selectionType.value = freshInitialState.reservedBy?.id ? "user" : "social"; reservationForm.value?.resetValidation(); } From 0261cf774f706444ba523578e76b72e928bcb4c9 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 15:05:29 +0100 Subject: [PATCH 13/30] chore(frontend): remove more redundant comments --- packages/frontend/src/boot/firebase-connection.ts | 2 -- .../frontend/src/components/Event/EventGuestSearch.vue | 1 - .../frontend/src/components/Event/EventViewControls.vue | 1 - .../Event/reservation/EventCreateReservation.vue | 1 - .../components/Event/reservation/WalkInReservationForm.vue | 1 - .../components/Event/reservation/reservation-form-utils.ts | 1 - .../src/components/admin/event/AdminPropertyEventsList.vue | 1 - .../src/components/admin/event/EventCreateForm.vue | 1 - .../frontend/src/components/admin/user/UserEditForm.vue | 1 - .../src/composables/analytics/bucketize-reservations.ts | 1 - .../src/composables/analytics/useReservationsAnalytics.ts | 3 --- .../src/composables/reservations/useTableOperations.ts | 2 +- .../frontend/src/helpers/inventory/import-inventory.ts | 1 - packages/frontend/src/pages/Admin/PageAdminEvent.vue | 7 ++----- packages/frontend/src/pages/Admin/PageAdminGuests.vue | 4 ---- packages/frontend/src/pages/Admin/PageAdminUsers.vue | 2 -- 16 files changed, 3 insertions(+), 27 deletions(-) diff --git a/packages/frontend/src/boot/firebase-connection.ts b/packages/frontend/src/boot/firebase-connection.ts index 28ba3881..5c463f49 100644 --- a/packages/frontend/src/boot/firebase-connection.ts +++ b/packages/frontend/src/boot/firebase-connection.ts @@ -61,12 +61,10 @@ function handleOnAuthStateChanged( guestsStore.cleanup(); propertiesStore.cleanup(); - // Check if the current route requires authentication const currentRoute = router.currentRoute.value; const requiresAuth = currentRoute.meta.requiresAuth; if (requiresAuth) { - // Redirect to the auth page only if the route requires authentication router.replace({ path: "/auth" }).catch(showErrorMessage); } } diff --git a/packages/frontend/src/components/Event/EventGuestSearch.vue b/packages/frontend/src/components/Event/EventGuestSearch.vue index ef12af59..928300ba 100644 --- a/packages/frontend/src/components/Event/EventGuestSearch.vue +++ b/packages/frontend/src/components/Event/EventGuestSearch.vue @@ -61,7 +61,6 @@ function createTableLabel(reservation: PlannedReservation): string { } function findSearchedTable(inputVal: string | { value: PlannedReservation }): PlannedReservation[] { - // Determine the value to match against const val = typeof inputVal === "string" ? inputVal : inputVal.value.guestName; const normalizedVal = val.toLowerCase().trim(); diff --git a/packages/frontend/src/components/Event/EventViewControls.vue b/packages/frontend/src/components/Event/EventViewControls.vue index 8bc530ca..708d1925 100644 --- a/packages/frontend/src/components/Event/EventViewControls.vue +++ b/packages/frontend/src/components/Event/EventViewControls.vue @@ -65,7 +65,6 @@ function showPrevFloor(): void { } } -// Handle "Show Next Floor" button click function showNextFloor(): void { if (nextFloor.value) { emit("set-active-floor", nextFloor.value); diff --git a/packages/frontend/src/components/Event/reservation/EventCreateReservation.vue b/packages/frontend/src/components/Event/reservation/EventCreateReservation.vue index 001ec4f7..a440954d 100644 --- a/packages/frontend/src/components/Event/reservation/EventCreateReservation.vue +++ b/packages/frontend/src/components/Event/reservation/EventCreateReservation.vue @@ -41,7 +41,6 @@ const currentlyActiveRef = computed(function () { }); const currentReservationType = computed(function () { - // Default to PLANNED type if reservationData is not provided return props.reservationData?.type ?? ReservationType.PLANNED; }); diff --git a/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue b/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue index 3edbd422..c990fd99 100644 --- a/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue +++ b/packages/frontend/src/components/Event/reservation/WalkInReservationForm.vue @@ -41,7 +41,6 @@ function generateInitialState(): State { const now = Date.now(); // Set the initial time to either the current hour or the event start hour const initialTime = Math.max(now, eventStart); - // Format the time as a string "HH:MM" const formattedTime = hourFromTimestamp(initialTime, locale.value, props.timezone); return { type: ReservationType.WALK_IN as const, diff --git a/packages/frontend/src/components/Event/reservation/reservation-form-utils.ts b/packages/frontend/src/components/Event/reservation/reservation-form-utils.ts index 1e887e05..967837df 100644 --- a/packages/frontend/src/components/Event/reservation/reservation-form-utils.ts +++ b/packages/frontend/src/components/Event/reservation/reservation-form-utils.ts @@ -16,7 +16,6 @@ export function isTimeWithinEventDuration( hours: number, minutes: number | null = 0, ): boolean { - // Validate inputs if ( hours < 0 || hours > 23 || diff --git a/packages/frontend/src/components/admin/event/AdminPropertyEventsList.vue b/packages/frontend/src/components/admin/event/AdminPropertyEventsList.vue index 04d06b9e..9aea840e 100644 --- a/packages/frontend/src/components/admin/event/AdminPropertyEventsList.vue +++ b/packages/frontend/src/components/admin/event/AdminPropertyEventsList.vue @@ -31,7 +31,6 @@ const bucketizedEvents = computed(function () { const eventDate = new Date(event.date); const isPastEvent = eventDate < now; - // Decide which bucket to put the event in const targetBucket = isPastEvent ? pastEvents : upcomingEvents; const year = eventDate.getUTCFullYear().toString(); diff --git a/packages/frontend/src/components/admin/event/EventCreateForm.vue b/packages/frontend/src/components/admin/event/EventCreateForm.vue index cc6ecbf3..3116e928 100644 --- a/packages/frontend/src/components/admin/event/EventCreateForm.vue +++ b/packages/frontend/src/components/admin/event/EventCreateForm.vue @@ -56,7 +56,6 @@ const emit = defineEmits<{ const { t, locale } = useI18n(); -// Set to eventStartHours in property timezone const initialDate = createTodayUTCTimestamp(props.eventStartHours, props.propertyTimezone); const eventObj: CreateEventForm = { diff --git a/packages/frontend/src/components/admin/user/UserEditForm.vue b/packages/frontend/src/components/admin/user/UserEditForm.vue index 3048da73..d9db17b6 100644 --- a/packages/frontend/src/components/admin/user/UserEditForm.vue +++ b/packages/frontend/src/components/admin/user/UserEditForm.vue @@ -66,7 +66,6 @@ const emailSuffix = computed(function () { return `@${props.organisation.name}.org`; }); -// Store custom capabilities per role const previousCapabilities: Partial> = { [props.user.role]: { ...form.value.capabilities }, }; diff --git a/packages/frontend/src/composables/analytics/bucketize-reservations.ts b/packages/frontend/src/composables/analytics/bucketize-reservations.ts index a57b0e6b..b96f08c8 100644 --- a/packages/frontend/src/composables/analytics/bucketize-reservations.ts +++ b/packages/frontend/src/composables/analytics/bucketize-reservations.ts @@ -28,7 +28,6 @@ export function bucketizeReservations( }; events.forEach(function (event) { - // Filter and categorize reservations for the current event const eventReservations = fetchedReservations .filter(matchesProperty("eventId", event.id)) // Skip cancelled planned reservations diff --git a/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts b/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts index a608845d..7c8a8908 100644 --- a/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts +++ b/packages/frontend/src/composables/analytics/useReservationsAnalytics.ts @@ -326,14 +326,12 @@ export function useReservationsAnalytics( const monthKey = selectedMonth.value; const cacheKey = monthKey + organisationId; - // Check if data for the month is already in the store const cachedData = analyticsStore.getDataForMonth(cacheKey, property.value.id); if (cachedData) { reservationBucket.value = cachedData; return; } - // If not in store, fetch data reservationBucket.value = undefined; Loading.show(); @@ -346,7 +344,6 @@ export function useReservationsAnalytics( ); analyticsStore.cacheData(cacheKey, reservationBucket.value, property.value.id); - // Set the active tab to the first day with reservations const firstDayWithReservations = Object.keys(plannedReservationsByDay.value)[0]; if (firstDayWithReservations) { selectedDay.value = firstDayWithReservations; diff --git a/packages/frontend/src/composables/reservations/useTableOperations.ts b/packages/frontend/src/composables/reservations/useTableOperations.ts index 6d5f94aa..d08a869d 100644 --- a/packages/frontend/src/composables/reservations/useTableOperations.ts +++ b/packages/frontend/src/composables/reservations/useTableOperations.ts @@ -51,7 +51,7 @@ export function useTableOperations() { const ongoingTableOperation = computed(() => currentTableOperation.value); - const tableOperationWatcher = watch(currentTableOperation, (newOperation) => { + const tableOperationWatcher = watch(currentTableOperation, function (newOperation) { if (newOperation) { showOperationNotification(newOperation); } else if (operationNotification) { diff --git a/packages/frontend/src/helpers/inventory/import-inventory.ts b/packages/frontend/src/helpers/inventory/import-inventory.ts index bfe313b3..796796d0 100644 --- a/packages/frontend/src/helpers/inventory/import-inventory.ts +++ b/packages/frontend/src/helpers/inventory/import-inventory.ts @@ -52,7 +52,6 @@ export async function importInventory({ organisationId, propertyId, }: ImportInventoryParams): Promise { - // Parse CSV content const parseResults = await new Promise>( (resolve, reject) => { parse(fileContent, { diff --git a/packages/frontend/src/pages/Admin/PageAdminEvent.vue b/packages/frontend/src/pages/Admin/PageAdminEvent.vue index b3216119..9b08b116 100644 --- a/packages/frontend/src/pages/Admin/PageAdminEvent.vue +++ b/packages/frontend/src/pages/Admin/PageAdminEvent.vue @@ -14,6 +14,7 @@ import { isActiveReservation } from "@firetable/types"; import { computed, onMounted, onUnmounted, watch } from "vue"; import { useRouter } from "vue-router"; import { formatEventDate } from "src/helpers/date-utils"; +import { matchesProperty } from "es-toolkit/compat"; import FTTitle from "src/components/FTTitle.vue"; import AdminEventRTInfo from "src/components/admin/event/AdminEventRTInfo.vue"; @@ -207,11 +208,7 @@ async function addFloor(floor: FloorDoc): Promise { async function removeFloor(index: number): Promise { const floor = eventFloors.value[index]; // Check if floor has reservations - if ( - allReservations.value.some(function ({ floorId }) { - return floorId === floor.id; - }) - ) { + if (allReservations.value.some(matchesProperty("floorId", floor.id))) { showErrorMessage("Cannot remove floor with active reservations"); return; } diff --git a/packages/frontend/src/pages/Admin/PageAdminGuests.vue b/packages/frontend/src/pages/Admin/PageAdminGuests.vue index 4757f204..5fac52c0 100644 --- a/packages/frontend/src/pages/Admin/PageAdminGuests.vue +++ b/packages/frontend/src/pages/Admin/PageAdminGuests.vue @@ -125,7 +125,6 @@ const sortedGuests = computed(function () { break; case "bookings": - // Existing bookings logic if (b.totalReservations === a.totalReservations) { comparison = Number.parseFloat(b.overallPercentage) - @@ -166,7 +165,6 @@ const availableTags = computed(function () { const filteredGuests = computed(function () { let filtered = sortedGuests.value; - // Apply search filter if (searchQuery.value?.trim()) { const query = searchQuery.value.trim().toLowerCase(); filtered = filtered.filter( @@ -176,7 +174,6 @@ const filteredGuests = computed(function () { ); } - // Apply tag filter if (selectedTags.value.length > 0) { filtered = filtered.filter(function (guest) { if (!guest.tags) { @@ -281,7 +278,6 @@ function handleScroll(): void { } } -// Guests selection const selectedGuests = ref([]); const selectionMode = ref(false); const allSelected = computed(function () { diff --git a/packages/frontend/src/pages/Admin/PageAdminUsers.vue b/packages/frontend/src/pages/Admin/PageAdminUsers.vue index 62f24c58..29ad4a42 100644 --- a/packages/frontend/src/pages/Admin/PageAdminUsers.vue +++ b/packages/frontend/src/pages/Admin/PageAdminUsers.vue @@ -63,7 +63,6 @@ const unassignedUsers = computed(function () { const bucketizedUsers = computed(function () { const buckets: BucketizedUsers = {}; - // Helper function to add users to buckets function addUserToBucket(bucketizedUser: BucketizedUser, property: PropertyDoc): void { bucketizedUser.memberOf?.push(property.name); if (!buckets[property.id]) { @@ -86,7 +85,6 @@ const bucketizedUsers = computed(function () { return; } - // Process user-related properties bucketizedUser.relatedProperties.forEach(function (propertyId) { const property = findPropertyById(propertyId); if (property) { From 3e7cce5436570a384a2130a87a9b31494cda62ed Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 16:33:23 +0100 Subject: [PATCH 14/30] chore(frontend): unflake PageIssueReport.spec.ts --- .../src/pages/PageIssueReport.spec.ts | 54 ++++--------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/packages/frontend/src/pages/PageIssueReport.spec.ts b/packages/frontend/src/pages/PageIssueReport.spec.ts index 222c57d6..44b1b9cf 100644 --- a/packages/frontend/src/pages/PageIssueReport.spec.ts +++ b/packages/frontend/src/pages/PageIssueReport.spec.ts @@ -6,29 +6,17 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; import { userEvent } from "@vitest/browser/context"; import { ref } from "vue"; import { IssueCategory, IssueStatus, Role } from "@firetable/types"; -import FTDialog from "src/components/FTDialog.vue"; -import IssueCreateForm from "src/components/issue/IssueCreateForm.vue"; const { - createDialogSpy, useFirestoreCollectionMock, createIssueReportMock, updateIssueReportMock, deleteIssueReportMock, - notifyMock, } = vi.hoisted(() => ({ - createDialogSpy: vi.fn(), useFirestoreCollectionMock: vi.fn(), createIssueReportMock: vi.fn(), updateIssueReportMock: vi.fn(), deleteIssueReportMock: vi.fn(), - notifyMock: vi.fn(), -})); - -vi.mock("src/composables/useDialog", () => ({ - useDialog: () => ({ - createDialog: createDialogSpy, - }), })); vi.mock("src/composables/useFirestore", () => ({ @@ -50,17 +38,6 @@ vi.mock("../backend-proxy", () => ({ getIssueReportsPath: vi.fn(), })); -vi.mock("quasar", async (importOriginal) => ({ - ...(await importOriginal()), - useQuasar: () => ({ - notify: notifyMock, - }), - Loading: { - show: vi.fn(), - hide: vi.fn(), - }, -})); - describe("PageIssueReport.vue", () => { const mockIssues: IssueReportDoc[] = [ { @@ -150,15 +127,11 @@ describe("PageIssueReport.vue", () => { const addButton = screen.getByLabelText("Report new issue"); await userEvent.click(addButton); - expect(createDialogSpy).toHaveBeenCalledWith( - expect.objectContaining({ - component: FTDialog, - componentProps: expect.objectContaining({ - component: IssueCreateForm, - title: t("PageIssueReport.createNewIssue"), - }), - }), - ); + await expect + .element(screen.getByText(t("PageIssueReport.createNewIssue"))) + .toBeVisible(); + + await userEvent.click(screen.getByLabelText("Close dialog")); }); }); @@ -170,18 +143,9 @@ describe("PageIssueReport.vue", () => { await userEvent.click(actionsButton); await userEvent.click(screen.getByText(t("Global.edit"))); - expect(createDialogSpy).toHaveBeenCalledWith( - expect.objectContaining({ - component: FTDialog, - componentProps: expect.objectContaining({ - component: IssueCreateForm, - title: t("PageIssueReport.editIssue"), - componentPropsObject: { - issueToEdit: mockIssues[0], - }, - }), - }), - ); + await expect.element(screen.getByText(t("PageIssueReport.editIssue"))).toBeVisible(); + + await userEvent.click(screen.getByLabelText("Close dialog")); }); it("disables edit for resolved issues", async () => { @@ -216,7 +180,7 @@ describe("PageIssueReport.vue", () => { const actionsButton = screen.getByLabelText("Actions for issue My Test Issue"); await userEvent.click(actionsButton); - await userEvent.click(screen.getByText(t("Global.delete"))); + await userEvent.click(screen.getByText(t("Global.delete"), { exact: true })); await userEvent.click(screen.getByText("cancel")); expect(deleteIssueReportMock).not.toHaveBeenCalled(); From 931a101ea7f2684e6e0d899cc74be2cf4d0d6497 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 16:53:48 +0100 Subject: [PATCH 15/30] feat(floor-creator, frontend): show element angle --- packages/floor-creator/src/elements/Bar.ts | 6 ++ .../floor-creator/src/elements/Cloakroom.ts | 6 ++ .../floor-creator/src/elements/DJBooth.ts | 6 ++ packages/floor-creator/src/elements/Door.ts | 6 ++ .../src/elements/EditableShape.ts | 6 ++ packages/floor-creator/src/elements/Sofa.ts | 6 ++ packages/floor-creator/src/elements/Stage.ts | 6 ++ packages/floor-creator/src/elements/Table.ts | 6 ++ packages/floor-creator/src/elements/Wall.ts | 6 ++ packages/floor-creator/src/types.ts | 1 + .../Floor/FloorEditorTopControls.vue | 70 +++++++++---------- 11 files changed, 90 insertions(+), 35 deletions(-) diff --git a/packages/floor-creator/src/elements/Bar.ts b/packages/floor-creator/src/elements/Bar.ts index eefff30a..b717b7c4 100644 --- a/packages/floor-creator/src/elements/Bar.ts +++ b/packages/floor-creator/src/elements/Bar.ts @@ -405,6 +405,12 @@ export class Bar extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + getBaseFill(): string { return this.barBody.get("fill") as string; } diff --git a/packages/floor-creator/src/elements/Cloakroom.ts b/packages/floor-creator/src/elements/Cloakroom.ts index 8cb30e98..c7e018d0 100644 --- a/packages/floor-creator/src/elements/Cloakroom.ts +++ b/packages/floor-creator/src/elements/Cloakroom.ts @@ -113,6 +113,12 @@ export class Cloakroom extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + getBaseFill(): string { return this.counterBody.get("fill") as string; } diff --git a/packages/floor-creator/src/elements/DJBooth.ts b/packages/floor-creator/src/elements/DJBooth.ts index c607631d..9cacf1d6 100644 --- a/packages/floor-creator/src/elements/DJBooth.ts +++ b/packages/floor-creator/src/elements/DJBooth.ts @@ -97,6 +97,12 @@ export class DJBooth extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + getBaseFill(): string { return ""; } diff --git a/packages/floor-creator/src/elements/Door.ts b/packages/floor-creator/src/elements/Door.ts index 98e9f161..ec48c326 100644 --- a/packages/floor-creator/src/elements/Door.ts +++ b/packages/floor-creator/src/elements/Door.ts @@ -48,6 +48,12 @@ export class Door extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + flip(): void { // Store the current bounding rectangle before flipping const boundingRectBefore = this.getBoundingRect(); diff --git a/packages/floor-creator/src/elements/EditableShape.ts b/packages/floor-creator/src/elements/EditableShape.ts index 1f3fc478..18b54cc6 100644 --- a/packages/floor-creator/src/elements/EditableShape.ts +++ b/packages/floor-creator/src/elements/EditableShape.ts @@ -63,6 +63,12 @@ export class EditableShape extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + setBaseFill(val: string): void { this.shape.set("fill", val); this.canvas?.requestRenderAll(); diff --git a/packages/floor-creator/src/elements/Sofa.ts b/packages/floor-creator/src/elements/Sofa.ts index 0b45b0c6..db493b73 100644 --- a/packages/floor-creator/src/elements/Sofa.ts +++ b/packages/floor-creator/src/elements/Sofa.ts @@ -59,6 +59,12 @@ export class Sofa extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + getBaseFill(): string { return this.sofaBase.get("fill") as string; } diff --git a/packages/floor-creator/src/elements/Stage.ts b/packages/floor-creator/src/elements/Stage.ts index f2a4228f..03ddb74a 100644 --- a/packages/floor-creator/src/elements/Stage.ts +++ b/packages/floor-creator/src/elements/Stage.ts @@ -89,6 +89,12 @@ export class Stage extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + getBaseFill(): string { return ""; } diff --git a/packages/floor-creator/src/elements/Table.ts b/packages/floor-creator/src/elements/Table.ts index 9643845d..c991a1f2 100644 --- a/packages/floor-creator/src/elements/Table.ts +++ b/packages/floor-creator/src/elements/Table.ts @@ -98,6 +98,12 @@ export abstract class Table extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + private handleScaling(): void { this.enforceMinimumDimensions(); this.adjustTextScaling(); diff --git a/packages/floor-creator/src/elements/Wall.ts b/packages/floor-creator/src/elements/Wall.ts index cffa861e..3f3697c7 100644 --- a/packages/floor-creator/src/elements/Wall.ts +++ b/packages/floor-creator/src/elements/Wall.ts @@ -49,6 +49,12 @@ export class Wall extends Group implements FloorEditorElement { this.canvas?.requestRenderAll(); } + setAngle(angle: number): void { + this.angle = angle; + this.setCoords(); + this.canvas?.requestRenderAll(); + } + changeToOutlinedMode(): void { this.wallRect.set({ // Makes the background transparent diff --git a/packages/floor-creator/src/types.ts b/packages/floor-creator/src/types.ts index 4b9fb6e7..07ac5fdc 100644 --- a/packages/floor-creator/src/types.ts +++ b/packages/floor-creator/src/types.ts @@ -36,6 +36,7 @@ export interface FloorEditorElement extends FabricObject { changeToFilledMode?(): void; setDimensions(width: number, height: number): void; + setAngle(angle: number): void; nextDesign?(): void; } diff --git a/packages/frontend/src/components/Floor/FloorEditorTopControls.vue b/packages/frontend/src/components/Floor/FloorEditorTopControls.vue index e5069208..67c10ff0 100644 --- a/packages/frontend/src/components/Floor/FloorEditorTopControls.vue +++ b/packages/frontend/src/components/Floor/FloorEditorTopControls.vue @@ -32,8 +32,11 @@ const getElementHeight = computed(function () { ? Math.round(selectedFloorElement.height * selectedFloorElement.scaleY) : 0; }); +const getElementAngle = computed(() => (selectedFloorElement ? selectedFloorElement.angle : 0)); + const localWidth = ref(getElementWidth.value); const localHeight = ref(getElementHeight.value); +const localAngle = ref(getElementAngle.value); const elementColor = ref(selectedFloorElement?.getBaseFill?.() ?? ""); watch( @@ -41,13 +44,20 @@ watch( function (newEl) { localWidth.value = newEl ? Math.round(newEl.width * newEl.scaleX) : 0; localHeight.value = newEl ? Math.round(newEl.height * newEl.scaleY) : 0; + localAngle.value = newEl ? newEl.angle : 0; if (newEl?.getBaseFill) { elementColor.value = newEl.getBaseFill(); } }, + { immediate: true }, ); +watch(localAngle, (newAngle) => { + if (!selectedFloorElement) return; + selectedFloorElement.setAngle(newAngle); +}); + watch(localWidth, function (newWidth) { if (!selectedFloorElement) { return; @@ -113,32 +123,38 @@ onMounted(function () { From b44e5d7f8a9e1becafc3d9ee70697f218c18c6e6 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 17:25:58 +0100 Subject: [PATCH 16/30] chore(frontend): improve floor editor controls ui --- .../components/Floor/FloorEditorControls.vue | 65 ++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/packages/frontend/src/components/Floor/FloorEditorControls.vue b/packages/frontend/src/components/Floor/FloorEditorControls.vue index 99642b40..77457b21 100644 --- a/packages/frontend/src/components/Floor/FloorEditorControls.vue +++ b/packages/frontend/src/components/Floor/FloorEditorControls.vue @@ -124,7 +124,6 @@ function onFloorSave(): void { - - - - - +
+ + + + + + +
From a3d351f9f115c7ce6bd19aa12ba946dcb1ba8870 Mon Sep 17 00:00:00 2001 From: Smrtnyk Date: Fri, 27 Dec 2024 17:41:03 +0100 Subject: [PATCH 17/30] chore(frontend): improve floor editor ui overall --- .../src/components/Floor/FloorEditorTopControls.vue | 4 ++-- .../components/admin/event/AdminEventFloorViewer.vue | 2 +- .../frontend/src/pages/Admin/PageAdminFloorEdit.vue | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/components/Floor/FloorEditorTopControls.vue b/packages/frontend/src/components/Floor/FloorEditorTopControls.vue index 67c10ff0..966750a3 100644 --- a/packages/frontend/src/components/Floor/FloorEditorTopControls.vue +++ b/packages/frontend/src/components/Floor/FloorEditorTopControls.vue @@ -123,7 +123,7 @@ onMounted(function () { diff --git a/packages/frontend/src/components/admin/event/AdminEventFloorViewer.vue b/packages/frontend/src/components/admin/event/AdminEventFloorViewer.vue index ec94d9bd..ce294413 100644 --- a/packages/frontend/src/components/admin/event/AdminEventFloorViewer.vue +++ b/packages/frontend/src/components/admin/event/AdminEventFloorViewer.vue @@ -21,7 +21,7 @@ :existing-labels="new Set(extractAllTablesLabels(floorInstance))" /> -
+
diff --git a/packages/frontend/src/pages/Admin/PageAdminFloorEdit.vue b/packages/frontend/src/pages/Admin/PageAdminFloorEdit.vue index d78a97f0..8058da43 100644 --- a/packages/frontend/src/pages/Admin/PageAdminFloorEdit.vue +++ b/packages/frontend/src/pages/Admin/PageAdminFloorEdit.vue @@ -100,7 +100,7 @@ async function onFloorSave(): Promise {