Skip to content

Migrate to shadcn and implement username password login#1018

Draft
Isti01 wants to merge 1 commit intostagingfrom
feature/username-password
Draft

Migrate to shadcn and implement username password login#1018
Isti01 wants to merge 1 commit intostagingfrom
feature/username-password

Conversation

@Isti01
Copy link
Collaborator

@Isti01 Isti01 commented Feb 27, 2026

Summary by CodeRabbit

  • New Features

    • Email/password login with registration, email confirmation, forgot/reset password, and rate limiting; optional reCAPTCHA.
    • Dark mode and expanded theming; new styling color options for light/dark themes.
    • In-app toasts and a Reset Password page/route.
  • Improvements

    • Major UI refresh with new components (tabs, dialogs, alerts, inputs), redesigned pages (home, events, news, profile, communities, QR Fight).
    • Clearer labels and descriptions across settings; events can auto-scroll to the current item.
  • Documentation

    • Embedded, rich admin docs added to multiple components.
  • Chores

    • Frontend framework/tooling migration and version bump.

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Important

Review skipped

Too many files!

This PR contains 299 files, which is 149 over the limit of 150.

📥 Commits

Reviewing files that changed from the base of the PR and between fe025c3 and 79e5a3b.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (299)
  • README.md
  • backend/docs/event.md
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/admission/AdmissionComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/admission/AdmissionComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/ApplicationComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/ApplicationComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/FooterComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/ManifestComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/StylingComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/StylingComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/bmejegy/BmejegyComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/challenge/ChallengeComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/communities/CommunitiesComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/conference/ConferenceComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/countdown/CountdownComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/countdown/CountdownComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/debt/DebtComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/email/EmailComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/errorlog/ErrorLogComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/event/EventComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/event/EventComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/form/FormComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/gallery/GalleryComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/home/HomeComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/home/HomeComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/impressum/ImpressumComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/impressum/ImpressumComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/key/AccessKeyComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/leaderboard/LeaderBoardComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/leaderboard/LeaderBoardComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/CmschUserDetailsService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/PasswordLoginController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/messaging/MessagingComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/news/NewsComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/news/NewsComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/profile/ProfileComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/profile/ProfileComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/proto/ProtoComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/pushnotification/PushNotificationComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/qrfight/QrFightComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/qrfight/QrFightComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/race/RaceComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/race/RaceComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/riddle/RiddleComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/riddle/RiddleComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/script/ScriptComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/serviceaccount/ServiceAccountComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/serviceaccount/ServiceAccountComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/sheets/SheetsComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/staticpage/StaticPageComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/staticpage/StaticPageComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/task/TaskComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/task/TaskComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/team/TeamComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/team/TeamComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/token/TokenComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/token/TokenComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/config/SecurityConfig.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/model/UserEntity.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/service/RateLimiterService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/service/UserService.kt
  • frontend/components.json
  • frontend/index.html
  • frontend/package.json
  • frontend/postcss.config.js
  • frontend/src/App.tsx
  • frontend/src/api/contexts/auth/AuthContext.tsx
  • frontend/src/api/contexts/config/ConfigContext.tsx
  • frontend/src/api/contexts/config/types.ts
  • frontend/src/api/contexts/service/ServiceContext.tsx
  • frontend/src/api/contexts/themeConfig/ThemeConfig.tsx
  • frontend/src/api/hooks/access-key/useAccessKeyMutation.ts
  • frontend/src/api/hooks/access-key/useAccessKeyQuery.ts
  • frontend/src/api/hooks/alias/useAliasChangeMutation.ts
  • frontend/src/api/hooks/auth/useAuthInfo.ts
  • frontend/src/api/hooks/community/useCommunity.ts
  • frontend/src/api/hooks/community/useCommunityList.ts
  • frontend/src/api/hooks/community/useOrganization.ts
  • frontend/src/api/hooks/community/useOrganizationList.ts
  • frontend/src/api/hooks/community/useTinderAnswerSend.ts
  • frontend/src/api/hooks/community/useTinderAnswers.ts
  • frontend/src/api/hooks/community/useTinderCommunity.ts
  • frontend/src/api/hooks/community/useTinderInteractionReset.ts
  • frontend/src/api/hooks/community/useTinderInteractionSend.ts
  • frontend/src/api/hooks/community/useTinderQuestions.ts
  • frontend/src/api/hooks/config/useConfigQuery.ts
  • frontend/src/api/hooks/debt/useDebtQuery.ts
  • frontend/src/api/hooks/developers/useDevelopers.ts
  • frontend/src/api/hooks/event/useEventListQuery.ts
  • frontend/src/api/hooks/event/useEventQuery.ts
  • frontend/src/api/hooks/extra/useExtraPage.tsx
  • frontend/src/api/hooks/form/useFormPage.tsx
  • frontend/src/api/hooks/form/useFormSubmit.tsx
  • frontend/src/api/hooks/gallery/useHomeGallery.ts
  • frontend/src/api/hooks/group-change/useGroupChangeMutation.ts
  • frontend/src/api/hooks/home/useHomeNews.tsx
  • frontend/src/api/hooks/leaderboard/useLeaderBoardQuery.ts
  • frontend/src/api/hooks/location/useLocationQuery.ts
  • frontend/src/api/hooks/news/useNewsListQuery.ts
  • frontend/src/api/hooks/news/useNewsQuery.ts
  • frontend/src/api/hooks/profile/useProfileQuery.ts
  • frontend/src/api/hooks/qr/useQrLevelsQuery.ts
  • frontend/src/api/hooks/race/useFreestyleRaceQuery.ts
  • frontend/src/api/hooks/race/useRaceByTeamQuery.ts
  • frontend/src/api/hooks/race/useRaceQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleDeatilsQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleHintQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleHistoryQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleListQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleSkipMutation.ts
  • frontend/src/api/hooks/riddle/useRiddleSubmitMutation.ts
  • frontend/src/api/hooks/task/useTaskCategoriesQuery.ts
  • frontend/src/api/hooks/task/useTaskFullDetailsQuery.ts
  • frontend/src/api/hooks/task/useTaskSubmissionMutation.ts
  • frontend/src/api/hooks/task/useTasksInCategoryQuery.ts
  • frontend/src/api/hooks/team/actions/useTeamCancelJoin.ts
  • frontend/src/api/hooks/team/actions/useTeamCreate.ts
  • frontend/src/api/hooks/team/actions/useTeamEdit.ts
  • frontend/src/api/hooks/team/actions/useTeamJoin.ts
  • frontend/src/api/hooks/team/actions/useTeamLeave.ts
  • frontend/src/api/hooks/team/queries/useMyTeam.ts
  • frontend/src/api/hooks/team/queries/useTeamDetails.ts
  • frontend/src/api/hooks/team/queries/useTeamList.ts
  • frontend/src/api/hooks/token/useScanTokenMutation.ts
  • frontend/src/api/hooks/token/useTokensQuery.ts
  • frontend/src/api/hooks/useTokenRefresh.ts
  • frontend/src/common-components/BoardStat.tsx
  • frontend/src/common-components/CmschLink.tsx
  • frontend/src/common-components/CollapsableTableRow.tsx
  • frontend/src/common-components/ComponentUnavailable.tsx
  • frontend/src/common-components/ConfirmDialogButton.tsx
  • frontend/src/common-components/CurrentEventCard.tsx
  • frontend/src/common-components/CustomBreadcrumb.tsx
  • frontend/src/common-components/CustomTabButton.tsx
  • frontend/src/common-components/EnableNotifications.tsx
  • frontend/src/common-components/EventIndicator.tsx
  • frontend/src/common-components/LeaderboardTable.tsx
  • frontend/src/common-components/LinkButton.tsx
  • frontend/src/common-components/Loading.tsx
  • frontend/src/common-components/PageStatus.tsx
  • frontend/src/common-components/PresenceAlert.tsx
  • frontend/src/common-components/PulsingDot.tsx
  • frontend/src/common-components/PushNotificationHandler.tsx
  • frontend/src/common-components/SearchBar.tsx
  • frontend/src/common-components/StopItModal.tsx
  • frontend/src/common-components/TokenRarityDisplay.tsx
  • frontend/src/common-components/VotingField.tsx
  • frontend/src/common-components/WalletButton.tsx
  • frontend/src/common-components/Warning.tsx
  • frontend/src/common-components/chakra-md-renderer.tsx
  • frontend/src/common-components/cmsch-ui-renderer.tsx
  • frontend/src/common-components/footer/Footer.tsx
  • frontend/src/common-components/footer/OrganizerLogo.tsx
  • frontend/src/common-components/footer/PartnerLogo.tsx
  • frontend/src/common-components/layout/AppBackground.tsx
  • frontend/src/common-components/layout/CmschContainer.tsx
  • frontend/src/common-components/layout/CmschLayout.tsx
  • frontend/src/common-components/layout/CmschPage.tsx
  • frontend/src/common-components/map/GroupMapContainer.tsx
  • frontend/src/common-components/map/MapContent.tsx
  • frontend/src/common-components/map/MapMarker.tsx
  • frontend/src/common-components/navigation/ColorModeSwitcher.tsx
  • frontend/src/common-components/navigation/Navbar.tsx
  • frontend/src/common-components/navigation/desktop/ChildNavItem.tsx
  • frontend/src/common-components/navigation/desktop/DesktopNav.tsx
  • frontend/src/common-components/navigation/desktop/NavItemNoChildren.tsx
  • frontend/src/common-components/navigation/desktop/NavItemWithChildren.tsx
  • frontend/src/common-components/navigation/mobile/MobileNav.tsx
  • frontend/src/common-components/navigation/mobile/NavItemNoChildren.tsx
  • frontend/src/common-components/navigation/mobile/NavItemWithChildren.tsx
  • frontend/src/components/ui/accordion.tsx
  • frontend/src/components/ui/alert-dialog.tsx
  • frontend/src/components/ui/alert.tsx
  • frontend/src/components/ui/avatar.tsx
  • frontend/src/components/ui/badge.tsx
  • frontend/src/components/ui/breadcrumb.tsx
  • frontend/src/components/ui/button.tsx
  • frontend/src/components/ui/card.tsx
  • frontend/src/components/ui/checkbox.tsx
  • frontend/src/components/ui/dialog.tsx
  • frontend/src/components/ui/dropdown-menu.tsx
  • frontend/src/components/ui/input.tsx
  • frontend/src/components/ui/label.tsx
  • frontend/src/components/ui/popover.tsx
  • frontend/src/components/ui/progress.tsx
  • frontend/src/components/ui/radio-group.tsx
  • frontend/src/components/ui/select.tsx
  • frontend/src/components/ui/separator.tsx
  • frontend/src/components/ui/skeleton.tsx
  • frontend/src/components/ui/switch.tsx
  • frontend/src/components/ui/table.tsx
  • frontend/src/components/ui/tabs.tsx
  • frontend/src/components/ui/textarea.tsx
  • frontend/src/components/ui/toast.tsx
  • frontend/src/components/ui/toaster.tsx
  • frontend/src/components/ui/tooltip.tsx
  • frontend/src/hooks/use-toast.ts
  • frontend/src/index.css
  • frontend/src/index.tsx
  • frontend/src/lib/utils.ts
  • frontend/src/pages/access-key/accessKey.page.tsx
  • frontend/src/pages/communities/community.page.tsx
  • frontend/src/pages/communities/communityList.page.tsx
  • frontend/src/pages/communities/components/CardListItem.tsx
  • frontend/src/pages/communities/components/DataSheet.tsx
  • frontend/src/pages/communities/components/Frame.tsx
  • frontend/src/pages/communities/components/TinderCard.tsx
  • frontend/src/pages/communities/likedCommunityList.page.tsx
  • frontend/src/pages/communities/organization.page.tsx
  • frontend/src/pages/communities/organizationList.page.tsx
  • frontend/src/pages/communities/tinder.page.tsx
  • frontend/src/pages/communities/tinderQuestions.page.tsx
  • frontend/src/pages/communities/tinderRouter.tsx
  • frontend/src/pages/countdown/components/clock.tsx
  • frontend/src/pages/countdown/countdown.page.tsx
  • frontend/src/pages/countdown/countdown.util.tsx
  • frontend/src/pages/debt/components/debt-list-item.tsx
  • frontend/src/pages/debt/debt.page.tsx
  • frontend/src/pages/error/error.page.tsx
  • frontend/src/pages/error/unauthorized.page.tsx
  • frontend/src/pages/events/components/CardListItem.tsx
  • frontend/src/pages/events/components/CurrentEvent.tsx
  • frontend/src/pages/events/components/CustomTab.tsx
  • frontend/src/pages/events/components/EventFilterOption.tsx
  • frontend/src/pages/events/components/EventList.tsx
  • frontend/src/pages/events/components/EventListItem.tsx
  • frontend/src/pages/events/components/EventTags.tsx
  • frontend/src/pages/events/components/event-calendar/CurrentDateBar.tsx
  • frontend/src/pages/events/components/event-calendar/DayCalendar.tsx
  • frontend/src/pages/events/components/event-calendar/EventBox.tsx
  • frontend/src/pages/events/components/event-calendar/HourColumn.tsx
  • frontend/src/pages/events/components/event-calendar/WeekCalendar.tsx
  • frontend/src/pages/events/components/event-calendar/ZoomBar.tsx
  • frontend/src/pages/events/components/event-calendar/utils.ts
  • frontend/src/pages/events/event.page.tsx
  • frontend/src/pages/events/eventCalendar.page.tsx
  • frontend/src/pages/events/eventList.page.tsx
  • frontend/src/pages/events/util/filter.tsx
  • frontend/src/pages/extra/extra.page.tsx
  • frontend/src/pages/form/components/GridField.tsx
  • frontend/src/pages/form/components/GridFieldItem.tsx
  • frontend/src/pages/form/components/autoFormField.tsx
  • frontend/src/pages/form/components/formStatusBadge.tsx
  • frontend/src/pages/form/form.page.tsx
  • frontend/src/pages/home/components/EmbeddedVideo.tsx
  • frontend/src/pages/home/components/HomePageEventList.tsx
  • frontend/src/pages/home/components/HomePageGalleryCarousel.tsx
  • frontend/src/pages/home/components/HomePageNewsList.tsx
  • frontend/src/pages/home/components/ImageCarousel.tsx
  • frontend/src/pages/home/components/Schedule.tsx
  • frontend/src/pages/home/home.page.tsx
  • frontend/src/pages/impressum/components/DeveloperWrapItem.tsx
  • frontend/src/pages/impressum/components/OrganizerSection.tsx
  • frontend/src/pages/impressum/components/OrganizerWrapItem.tsx
  • frontend/src/pages/impressum/impressum.page.tsx
  • frontend/src/pages/impressum/util/arrayFunctions.ts
  • frontend/src/pages/index/index.page.tsx
  • frontend/src/pages/leaderboard/leaderboard.page.tsx
  • frontend/src/pages/leaderboard/leaderboardByCategory.page.tsx
  • frontend/src/pages/leaderboard/leaderboardByUserOrGroup.page.tsx
  • frontend/src/pages/loading/loading.page.tsx
  • frontend/src/pages/login/login.page.tsx
  • frontend/src/pages/login/resetPassword.page.tsx
  • frontend/src/pages/map/map.page.tsx
  • frontend/src/pages/news/components/News.tsx
  • frontend/src/pages/news/components/NewsList.tsx
  • frontend/src/pages/news/components/NewsListItem.tsx
  • frontend/src/pages/news/news.page.tsx
  • frontend/src/pages/news/newsList.page.tsx
  • frontend/src/pages/news/util/sortNewsList.ts
  • frontend/src/pages/profile/components/GroupLeaderContactList.tsx
  • frontend/src/pages/profile/components/Popup.tsx
  • frontend/src/pages/profile/components/ProfileQR.tsx
  • frontend/src/pages/profile/profile.aliasChange.page.tsx
  • frontend/src/pages/profile/profile.groupChange.page.tsx
  • frontend/src/pages/profile/profile.page.tsx
  • frontend/src/pages/profile/util/percentFunctions.ts
  • frontend/src/pages/qr-fight/components/DataDisplayWrapper.tsx
  • frontend/src/pages/qr-fight/components/LevelDataDisplay.tsx
  • frontend/src/pages/qr-fight/components/LevelStatusBadge.tsx
  • frontend/src/pages/qr-fight/components/TotemDataDisplay.tsx
  • frontend/src/pages/qr-fight/components/TotemField.tsx
  • frontend/src/pages/qr-fight/components/TowerDataDisplay.tsx
  • frontend/src/pages/qr-fight/components/TowerField.tsx
  • frontend/src/pages/qr-fight/components/TreasureDataDisplayWrapper.tsx
  • frontend/src/pages/qr-fight/qrLevels.page.tsx
  • frontend/src/pages/race/components/RaceBoard.tsx
  • frontend/src/pages/race/freestyleRace.page.tsx
  • frontend/src/pages/race/race.page.tsx
  • frontend/src/pages/race/raceByTeam.page.tsx
  • frontend/src/pages/riddle/components/RiddleCategoryListItem.tsx
  • frontend/src/pages/riddle/components/RiddleListItem.tsx
  • frontend/src/pages/riddle/components/SpoilerText.tsx
  • frontend/src/pages/riddle/riddle.page.tsx

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/username-password

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (17)
frontend/src/common-components/WalletButton.tsx (1)

