diff --git a/src/Evently.Server/Common/Domains/Interfaces/IGatheringService.cs b/src/Evently.Server/Common/Domains/Interfaces/IGatheringService.cs index 894c480..e1ad4f8 100644 --- a/src/Evently.Server/Common/Domains/Interfaces/IGatheringService.cs +++ b/src/Evently.Server/Common/Domains/Interfaces/IGatheringService.cs @@ -15,6 +15,7 @@ Task> GetGatherings( DateTimeOffset? endDateBefore, DateTimeOffset? endDateAfter, bool? isCancelled, + HashSet? categoryIds, int? offset, int? limit); diff --git a/src/Evently.Server/Features/Bookings/Controllers/BookingsController.cs b/src/Evently.Server/Features/Bookings/Controllers/BookingsController.cs index fd74014..321861f 100644 --- a/src/Evently.Server/Features/Bookings/Controllers/BookingsController.cs +++ b/src/Evently.Server/Features/Bookings/Controllers/BookingsController.cs @@ -63,12 +63,11 @@ public async Task> CreateBooking([FromBody] BookingReqDto await emailQueue.WriteAsync(booking.BookingId); return Ok(booking); } - - [HttpPut("{bookingId}", Name = "UpdateBooking")] - public async Task UpdateBooking(string bookingId, - [FromBody] BookingReqDto bookingReqDto) { + + [HttpPatch("{bookingId}/cancel", Name = "CancelBooking")] + public async Task CancelBooking(string bookingId) { Booking? booking = await bookingService.GetBooking(bookingId); - if (booking is null) { + if (booking?.Gathering is null) { return NotFound(); } @@ -78,7 +77,8 @@ public async Task UpdateBooking(string bookingId, return Forbid(); } - booking = await bookingService.UpdateBooking(bookingId, bookingReqDto); + booking.CancellationDateTime = DateTimeOffset.UtcNow; + booking = await bookingService.UpdateBooking(bookingId, bookingReqDto: booking.ToBookingDto()); return Ok(booking); } diff --git a/src/Evently.Server/Features/Gatherings/Controllers/GatheringsController.cs b/src/Evently.Server/Features/Gatherings/Controllers/GatheringsController.cs index 93fa591..011625c 100644 --- a/src/Evently.Server/Features/Gatherings/Controllers/GatheringsController.cs +++ b/src/Evently.Server/Features/Gatherings/Controllers/GatheringsController.cs @@ -14,6 +14,7 @@ namespace Evently.Server.Features.Gatherings.Controllers; [ApiController] [Route("api/v1/[controller]")] public sealed class GatheringsController( + ILogger logger, IGatheringService gatheringService, IFileStorageService fileStorageService) : ControllerBase { [HttpGet("{gatheringId:long}", Name = "GetGathering")] @@ -31,7 +32,9 @@ public async Task>> GetGatherings(string? attendeeI string? name, DateTimeOffset? startDateBefore, DateTimeOffset? startDateAfter, DateTimeOffset? endDateBefore, DateTimeOffset? endDateAfter, bool? isCancelled, + long[]? categoryIds, int? offset, int? limit) { + logger.LogInformation("categoryIds: {}", string.Join(",", categoryIds ?? [])); PageResult result = await gatheringService.GetGatherings(attendeeId, organiserId, name, @@ -40,6 +43,7 @@ public async Task>> GetGatherings(string? attendeeI endDateBefore, endDateAfter, isCancelled, + categoryIds?.ToHashSet(), offset, limit); List exhibitions = result.Items; @@ -105,7 +109,6 @@ public async Task> DeleteGathering(long gatheringId) { } private async Task UploadCoverImage(long gatheringId, IFormFile coverImg) { - string t = coverImg.FileName; string fileName = $"gatherings/{gatheringId}/cover-image{Path.GetExtension(coverImg.FileName)}"; BinaryData binaryData = await coverImg.ToBinaryData(); return await fileStorageService.UploadFile(fileName, diff --git a/src/Evently.Server/Features/Gatherings/Services/GatheringService.cs b/src/Evently.Server/Features/Gatherings/Services/GatheringService.cs index 377528c..6269ccb 100644 --- a/src/Evently.Server/Features/Gatherings/Services/GatheringService.cs +++ b/src/Evently.Server/Features/Gatherings/Services/GatheringService.cs @@ -27,6 +27,7 @@ public async Task> GetGatherings( DateTimeOffset? endDateBefore, DateTimeOffset? endDateAfter, bool? isCancelled, + HashSet? categoryIds, int? offset, int? limit) { IQueryable query = db.Gatherings @@ -37,6 +38,7 @@ public async Task> GetGatherings( .Where((gathering) => endDateAfter == null || gathering.End >= endDateAfter) .Where((gathering) => organiserId == null || gathering.OrganiserId == organiserId) .Where(gathering => isCancelled == null || gathering.CancellationDateTime.HasValue == isCancelled) + .Where((gathering) => categoryIds == null || gathering.GatheringCategoryDetails.Any(detail => categoryIds.Contains(detail.CategoryId))) .Where((gathering) => attendeeId == null || gathering.Bookings.Any((be) => be.AttendeeId == attendeeId)) .Include(gathering => gathering.Bookings.Where((be) => be.AttendeeId == attendeeId)) diff --git a/src/evently.client/eslint.config.js b/src/evently.client/eslint.config.js index dbacb84..fd5ee9d 100644 --- a/src/evently.client/eslint.config.js +++ b/src/evently.client/eslint.config.js @@ -18,6 +18,9 @@ export default tseslint.config([ languageOptions: { ecmaVersion: 2020, globals: globals.browser + }, + rules: { + "@typescript-eslint/no-explicit-any": "off" } } ]); diff --git a/src/evently.client/package.json b/src/evently.client/package.json index fee64ab..ce08d47 100644 --- a/src/evently.client/package.json +++ b/src/evently.client/package.json @@ -12,6 +12,7 @@ "preview": "vite preview" }, "dependencies": { + "@oddbird/css-anchor-positioning": "^0.6.1", "@tailwindcss/vite": "^4.1.11", "@tanstack/react-form": "^1.19.2", "@tanstack/react-query": "^5.84.2", diff --git a/src/evently.client/pnpm-lock.yaml b/src/evently.client/pnpm-lock.yaml index 16158e1..b12d1ff 100644 --- a/src/evently.client/pnpm-lock.yaml +++ b/src/evently.client/pnpm-lock.yaml @@ -7,6 +7,9 @@ settings: importers: .: dependencies: + "@oddbird/css-anchor-positioning": + specifier: ^0.6.1 + version: 0.6.1 "@tailwindcss/vite": specifier: ^4.1.11 version: 4.1.11(vite@7.1.2(@types/node@22.17.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)) @@ -747,6 +750,24 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + "@floating-ui/core@1.7.3": + resolution: + { + integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w== + } + + "@floating-ui/dom@1.7.4": + resolution: + { + integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA== + } + + "@floating-ui/utils@0.2.10": + resolution: + { + integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ== + } + "@humanfs/core@0.19.1": resolution: { @@ -849,6 +870,12 @@ packages: } engines: { node: ">= 8" } + "@oddbird/css-anchor-positioning@0.6.1": + resolution: + { + integrity: sha512-/M1guQMJROMAFjS0uTmcg93S2X5r0Gwe6m5DqmRrGxNEUe1llmY6/Qw8UuhvexP6yeD+GDCP9CJtXBJS1vS9UQ== + } + "@rolldown/pluginutils@1.0.0-beta.27": resolution: { @@ -1369,6 +1396,12 @@ packages: integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg== } + "@types/css-tree@2.3.10": + resolution: + { + integrity: sha512-WcaBazJ84RxABvRttQjjFWgTcHvZR9jGr0Y3hccPkHjFyk/a3N8EuxjKr+QfrwjoM5b1yI1Uj1i7EzOAAwBwag== + } + "@types/deep-eql@4.0.2": resolution: { @@ -1879,6 +1912,13 @@ packages: } engines: { node: ">= 8" } + css-tree@3.1.0: + resolution: + { + integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w== + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + css.escape@1.5.1: resolution: { @@ -2852,6 +2892,12 @@ packages: } engines: { node: ">= 0.4" } + mdn-data@2.12.2: + resolution: + { + integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA== + } + merge2@1.4.1: resolution: { @@ -2936,6 +2982,14 @@ packages: engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true + nanoid@5.1.5: + resolution: + { + integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw== + } + engines: { node: ^18 || >=20 } + hasBin: true + natural-compare@1.4.0: resolution: { @@ -4288,6 +4342,17 @@ snapshots: "@eslint/core": 0.15.2 levn: 0.4.1 + "@floating-ui/core@1.7.3": + dependencies: + "@floating-ui/utils": 0.2.10 + + "@floating-ui/dom@1.7.4": + dependencies: + "@floating-ui/core": 1.7.3 + "@floating-ui/utils": 0.2.10 + + "@floating-ui/utils@0.2.10": {} + "@humanfs/core@0.19.1": {} "@humanfs/node@0.16.6": @@ -4338,6 +4403,13 @@ snapshots: "@nodelib/fs.scandir": 2.1.5 fastq: 1.19.1 + "@oddbird/css-anchor-positioning@0.6.1": + dependencies: + "@floating-ui/dom": 1.7.4 + "@types/css-tree": 2.3.10 + css-tree: 3.1.0 + nanoid: 5.1.5 + "@rolldown/pluginutils@1.0.0-beta.27": {} "@rollup/rollup-android-arm-eabi@4.46.2": @@ -4655,6 +4727,8 @@ snapshots: dependencies: "@types/deep-eql": 4.0.2 + "@types/css-tree@2.3.10": {} + "@types/deep-eql@4.0.2": {} "@types/estree@1.0.8": {} @@ -5002,6 +5076,11 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + css.escape@1.5.1: {} cssstyle@4.6.0: @@ -5536,6 +5615,8 @@ snapshots: math-intrinsics@1.1.0: {} + mdn-data@2.12.2: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -5571,6 +5652,8 @@ snapshots: nanoid@3.3.11: {} + nanoid@5.1.5: {} + natural-compare@1.4.0: {} node-releases@2.0.19: {} diff --git a/src/evently.client/src/lib/components/-card.test.tsx b/src/evently.client/src/lib/components/-card.test.tsx index c85118f..693ccd4 100644 --- a/src/evently.client/src/lib/components/-card.test.tsx +++ b/src/evently.client/src/lib/components/-card.test.tsx @@ -2,7 +2,10 @@ import { Card } from "~/lib/components/card.tsx"; import { Gathering } from "~/lib/domains/entities"; import { getMockGathering } from "~/lib/services/gathering-service.mock"; -import { TestWrapper, WrapperDataTestId } from "~/lib/components/test-wrapper.tsx"; +import { + TestWrappers, + WrapperDataTestId +} from "~/lib/components/test-wrappers.tsx"; describe("Card Component", () => { let mockGathering: Gathering; @@ -12,9 +15,9 @@ describe("Card Component", () => { it("renders gathering name", async () => { render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); expect(screen.getByText("Tech Conference 2024")).toBeInTheDocument(); @@ -22,9 +25,9 @@ describe("Card Component", () => { it("renders gathering description", async () => { render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); @@ -35,9 +38,9 @@ describe("Card Component", () => { it("renders gathering location", async () => { render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); @@ -46,9 +49,9 @@ describe("Card Component", () => { it("renders cover image when coverSrc is provided", async () => { render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); @@ -64,9 +67,9 @@ describe("Card Component", () => { }; render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); expect(screen.getByText(/cancelled/i)).toBeInTheDocument(); @@ -76,9 +79,9 @@ describe("Card Component", () => { const gatheringWithMultipleCategories: Gathering = await getMockGathering(1); render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); diff --git a/src/evently.client/src/lib/components/index.ts b/src/evently.client/src/lib/components/index.ts index c557861..3f60a1f 100644 --- a/src/evently.client/src/lib/components/index.ts +++ b/src/evently.client/src/lib/components/index.ts @@ -3,5 +3,8 @@ export { Card } from "./card"; export { Tabs } from "./tabs"; export * from "./tab-state.ts"; export { FieldErrMsg } from "./field-err-msg.tsx"; -export { TestWrapper, WrapperDataTestId } from "./test-wrapper.tsx"; -export * from "./render-with-test-providers.tsx"; +export { + TestWrappers, + WrapperDataTestId, + TestRouteWrapper +} from "./test-wrappers.tsx"; diff --git a/src/evently.client/src/lib/components/render-with-test-providers.tsx b/src/evently.client/src/lib/components/render-with-test-providers.tsx deleted file mode 100644 index 405f14b..0000000 --- a/src/evently.client/src/lib/components/render-with-test-providers.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from "react"; -import { - createMemoryHistory, - createRootRoute, - createRoute, - createRouter, - Outlet, - RouterProvider -} from "@tanstack/react-router"; -import { render, screen } from "@testing-library/react"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; - -type RenderOptions = { - pathPattern: string; - initialEntry?: string; -}; - -/** - * Taken from https://dev.to/saltorgil/testing-tanstack-router-4io3 - * Renders a component under: - * - a minimal TanStack Router instance (memory history), - * - optionally wrapped in a QueryClientProvider. - * - * If `initialEntry` is omitted, it defaults to `pathPattern`. - * - * @param Component The React component to mount. - * @param opts Render options. - * @returns { router, renderResult } - */ - -export async function renderWithTestProviders( - Component: React.ComponentType, - { pathPattern, initialEntry = pathPattern }: RenderOptions -) { - // Root route with minimal Outlet for rendering child routes - const rootRoute = createRootRoute({ - component: () => ( - <> -
- - - ) - }); - - // Index route so '/' always matches - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: "/", - component: () =>
Index
- }); - - // Test route mounting your Component at the dynamic path - const testRoute = createRoute({ - getParentRoute: () => rootRoute, - path: pathPattern, - component: () => - }); - - // Create the router instance with memory history - const router = createRouter({ - routeTree: rootRoute.addChildren([indexRoute, testRoute]), - history: createMemoryHistory({ initialEntries: [initialEntry] }), - defaultPendingMinMs: 0 - }); - - const queryClient = new QueryClient(); - // Build the render tree and add QueryClientProvider if provided - let tree = ; - tree = {tree}; - - // Render and wait for the route to resolve and the component to mount - const renderResult = render(tree); - await screen.findByTestId("root-layout"); - - return { router, renderResult }; -} diff --git a/src/evently.client/src/lib/components/test-wrapper.tsx b/src/evently.client/src/lib/components/test-wrappers.tsx similarity index 62% rename from src/evently.client/src/lib/components/test-wrapper.tsx rename to src/evently.client/src/lib/components/test-wrappers.tsx index f07f351..e4703f9 100644 --- a/src/evently.client/src/lib/components/test-wrapper.tsx +++ b/src/evently.client/src/lib/components/test-wrappers.tsx @@ -1,16 +1,18 @@ import type { JSX, ReactNode } from "react"; import { Account } from "~/lib/domains/entities"; import { + createMemoryHistory, createRootRouteWithContext, createRoute, createRouter, Outlet, - RouterProvider + RouterProvider, + type AnyRoute } from "@tanstack/react-router"; import type { RouteContext } from "~/lib/domains/interfaces"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -interface Props { +interface TestComponentProps { children: ReactNode; } @@ -23,7 +25,7 @@ export const WrapperDataTestId = "root-layout"; * @param children The React Component to be tested * @constructor */ -export function TestWrapper({ children }: Props): JSX.Element { +export function TestWrappers({ children }: TestComponentProps): JSX.Element { const rootRoute = createRootRouteWithContext()({ beforeLoad: async () => { const account: Account | null = new Account(); @@ -45,6 +47,7 @@ export function TestWrapper({ children }: Props): JSX.Element { const router = createRouter({ routeTree: rootRoute.addChildren([indexRoute]), defaultPendingMinMs: 0, + history: createMemoryHistory({ initialEntries: ["/"] }), context: { account: undefined! } @@ -57,3 +60,30 @@ export function TestWrapper({ children }: Props): JSX.Element { ); } + +interface TestRouteProps { + route: AnyRoute; +} + +/** + * A HOC to wire up a TanStack route variable so that it can be tested + * @param route A TanStack route variable of type `Route` + * @constructor + */ +export function TestRouteWrapper({ route }: TestRouteProps): JSX.Element { + const router = createRouter({ + routeTree: route, + defaultPendingMinMs: 0, + history: createMemoryHistory({ initialEntries: ["/"] }), + context: { + account: undefined! + } + }); + + const queryClient = new QueryClient(); + return ( + + + + ); +} diff --git a/src/evently.client/src/lib/services/booking-service.ts b/src/evently.client/src/lib/services/booking-service.ts index a77ea3c..ff0cec2 100644 --- a/src/evently.client/src/lib/services/booking-service.ts +++ b/src/evently.client/src/lib/services/booking-service.ts @@ -58,15 +58,12 @@ export async function getBooking(bookingId: string): Promise { return booking; } -export async function updateBooking( - bookingId: string, - bookingDto: BookingReqDto -): Promise { - const response = await axios.put(`/api/v1/Bookings/${bookingId}`, bookingDto); +export async function checkInBooking(bookingId: string): Promise { + const response = await axios.patch(`/api/v1/Bookings/${bookingId}/checkIn`); return response.data; } -export async function checkInBooking(bookingId: string): Promise { - const response = await axios.patch(`/api/v1/Bookings/${bookingId}/checkIn`); +export async function cancelBooking(bookingId: string): Promise { + const response = await axios.patch(`/api/v1/Bookings/${bookingId}/cancel`); return response.data; } diff --git a/src/evently.client/src/lib/services/gathering-service.ts b/src/evently.client/src/lib/services/gathering-service.ts index 8e9f1fe..45a990d 100644 --- a/src/evently.client/src/lib/services/gathering-service.ts +++ b/src/evently.client/src/lib/services/gathering-service.ts @@ -2,6 +2,7 @@ import axios from "axios"; import { GatheringCategoryDetailReqDto, GatheringReqDto } from "~/lib/domains/models"; import type { PageResult } from "~/lib/domains/interfaces"; +import cloneDeep from "lodash.clonedeep"; export interface GetGatheringsParams { attendeeId?: string; @@ -12,12 +13,20 @@ export interface GetGatheringsParams { endDateBefore?: Date; endDateAfter?: Date; isCancelled?: boolean; + categoryIds?: number[]; offset?: number; limit?: number; } export async function getGatherings(params: GetGatheringsParams): Promise> { - const response = await axios.get("/api/v1/Gatherings", { params }); + const categoryIds: number[] = params.categoryIds ?? []; + const queryParams: Record = cloneDeep(params); + delete queryParams["categoryIds"]; + + for (let i = 0; i < categoryIds.length; i++) { + queryParams[`categoryIds[${i}]`] = categoryIds[i]; + } + const response = await axios.get("/api/v1/Gatherings", { params: queryParams }); const gatherings: Gathering[] = response.data; for (const gathering of gatherings) { gathering.start = new Date(gathering.start); diff --git a/src/evently.client/src/lib/services/util-service.ts b/src/evently.client/src/lib/services/util-service.ts index 80822a1..c8d9a84 100644 --- a/src/evently.client/src/lib/services/util-service.ts +++ b/src/evently.client/src/lib/services/util-service.ts @@ -40,7 +40,7 @@ export function downloadFile(blob: Blob, fileName = ""): void { link.remove(); } -export function toIsoString(date: Date | null): string { +export function toIsoDateTimeString(date: Date | null): string { if (date == null) { return ""; } @@ -48,6 +48,14 @@ export function toIsoString(date: Date | null): string { return dateTime.toFormat("yyyy-MM-dd'T'HH:mm"); } +export function toIsoDateString(date: Date | null): string { + if (date == null) { + return ""; + } + const dateTime: DateTime = DateTime.fromJSDate(date); + return dateTime.toFormat("yyyy-MM-dd"); +} + export async function fetchFile(src: string, fileName?: string): Promise { const urlObj = new URL(src); if (fileName == null) { diff --git a/src/evently.client/src/routeTree.gen.ts b/src/evently.client/src/routeTree.gen.ts index c81683f..fd2b967 100644 --- a/src/evently.client/src/routeTree.gen.ts +++ b/src/evently.client/src/routeTree.gen.ts @@ -8,320 +8,317 @@ // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. -import { Route as rootRouteImport } from './routes/__root' -import { Route as AuthRouteImport } from './routes/_auth' -import { Route as IndexRouteImport } from './routes/index' -import { Route as LoginIndexRouteImport } from './routes/login/index' -import { Route as HealthcheckIndexRouteImport } from './routes/healthcheck/index' -import { Route as GatheringsIndexRouteImport } from './routes/gatherings/index' -import { Route as BookingsauthRouteRouteImport } from './routes/bookings/(auth)/route' -import { Route as GatheringsGatheringIdIndexRouteImport } from './routes/gatherings/$gatheringId/index' -import { Route as GatheringsauthCreateRouteImport } from './routes/gatherings/(auth).create' -import { Route as BookingsauthHostingIndexRouteImport } from './routes/bookings/(auth)/hosting/index' -import { Route as BookingsauthAttendingIndexRouteImport } from './routes/bookings/(auth)/attending/index' -import { Route as GatheringsGatheringIdauthUpdateRouteImport } from './routes/gatherings/$gatheringId/(auth).update' -import { Route as BookingsauthHostingGatheringIdDashboardIndexRouteImport } from './routes/bookings/(auth)/hosting/$gatheringId/dashboard.index' -import { Route as BookingsauthHostingGatheringIdDashboardScanRouteImport } from './routes/bookings/(auth)/hosting/$gatheringId/dashboard.scan' +import { Route as rootRouteImport } from "./routes/__root"; +import { Route as AuthRouteImport } from "./routes/_auth"; +import { Route as IndexRouteImport } from "./routes/index"; +import { Route as LoginIndexRouteImport } from "./routes/login/index"; +import { Route as HealthcheckIndexRouteImport } from "./routes/healthcheck/index"; +import { Route as GatheringsIndexRouteImport } from "./routes/gatherings/index"; +import { Route as BookingsauthRouteRouteImport } from "./routes/bookings/(auth)/route"; +import { Route as GatheringsGatheringIdIndexRouteImport } from "./routes/gatherings/$gatheringId/index"; +import { Route as GatheringsauthCreateRouteImport } from "./routes/gatherings/(auth).create"; +import { Route as BookingsauthHostingIndexRouteImport } from "./routes/bookings/(auth)/hosting/index"; +import { Route as BookingsauthAttendingIndexRouteImport } from "./routes/bookings/(auth)/attending/index"; +import { Route as GatheringsGatheringIdauthUpdateRouteImport } from "./routes/gatherings/$gatheringId/(auth).update"; +import { Route as BookingsauthHostingGatheringIdDashboardIndexRouteImport } from "./routes/bookings/(auth)/hosting/$gatheringId/dashboard.index"; +import { Route as BookingsauthHostingGatheringIdDashboardScanRouteImport } from "./routes/bookings/(auth)/hosting/$gatheringId/dashboard.scan"; const AuthRoute = AuthRouteImport.update({ - id: '/_auth', - getParentRoute: () => rootRouteImport, -} as any) + id: "/_auth", + getParentRoute: () => rootRouteImport +} as any); const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) + id: "/", + path: "/", + getParentRoute: () => rootRouteImport +} as any); const LoginIndexRoute = LoginIndexRouteImport.update({ - id: '/login/', - path: '/login/', - getParentRoute: () => rootRouteImport, -} as any) + id: "/login/", + path: "/login/", + getParentRoute: () => rootRouteImport +} as any); const HealthcheckIndexRoute = HealthcheckIndexRouteImport.update({ - id: '/healthcheck/', - path: '/healthcheck/', - getParentRoute: () => rootRouteImport, -} as any) + id: "/healthcheck/", + path: "/healthcheck/", + getParentRoute: () => rootRouteImport +} as any); const GatheringsIndexRoute = GatheringsIndexRouteImport.update({ - id: '/gatherings/', - path: '/gatherings/', - getParentRoute: () => rootRouteImport, -} as any) + id: "/gatherings/", + path: "/gatherings/", + getParentRoute: () => rootRouteImport +} as any); const BookingsauthRouteRoute = BookingsauthRouteRouteImport.update({ - id: '/bookings/(auth)', - path: '/bookings/', - getParentRoute: () => rootRouteImport, -} as any) -const GatheringsGatheringIdIndexRoute = - GatheringsGatheringIdIndexRouteImport.update({ - id: '/gatherings/$gatheringId/', - path: '/gatherings/$gatheringId/', - getParentRoute: () => rootRouteImport, - } as any) + id: "/bookings/(auth)", + path: "/bookings/", + getParentRoute: () => rootRouteImport +} as any); +const GatheringsGatheringIdIndexRoute = GatheringsGatheringIdIndexRouteImport.update({ + id: "/gatherings/$gatheringId/", + path: "/gatherings/$gatheringId/", + getParentRoute: () => rootRouteImport +} as any); const GatheringsauthCreateRoute = GatheringsauthCreateRouteImport.update({ - id: '/gatherings/(auth)/create', - path: '/gatherings/create', - getParentRoute: () => rootRouteImport, -} as any) -const BookingsauthHostingIndexRoute = - BookingsauthHostingIndexRouteImport.update({ - id: '/hosting/', - path: '/hosting/', - getParentRoute: () => BookingsauthRouteRoute, - } as any) -const BookingsauthAttendingIndexRoute = - BookingsauthAttendingIndexRouteImport.update({ - id: '/attending/', - path: '/attending/', - getParentRoute: () => BookingsauthRouteRoute, - } as any) -const GatheringsGatheringIdauthUpdateRoute = - GatheringsGatheringIdauthUpdateRouteImport.update({ - id: '/gatherings/$gatheringId/(auth)/update', - path: '/gatherings/$gatheringId/update', - getParentRoute: () => rootRouteImport, - } as any) + id: "/gatherings/(auth)/create", + path: "/gatherings/create", + getParentRoute: () => rootRouteImport +} as any); +const BookingsauthHostingIndexRoute = BookingsauthHostingIndexRouteImport.update({ + id: "/hosting/", + path: "/hosting/", + getParentRoute: () => BookingsauthRouteRoute +} as any); +const BookingsauthAttendingIndexRoute = BookingsauthAttendingIndexRouteImport.update({ + id: "/attending/", + path: "/attending/", + getParentRoute: () => BookingsauthRouteRoute +} as any); +const GatheringsGatheringIdauthUpdateRoute = GatheringsGatheringIdauthUpdateRouteImport.update({ + id: "/gatherings/$gatheringId/(auth)/update", + path: "/gatherings/$gatheringId/update", + getParentRoute: () => rootRouteImport +} as any); const BookingsauthHostingGatheringIdDashboardIndexRoute = - BookingsauthHostingGatheringIdDashboardIndexRouteImport.update({ - id: '/hosting/$gatheringId/dashboard/', - path: '/hosting/$gatheringId/dashboard/', - getParentRoute: () => BookingsauthRouteRoute, - } as any) + BookingsauthHostingGatheringIdDashboardIndexRouteImport.update({ + id: "/hosting/$gatheringId/dashboard/", + path: "/hosting/$gatheringId/dashboard/", + getParentRoute: () => BookingsauthRouteRoute + } as any); const BookingsauthHostingGatheringIdDashboardScanRoute = - BookingsauthHostingGatheringIdDashboardScanRouteImport.update({ - id: '/hosting/$gatheringId/dashboard/scan', - path: '/hosting/$gatheringId/dashboard/scan', - getParentRoute: () => BookingsauthRouteRoute, - } as any) + BookingsauthHostingGatheringIdDashboardScanRouteImport.update({ + id: "/hosting/$gatheringId/dashboard/scan", + path: "/hosting/$gatheringId/dashboard/scan", + getParentRoute: () => BookingsauthRouteRoute + } as any); export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/bookings': typeof BookingsauthRouteRouteWithChildren - '/gatherings': typeof GatheringsIndexRoute - '/healthcheck': typeof HealthcheckIndexRoute - '/login': typeof LoginIndexRoute - '/gatherings/create': typeof GatheringsauthCreateRoute - '/gatherings/$gatheringId': typeof GatheringsGatheringIdIndexRoute - '/gatherings/$gatheringId/update': typeof GatheringsGatheringIdauthUpdateRoute - '/bookings/attending': typeof BookingsauthAttendingIndexRoute - '/bookings/hosting': typeof BookingsauthHostingIndexRoute - '/bookings/hosting/$gatheringId/dashboard/scan': typeof BookingsauthHostingGatheringIdDashboardScanRoute - '/bookings/hosting/$gatheringId/dashboard': typeof BookingsauthHostingGatheringIdDashboardIndexRoute + "/": typeof IndexRoute; + "/bookings": typeof BookingsauthRouteRouteWithChildren; + "/gatherings": typeof GatheringsIndexRoute; + "/healthcheck": typeof HealthcheckIndexRoute; + "/login": typeof LoginIndexRoute; + "/gatherings/create": typeof GatheringsauthCreateRoute; + "/gatherings/$gatheringId": typeof GatheringsGatheringIdIndexRoute; + "/gatherings/$gatheringId/update": typeof GatheringsGatheringIdauthUpdateRoute; + "/bookings/attending": typeof BookingsauthAttendingIndexRoute; + "/bookings/hosting": typeof BookingsauthHostingIndexRoute; + "/bookings/hosting/$gatheringId/dashboard/scan": typeof BookingsauthHostingGatheringIdDashboardScanRoute; + "/bookings/hosting/$gatheringId/dashboard": typeof BookingsauthHostingGatheringIdDashboardIndexRoute; } export interface FileRoutesByTo { - '/': typeof IndexRoute - '/bookings': typeof BookingsauthRouteRouteWithChildren - '/gatherings': typeof GatheringsIndexRoute - '/healthcheck': typeof HealthcheckIndexRoute - '/login': typeof LoginIndexRoute - '/gatherings/create': typeof GatheringsauthCreateRoute - '/gatherings/$gatheringId': typeof GatheringsGatheringIdIndexRoute - '/gatherings/$gatheringId/update': typeof GatheringsGatheringIdauthUpdateRoute - '/bookings/attending': typeof BookingsauthAttendingIndexRoute - '/bookings/hosting': typeof BookingsauthHostingIndexRoute - '/bookings/hosting/$gatheringId/dashboard/scan': typeof BookingsauthHostingGatheringIdDashboardScanRoute - '/bookings/hosting/$gatheringId/dashboard': typeof BookingsauthHostingGatheringIdDashboardIndexRoute + "/": typeof IndexRoute; + "/bookings": typeof BookingsauthRouteRouteWithChildren; + "/gatherings": typeof GatheringsIndexRoute; + "/healthcheck": typeof HealthcheckIndexRoute; + "/login": typeof LoginIndexRoute; + "/gatherings/create": typeof GatheringsauthCreateRoute; + "/gatherings/$gatheringId": typeof GatheringsGatheringIdIndexRoute; + "/gatherings/$gatheringId/update": typeof GatheringsGatheringIdauthUpdateRoute; + "/bookings/attending": typeof BookingsauthAttendingIndexRoute; + "/bookings/hosting": typeof BookingsauthHostingIndexRoute; + "/bookings/hosting/$gatheringId/dashboard/scan": typeof BookingsauthHostingGatheringIdDashboardScanRoute; + "/bookings/hosting/$gatheringId/dashboard": typeof BookingsauthHostingGatheringIdDashboardIndexRoute; } export interface FileRoutesById { - __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/_auth': typeof AuthRoute - '/bookings/(auth)': typeof BookingsauthRouteRouteWithChildren - '/gatherings/': typeof GatheringsIndexRoute - '/healthcheck/': typeof HealthcheckIndexRoute - '/login/': typeof LoginIndexRoute - '/gatherings/(auth)/create': typeof GatheringsauthCreateRoute - '/gatherings/$gatheringId/': typeof GatheringsGatheringIdIndexRoute - '/gatherings/$gatheringId/(auth)/update': typeof GatheringsGatheringIdauthUpdateRoute - '/bookings/(auth)/attending/': typeof BookingsauthAttendingIndexRoute - '/bookings/(auth)/hosting/': typeof BookingsauthHostingIndexRoute - '/bookings/(auth)/hosting/$gatheringId/dashboard/scan': typeof BookingsauthHostingGatheringIdDashboardScanRoute - '/bookings/(auth)/hosting/$gatheringId/dashboard/': typeof BookingsauthHostingGatheringIdDashboardIndexRoute + __root__: typeof rootRouteImport; + "/": typeof IndexRoute; + "/_auth": typeof AuthRoute; + "/bookings/(auth)": typeof BookingsauthRouteRouteWithChildren; + "/gatherings/": typeof GatheringsIndexRoute; + "/healthcheck/": typeof HealthcheckIndexRoute; + "/login/": typeof LoginIndexRoute; + "/gatherings/(auth)/create": typeof GatheringsauthCreateRoute; + "/gatherings/$gatheringId/": typeof GatheringsGatheringIdIndexRoute; + "/gatherings/$gatheringId/(auth)/update": typeof GatheringsGatheringIdauthUpdateRoute; + "/bookings/(auth)/attending/": typeof BookingsauthAttendingIndexRoute; + "/bookings/(auth)/hosting/": typeof BookingsauthHostingIndexRoute; + "/bookings/(auth)/hosting/$gatheringId/dashboard/scan": typeof BookingsauthHostingGatheringIdDashboardScanRoute; + "/bookings/(auth)/hosting/$gatheringId/dashboard/": typeof BookingsauthHostingGatheringIdDashboardIndexRoute; } export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - | '/' - | '/bookings' - | '/gatherings' - | '/healthcheck' - | '/login' - | '/gatherings/create' - | '/gatherings/$gatheringId' - | '/gatherings/$gatheringId/update' - | '/bookings/attending' - | '/bookings/hosting' - | '/bookings/hosting/$gatheringId/dashboard/scan' - | '/bookings/hosting/$gatheringId/dashboard' - fileRoutesByTo: FileRoutesByTo - to: - | '/' - | '/bookings' - | '/gatherings' - | '/healthcheck' - | '/login' - | '/gatherings/create' - | '/gatherings/$gatheringId' - | '/gatherings/$gatheringId/update' - | '/bookings/attending' - | '/bookings/hosting' - | '/bookings/hosting/$gatheringId/dashboard/scan' - | '/bookings/hosting/$gatheringId/dashboard' - id: - | '__root__' - | '/' - | '/_auth' - | '/bookings/(auth)' - | '/gatherings/' - | '/healthcheck/' - | '/login/' - | '/gatherings/(auth)/create' - | '/gatherings/$gatheringId/' - | '/gatherings/$gatheringId/(auth)/update' - | '/bookings/(auth)/attending/' - | '/bookings/(auth)/hosting/' - | '/bookings/(auth)/hosting/$gatheringId/dashboard/scan' - | '/bookings/(auth)/hosting/$gatheringId/dashboard/' - fileRoutesById: FileRoutesById + fileRoutesByFullPath: FileRoutesByFullPath; + fullPaths: + | "/" + | "/bookings" + | "/gatherings" + | "/healthcheck" + | "/login" + | "/gatherings/create" + | "/gatherings/$gatheringId" + | "/gatherings/$gatheringId/update" + | "/bookings/attending" + | "/bookings/hosting" + | "/bookings/hosting/$gatheringId/dashboard/scan" + | "/bookings/hosting/$gatheringId/dashboard"; + fileRoutesByTo: FileRoutesByTo; + to: + | "/" + | "/bookings" + | "/gatherings" + | "/healthcheck" + | "/login" + | "/gatherings/create" + | "/gatherings/$gatheringId" + | "/gatherings/$gatheringId/update" + | "/bookings/attending" + | "/bookings/hosting" + | "/bookings/hosting/$gatheringId/dashboard/scan" + | "/bookings/hosting/$gatheringId/dashboard"; + id: + | "__root__" + | "/" + | "/_auth" + | "/bookings/(auth)" + | "/gatherings/" + | "/healthcheck/" + | "/login/" + | "/gatherings/(auth)/create" + | "/gatherings/$gatheringId/" + | "/gatherings/$gatheringId/(auth)/update" + | "/bookings/(auth)/attending/" + | "/bookings/(auth)/hosting/" + | "/bookings/(auth)/hosting/$gatheringId/dashboard/scan" + | "/bookings/(auth)/hosting/$gatheringId/dashboard/"; + fileRoutesById: FileRoutesById; } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - AuthRoute: typeof AuthRoute - BookingsauthRouteRoute: typeof BookingsauthRouteRouteWithChildren - GatheringsIndexRoute: typeof GatheringsIndexRoute - HealthcheckIndexRoute: typeof HealthcheckIndexRoute - LoginIndexRoute: typeof LoginIndexRoute - GatheringsauthCreateRoute: typeof GatheringsauthCreateRoute - GatheringsGatheringIdIndexRoute: typeof GatheringsGatheringIdIndexRoute - GatheringsGatheringIdauthUpdateRoute: typeof GatheringsGatheringIdauthUpdateRoute + IndexRoute: typeof IndexRoute; + AuthRoute: typeof AuthRoute; + BookingsauthRouteRoute: typeof BookingsauthRouteRouteWithChildren; + GatheringsIndexRoute: typeof GatheringsIndexRoute; + HealthcheckIndexRoute: typeof HealthcheckIndexRoute; + LoginIndexRoute: typeof LoginIndexRoute; + GatheringsauthCreateRoute: typeof GatheringsauthCreateRoute; + GatheringsGatheringIdIndexRoute: typeof GatheringsGatheringIdIndexRoute; + GatheringsGatheringIdauthUpdateRoute: typeof GatheringsGatheringIdauthUpdateRoute; } -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/_auth': { - id: '/_auth' - path: '' - fullPath: '' - preLoaderRoute: typeof AuthRouteImport - parentRoute: typeof rootRouteImport - } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/login/': { - id: '/login/' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/healthcheck/': { - id: '/healthcheck/' - path: '/healthcheck' - fullPath: '/healthcheck' - preLoaderRoute: typeof HealthcheckIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/gatherings/': { - id: '/gatherings/' - path: '/gatherings' - fullPath: '/gatherings' - preLoaderRoute: typeof GatheringsIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/bookings/(auth)': { - id: '/bookings/(auth)' - path: '/bookings' - fullPath: '/bookings' - preLoaderRoute: typeof BookingsauthRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/gatherings/$gatheringId/': { - id: '/gatherings/$gatheringId/' - path: '/gatherings/$gatheringId' - fullPath: '/gatherings/$gatheringId' - preLoaderRoute: typeof GatheringsGatheringIdIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/gatherings/(auth)/create': { - id: '/gatherings/(auth)/create' - path: '/gatherings/create' - fullPath: '/gatherings/create' - preLoaderRoute: typeof GatheringsauthCreateRouteImport - parentRoute: typeof rootRouteImport - } - '/bookings/(auth)/hosting/': { - id: '/bookings/(auth)/hosting/' - path: '/hosting' - fullPath: '/bookings/hosting' - preLoaderRoute: typeof BookingsauthHostingIndexRouteImport - parentRoute: typeof BookingsauthRouteRoute - } - '/bookings/(auth)/attending/': { - id: '/bookings/(auth)/attending/' - path: '/attending' - fullPath: '/bookings/attending' - preLoaderRoute: typeof BookingsauthAttendingIndexRouteImport - parentRoute: typeof BookingsauthRouteRoute - } - '/gatherings/$gatheringId/(auth)/update': { - id: '/gatherings/$gatheringId/(auth)/update' - path: '/gatherings/$gatheringId/update' - fullPath: '/gatherings/$gatheringId/update' - preLoaderRoute: typeof GatheringsGatheringIdauthUpdateRouteImport - parentRoute: typeof rootRouteImport - } - '/bookings/(auth)/hosting/$gatheringId/dashboard/': { - id: '/bookings/(auth)/hosting/$gatheringId/dashboard/' - path: '/hosting/$gatheringId/dashboard' - fullPath: '/bookings/hosting/$gatheringId/dashboard' - preLoaderRoute: typeof BookingsauthHostingGatheringIdDashboardIndexRouteImport - parentRoute: typeof BookingsauthRouteRoute - } - '/bookings/(auth)/hosting/$gatheringId/dashboard/scan': { - id: '/bookings/(auth)/hosting/$gatheringId/dashboard/scan' - path: '/hosting/$gatheringId/dashboard/scan' - fullPath: '/bookings/hosting/$gatheringId/dashboard/scan' - preLoaderRoute: typeof BookingsauthHostingGatheringIdDashboardScanRouteImport - parentRoute: typeof BookingsauthRouteRoute - } - } +declare module "@tanstack/react-router" { + interface FileRoutesByPath { + "/_auth": { + id: "/_auth"; + path: ""; + fullPath: ""; + preLoaderRoute: typeof AuthRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/": { + id: "/"; + path: "/"; + fullPath: "/"; + preLoaderRoute: typeof IndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/login/": { + id: "/login/"; + path: "/login"; + fullPath: "/login"; + preLoaderRoute: typeof LoginIndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/healthcheck/": { + id: "/healthcheck/"; + path: "/healthcheck"; + fullPath: "/healthcheck"; + preLoaderRoute: typeof HealthcheckIndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/gatherings/": { + id: "/gatherings/"; + path: "/gatherings"; + fullPath: "/gatherings"; + preLoaderRoute: typeof GatheringsIndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/bookings/(auth)": { + id: "/bookings/(auth)"; + path: "/bookings"; + fullPath: "/bookings"; + preLoaderRoute: typeof BookingsauthRouteRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/gatherings/$gatheringId/": { + id: "/gatherings/$gatheringId/"; + path: "/gatherings/$gatheringId"; + fullPath: "/gatherings/$gatheringId"; + preLoaderRoute: typeof GatheringsGatheringIdIndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/gatherings/(auth)/create": { + id: "/gatherings/(auth)/create"; + path: "/gatherings/create"; + fullPath: "/gatherings/create"; + preLoaderRoute: typeof GatheringsauthCreateRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/bookings/(auth)/hosting/": { + id: "/bookings/(auth)/hosting/"; + path: "/hosting"; + fullPath: "/bookings/hosting"; + preLoaderRoute: typeof BookingsauthHostingIndexRouteImport; + parentRoute: typeof BookingsauthRouteRoute; + }; + "/bookings/(auth)/attending/": { + id: "/bookings/(auth)/attending/"; + path: "/attending"; + fullPath: "/bookings/attending"; + preLoaderRoute: typeof BookingsauthAttendingIndexRouteImport; + parentRoute: typeof BookingsauthRouteRoute; + }; + "/gatherings/$gatheringId/(auth)/update": { + id: "/gatherings/$gatheringId/(auth)/update"; + path: "/gatherings/$gatheringId/update"; + fullPath: "/gatherings/$gatheringId/update"; + preLoaderRoute: typeof GatheringsGatheringIdauthUpdateRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/bookings/(auth)/hosting/$gatheringId/dashboard/": { + id: "/bookings/(auth)/hosting/$gatheringId/dashboard/"; + path: "/hosting/$gatheringId/dashboard"; + fullPath: "/bookings/hosting/$gatheringId/dashboard"; + preLoaderRoute: typeof BookingsauthHostingGatheringIdDashboardIndexRouteImport; + parentRoute: typeof BookingsauthRouteRoute; + }; + "/bookings/(auth)/hosting/$gatheringId/dashboard/scan": { + id: "/bookings/(auth)/hosting/$gatheringId/dashboard/scan"; + path: "/hosting/$gatheringId/dashboard/scan"; + fullPath: "/bookings/hosting/$gatheringId/dashboard/scan"; + preLoaderRoute: typeof BookingsauthHostingGatheringIdDashboardScanRouteImport; + parentRoute: typeof BookingsauthRouteRoute; + }; + } } interface BookingsauthRouteRouteChildren { - BookingsauthAttendingIndexRoute: typeof BookingsauthAttendingIndexRoute - BookingsauthHostingIndexRoute: typeof BookingsauthHostingIndexRoute - BookingsauthHostingGatheringIdDashboardScanRoute: typeof BookingsauthHostingGatheringIdDashboardScanRoute - BookingsauthHostingGatheringIdDashboardIndexRoute: typeof BookingsauthHostingGatheringIdDashboardIndexRoute + BookingsauthAttendingIndexRoute: typeof BookingsauthAttendingIndexRoute; + BookingsauthHostingIndexRoute: typeof BookingsauthHostingIndexRoute; + BookingsauthHostingGatheringIdDashboardScanRoute: typeof BookingsauthHostingGatheringIdDashboardScanRoute; + BookingsauthHostingGatheringIdDashboardIndexRoute: typeof BookingsauthHostingGatheringIdDashboardIndexRoute; } const BookingsauthRouteRouteChildren: BookingsauthRouteRouteChildren = { - BookingsauthAttendingIndexRoute: BookingsauthAttendingIndexRoute, - BookingsauthHostingIndexRoute: BookingsauthHostingIndexRoute, - BookingsauthHostingGatheringIdDashboardScanRoute: - BookingsauthHostingGatheringIdDashboardScanRoute, - BookingsauthHostingGatheringIdDashboardIndexRoute: - BookingsauthHostingGatheringIdDashboardIndexRoute, -} + BookingsauthAttendingIndexRoute: BookingsauthAttendingIndexRoute, + BookingsauthHostingIndexRoute: BookingsauthHostingIndexRoute, + BookingsauthHostingGatheringIdDashboardScanRoute: + BookingsauthHostingGatheringIdDashboardScanRoute, + BookingsauthHostingGatheringIdDashboardIndexRoute: + BookingsauthHostingGatheringIdDashboardIndexRoute +}; -const BookingsauthRouteRouteWithChildren = - BookingsauthRouteRoute._addFileChildren(BookingsauthRouteRouteChildren) +const BookingsauthRouteRouteWithChildren = BookingsauthRouteRoute._addFileChildren( + BookingsauthRouteRouteChildren +); const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - AuthRoute: AuthRoute, - BookingsauthRouteRoute: BookingsauthRouteRouteWithChildren, - GatheringsIndexRoute: GatheringsIndexRoute, - HealthcheckIndexRoute: HealthcheckIndexRoute, - LoginIndexRoute: LoginIndexRoute, - GatheringsauthCreateRoute: GatheringsauthCreateRoute, - GatheringsGatheringIdIndexRoute: GatheringsGatheringIdIndexRoute, - GatheringsGatheringIdauthUpdateRoute: GatheringsGatheringIdauthUpdateRoute, -} + IndexRoute: IndexRoute, + AuthRoute: AuthRoute, + BookingsauthRouteRoute: BookingsauthRouteRouteWithChildren, + GatheringsIndexRoute: GatheringsIndexRoute, + HealthcheckIndexRoute: HealthcheckIndexRoute, + LoginIndexRoute: LoginIndexRoute, + GatheringsauthCreateRoute: GatheringsauthCreateRoute, + GatheringsGatheringIdIndexRoute: GatheringsGatheringIdIndexRoute, + GatheringsGatheringIdauthUpdateRoute: GatheringsGatheringIdauthUpdateRoute +}; export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() + ._addFileChildren(rootRouteChildren) + ._addFileTypes(); diff --git a/src/evently.client/src/routes/__root.tsx b/src/evently.client/src/routes/__root.tsx index 1d2ab72..3e664c1 100644 --- a/src/evently.client/src/routes/__root.tsx +++ b/src/evently.client/src/routes/__root.tsx @@ -18,8 +18,9 @@ export function App(): JSX.Element { return (
-
+
+
diff --git a/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/-components/bookings-table.tsx b/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/-components/bookings-table.tsx index 7217a26..a342389 100644 --- a/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/-components/bookings-table.tsx +++ b/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/-components/bookings-table.tsx @@ -1,6 +1,6 @@ import type { JSX } from "react"; import { Booking } from "~/lib/domains/entities"; -import { toIsoString } from "~/lib/services"; +import { toIsoDateTimeString } from "~/lib/services"; import { Icon } from "@iconify/react"; interface BookingsTableProps { @@ -31,7 +31,7 @@ export function BookingsTable({ bookings }: BookingsTableProps): JSX.Element { {booking.accountDto.name} {booking.accountDto.email} - {toIsoString(booking.creationDateTime)} + {toIsoDateTimeString(booking.creationDateTime)} {booking.checkInDateTime ? (
Checked In
diff --git a/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/dashboard.index.tsx b/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/dashboard.index.tsx index d0f1bf4..02088ca 100644 --- a/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/dashboard.index.tsx +++ b/src/evently.client/src/routes/bookings/(auth)/hosting/$gatheringId/dashboard.index.tsx @@ -4,7 +4,7 @@ import { getBookings, type GetBookingsParams, getGathering, - toIsoString + toIsoDateTimeString } from "~/lib/services"; import { createFileRoute, Link } from "@tanstack/react-router"; import { type JSX, useState } from "react"; @@ -50,8 +50,8 @@ export function DashboardPage(): JSX.Element { const rows = bookings.map((booking) => ({ Name: booking.accountDto.name, Email: booking.accountDto.email, - RegistrationDate: toIsoString(booking.creationDateTime), - CheckInDate: toIsoString(booking.checkInDateTime) + RegistrationDate: toIsoDateTimeString(booking.creationDateTime), + CheckInDate: toIsoDateTimeString(booking.checkInDateTime) })); const csv: string = json2csv(rows, {}); diff --git a/src/evently.client/src/routes/bookings/(auth)/route.tsx b/src/evently.client/src/routes/bookings/(auth)/route.tsx index e7c6114..a141141 100644 --- a/src/evently.client/src/routes/bookings/(auth)/route.tsx +++ b/src/evently.client/src/routes/bookings/(auth)/route.tsx @@ -7,5 +7,9 @@ export const Route = createFileRoute("/bookings/(auth)")({ }); function AuthLayout() { - return ; + return ( + <> + + + ); } diff --git a/src/evently.client/src/routes/gatherings/$gatheringId/index.tsx b/src/evently.client/src/routes/gatherings/$gatheringId/index.tsx index 0f6706b..9b7e4c7 100644 --- a/src/evently.client/src/routes/gatherings/$gatheringId/index.tsx +++ b/src/evently.client/src/routes/gatherings/$gatheringId/index.tsx @@ -2,11 +2,11 @@ import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; import { type JSX, useRef } from "react"; import { Booking, Gathering } from "~/lib/domains/entities"; import { + cancelBooking, createBooking, getBookings, getGathering, hashString, - updateBooking, updateGathering } from "~/lib/services"; import { useMutation } from "@tanstack/react-query"; @@ -71,13 +71,7 @@ export function GatheringPage(): JSX.Element { if (booking == null) { return; } - - const bookingDto: BookingReqDto = { - ...booking, - attendeeId: booking.accountDto.id, - cancellationDateTime: new Date() - }; - await updateBooking(booking?.bookingId ?? "", bookingDto); + await cancelBooking(booking?.bookingId ?? ""); navigate({ to: `/gatherings/${gathering.gatheringId}`, reloadDocument: true diff --git a/src/evently.client/src/routes/gatherings/-components/filter-bar.tsx b/src/evently.client/src/routes/gatherings/-components/filter-bar.tsx new file mode 100644 index 0000000..196ab53 --- /dev/null +++ b/src/evently.client/src/routes/gatherings/-components/filter-bar.tsx @@ -0,0 +1,140 @@ +import { type JSX } from "react"; +import { type GetGatheringsParams, toIsoDateString } from "~/lib/services"; +import { Category } from "~/lib/domains/entities"; +import { DateTime } from "luxon"; +import { Icon } from "@iconify/react"; + +interface FilterBarProps { + categories: Category[]; + queryParams: GetGatheringsParams; + handleParamsChange: (queryParams: GetGatheringsParams) => void; +} + +export function FilterBar({ + categories, + queryParams, + handleParamsChange +}: FilterBarProps): JSX.Element { + const dropdownButtonStyle = { anchorName: "--anchor-1" } as React.CSSProperties; + const popoverStyle = { positionAnchor: "--anchor-1" } as React.CSSProperties; + + return ( +
+
+ +
    + {categories.map((category) => { + const oldCategoryIds: number[] = queryParams.categoryIds ?? []; + const wasChecked = oldCategoryIds.some((id) => id === category.categoryId); + return ( +
  • + +
  • + ); + })} +
+

+ {(queryParams.categoryIds?.length ?? 0) > 0 + ? `${queryParams.categoryIds?.length} Selected` + : ""} +

+
+ + + + + + + +
+ ); +} diff --git a/src/evently.client/src/routes/gatherings/-components/gathering-form.tsx b/src/evently.client/src/routes/gatherings/-components/gathering-form.tsx index 72ac6b6..a258b18 100644 --- a/src/evently.client/src/routes/gatherings/-components/gathering-form.tsx +++ b/src/evently.client/src/routes/gatherings/-components/gathering-form.tsx @@ -5,7 +5,7 @@ import { Icon } from "@iconify/react"; import { DateTime } from "luxon"; import { GatheringCategoryDetailReqDto, GatheringReqDto, ToastContent } from "~/lib/domains/models"; import { useRouter } from "@tanstack/react-router"; -import { toIsoString } from "~/lib/services"; +import { toIsoDateTimeString } from "~/lib/services"; import { Category } from "~/lib/domains/entities"; interface GatheringFormProps { @@ -180,7 +180,9 @@ export function GatheringForm({ { const value: DateTime = DateTime.fromISO(e.target.value); @@ -209,7 +211,9 @@ export function GatheringForm({ { const value: DateTime = DateTime.fromISO(e.target.value); diff --git a/src/evently.client/src/routes/gatherings/-components/index.ts b/src/evently.client/src/routes/gatherings/-components/index.ts index cef6675..8fd766c 100644 --- a/src/evently.client/src/routes/gatherings/-components/index.ts +++ b/src/evently.client/src/routes/gatherings/-components/index.ts @@ -1 +1,2 @@ export { GatheringForm } from "./gathering-form.tsx"; +export { FilterBar } from "./filter-bar.tsx"; diff --git a/src/evently.client/src/routes/gatherings/-index.test.tsx b/src/evently.client/src/routes/gatherings/-index.test.tsx index c78cf82..8ead7a9 100644 --- a/src/evently.client/src/routes/gatherings/-index.test.tsx +++ b/src/evently.client/src/routes/gatherings/-index.test.tsx @@ -3,35 +3,51 @@ import { getMockGatherings } from "~/lib/services/gathering-service.mock"; import type { GetGatheringsParams } from "~/lib/services"; import * as GatheringService from "~/lib/services"; import userEvent from "@testing-library/user-event"; -import { TestWrapper, WrapperDataTestId } from "~/lib/components"; -import { GatheringsPage } from "./index.tsx"; +import * as CategoryService from "~/lib/services/category-service"; +import { Route as GatheringsRoute } from "./index.tsx"; +import { TestRouteWrapper } from "~/lib/components"; -it("renders GatheringPage", async () => { - const spy = vi.spyOn(GatheringService, "getGatherings"); - spy.mockImplementation(async (params: GetGatheringsParams) => await getMockGatherings(params)); +describe("test gatherings page", () => { + beforeAll(() => { + let state = {}; - render( - - - - ); - await waitFor(() => screen.findByTestId(WrapperDataTestId)); + window.setState = (changes: any) => { + state = Object.assign({}, state, changes); + }; + }); - expect(spy).toHaveBeenCalledTimes(1); - let element = await screen.findByText("Tech Conference 2024"); - expect(element).toBeInTheDocument(); + it("renders GatheringPage", async () => { + const gatheringSpy = vi.spyOn(GatheringService, "getGatherings"); + gatheringSpy.mockImplementation( + async (params: GetGatheringsParams) => await getMockGatherings(params) + ); - element = await screen.findByText("Design Workshop"); - expect(element).toBeInTheDocument(); + const categorySpy = vi.spyOn(CategoryService, "getCategories"); + categorySpy.mockResolvedValue([]); - element = await screen.findByText("Networking Event"); - expect(element).toBeInTheDocument(); + render(); + await waitFor(() => screen.findByTestId("gatherings-page")); + expect(categorySpy).toHaveBeenCalledTimes(1); - const input: HTMLInputElement = screen.getByPlaceholderText("Search Gatherings"); - await userEvent.type(input, "T"); - expect(spy).toHaveBeenCalledTimes(2); + expect(gatheringSpy).toHaveBeenCalledTimes(1); + let element = await screen.findByText("Tech Conference 2024"); + expect(element).toBeInTheDocument(); - const button: HTMLButtonElement = screen.getByRole("button", { name: "»" }); - await userEvent.click(button); - expect(spy).toHaveBeenCalledTimes(3); + element = await screen.findByText("Design Workshop"); + expect(element).toBeInTheDocument(); + + element = await screen.findByText("Networking Event"); + expect(element).toBeInTheDocument(); + + const filterBar: HTMLDivElement = await screen.findByTestId("filter-bar"); + userEvent.click(filterBar); + + const input: HTMLInputElement = screen.getByPlaceholderText("Search gatherings..."); + await userEvent.type(input, "T"); + expect(gatheringSpy).toHaveBeenCalledTimes(2); + + const button: HTMLButtonElement = screen.getByRole("button", { name: "»" }); + await userEvent.click(button); + expect(gatheringSpy).toHaveBeenCalledTimes(3); + }); }); diff --git a/src/evently.client/src/routes/gatherings/index.tsx b/src/evently.client/src/routes/gatherings/index.tsx index 4eecb98..e7d5b05 100644 --- a/src/evently.client/src/routes/gatherings/index.tsx +++ b/src/evently.client/src/routes/gatherings/index.tsx @@ -1,14 +1,20 @@ import { createFileRoute } from "@tanstack/react-router"; -import { type JSX, useState } from "react"; +import { type JSX, useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; -import { Gathering } from "~/lib/domains/entities"; -import { getGatherings, type GetGatheringsParams } from "~/lib/services"; +import { Category, Gathering } from "~/lib/domains/entities"; +import { getCategories, getGatherings, type GetGatheringsParams } from "~/lib/services"; import { Card } from "~/lib/components"; -import { Icon } from "@iconify/react"; import type { PageResult } from "~/lib/domains/interfaces"; +import { FilterBar } from "~/routes/gatherings/-components"; +import { Icon } from "@iconify/react"; +import polyfill from "@oddbird/css-anchor-positioning/fn"; export const Route = createFileRoute("/gatherings/")({ component: GatheringsPage, + loader: async () => { + const categories: Category[] = await getCategories(); + return { categories }; + }, pendingComponent: () => (
@@ -17,10 +23,20 @@ export const Route = createFileRoute("/gatherings/")({ }); export function GatheringsPage(): JSX.Element { + const { categories } = Route.useLoaderData(); const { account } = Route.useRouteContext(); const accountId: string | undefined = account?.id; + useEffect(() => { + polyfill({ + elements: undefined, + excludeInlineStyles: false, + useAnimationFrame: false + }).catch((err) => console.error(err)); + }, []); + const pageSize = 6; + const [page, setPage] = useState(1); const [queryParams, setQueryParams] = useState({ endDateAfter: new Date(), offset: 0, @@ -28,12 +44,13 @@ export function GatheringsPage(): JSX.Element { }); const { data, isLoading } = useQuery({ queryKey: ["getGatherings", queryParams], - queryFn: (): Promise> => getGatherings(queryParams) + queryFn: (): Promise> => { + return getGatherings(queryParams); + } }); const gatherings: Gathering[] = data == null ? [] : data.data; const totalCount: number = data == null ? 0 : data.totalCount; - const [page, setPage] = useState(1); const maxPage = Math.ceil(totalCount / pageSize); const onPrevPage = () => { let prevPage = page - 1; @@ -59,25 +76,48 @@ export function GatheringsPage(): JSX.Element { }); }; + const handleParamsChange = (queryParams: GetGatheringsParams) => { + setQueryParams({ + ...queryParams, + offset: 0, + limit: pageSize + }); + setPage(1); + }; + + let filterCount: number = queryParams.categoryIds?.length ?? 0; + filterCount += queryParams.name ? 1 : 0; + filterCount += queryParams.startDateAfter ? 1 : 0; + filterCount += queryParams.endDateBefore ? 1 : 0; + return ( -
-
-
+
); } diff --git a/src/evently.client/src/routes/healthcheck/-index.test.tsx b/src/evently.client/src/routes/healthcheck/-index.test.tsx index 39b7a5b..2f2c07b 100644 --- a/src/evently.client/src/routes/healthcheck/-index.test.tsx +++ b/src/evently.client/src/routes/healthcheck/-index.test.tsx @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import * as healthCheckService from "./-services/health-check-service"; import { HealthcheckPage } from "./index.tsx"; -import { TestWrapper, WrapperDataTestId } from "~/lib/components"; +import { TestWrappers, WrapperDataTestId } from "~/lib/components"; // Mock the health check service vi.mock("./-services/health-check-service", () => ({ @@ -23,9 +23,9 @@ describe("HealthcheckPage", () => { it("should render loading state initially", async () => { mockGetStatus.mockReturnValue(new Promise(() => {})); // Never resolving with a promise render( - + - + ); await waitFor(() => screen.findByTestId(WrapperDataTestId)); @@ -41,9 +41,9 @@ describe("HealthcheckPage", () => { mockGetStatus.mockResolvedValue(mockStatuses); render( - + - + ); await screen.findByTestId("root-layout"); diff --git a/src/evently.client/src/setup-tests.ts b/src/evently.client/src/setup-tests.ts index f149f27..4f9cda5 100644 --- a/src/evently.client/src/setup-tests.ts +++ b/src/evently.client/src/setup-tests.ts @@ -1 +1,7 @@ import "@testing-library/jest-dom/vitest"; + +declare global { + interface Window { + setState: (changes: any) => void; + } +} diff --git a/tests/Evently.Server.Test/Features/Gatherings/Services/GatheringServiceTests.cs b/tests/Evently.Server.Test/Features/Gatherings/Services/GatheringServiceTests.cs index 690a0b7..3be7ecd 100644 --- a/tests/Evently.Server.Test/Features/Gatherings/Services/GatheringServiceTests.cs +++ b/tests/Evently.Server.Test/Features/Gatherings/Services/GatheringServiceTests.cs @@ -168,6 +168,7 @@ public async Task GetGatherings_WithNameFilter_ShouldReturnFilteredResults() { endDateBefore: null, endDateAfter: null, isCancelled: null, + categoryIds: [], offset: null, limit: null);