Conversation
…, settings for penalties, and updates to reservation handling
…c; update reservation handling to exclude sanctioned reservations.
…or reservation management
…h related admin functionality; introduce new page protection system for enhanced role-based access control.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…reservationadmission-page
|
Please run |
…tions service, add MyGatekeeps page for gatekeeper-specific reservations, and implement admin panels for managing opened weeks, periods, and settings. Update UI components for consistent styling and improve accessibility features.
…All method in ReservationsController and ReservationsService, improve error messages for better user experience, and update UI components for improved visibility and accessibility in reservation details.
… record entity and DTO, implement findAll method in SanctionRecordsService, and update sanction management in controllers and UI components for better user experience. Added RBAC for sanctions, periods and openWeeks so that only admins or authorized personel can execute CRUD operations on them. Implemented the base structure for testing with Jest
| @Query('page_size', ParseIntPipe) pageSize: number, | ||
| @Query('gateKeeperId') gateKeeperId?: string | ||
| ) { | ||
| const parsedGateKeeperId = gateKeeperId ? parseInt(gateKeeperId, 10) : undefined; |
There was a problem hiding this comment.
do we need this? if not, remove it.
There was a problem hiding this comment.
Pull request overview
This PR introduces a gatekeeper-based reservation admission flow and expands reservation governance (open weeks/periods, sanctions, settings), while also aligning the frontend UI to a monochromatic design system and adding route/page protection scaffolding.
Changes:
- Add gatekeeper priority + “need to be let in” support across reservation UI and backend validation.
- Add admin tooling for Settings / Periods / Opened Weeks, plus sanction record endpoints and user-facing sanction views.
- Refresh UI theming to use
primary/bordertokens and add basic page protection (middleware + HOCs).
Reviewed changes
Copilot reviewed 103 out of 106 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| project_info/guidelines.md | Documents the monochromatic design system guidance. |
| apps/frontend/src/utils/withAuth.tsx | Adds client-side HOC for role-based page protection. |
| apps/frontend/src/types/settings.ts | Introduces Settings type used by admin/settings and quota logic. |
| apps/frontend/src/types/sanction-record.ts | Adds SanctionRecord type for sanction history UIs. |
| apps/frontend/src/types/reservation.ts | Adds gatekeeper priority + needToBeLetIn to Reservation type. |
| apps/frontend/src/types/period.ts | Adds Period type for period management UI. |
| apps/frontend/src/types/openedWeek.ts | Adds OpenedWeek type for open-week gating. |
| apps/frontend/src/types/admin.ts | Removes old admin config types (deprecated approach). |
| apps/frontend/src/middleware.ts | Adds server-side JWT presence check for protected routes. |
| apps/frontend/src/lib/reservationSubmitter.ts | Updates reservation submission for user-or-band, needToBeLetIn, better errors. |
| apps/frontend/src/lib/errorToast.ts | Improves extraction of backend error messages for toast display. |
| apps/frontend/src/hooks/useWeekStatus.ts | Adds hook to determine whether a week is open (period-based). |
| apps/frontend/src/hooks/useUser.tsx | Adds loading state to the user fetch hook. |
| apps/frontend/src/hooks/useReservationDetails.ts | Extends reservation details logic (gatekeeper priority, delete/edit handling). |
| apps/frontend/src/hooks/useQuotaCheck.ts | Adds quota computation including settings + sanction penalties. |
| apps/frontend/src/hooks/useAdminConfig.ts | Removes old admin config hook. |
| apps/frontend/src/hooks/collisionWithAdminRes.tsx | Attempts to adjust collision logic for band reservations. |
| apps/frontend/src/components/ui/rules-card.tsx | Updates link color tokens to text-primary. |
| apps/frontend/src/components/ui/card.tsx | Aligns Card border styling to border-border. |
| apps/frontend/src/components/ui/button.tsx | Replaces orange-based variants with primary token usage. |
| apps/frontend/src/components/ui/badge.tsx | Aligns default badge variant to primary tokens. |
| apps/frontend/src/components/profile/profile-page.tsx | Displays sanction history on user profile component. |
| apps/frontend/src/components/news/news-card.tsx | Updates pinned styling from orange to primary. |
| apps/frontend/src/components/layout/theme-toggle.tsx | Adds trigger id for testing/targeting. |
| apps/frontend/src/components/layout/sidebar.tsx | Adds gatekeeper/admin nav + stats link. |
| apps/frontend/src/components/layout/player.tsx | Updates player accent colors to primary tokens. |
| apps/frontend/src/components/layout/header.tsx | Adds trigger id for user menu. |
| apps/frontend/src/components/layout/footer.tsx | Aligns footer border color to border-border. |
| apps/frontend/src/components/calendar/week/daily-weekly-view.tsx | Adds open-week gating for week navigation + UI refresh. |
| apps/frontend/src/components/calendar/validDate.tsx | Tightens validation (duration + 15-min intervals). |
| apps/frontend/src/components/calendar/time-picker.tsx | Introduces 15-minute interval time picker component. |
| apps/frontend/src/components/calendar/reservation-legend.tsx | Adds legend UI for reservation types. |
| apps/frontend/src/components/calendar/reservation-details.tsx | Updates reservation details UI (gatekeeper priority actions, TimePicker). |
| apps/frontend/src/components/calendar/isReservationOvertime.tsx | Updates overtime calculations and day/week filtering signatures. |
| apps/frontend/src/components/calendar/interval-swithcer.tsx | Updates interval switcher styling to primary tokens. |
| apps/frontend/src/components/calendar/day/day-reservation.tsx | Improves day reservation rendering and labeling for user/band. |
| apps/frontend/src/components/calendar/day/day-comment.tsx | Aligns day comment layout to updated grid sizing and pointer events. |
| apps/frontend/src/components/calendar/day/daily-view.tsx | Updates day view layout + open-week gating for next-day navigation. |
| apps/frontend/src/components/calendar/day/daily-view-without-date.tsx | Updates grid borders to new tokens. |
| apps/frontend/src/components/calendar/comment-details.tsx | Uses TimePicker; limits edit/delete UI to admins. |
| apps/frontend/src/components/calendar/calendar.tsx | Fetches opened weeks and passes into day/week views. |
| apps/frontend/src/components/calendar/add-reservation.tsx | Adds exclusive user/band selection, TimePicker, week locking, needToBeLetIn. |
| apps/frontend/src/components/calendar/add-panel.tsx | UI tweaks + “comment” renamed to “felhívás” in modal. |
| apps/frontend/src/components/calendar/add-comment.tsx | Uses TimePicker and aligns styling to new tokens. |
| apps/frontend/src/components/calendar/Line.tsx | Updates current-time line styling to primary token. |
| apps/frontend/src/components/admin/reservation-limits-form.tsx | Removes old reservation limits admin UI. |
| apps/frontend/src/components/admin/SettingsPanel.tsx | Adds admin UI for Settings singleton editing. |
| apps/frontend/src/components/admin/PeriodsPanel.tsx | Adds admin UI for creating/toggling/deleting periods. |
| apps/frontend/src/components/admin/OpenedWeeksPanel.tsx | Adds admin UI for opening/closing upcoming weeks. |
| apps/frontend/src/app/stats/page.tsx | Restricts stats page to gatekeepers/admins via HOC. |
| apps/frontend/src/app/rules/page.tsx | Updates rule page links to primary tokens. |
| apps/frontend/src/app/reservation/page.tsx | Adds “Beengedéseim” navigation from reservation page header. |
| apps/frontend/src/app/profile/page.tsx | Adds authenticated “My profile” page with sanction history table. |
| apps/frontend/src/app/my-gatekeeps/page.tsx | Adds gatekeeper page for managing assigned reservations + sanctions. |
| apps/frontend/src/app/globals.css | Updates CSS variables to zinc-style monochrome palette and simplifies scroll tweaks. |
| apps/frontend/src/app/admin/page.tsx | Replaces old admin config section with OpenedWeeks/Periods/Settings panels. |
| apps/frontend/package.json | Updates sonner dependency version. |
| apps/frontend/docs/PAGE_PROTECTION.md | Documents middleware + HOC protection approach. |
| apps/frontend/declarations.d.ts | Adds framer module declaration. |
| apps/backend/src/settings/settings.service.ts | Implements singleton Settings retrieval + update. |
| apps/backend/src/settings/settings.module.ts | Registers Settings module. |
| apps/backend/src/settings/settings.controller.ts | Adds settings endpoints (get + patch). |
| apps/backend/src/settings/dto/update-settings.dto.ts | Adds validation for Settings updates. |
| apps/backend/src/sanction-records/sanction-records.service.ts | Adds sanction record CRUD + “me” aggregation logic. |
| apps/backend/src/sanction-records/sanction-records.module.ts | Registers sanction records module. |
| apps/backend/src/sanction-records/sanction-records.controller.ts | Adds sanction record endpoints (create/update/me/all/etc.). |
| apps/backend/src/sanction-records/entities/sanction-record.entity.ts | Defines sanction record validation entity. |
| apps/backend/src/sanction-records/dto/update-sanction-record.dto.ts | Adds update DTO validation. |
| apps/backend/src/sanction-records/dto/create-sanction-record.dto.ts | Adds create DTO validation. |
| apps/backend/src/reservations/reservations.service.ts | Adds reservation validation, open-week/period enforcement, quota/sanction-based status. |
| apps/backend/src/reservations/reservations.service.spec.ts | Introduces initial ReservationsService test scaffold. |
| apps/backend/src/reservations/reservations.controller.ts | Adds (currently unused) gateKeeperId query param parsing. |
| apps/backend/src/reservations/entities/reservation.entity.ts | Adds gatekeeper priority + needToBeLetIn fields to reservation entity. |
| apps/backend/src/reservations/dto/update-reservation.dto.ts | Simplifies UpdateReservationDto to PartialType of SimpleReservationDto. |
| apps/backend/src/reservations/dto/simple-reservation.dto.ts | Updates DTO to include userId/bandId (not omitted). |
| apps/backend/src/periods/periods.service.ts | Adds periods CRUD. |
| apps/backend/src/periods/periods.module.ts | Registers periods module. |
| apps/backend/src/periods/periods.controller.ts | Adds guarded periods CRUD endpoints. |
| apps/backend/src/periods/dto/update-period.dto.ts | Adds update DTO validation for periods. |
| apps/backend/src/periods/dto/create-period.dto.ts | Adds create DTO validation for periods. |
| apps/backend/src/opened-weeks/opened-weeks.service.ts | Adds opened-week list + upsert logic. |
| apps/backend/src/opened-weeks/opened-weeks.module.ts | Registers opened-weeks module. |
| apps/backend/src/opened-weeks/opened-weeks.controller.ts | Adds opened-weeks list + admin upsert endpoint. |
| apps/backend/src/opened-weeks/dto/update-opened-week.dto.ts | Adds opened-week DTO validation. |
| apps/backend/src/comments/comments.service.ts | Adds auto-deletion of overlapping reservations for non-reservable comments. |
| apps/backend/src/bands/bands.service.ts | Ensures band members are included in findOne response. |
| apps/backend/src/app.module.ts | Removes AdminModule and registers new Settings/Periods/Sanctions/OpenedWeeks modules. |
| apps/backend/src/admin/entities/reservation-config.entity.ts | Removes legacy reservation config entity. |
| apps/backend/src/admin/dto/update-config.dto.ts | Removes legacy config DTO. |
| apps/backend/src/admin/dto/set-role.dto.ts | Removes legacy set-role DTO. |
| apps/backend/src/admin/admin.service.ts | Removes legacy admin service. |
| apps/backend/src/admin/admin.module.ts | Removes legacy admin module. |
| apps/backend/src/admin/admin.controller.ts | Removes legacy admin controller. |
| apps/backend/prisma/schema.prisma | Adds Settings/OpenedWeek/SanctionRecord models; adds gatekeeper priority + needToBeLetIn. |
| apps/backend/prisma/migrations/20260326114226_init/migration.sql | Adds reservationId FK on SanctionRecord. |
| apps/backend/prisma/migrations/20260323103330_removed_unnecessary_reservation_config_models/migration.sql | Drops ReservationConfig/SanctionTier tables. |
| apps/backend/prisma/migrations/20260322155651_add_weeks_and_extra_settings_attributes/migration.sql | Adds OpenedWeek + extra Settings columns. |
| apps/backend/prisma/migrations/20260322135731_remove_sanction_type/migration.sql | Removes SANCTIONED from ReservationStatus enum. |
| apps/backend/prisma/migrations/20260320130812_add_gatekeeper_priority/migration.sql | Adds GateKeeperPriority enum + column. |
| apps/backend/prisma/migrations/20260116170512_add_sanction_records/migration.sql | Adds SanctionRecord table and removes User.sanctionPoints. |
| apps/backend/prisma/migrations/20260116023247_kolis_fogadas/migration.sql | Adds needToBeLetIn column to Reservation. |
| apps/backend/prisma/migrations/20260109180915_reservation_enhancements/migration.sql | Adds initial Settings + Period.isOpen + (legacy) SANCTIONED status. |
| apps/backend/package.json | Adds backend test script and Jest dev deps. |
| apps/backend/jest.config.js | Adds Jest config for backend tests. |
Comments suppressed due to low confidence (1)
apps/frontend/src/hooks/collisionWithAdminRes.tsx:17
reservationsOfDay.filter(...)never returns a boolean because the predicate only triggers an asyncgetUser(...).then(...)and returnsundefined. As a resultadminReservationswill always be empty, so admin-collision detection is effectively disabled. Consider making this function synchronous (e.g., rely onReservation.status === 'ADMINMADE'instead of fetching users) or refactor to async and await the role checks before filtering.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export default function ReservationDetails(props: EventDetailsProps) { | ||
| if (!props.clickedEvent) return null; | ||
| if (!props.clickedEvent) return; | ||
|
|
There was a problem hiding this comment.
This component returns undefined when clickedEvent is missing (if (!props.clickedEvent) return;). React components should return null to render nothing; returning undefined can trigger type/lint issues (and is easy to miss during refactors).
| const isNextWeekOpen = () => { | ||
| const nextWeekMonday = new Date(firstDayOfWeek); | ||
| nextWeekMonday.setDate(nextWeekMonday.getDate() + 7); | ||
| nextWeekMonday.setHours(0, 0, 0, 0); | ||
|
|
||
| return props.openedWeeks.some((w) => { | ||
| const wDate = new Date(w.monday); | ||
| return wDate.getTime() === nextWeekMonday.getTime() && w.isOpen; | ||
| }); | ||
| }; | ||
|
|
||
| const handleNextWeek = () => { | ||
| if (!isNextWeekOpen()) return; | ||
|
|
There was a problem hiding this comment.
isNextWeekOpen() strictly requires an openedWeeks entry for the next week. If you navigate to an older week (or openedWeeks is only populated around “now”), the "next" button can become permanently disabled even though the next week is still in the past and should be viewable. Consider allowing navigation to past/current weeks unconditionally (similar to DailyView’s isNextDayOpen logic) and only enforcing openedWeeks for future weeks.
| async update(id: number, updateCommentDto: UpdateCommentDto) { | ||
| try { | ||
| return await this.prisma.comment.update({ | ||
| const updated = await this.prisma.comment.update({ | ||
| where: { | ||
| id, | ||
| }, | ||
| data: { | ||
| ...updateCommentDto, | ||
| }, | ||
| }); | ||
|
|
||
| // If the updated comment now blocks reservations, purge any overlapping ones | ||
| if (updated.isReservable === false) { | ||
| await this.deleteOverlappingReservations(new Date(updated.startTime), new Date(updated.endTime)); | ||
| } | ||
|
|
||
| return updated; |
There was a problem hiding this comment.
Similarly in update(), the comment update is committed before overlapping reservations are deleted. If the deletion fails, the API call errors but the updated comment remains saved. Consider using a $transaction to update the comment and perform the conditional reservation deletion atomically.
| @Get('user/:userId') | ||
| async findByUserId(@Param('userId', ParseIntPipe) userId: number) { | ||
| return this.sanctionRecordsService.findByUserId(userId); | ||
| } | ||
|
|
||
| @Get('band/:bandId') | ||
| async findByBandId(@Param('bandId', ParseIntPipe) bandId: number) { | ||
| return this.sanctionRecordsService.findByBandId(bandId); | ||
| } |
There was a problem hiding this comment.
The user/:userId and band/:bandId sanction-record endpoints are missing any auth guard, so they’re publicly accessible while other sanction endpoints require JWT. These records (points + reasons) are typically sensitive. Add @UseGuards(AuthGuard('jwt')) and enforce authorization (e.g., only ADMIN can query arbitrary user/band sanctions; non-admins should be restricted to their own via /me).
| try { | ||
| const settingsRes = await axiosApi.get('/settings/1'); | ||
| if (settingsRes.data) { | ||
| settings = settingsRes.data; | ||
| } |
There was a problem hiding this comment.
This hook fetches settings from /settings/1, but the backend provides a singleton settings record via GET /settings and the id may not be 1 in existing environments. Fetch from /settings (or first fetch /settings to discover the actual id) to avoid 404s and silently falling back to defaults.
| <select | ||
| value={selectedHour} | ||
| onChange={handleHourChange} | ||
| className='w-20 bg-white hover:bg-slate-200 dark:bg-zinc-700 dark:hover:bg-zinc-600 text-black dark:text-zinc-200 rounded-md border border-zinc-600 px-2 py-2 focus:ring-2 focus:ring-orange-500 focus:border-transparent' | ||
| > |
There was a problem hiding this comment.
The hour <select> still uses focus:ring-orange-500, which conflicts with the new monochromatic --primary/ring design tokens used elsewhere in this PR. Use focus:ring-primary/focus:ring-ring for consistency with the rest of the inputs.
| import OpenedWeeksPanel from '../../components/admin/OpenedWeeksPanel'; | ||
| import PeriodsPanel from '../../components/admin/PeriodsPanel'; | ||
| import SettingsPanel from '../../components/admin/SettingsPanel'; |
There was a problem hiding this comment.
This page mixes absolute alias imports (e.g. @/components/...) with new relative ../../components/... imports. For consistency with the rest of the codebase (and to avoid fragile paths when files move), import these panels via the existing @/components/... alias as well.
| transform: { | ||
| '^.+\\.(t|j)s$': 'ts-jest', | ||
| }, | ||
| collectCoverageFrom: ['**/*.(t|j)s'], |
There was a problem hiding this comment.
collectCoverageFrom: ['**/*.(t|j)s'] is not a valid glob for Jest’s micromatch patterns, so coverage collection may silently skip files. Use a supported pattern such as ['**/*.{ts,js}'] (and optionally exclude .d.ts, main.ts, etc.).
| collectCoverageFrom: ['**/*.(t|j)s'], | |
| collectCoverageFrom: ['**/*.{ts,js}'], |
| if (reservationTimes[2] && reservationTimes[3]) { | ||
| await axiosApi.post('http://localhost:3030/reservations', { | ||
| userId: submissionUserId, | ||
| bandId: band?.id, | ||
| ...(band?.id ? { bandId: band.id } : { userId: submissionUserId }), | ||
| startTime: reservationTimes[2].toISOString(), | ||
| endTime: reservationTimes[3].toISOString(), | ||
| status: 'OVERTIME', | ||
| needToBeLetIn: needToBeLetIn, | ||
| }); |
There was a problem hiding this comment.
The overtime reservation request still posts to a hard-coded http://localhost:3030/reservations URL. This will break outside local dev and also bypasses the configured axiosApi base URL/interceptors. Use the same relative /reservations endpoint (via axiosApi) as the other reservation creations.
| async findAll( | ||
| @Query('page', ParseIntPipe) page: number, | ||
| @Query('page_size', ParseIntPipe) pageSize: number, | ||
| @Query('gateKeeperId') gateKeeperId?: string | ||
| ) { | ||
| const parsedGateKeeperId = gateKeeperId ? parseInt(gateKeeperId, 10) : undefined; | ||
| return this.reservationsService.findAll(page, pageSize); | ||
| } |
There was a problem hiding this comment.
gateKeeperId is parsed (parsedGateKeeperId) but then ignored, and findAll always returns this.reservationsService.findAll(page, pageSize). Either remove this query param or pass it through and implement filtering in the service; otherwise callers will assume server-side filtering works when it doesn’t.
No description provided.