25-32: ⚠️ Potential issue | 🟠 Major

Accessibility issue: interactive img is not keyboard-accessible.

The img element with onClick is not focusable and cannot be activated via keyboard. This blocks keyboard-only users from adding the pass to their wallet.

Since clicking opens a URL, an anchor element is more semantic and provides keyboard accessibility by default.

♿ Proposed fix using an anchor element
   return (
-    <img
-      className="cursor-pointer max-w-full w-48 mb-3"
-      src={type === 'apple' ? AppleWalletImage : GoogleWalletImage}
-      alt={type === 'apple' ? 'Apple Wallet' : 'Google Wallet'}
-      onClick={() => {
-        window.open(url)
-      }}
-    />
+    <a href={url} target="_blank" rel="noopener noreferrer">
+      <img
+        className="max-w-full w-48 mb-3"
+        src={type === 'apple' ? AppleWalletImage : GoogleWalletImage}
+        alt={type === 'apple' ? 'Apple Wallet' : 'Google Wallet'}
+      />
+    </a>
   )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/common-components/WalletButton.tsx` around lines 25 - 32,
Replace the interactive img+onClick in the WalletButton component with a
semantic anchor so keyboard users can activate it: wrap or replace the <img>
with an <a href={url} target="_blank" rel="noopener noreferrer" aria-label={type
=== 'apple' ? 'Add to Apple Wallet' : 'Add to Google Wallet'}> and keep the
current img inside (preserve className and alt), removing the onClick; this
ensures the link is focusable and opens the URL securely in a new tab.
frontend/src/api/hooks/form/useFormSubmit.tsx (1)

10-31: ⚠️ Potential issue | 🔴 Critical

Missing setLoading(true) at the start of the submit function.

The loading state is initialized to false and only setLoading(false) is called in the .finally() blocks. This means submitLoading will always remain false, rendering the loading state useless for consumers of this hook.

🐛 Proposed fix to set loading state correctly
   const submit = (formBody: object, edit?: boolean) => {
+    setLoading(true)
     if (edit) {
       axios
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/api/hooks/form/useFormSubmit.tsx` around lines 10 - 31, The
submit function never sets loading to true, so setLoading(true) at the start of
the submit(formBody, edit?) function (before the if (edit) branch) so both the
axios.put and axios.post paths flip loading on; keep the existing .finally(() =>
setLoading(false)) in both branches and ensure you call setLoading(true) before
invoking axios (referencing the submit function and setLoading state updater).
frontend/src/api/hooks/team/actions/useTeamJoin.ts (1)

10-11: ⚠️ Potential issue | 🟡 Minor

Remove the //todo comment on line 10.

This //todo comment is a leftover development artifact. Verification confirms this appears across all 10 team action hooks in the directory—ensure these are cleaned up project-wide before merging.

Proposed fix
-    axios //todo
+    axios
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/api/hooks/team/actions/useTeamJoin.ts` around lines 10 - 11,
Remove the stray “//todo” inline comment from the axios.post call in useTeamJoin
(the axios.post<TeamResponses>(`/api/team/join`, { id }) expression) and
similarly remove that same leftover comment from the other nine team action
hooks in the directory so no development artifact comments remain; search for
"//todo" within frontend/src/api/hooks/team/actions and delete those
occurrences, then run a quick lint/format pass to ensure no trailing whitespace
changes.
frontend/src/pages/riddle/riddle.page.tsx (1)

90-115: ⚠️ Potential issue | 🟡 Minor

Add explicit handling for failed skip status to prevent silent failures.

The skip success handler doesn't account for all possible response statuses. When result.nextRiddles.length is 0 and the server returns CANNOT_SKIP (due to a race condition where skipPermitted changed after the UI rendered), the handler silently does nothing with no feedback to the user. Additionally, the first condition at line 94 checks only for nextRiddles.length without verifying the status, which could navigate incorrectly if the skip failed.

Add explicit status validation to handle unexpected statuses:

Suggested improvement
 const skipSolution = () => {
   if (riddleConfig.skipEnabled && data.skipPermitted) {
     skipMutation.mutate(id, {
       onSuccess: (result) => {
+        if (result.status === RiddleSubmissionStatus.CANNOT_SKIP) {
+          toast({ title: l('riddle-cannot-skip'), variant: 'destructive' })
+          return
+        }
         if (result.nextRiddles.length) {
           navigate(`${AbsolutePaths.RIDDLE}/solve/${result.nextRiddles[0].id}`)
           if (solutionInput.current) solutionInput.current.value = ''
           toast({
             title: l('riddle-skipped-title'),
             description: l('riddle-skipped-description')
           })
         }
         if (result.status === RiddleSubmissionStatus.CORRECT && !result.nextRiddles.length) {
           navigate(AbsolutePaths.RIDDLE)
           toast({
             title: l('riddle-completed-title'),
             description: l('riddle-completed-description')
           })
         }
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/riddle/riddle.page.tsx` around lines 90 - 115, The success
handler in skipSolution (inside skipMutation.mutate) must validate the returned
result.status before acting: only navigate to result.nextRiddles[0] and clear
solutionInput when the status indicates the skip actually succeeded (i.e., not
RiddleSubmissionStatus.CANNOT_SKIP); if result.status ===
RiddleSubmissionStatus.CANNOT_SKIP or any other unexpected status, call toast
with a descriptive failure message instead of silently doing nothing; also
update the CORRECT branch to only trigger when status ===
RiddleSubmissionStatus.CORRECT and nextRiddles.length === 0, and add a default
fallback toast for any unhandled statuses so the user always receives feedback.
frontend/src/pages/race/components/RaceBoard.tsx (1)

20-20: ⚠️ Potential issue | 🔴 Critical

Guard board before calling .some() to prevent render-time crash.

Line 20 uses data?.board.some(...), but Props accepts data: RaceView | undefined. When data is undefined, the useMemo callback executes before the guard on line 21, causing a runtime error. The optional chaining needs to extend to .some().

Proposed fix
-  const showDescription = useMemo(() => data?.board.some((i) => !!i.description), [data?.board])
+  const showDescription = useMemo(
+    () => data?.board?.some((i) => Boolean(i.description)) ?? false,
+    [data?.board]
+  )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/race/components/RaceBoard.tsx` at line 20, The useMemo for
showDescription in RaceBoard currently calls data?.board.some(...) which can
throw when data is undefined; update the guard so you call .some only if board
exists (e.g., change the expression to check data?.board?.some(...) or use a
boolean guard like data?.board && data.board.some(...)) and keep the same
dependency array [data?.board]; adjust the showDescription computation
accordingly to avoid render-time crashes.
frontend/src/pages/profile/profile.groupChange.page.tsx (1)

61-103: ⚠️ Potential issue | 🟠 Major

Enter-key submission is blocked; form semantic structure is broken.

Line 72 prevents default submission without calling the onSubmit() handler, making keyboard submission impossible. Only the click handler at line 102 triggers the mutation.

Fix the form submission flow:

  1. Restore form submission by calling onSubmit() in the form's onSubmit handler (after preventDefault)
  2. Add type="submit" to the save button (line 102)
  3. Add type="button" to secondary action buttons to prevent accidental form submission
Proposed fix
-        <form onSubmit={(e) => e.preventDefault()}>
+        <form
+          onSubmit={(e) => {
+            e.preventDefault()
+            onSubmit()
+          }}
+        >
@@
-                <Button
+                <Button
+                  type="button"
                   variant="ghost"
                   onClick={() => {
                     setValue(profile.fallbackGroup?.toString() || '')
                   }}
                 >
@@
-              <Button onClick={onSubmit} disabled={isPending}>
+              <Button type="submit" disabled={isPending}>
                 {isPending ? 'Mentés...' : 'Mentés'}
               </Button>
-              <LinkButton href={AbsolutePaths.PROFILE} variant="outline">
+              <LinkButton href={AbsolutePaths.PROFILE} variant="outline" type="button">
                 Mégse
               </LinkButton>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/profile/profile.groupChange.page.tsx` around lines 61 -
103, The form currently prevents default submission but never invokes
onSubmit(), blocking Enter-key submits; update the form's onSubmit handler to
call onSubmit() after e.preventDefault(), change the primary save Button (the
one using onClick={onSubmit} and isPending) to type="submit" and keep its
onClick optional, and ensure any other Buttons inside the form (e.g., the
"Vendég vagyok" ghost Button) are marked type="button" to avoid accidental
submissions; reference the onSubmit function, the form element's onSubmit, the
primary save Button (uses isPending and onClick={onSubmit}), and the guest
Button (calls setValue) when applying these changes.
frontend/src/pages/extra/extra.page.tsx (1)

20-23: ⚠️ Potential issue | 🔴 Critical

Authorization check can be bypassed for guests

Line 20 short-circuits on authInfo, so unauthenticated users skip the role comparison and can see protected content. Compare roles unconditionally with a guest fallback.

🔐 Proposed fix
-  if (RoleType[data.minRole] > RoleType.GUEST && authInfo && RoleType[authInfo?.role ?? RoleTypeString.GUEST] < RoleType[data.minRole]) {
+  const requiredRole = RoleType[data.minRole]
+  const currentRole = RoleType[authInfo?.role ?? RoleTypeString.GUEST]
+  if (requiredRole > RoleType.GUEST && currentRole < requiredRole) {
     sendMessage(l('no-permission'))
     return <Navigate replace to={AbsolutePaths.ERROR} />
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/extra/extra.page.tsx` around lines 20 - 23, The
authorization check currently short-circuits on authInfo, letting
unauthenticated users bypass the role comparison; change the conditional to
always compare numeric role values by using a guest fallback for missing
authInfo (e.g., use (authInfo?.role ?? RoleTypeString.GUEST) or equivalent) so
the comparison RoleType[authInfo?.role ?? RoleTypeString.GUEST] <
RoleType[data.minRole] is evaluated unconditionally; update the if in
extra.page.tsx (references: RoleType, authInfo, data.minRole,
RoleTypeString.GUEST) to remove the authInfo short-circuit and keep the existing
sendMessage(l('no-permission')) and Navigate to AbsolutePaths.ERROR behavior.
frontend/src/common-components/PageStatus.tsx (2)

17-25: ⚠️ Potential issue | 🔴 Critical

Logic error: error message sent when page loads successfully.

The current logic sends 'page-load-failed-contact-developers' message when isError is false (line 24). This means a successful page load triggers an error notification, which appears to be inverted logic.

The return statement on line 21 exits the effect when there IS an error, then line 24 executes for the non-error case.

🐛 Proposed fix
   useEffect(() => {
     if (isLoading) return
     if (isError) {
-      sendMessage(l('page-load-failed', { title }))
-      return
+      sendMessage(l('page-load-failed-contact-developers', { title }))
     }
-
-    sendMessage(l('page-load-failed-contact-developers', { title }))
   }, [isLoading, isError, sendMessage, title])

Alternatively, if both messages are intentional for different scenarios, the current logic needs to be inverted so the "contact developers" message is sent for errors and a different handling exists for success.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/common-components/PageStatus.tsx` around lines 17 - 25, The
effect in PageStatus's useEffect uses inverted logic causing a success load to
send the 'page-load-failed-contact-developers' message; update the conditional
flow so that when isLoading is true it returns, when isError is true it sends
the more urgent 'page-load-failed-contact-developers' (using sendMessage(l(...,
{ title }))) and otherwise sends the normal success/failed fallback message (or
no message) — adjust the order/conditions around isLoading, isError,
sendMessage, l and title in the useEffect callback accordingly.

31-35: ⚠️ Potential issue | 🟠 Major

Redundant fallback case navigates to same destination.

Lines 31-33 handle isError by navigating to AbsolutePaths.ERROR, and line 35 does the exact same thing as a fallback. This fallback case would only be reached when !isLoading && !isError, which suggests either:

  1. The fallback should handle a different scenario (e.g., return null or render children)
  2. The component's purpose is unclear if both success and error states navigate to the error page
🔧 Suggested fix if fallback should not navigate to error
   if (isError) {
     return <Navigate replace to={AbsolutePaths.ERROR} />
   }
 
-  return <Navigate replace to={AbsolutePaths.ERROR} />
+  return null
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/common-components/PageStatus.tsx` around lines 31 - 35, The
fallback currently navigates to AbsolutePaths.ERROR even when neither isLoading
nor isError, making the earlier isError check redundant; update the PageStatus
component so that when !isLoading && !isError it does not navigate to
AbsolutePaths.ERROR but instead returns the successful UI (e.g., render children
or null) or navigates to the correct success path — adjust the return after the
isError check (referencing isError, isLoading, Navigate, and
AbsolutePaths.ERROR) to handle the non-error, non-loading case appropriately.
frontend/src/common-components/map/MapContent.tsx (1)

50-54: ⚠️ Potential issue | 🟠 Major

Replace stale Chakra color token with valid hex color.

color="blue.500" is a Chakra token that won't work in this context. The MapMarker component expects a hex color format (used directly in style={{ backgroundColor: color }} and passed to getTextColorFromLuminance() which parses hex values). Use a valid CSS hex color like "#3b82f6" (Tailwind's blue-500 equivalent) instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/common-components/map/MapContent.tsx` around lines 50 - 54, The
MapMarker color prop is using an invalid Chakra token ("blue.500"); update the
JSX in MapContent where Marker and MapMarker are rendered (the Marker wrapper
and MapMarker component) to pass a hex color string instead (e.g. "#3b82f6") so
MapMarker's style={{ backgroundColor: color }} and getTextColorFromLuminance()
receive a valid hex value; locate the MapMarker usage in MapContent (the
Marker/MapMarker pair) and replace color="blue.500" with color="#3b82f6".
frontend/src/pages/events/components/event-calendar/HourColumn.tsx (1)

6-14: ⚠️ Potential issue | 🟡 Minor

useMemo has no effect because originDate changes every render.

startOfDay(new Date()) on line 6 creates a new Date object on every render. Since originDate is in the dependency array, useMemo will recompute dates on every render, defeating its purpose.

Move originDate inside the useMemo or memoize it separately:

Suggested fix
 export function HourColumn({ h }: { h: number }) {
-  const originDate = startOfDay(new Date())
   const dates = useMemo(() => {
+    const originDate = startOfDay(new Date())
     const datesTemp: Date[] = []
     for (let i = 0; i < 24; i++) {
       const date = addHours(originDate, i)
       datesTemp.push(date)
     }
-    return datesTemp
-  }, [originDate])
+    return { dates: datesTemp, originDate }
+  }, [])
 
-  const minDate = originDate
-  const maxDate = endOfDay(originDate)
+  const minDate = dates.originDate
+  const maxDate = endOfDay(dates.originDate)

Or, simpler refactor returning both values:

 export function HourColumn({ h }: { h: number }) {
-  const originDate = startOfDay(new Date())
-  const dates = useMemo(() => {
+  const { dates, minDate, maxDate } = useMemo(() => {
+    const originDate = startOfDay(new Date())
     const datesTemp: Date[] = []
     for (let i = 0; i < 24; i++) {
       const date = addHours(originDate, i)
       datesTemp.push(date)
     }
-    return datesTemp
-  }, [originDate])
-
-  const minDate = originDate
-  const maxDate = endOfDay(originDate)
+    return { dates: datesTemp, minDate: originDate, maxDate: endOfDay(originDate) }
+  }, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/events/components/event-calendar/HourColumn.tsx` around
lines 6 - 14, The useMemo for dates is ineffective because originDate is
recomputed each render (originDate = startOfDay(new Date())), so move the
originDate creation into the same useMemo (or memoize it separately) so the
dependency array is stable: update the dates useMemo to compute const originDate
= startOfDay(new Date()) inside the useMemo (or create originDate via useMemo(()
=> startOfDay(new Date()), [])) and keep dates computed from that stable
originDate; modify references to originDate/dates accordingly in HourColumn
(useMemo, originDate, dates, startOfDay, addHours).
frontend/src/pages/home/components/ImageCarousel.tsx (1)

62-71: ⚠️ Potential issue | 🟡 Minor

Add explicit button semantics to carousel indicator dots.

These controls are unlabeled for assistive tech, and default button type may submit surrounding forms.

Suggested fix
     <button
+      type="button"
+      aria-label={`Ugrás a(z) ${index + 1}. képre`}
+      aria-current={index === currentIndex}
       className="h-[10px] w-[10px] p-0 border-2 border-solid rounded-full cursor-pointer transition-all hover:border-[5px]"
       style={{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/home/components/ImageCarousel.tsx` around lines 62 - 71,
The carousel indicator button in ImageCarousel is missing accessible semantics
and a safe button type; update the button element (the indicator rendering that
uses onClick, currentIndex and brandColor) to include type="button", an
appropriate aria-label (e.g. `aria-label={`Go to slide ${index + 1}`}`), and
aria-current={index === currentIndex ? "true" : undefined} (or aria-pressed) so
assistive tech can identify the control and current slide; keep the existing
styles and onClick behavior.
frontend/src/pages/home/components/HomePageNewsList.tsx (1)

6-16: ⚠️ Potential issue | 🟡 Minor

Avoid mutating the input array in sortByHighlighted.

Array.sort() mutates the original array in place. Since homeNews.data comes from a hook (likely React Query), mutating it can cause unexpected behavior or stale UI issues.

🛡️ Proposed fix to avoid mutation
 const sortByHighlighted = (news: NewsArticleView[]) => {
-  return news.sort((a, b) => {
+  return [...news].sort((a, b) => {
     if (a.highlighted && !b.highlighted) {
       return -1
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/home/components/HomePageNewsList.tsx` around lines 6 - 16,
sortByHighlighted currently mutates its input by calling news.sort(), which can
corrupt data from hooks like homeNews.data; change it to operate on a shallow
copy (e.g., copy the array with [...news] or news.slice()) and return the sorted
copy instead, keeping the comparator logic in sortByHighlighted unchanged so
callers (including where homeNews.data is passed) receive a new array without
mutating the original.
frontend/src/pages/events/components/event-calendar/DayCalendar.tsx (1)

34-40: ⚠️ Potential issue | 🟡 Minor

Add bounds to scale increment/decrement to prevent invalid values.

Unlike WeekCalendar, which clamps the scale between 0.6 and 2, DayCalendar has no bounds. This allows the scale to become negative or excessively large, breaking the layout.

Proposed fix to match WeekCalendar bounds
   const incrementScale = () => {
-    setScale((prev) => prev + 0.2)
+    setScale((prev) => Math.min(prev + 0.2, 2))
   }

   const decrementScale = () => {
-    setScale((prev) => prev - 0.2)
+    setScale((prev) => Math.max(prev - 0.2, 0.6))
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/events/components/event-calendar/DayCalendar.tsx` around
lines 34 - 40, The incrementScale and decrementScale functions allow scale to go
outside sane limits; update them to clamp the new scale between 0.6 and 2 (same
bounds used in WeekCalendar) before calling setScale. In other words, compute
the candidate value inside incrementScale/decrementScale (prev + 0.2 / prev -
0.2), then clamp it to the range [0.6, 2] and pass the clamped value to setScale
so scale never becomes negative or excessively large.
frontend/src/common-components/layout/CmschPage.tsx (1)

14-27: ⚠️ Potential issue | 🟠 Major

Preserve incoming className when adding page padding.

className="pb-10" currently overrides any className passed through CmschPageProps.

Suggested fix
+import { cn } from '@/lib/utils'
...
-export const CmschPage = ({ children, loginRequired, minRole, title, ...props }: CmschPageProps) => {
+export const CmschPage = ({ children, loginRequired, minRole, title, className, ...props }: CmschPageProps) => {
...
-    <CmschContainer {...props} className="pb-10">
+    <CmschContainer {...props} className={cn('pb-10', className)}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/common-components/layout/CmschPage.tsx` around lines 14 - 27,
CmschPage currently forces className="pb-10" on the CmschContainer which
overwrites any incoming className in props; update CmschPage so it merges the
incoming props.className with "pb-10" (e.g., combine props.className and "pb-10"
into a single className string) before passing to CmschContainer, ensuring you
reference CmschPage, CmschContainer and the incoming props to build the merged
className.
backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginService.kt (1)

396-429: ⚠️ Potential issue | 🟠 Major

Missing concurrency lock in fetchKeycloakUserEntity — potential race condition.

Unlike fetchUserEntity (authsch) and fetchGoogleUserEntity, this method does not acquire a lock via userLocks.lockForKey(). Concurrent logins with the same Keycloak user could result in duplicate user creation or lost updates when updating internalId.

Suggested fix: Add locking consistent with other methods
 fun fetchKeycloakUserEntity(profile: KeycloakUserInfoResponse): UserEntity {
+    val lock = userLocks.lockForKey(profile.sid)
+    try {
         var user: UserEntity
         val existingByInternalId = users.findById(profile.sid)
         // ... existing logic ...
         updateFieldsForKeycloak(profile, user)
         users.save(user)
+        adminMenuService.invalidateUser(user.internalId)
         return user
+    } finally {
+        lock.unlock()
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginService.kt`
around lines 396 - 429, fetchKeycloakUserEntity currently lacks the concurrency
lock used by fetchUserEntity/fetchGoogleUserEntity, risking duplicate users or
lost internalId updates; wrap the lookup/create/update/save logic inside
userLocks.lockForKey(...) using a stable key (preferably profile.sid, falling
back to profile.email when sid is blank) so the entire block that reads
users.findById/profile.email, mutates user.internalId, calls
updateFieldsForKeycloak(profile, user) and users.save(user) executes under the
same lock; mirror the locking pattern and exception-safe release used in
fetchUserEntity/fetchGoogleUserEntity.
frontend/src/pages/news/components/NewsList.tsx (1)

18-18: ⚠️ Potential issue | 🟠 Major

Array mutation: .sort() mutates the original array.

newsList.sort() mutates the prop array in place, which violates React's immutability principle and can cause subtle bugs. Create a copy before sorting.

Fix
-  const sortedNewsList = useMemo(() => newsList.sort((a, b) => b.timestamp - a.timestamp), [newsList])
+  const sortedNewsList = useMemo(() => [...newsList].sort((a, b) => b.timestamp - a.timestamp), [newsList])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/news/components/NewsList.tsx` at line 18, The current
useMemo in NewsList uses newsList.sort() which mutates the original prop array;
change the computation for sortedNewsList to sort a shallow copy instead (e.g.,
copy newsList via slice/spread before calling sort) so the original newsList
prop is not mutated; update the useMemo callback that computes sortedNewsList to
use a copied array and keep the dependency [newsList] unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe025c3 and 79e5a3b.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (299)
  • README.md
  • backend/docs/event.md
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/admission/AdmissionComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/admission/AdmissionComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/ApplicationComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/ApplicationComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/FooterComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/ManifestComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/StylingComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/StylingComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/bmejegy/BmejegyComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/challenge/ChallengeComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/communities/CommunitiesComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/conference/ConferenceComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/countdown/CountdownComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/countdown/CountdownComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/debt/DebtComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/email/EmailComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/errorlog/ErrorLogComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/event/EventComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/event/EventComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/form/FormComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/gallery/GalleryComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/home/HomeComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/home/HomeComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/impressum/ImpressumComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/impressum/ImpressumComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/key/AccessKeyComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/leaderboard/LeaderBoardComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/leaderboard/LeaderBoardComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/CmschUserDetailsService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/LoginService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/PasswordLoginController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/messaging/MessagingComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/news/NewsComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/news/NewsComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/profile/ProfileComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/profile/ProfileComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/proto/ProtoComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/pushnotification/PushNotificationComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/qrfight/QrFightComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/qrfight/QrFightComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/race/RaceComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/race/RaceComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/riddle/RiddleComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/riddle/RiddleComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/script/ScriptComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/serviceaccount/ServiceAccountComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/serviceaccount/ServiceAccountComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/sheets/SheetsComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/staticpage/StaticPageComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/staticpage/StaticPageComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/task/TaskComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/task/TaskComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/team/TeamComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/team/TeamComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/token/TokenComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/token/TokenComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/config/SecurityConfig.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/model/UserEntity.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/service/RateLimiterService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/service/UserService.kt
  • frontend/components.json
  • frontend/index.html
  • frontend/package.json
  • frontend/postcss.config.js
  • frontend/src/App.tsx
  • frontend/src/api/contexts/auth/AuthContext.tsx
  • frontend/src/api/contexts/config/ConfigContext.tsx
  • frontend/src/api/contexts/config/types.ts
  • frontend/src/api/contexts/service/ServiceContext.tsx
  • frontend/src/api/contexts/themeConfig/ThemeConfig.tsx
  • frontend/src/api/hooks/access-key/useAccessKeyMutation.ts
  • frontend/src/api/hooks/access-key/useAccessKeyQuery.ts
  • frontend/src/api/hooks/alias/useAliasChangeMutation.ts
  • frontend/src/api/hooks/auth/useAuthInfo.ts
  • frontend/src/api/hooks/community/useCommunity.ts
  • frontend/src/api/hooks/community/useCommunityList.ts
  • frontend/src/api/hooks/community/useOrganization.ts
  • frontend/src/api/hooks/community/useOrganizationList.ts
  • frontend/src/api/hooks/community/useTinderAnswerSend.ts
  • frontend/src/api/hooks/community/useTinderAnswers.ts
  • frontend/src/api/hooks/community/useTinderCommunity.ts
  • frontend/src/api/hooks/community/useTinderInteractionReset.ts
  • frontend/src/api/hooks/community/useTinderInteractionSend.ts
  • frontend/src/api/hooks/community/useTinderQuestions.ts
  • frontend/src/api/hooks/config/useConfigQuery.ts
  • frontend/src/api/hooks/debt/useDebtQuery.ts
  • frontend/src/api/hooks/developers/useDevelopers.ts
  • frontend/src/api/hooks/event/useEventListQuery.ts
  • frontend/src/api/hooks/event/useEventQuery.ts
  • frontend/src/api/hooks/extra/useExtraPage.tsx
  • frontend/src/api/hooks/form/useFormPage.tsx
  • frontend/src/api/hooks/form/useFormSubmit.tsx
  • frontend/src/api/hooks/gallery/useHomeGallery.ts
  • frontend/src/api/hooks/group-change/useGroupChangeMutation.ts
  • frontend/src/api/hooks/home/useHomeNews.tsx
  • frontend/src/api/hooks/leaderboard/useLeaderBoardQuery.ts
  • frontend/src/api/hooks/location/useLocationQuery.ts
  • frontend/src/api/hooks/news/useNewsListQuery.ts
  • frontend/src/api/hooks/news/useNewsQuery.ts
  • frontend/src/api/hooks/profile/useProfileQuery.ts
  • frontend/src/api/hooks/qr/useQrLevelsQuery.ts
  • frontend/src/api/hooks/race/useFreestyleRaceQuery.ts
  • frontend/src/api/hooks/race/useRaceByTeamQuery.ts
  • frontend/src/api/hooks/race/useRaceQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleDeatilsQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleHintQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleHistoryQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleListQuery.ts
  • frontend/src/api/hooks/riddle/useRiddleSkipMutation.ts
  • frontend/src/api/hooks/riddle/useRiddleSubmitMutation.ts
  • frontend/src/api/hooks/task/useTaskCategoriesQuery.ts
  • frontend/src/api/hooks/task/useTaskFullDetailsQuery.ts
  • frontend/src/api/hooks/task/useTaskSubmissionMutation.ts
  • frontend/src/api/hooks/task/useTasksInCategoryQuery.ts
  • frontend/src/api/hooks/team/actions/useTeamCancelJoin.ts
  • frontend/src/api/hooks/team/actions/useTeamCreate.ts
  • frontend/src/api/hooks/team/actions/useTeamEdit.ts
  • frontend/src/api/hooks/team/actions/useTeamJoin.ts
  • frontend/src/api/hooks/team/actions/useTeamLeave.ts
  • frontend/src/api/hooks/team/queries/useMyTeam.ts
  • frontend/src/api/hooks/team/queries/useTeamDetails.ts
  • frontend/src/api/hooks/team/queries/useTeamList.ts
  • frontend/src/api/hooks/token/useScanTokenMutation.ts
  • frontend/src/api/hooks/token/useTokensQuery.ts
  • frontend/src/api/hooks/useTokenRefresh.ts
  • frontend/src/common-components/BoardStat.tsx
  • frontend/src/common-components/CmschLink.tsx
  • frontend/src/common-components/CollapsableTableRow.tsx
  • frontend/src/common-components/ComponentUnavailable.tsx
  • frontend/src/common-components/ConfirmDialogButton.tsx
  • frontend/src/common-components/CurrentEventCard.tsx
  • frontend/src/common-components/CustomBreadcrumb.tsx
  • frontend/src/common-components/CustomTabButton.tsx
  • frontend/src/common-components/EnableNotifications.tsx
  • frontend/src/common-components/EventIndicator.tsx
  • frontend/src/common-components/LeaderboardTable.tsx
  • frontend/src/common-components/LinkButton.tsx
  • frontend/src/common-components/Loading.tsx
  • frontend/src/common-components/PageStatus.tsx
  • frontend/src/common-components/PresenceAlert.tsx
  • frontend/src/common-components/PulsingDot.tsx
  • frontend/src/common-components/PushNotificationHandler.tsx
  • frontend/src/common-components/SearchBar.tsx
  • frontend/src/common-components/StopItModal.tsx
  • frontend/src/common-components/TokenRarityDisplay.tsx
  • frontend/src/common-components/VotingField.tsx
  • frontend/src/common-components/WalletButton.tsx
  • frontend/src/common-components/Warning.tsx
  • frontend/src/common-components/chakra-md-renderer.tsx
  • frontend/src/common-components/cmsch-ui-renderer.tsx
  • frontend/src/common-components/footer/Footer.tsx
  • frontend/src/common-components/footer/OrganizerLogo.tsx
  • frontend/src/common-components/footer/PartnerLogo.tsx
  • frontend/src/common-components/layout/AppBackground.tsx
  • frontend/src/common-components/layout/CmschContainer.tsx
  • frontend/src/common-components/layout/CmschLayout.tsx
  • frontend/src/common-components/layout/CmschPage.tsx
  • frontend/src/common-components/map/GroupMapContainer.tsx
  • frontend/src/common-components/map/MapContent.tsx
  • frontend/src/common-components/map/MapMarker.tsx
  • frontend/src/common-components/navigation/ColorModeSwitcher.tsx
  • frontend/src/common-components/navigation/Navbar.tsx
  • frontend/src/common-components/navigation/desktop/ChildNavItem.tsx
  • frontend/src/common-components/navigation/desktop/DesktopNav.tsx
  • frontend/src/common-components/navigation/desktop/NavItemNoChildren.tsx
  • frontend/src/common-components/navigation/desktop/NavItemWithChildren.tsx
  • frontend/src/common-components/navigation/mobile/MobileNav.tsx
  • frontend/src/common-components/navigation/mobile/NavItemNoChildren.tsx
  • frontend/src/common-components/navigation/mobile/NavItemWithChildren.tsx
  • frontend/src/components/ui/accordion.tsx
  • frontend/src/components/ui/alert-dialog.tsx
  • frontend/src/components/ui/alert.tsx
  • frontend/src/components/ui/avatar.tsx
  • frontend/src/components/ui/badge.tsx
  • frontend/src/components/ui/breadcrumb.tsx
  • frontend/src/components/ui/button.tsx
  • frontend/src/components/ui/card.tsx
  • frontend/src/components/ui/checkbox.tsx
  • frontend/src/components/ui/dialog.tsx
  • frontend/src/components/ui/dropdown-menu.tsx
  • frontend/src/components/ui/input.tsx
  • frontend/src/components/ui/label.tsx
  • frontend/src/components/ui/popover.tsx
  • frontend/src/components/ui/progress.tsx
  • frontend/src/components/ui/radio-group.tsx
  • frontend/src/components/ui/select.tsx
  • frontend/src/components/ui/separator.tsx
  • frontend/src/components/ui/skeleton.tsx
  • frontend/src/components/ui/switch.tsx
  • frontend/src/components/ui/table.tsx
  • frontend/src/components/ui/tabs.tsx
  • frontend/src/components/ui/textarea.tsx
  • frontend/src/components/ui/toast.tsx
  • frontend/src/components/ui/toaster.tsx
  • frontend/src/components/ui/tooltip.tsx
  • frontend/src/hooks/use-toast.ts
  • frontend/src/index.css
  • frontend/src/index.tsx
  • frontend/src/lib/utils.ts
  • frontend/src/pages/access-key/accessKey.page.tsx
  • frontend/src/pages/communities/community.page.tsx
  • frontend/src/pages/communities/communityList.page.tsx
  • frontend/src/pages/communities/components/CardListItem.tsx
  • frontend/src/pages/communities/components/DataSheet.tsx
  • frontend/src/pages/communities/components/Frame.tsx
  • frontend/src/pages/communities/components/TinderCard.tsx
  • frontend/src/pages/communities/likedCommunityList.page.tsx
  • frontend/src/pages/communities/organization.page.tsx
  • frontend/src/pages/communities/organizationList.page.tsx
  • frontend/src/pages/communities/tinder.page.tsx
  • frontend/src/pages/communities/tinderQuestions.page.tsx
  • frontend/src/pages/communities/tinderRouter.tsx
  • frontend/src/pages/countdown/components/clock.tsx
  • frontend/src/pages/countdown/countdown.page.tsx
  • frontend/src/pages/countdown/countdown.util.tsx
  • frontend/src/pages/debt/components/debt-list-item.tsx
  • frontend/src/pages/debt/debt.page.tsx
  • frontend/src/pages/error/error.page.tsx
  • frontend/src/pages/error/unauthorized.page.tsx
  • frontend/src/pages/events/components/CardListItem.tsx
  • frontend/src/pages/events/components/CurrentEvent.tsx
  • frontend/src/pages/events/components/CustomTab.tsx
  • frontend/src/pages/events/components/EventFilterOption.tsx
  • frontend/src/pages/events/components/EventList.tsx
  • frontend/src/pages/events/components/EventListItem.tsx
  • frontend/src/pages/events/components/EventTags.tsx
  • frontend/src/pages/events/components/event-calendar/CurrentDateBar.tsx
  • frontend/src/pages/events/components/event-calendar/DayCalendar.tsx
  • frontend/src/pages/events/components/event-calendar/EventBox.tsx
  • frontend/src/pages/events/components/event-calendar/HourColumn.tsx
  • frontend/src/pages/events/components/event-calendar/WeekCalendar.tsx
  • frontend/src/pages/events/components/event-calendar/ZoomBar.tsx
  • frontend/src/pages/events/components/event-calendar/utils.ts
  • frontend/src/pages/events/event.page.tsx
  • frontend/src/pages/events/eventCalendar.page.tsx
  • frontend/src/pages/events/eventList.page.tsx
  • frontend/src/pages/events/util/filter.tsx
  • frontend/src/pages/extra/extra.page.tsx
  • frontend/src/pages/form/components/GridField.tsx
  • frontend/src/pages/form/components/GridFieldItem.tsx
  • frontend/src/pages/form/components/autoFormField.tsx
  • frontend/src/pages/form/components/formStatusBadge.tsx
  • frontend/src/pages/form/form.page.tsx
  • frontend/src/pages/home/components/EmbeddedVideo.tsx
  • frontend/src/pages/home/components/HomePageEventList.tsx
  • frontend/src/pages/home/components/HomePageGalleryCarousel.tsx
  • frontend/src/pages/home/components/HomePageNewsList.tsx
  • frontend/src/pages/home/components/ImageCarousel.tsx
  • frontend/src/pages/home/components/Schedule.tsx
  • frontend/src/pages/home/home.page.tsx
  • frontend/src/pages/impressum/components/DeveloperWrapItem.tsx
  • frontend/src/pages/impressum/components/OrganizerSection.tsx
  • frontend/src/pages/impressum/components/OrganizerWrapItem.tsx
  • frontend/src/pages/impressum/impressum.page.tsx
  • frontend/src/pages/impressum/util/arrayFunctions.ts
  • frontend/src/pages/index/index.page.tsx
  • frontend/src/pages/leaderboard/leaderboard.page.tsx
  • frontend/src/pages/leaderboard/leaderboardByCategory.page.tsx
  • frontend/src/pages/leaderboard/leaderboardByUserOrGroup.page.tsx
  • frontend/src/pages/loading/loading.page.tsx
  • frontend/src/pages/login/login.page.tsx
  • frontend/src/pages/login/resetPassword.page.tsx
  • frontend/src/pages/map/map.page.tsx
  • frontend/src/pages/news/components/News.tsx
  • frontend/src/pages/news/components/NewsList.tsx
  • frontend/src/pages/news/components/NewsListItem.tsx
  • frontend/src/pages/news/news.page.tsx
  • frontend/src/pages/news/newsList.page.tsx
  • frontend/src/pages/news/util/sortNewsList.ts
  • frontend/src/pages/profile/components/GroupLeaderContactList.tsx
  • frontend/src/pages/profile/components/Popup.tsx
  • frontend/src/pages/profile/components/ProfileQR.tsx
  • frontend/src/pages/profile/profile.aliasChange.page.tsx
  • frontend/src/pages/profile/profile.groupChange.page.tsx
  • frontend/src/pages/profile/profile.page.tsx
  • frontend/src/pages/profile/util/percentFunctions.ts
  • frontend/src/pages/qr-fight/components/DataDisplayWrapper.tsx
  • frontend/src/pages/qr-fight/components/LevelDataDisplay.tsx
  • frontend/src/pages/qr-fight/components/LevelStatusBadge.tsx
  • frontend/src/pages/qr-fight/components/TotemDataDisplay.tsx
  • frontend/src/pages/qr-fight/components/TotemField.tsx
  • frontend/src/pages/qr-fight/components/TowerDataDisplay.tsx
  • frontend/src/pages/qr-fight/components/TowerField.tsx
  • frontend/src/pages/qr-fight/components/TreasureDataDisplayWrapper.tsx
  • frontend/src/pages/qr-fight/qrLevels.page.tsx
  • frontend/src/pages/race/components/RaceBoard.tsx
  • frontend/src/pages/race/freestyleRace.page.tsx
  • frontend/src/pages/race/race.page.tsx
  • frontend/src/pages/race/raceByTeam.page.tsx
  • frontend/src/pages/riddle/components/RiddleCategoryListItem.tsx
  • frontend/src/pages/riddle/components/RiddleListItem.tsx
  • frontend/src/pages/riddle/components/SpoilerText.tsx
  • frontend/src/pages/riddle/riddle.page.tsx
💤 Files with no reviewable changes (2)
  • backend/docs/event.md
  • frontend/src/pages/events/components/CustomTab.tsx

Comment on lines +49 to +50
open fun findByEmail(email: String): Optional<UserEntity> {
return users.findByEmail(email)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== UserEntity email field and constraints =="
rg -n "class UserEntity|email|@Column|unique" --type kt backend/src/main/kotlin

echo
echo "== SQL/migration constraints involving user email =="
rg -n "email|unique|index|constraint" --type sql backend || true

echo
echo "== Repository methods for email lookups =="
rg -n "interface .*UserRepository|findByEmail\(" --type kt backend/src/main/kotlin

echo
echo "== Normalization patterns around email persistence/lookup =="
rg -n "lowercase\(|trim\(|findByEmail\(|email\s*=|setEmail" --type kt backend/src/main/kotlin

Repository: kir-dev/cmsch

Length of output: 145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Finding Kotlin files with UserEntity definition =="
fd -e kt . backend/src/main/kotlin | head -20

echo
echo "== UserEntity email field and constraints =="
rg -g "*.kt" "class UserEntity|email|@Column|unique" backend/src/main/kotlin

echo
echo "== SQL/migration files =="
fd -e sql backend

echo
echo "== UserRepository findByEmail =="
rg -g "*.kt" "findByEmail" backend/src/main/kotlin

echo
echo "== Email normalization patterns =="
rg -g "*.kt" "lowercase|trim|\.email" backend/src/main/kotlin -A 2 -B 2

Repository: kir-dev/cmsch

Length of output: 50370


🏁 Script executed:

#!/bin/bash

echo "=== UserEntity full definition ==="
rg -g "*.kt" -B5 -A30 "class UserEntity" backend/src/main/kotlin/hu/bme/sch/cmsch/model/UserEntity.kt | head -80

echo
echo "=== Email field definition in UserEntity ==="
rg -g "*.kt" -B2 -A2 "var email:" backend/src/main/kotlin/hu/bme/sch/cmsch/model/UserEntity.kt

echo
echo "=== Index definitions for UserEntity ==="
rg -g "*.kt" -B5 "Index\|@Table" backend/src/main/kotlin/hu/bme/sch/cmsch/model/UserEntity.kt | grep -A10 -B10 "index"

echo
echo "=== Login/registration email handling ==="
rg -g "*.kt" "findByEmail|email.lowercase|email.trim|email =" backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/ -B2 -A2

echo
echo "=== Email normalization in UserService ==="
rg -g "*.kt" "lowercase|trim" backend/src/main/kotlin/hu/bme/sch/cmsch/service/UserService.kt -B2 -A2

Repository: kir-dev/cmsch

Length of output: 7766


🏁 Script executed:

#!/bin/bash

echo "=== Check if email field has any `@Column` unique constraint ==="
rg -g "*.kt" -B5 "var email:" backend/src/main/kotlin/hu/bme/sch/cmsch/model/UserEntity.kt

echo
echo "=== Verify no normalization in registration or auth flows ==="
rg -g "*.kt" "\.trim\(\)|\.lowercase\(\)|\.uppercase\(\)" backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/ -c

echo
echo "=== Check PasswordLoginController registration flow ==="
rg -g "*.kt" -B10 -A10 "email = request.email" backend/src/main/kotlin/hu/bme/sch/cmsch/component/login/PasswordLoginController.kt

Repository: kir-dev/cmsch

Length of output: 1353


Email uniqueness is not enforced at the database level, and email canonicalization is missing from registration and auth lookup flows.

The email field in UserEntity has only a non-unique index and no @Column(unique=true) constraint. More critically, no trim/lowercase normalization is applied in registration (PasswordLoginController assigns email = request.email directly) or during lookups (findByEmail() is case-sensitive). This creates several risks:

  • Different email cases (e.g., Test@example.com vs test@example.com) are treated as separate accounts during signup
  • Lookups fail for case-mismatched emails
  • GroupToUserMappingEntity already uses findByEmailIgnoreCase(), indicating case sensitivity is a known concern elsewhere

Add a unique constraint on the email column, establish a canonicalization policy (trim + lowercase), and apply it consistently in PasswordLoginController registration, LoginService auth flows, and CmschUserDetailsService lookups to ensure predictable auth behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/kotlin/hu/bme/sch/cmsch/service/UserService.kt` around lines
49 - 50, Add a DB-level unique constraint to the email column on UserEntity
(e.g., `@Column`(unique=true) / migration) and enforce a canonicalization policy
(trim + lowercase) everywhere: normalize incoming emails in
PasswordLoginController during registration, normalize auth lookup parameters in
LoginService and CmschUserDetailsService before calling the repository, and
update repository usage to case-insensitive lookups if needed (e.g., use
users.findByEmailIgnoreCase(...) or ensure normalized values are
stored/queried). Also consider adding a PrePersist/PreUpdate hook on UserEntity
to ensure stored emails are always canonicalized and add a migration to dedupe
existing differing-case emails before applying the unique constraint.

Comment on lines 3 to 18
export const parseTopMessage = (topMessage: string) => {
const splits = topMessage.split(/(\[\[.*?\]\])/g)
const coloredComponents = splits.map((partial) => {
const coloredComponents = splits.map((partial, idx) => {
if (partial.match(/(\[\[.*?\]\])/)) {
const coloredText = partial.substring(2, partial.length - 2)
const color = useBrandColor()
return (
<chakra.span id={coloredText} color={useBrandColor(400, 400)}>
<span key={idx} id={coloredText} style={{ color }}>
{coloredText}
</chakra.span>
</span>
)
} else {
return <span>{partial}</span>
return <span key={idx}>{partial}</span>
}
})
return <>{coloredComponents}</>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Invalid hook call inside a non-component function and conditional block.

useBrandColor() is a React hook being called inside a .map() callback and within a conditional block at line 8. This violates React's Rules of Hooks—hooks must be called at the top level of React function components, not inside loops, conditions, or nested functions.

Additionally, parseTopMessage is a regular function (not a React component), so hooks cannot be used within it at all.

Since useBrandColor() simply returns 'var(--primary)' (a static CSS variable reference), you can inline the value directly.

🐛 Proposed fix: inline the CSS variable
-import { useBrandColor } from '@/util/core-functions.util.ts'
-
 export const parseTopMessage = (topMessage: string) => {
   const splits = topMessage.split(/(\[\[.*?\]\])/g)
   const coloredComponents = splits.map((partial, idx) => {
     if (partial.match(/(\[\[.*?\]\])/)) {
       const coloredText = partial.substring(2, partial.length - 2)
-      const color = useBrandColor()
       return (
-        <span key={idx} id={coloredText} style={{ color }}>
+        <span key={idx} id={coloredText} style={{ color: 'var(--primary)' }}>
           {coloredText}
         </span>
       )
     } else {
       return <span key={idx}>{partial}</span>
     }
   })
   return <>{coloredComponents}</>
 }
🧰 Tools
🪛 Biome (2.4.4)

[error] 8-8: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

(lint/correctness/useHookAtTopLevel)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/countdown/countdown.util.tsx` around lines 3 - 18,
parseTopMessage illegally calls the React hook useBrandColor() inside the .map()
callback (and inside a conditional) which violates the Rules of Hooks; fix by
removing the hook call from parseTopMessage and using a plain CSS variable
string instead (e.g., const color = 'var(--primary)') defined once at the top of
parseTopMessage (or accept color as an argument to parseTopMessage) and then use
that color variable in the span render for the coloredText; update references to
useBrandColor() in parseTopMessage to this plain value so no hooks are invoked
inside the function.

Comment on lines +48 to +51
} catch (error) {
console.error(error)
reportError(error)
toast({ title: 'Hiba', description: 'Hálózati hiba történt', variant: 'destructive' })
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check all reportError usages in the codebase
echo "=== All reportError usages ==="
rg -n "reportError" --type=ts --type=tsx -g '!node_modules' | head -40

echo -e "\n=== Checking resetPassword.page.tsx file ==="
fd "resetPassword.page.tsx" --type f | xargs -I {} sh -c 'echo "File: {}"; head -60 "{}"'

Repository: kir-dev/cmsch

Length of output: 2444


🏁 Script executed:

#!/bin/bash
# Search for reportError more broadly
echo "=== Searching for reportError definitions/exports ==="
rg "reportError" -B 2 -A 2 | head -50

echo -e "\n=== Searching specifically in utility and service files ==="
rg "reportError" frontend/src/util/ frontend/src/services/ 2>/dev/null || echo "No matches in util/services"

echo -e "\n=== Checking if it's a window global ==="
rg "window\.reportError" 

echo -e "\n=== Full content of resetPassword.page.tsx to verify imports ==="
cat frontend/src/pages/login/resetPassword.page.tsx | head -70

Repository: kir-dev/cmsch

Length of output: 5335


🏁 Script executed:

#!/bin/bash
# Get more context about reportError in index.html
grep -n "reportError\|processAndReportError" frontend/index.html -B 5 -A 10

Repository: kir-dev/cmsch

Length of output: 1483


Use processAndReportError(error) instead of reportError(error).

reportError is a global function defined in frontend/index.html that expects parameters (message, stack), but the code passes an error object. The correct global function for error objects is processAndReportError(error), which is already defined in the same index.html and properly extracts message and stack from the error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/login/resetPassword.page.tsx` around lines 48 - 51, In the
catch block that currently calls reportError(error) (in the reset password flow
within resetPassword.page.tsx), replace the reportError call with
processAndReportError(error) so the global error helper receives an Error object
and extracts message/stack correctly; keep console.error(error) and the toast
unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant