Feature/frontend redesign#16
Conversation
…imports for instant load
…tion and 3D ticket
…ilter only active events
…cross all layouts and pages
📝 WalkthroughWalkthroughAdds i18n (EN/UK) and dark theme boot, replaces many pages/components with translated, animated variants, introduces a full multi-step tournament registration flow with validation and preview, adds UI primitives/utilities, updates routing, mock data, app bootstrap (toasts, sockets), and updates tests. ChangesFrontend overhaul and new registration flow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
✨ Finishing Touches📝 Generate docstrings
|
AnnPoshtak
left a comment
There was a problem hiding this comment.
Такс. По перше все виглядає мега гарна. Дуже класний єфект з рандомним турніром. Сторінки почали швидше вантажитися.
Але не на всі сторінки є стилі. І деякі дивно вигяладають, бо там немає тексту, а лише hero.title, hero.description.
Немає перекладу та темної теми на сторінку отримання ролі організатора, профіля.
На деяких сторінкаї хедер не видно, якщо не пролистати вниз
По коду у мене критичних зауважень немає, все круто.
Але також зараз тоді треба дуже швидко переписати нові сторінки під такий стиль, додати їм перклад, написати їм темну тему.
А також тепер треба переписати та додати нові тести через те що зявлися інтернацоналізація та темна тема.
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
|
✅ Created PR with unit tests: #17 |
|
Прийду додому і гляну все, як виглядає. Код наче нормальний |
Fundi1330
left a comment
There was a problem hiding this comment.
Нормально в цілому. Але я зараз не вдома, тому не запуска пр
| VITE_FIREBASE_API_KEY="AIzaSyA0yV1uvA2ciTRYyed8XL9HgBbhnyouGNE" | ||
| VITE_FIREBASE_AUTH_DOMAIN="star-8a692.firebaseapp.com" | ||
| VITE_FIREBASE_PROJECT_ID="star-8a692" | ||
| VITE_FIREBASE_STORAGE_BUCKET="star-8a692.firebasestorage.app" | ||
| VITE_FIREBASE_MESSAGING_SENDER_ID="221685619212" | ||
| VITE_FIREBASE_APP_ID="1:221685619212:web:06d14cefaa600a7e3dec29" | ||
| VITE_FIREBASE_MEASUREMENT_ID="G-3J22BCL1QQ" |
| <!-- 1. Скрипт миттєвої теми --> | ||
| <script> | ||
| try { | ||
| if ( | ||
| localStorage.getItem("theme") === "dark" || | ||
| (!localStorage.getItem("theme") && | ||
| window.matchMedia("(prefers-color-scheme: dark)") | ||
| .matches) | ||
| ) { | ||
| document.documentElement.classList.add("dark"); | ||
| } | ||
| } catch (_) {} | ||
| </script> | ||
|
|
||
| <!-- 2. Хардкодимо фон, щоб він малювався МИТТЄВО до завантаження CSS --> |
There was a problem hiding this comment.
Краще коментарі залишати англійською
| toast.error( | ||
| "Проблеми з сервером :(. Сповіщення тимчасово не працюють", | ||
| { id: "socket-error" }, | ||
| ); |
There was a problem hiding this comment.
Чого тости не підгружаються з локалізації
| <span> | ||
| {bgText} ★ {bgText} ★ | ||
| </span> | ||
| <span> | ||
| {bgText} ★ {bgText} ★ | ||
| </span> | ||
| <span> | ||
| {bgText} ★ {bgText} ★ | ||
| </span> |
There was a problem hiding this comment.
Краще не використовувати такі юнікод символи. Заміни їх на іконки
There was a problem hiding this comment.
Зауваження все ще залишається в силі
|
Не розумію прикола випадкового турніру, але хай буде вже, якщо воно готове. Також треба буде переписати слайдер турнірів під якусь бібліотеку для кращої стабільності. Ну і доробити решту сторінок, щоб інтернаціоналізація була і там |
…nding panel alignment
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@AnnPoshtak, можеш підправити тести, щоб вони проходили? |
Так, звичайно. Я майже це зробила, там зі скрін тестами біда |
AnnPoshtak
left a comment
There was a problem hiding this comment.
Все чудвово, можна мерджити. Єдине що, кнопка мови скакає і на деяких сторінках не видно текст у режимі темної теми. але це можна вже і у деві пофіксити
2026-05-11.18-32-05.mp4
| return ( | ||
| <div | ||
| role="group" | ||
| aria-label="Вибір мови" |
There was a problem hiding this comment.
Оце теж мабуть має перекладатися
| import { Link } from "react-router-dom"; | ||
| import { useTranslation } from "react-i18next"; | ||
|
|
||
| const PLATFORM_LINKS = [ |
There was a problem hiding this comment.
Думаю краще це винести в конфіг файла, наприклад app_config.json. Так все буде в однмоу місці і буде зручно налаштовувати додаток
| const NAV_ITEMS = [ | ||
| { path: "/tournaments", key: "nav.tournaments" }, | ||
| { path: "/about-us", key: "nav.about" }, | ||
| { path: "/support", key: "nav.support" }, | ||
| { path: "/contact", key: "nav.contact" }, | ||
| { path: "/news", key: "nav.news" }, // Додано новий маршрут з гілки dev | ||
| ]; | ||
|
|
There was a problem hiding this comment.
Те саме. Краще винести в конфіг
|
|
||
| {user?.uid ? ( | ||
| <div onClick={() => setIsMobileMenuOpen(false)}> | ||
| {/* Додано userRoles та isMobile з гілки dev */} |
There was a problem hiding this comment.
Прошу коменти писати англійською. Переклади усі, будь ласка
| circularText: t( | ||
| "hero.mascot.circular", | ||
| "★ЗНАЙДИ КОМАНДУ ★ ПРОЯВИ СЕБЕ ", | ||
| ), | ||
| lottieSrc: "/star.json", |
There was a problem hiding this comment.
Так само, зірочки тут мають бути або іконкою, або їх не має бути
| Telegram: () => ( | ||
| <svg | ||
| width="16" | ||
| height="16" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| strokeWidth="2" | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| > | ||
| <line x1="22" y1="2" x2="11" y2="13" /> | ||
| <polygon points="22 2 15 22 11 13 2 9 22 2" /> | ||
| </svg> | ||
| ), |
There was a problem hiding this comment.
Як на мене всі ці іконки можна імпортувати з якоїсь бібліотеки, як-от remix-icons, а не хардкодити
| {t("step3.title", "Підтвердження")} | ||
| </h1> | ||
| <p className="text-[15px] text-text-muted font-medium mb-6 transition-colors"> | ||
| {t("step3.subtitle", "Перевірте дані перед реєстрацією.")} | ||
| </p> | ||
|
|
||
| <div className="bg-bg-card border-2 border-border rounded-[24px] overflow-hidden mb-5 transition-colors shadow-sm"> | ||
| <div className="bg-indigo-950 px-7 pt-7 pb-6 text-white relative overflow-hidden"> | ||
| <div className="absolute right-[-10px] bottom-[-20px] font-quicksand text-[60px] font-extrabold text-white/[0.03] tracking-[-0.03em] pointer-events-none select-none uppercase"> | ||
| UGalaxy | ||
| </div> | ||
| <div className="text-[10px] font-bold tracking-[0.12em] uppercase opacity-60 mb-1.5 font-quicksand relative z-10"> | ||
| {cfg.title} | ||
| </div> | ||
| <div className="font-quicksand font-extrabold text-[24px] tracking-[-0.01em] leading-tight relative z-10"> | ||
| {formValues.teamName || capName} | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="px-7 py-2"> | ||
| <div className="py-4 border-b border-border last:border-b-0 transition-colors"> | ||
| <div className="flex items-center gap-3 mb-1"> | ||
| <div className="text-primary"> | ||
| <Icon.User /> | ||
| </div> | ||
| <div className="text-[11px] font-bold tracking-[0.08em] uppercase text-text-muted font-quicksand"> | ||
| {isSolo | ||
| ? t("step3.labels.player", "Гравець") | ||
| : t("step3.labels.captain", "Капітан")} | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
Так само не зрозуміло, чому тут є альтернативний переклад
| ) { | ||
| ctx.addIssue({ | ||
| code: z.ZodIssueCode.custom, | ||
| message: t("validation.teamNameRequired", "Введіть назву команди"), |
There was a problem hiding this comment.
Чому в одному місці ключі перекладу у lower_snake_case, а тут camelCase?
| { id: "draft", dotColor: "bg-amber-500" }, | ||
| { id: "registration", dotColor: "bg-green-500" }, | ||
| { id: "running", dotColor: "bg-pink-accent" }, | ||
| { id: "finished", dotColor: "bg-text-muted" }, |
There was a problem hiding this comment.
Оце можна в конфіг запихнути. Можеш глянути app_config.json, там вже є щось подібне
|
Можеш сам переклад, де його немає, завайбкодити. Так буде швидше |
|
А чому для української і англійської шрифти різні? |
Бо треба знайти альтернативний шрифт, цей не підтримує кирилицю, але я не шукав поки |
…ucide-react icons
There was a problem hiding this comment.
Actionable comments posted: 31
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
frontend/src/pages/Auth/AuthPage.tsx (1)
23-41:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpdate Zod schema to use v4 unified error customization syntax.
The current schema uses Zod v3 syntax for error messages (passing strings directly to
.email()and.min()), which is deprecated in Zod v4. The code works but should be updated. At least three instances exist: two infrontend/src/pages/Auth/AuthPage.tsx(lines 27–28) and one infrontend/src/pages/Auth/ForgotPassword.tsx(line 18).Replace the old syntax with Zod v4's
errorparameter:🔧 Proposed fix for Zod v4 syntax
- email: z.string().email("errors.email_invalid"), - password: z.string().min(8, "errors.pass_short"), + email: z.string().email({ error: "errors.email_invalid" }), + password: z.string().min(8, { error: "errors.pass_short" }),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/Auth/AuthPage.tsx` around lines 23 - 41, The Zod schema uses deprecated v3 error syntax; update the validators to Zod v4's error option object: change email(...) to email({ message: "errors.email_invalid" }) and password.min(8, "errors.pass_short") to password.min(8, { message: "errors.pass_short" }); keep the superRefine custom issue for displayName as-is. Apply the same update for the email validator in frontend/src/pages/Auth/ForgotPassword.tsx (the email z.string() there) so all three instances use the { message: ... } form.frontend/src/App.tsx (1)
86-97:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHarden auth-token socket bootstrap against async races and token fetch failures.
This callback can create a stale socket when auth state changes quickly. The
getIdToken()call at line 88 lacks error handling—if token fetch fails, the promise rejection will crash the callback and leave the socket disconnected. Additionally, without a run-guard, if the auth state changes whilegetIdToken()is pending, the old callback's resolved token could overwrite a newer socket connection.Add a run ID counter to guard against stale callbacks, wrap the token fetch in
try/catch, and check the run ID before connecting to ensure only the latest auth state's socket is used.💡 Suggested fix
useEffect(() => { let currentSocket: Socket | null = null; + let authRunId = 0; const unsubscribeAuth = onIdTokenChanged(auth, async (user) => { + const runId = ++authRunId; if (currentSocket) { currentSocket.disconnect(); + currentSocket = null; setSocket(null); } if (user) { - const token = await user.getIdToken(); - currentSocket = io(import.meta.env.VITE_SOCKETIO_SERVER_URL, { - auth: { token }, - reconnectionDelay: 5000, - }); + try { + const token = await user.getIdToken(); + if (runId !== authRunId) return; + currentSocket = io(import.meta.env.VITE_SOCKETIO_SERVER_URL, { + auth: { token }, + reconnectionDelay: 5000, + }); + } catch { + return; + } currentSocket.on("connect_error", () => { // ... }); currentSocket.on("connect", () => { // ... }); setSocket(currentSocket); } }); return () => { + authRunId++; unsubscribeAuth(); if (currentSocket) currentSocket.disconnect(); }; }, [t]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/App.tsx` around lines 86 - 97, The onIdTokenChanged callback can race and crash; introduce a local run counter (e.g., authRunId incremented each callback) and capture the current runId at start of the async handler to ignore stale completions, wrap the await user.getIdToken() in try/catch to handle/recover from token fetch failures (log or return early without leaving socket disconnected), and before creating a new socket with io(...) check that the captured runId still matches the latest counter and that the auth state hasn't changed; also ensure setSocket and currentSocket manipulation happen only when the runId is still current to avoid overwriting a newer socket.
♻️ Duplicate comments (1)
frontend/src/pages/Auth/ForgotPassword.tsx (1)
17-19:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpdate Zod schema to use v4 unified error customization syntax.
Same issue as in
AuthPage.tsx: the schema uses deprecated Zod v3 syntax for error messages.🔧 Proposed fix for Zod v4 syntax
const resetSchema = z.object({ - email: z.string().email("errors.email_invalid"), + email: z.string().email({ + error: () => "errors.email_invalid" + }), });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/Auth/ForgotPassword.tsx` around lines 17 - 19, The Zod schema resetSchema uses deprecated v3-style error strings; update the email validator to Zod v4 unified error syntax by replacing the positional string with an options object (set message:"errors.email_invalid") on the z.string().email() call so resetSchema = z.object({ email: z.string().email({ message: "errors.email_invalid" }) }); ensure any other validators in this file follow the same pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/package.json`:
- Around line 25-46: Update the i18next-http-backend dependency in package.json
from "i18next-http-backend": "^3.0.4" to at least "^3.0.5" to pull in the patch
that fixes the path traversal/URL injection issue; run your package manager
(npm/yarn/pnpm) to update the lockfile (package-lock.json / yarn.lock /
pnpm-lock.yaml) and ensure CI installs the updated version before merging.
In `@frontend/src/App.tsx`:
- Around line 83-84: The socket effect currently depends on the translation
function `t`, causing socket tear-down/reconnect on language changes; decouple
them by storing `t` in a ref (e.g., `const tRef = useRef(t)`) and updating
`tRef.current = t` in a separate effect, then remove `t` from the socket
`useEffect` dependency array so `useEffect(() => { let currentSocket... },
[...deps without t])` manages socket lifecycle stably; update any places inside
the socket handlers to call `tRef.current(...)` instead of `t(...)`; apply the
same ref approach to the other effect mentioned (the one at line ~141) that
currently depends on `t`.
In `@frontend/src/components/Header.tsx`:
- Line 126: The mobile menu button in Header.tsx currently removes the keyboard
focus indicator via "focus:outline-none"; restore an accessible focus style by
removing that utility and adding a visible focus replacement (e.g., Tailwind
focus-visible:ring or focus:ring and focus-visible:ring-offset classes) on the
button element so keyboard users see a clear focus outline when interacting with
the mobile menu button.
- Around line 175-180: The mobile menu's parent wrapper currently closes the
menu on any click because of the div with onClick={() =>
setIsMobileMenuOpen(false)}, which prevents interacting with ProfileDropdown;
fix this by preventing event propagation for the dropdown area or moving the
close handler to the backdrop/explicit close control: replace the wrapping div
around ProfileDropdown with a container that stops propagation (e.g., onClick={e
=> e.stopPropagation()}) or remove the onClick entirely and attach
setIsMobileMenuOpen(false) to the backdrop/close button instead; update
references around setIsMobileMenuOpen and the ProfileDropdown usage in
Header.tsx accordingly.
In `@frontend/src/components/layouts/AuthLayout.tsx`:
- Line 10: The import in AuthLayout.tsx uses a brittle relative path for
starAnimation (import starAnimation from "../../../public/star.json"); move
star.json into src/assets and update the import to reference the new location or
use the project alias (e.g., "@/assets/star.json") so the component imports
starAnimation from src/assets via the alias; update any build/tsconfig paths if
needed and ensure AuthLayout.tsx's import statement references the new path.
In `@frontend/src/components/NotificationsDropdown.tsx`:
- Around line 19-21: The NotificationsDropdown trigger button is missing
accessible state and focus-visible styling; update the button rendered in
NotificationsDropdown.tsx (the element using onClick={toggleMenu}) to include
aria-expanded={isOpen} and aria-controls pointing to the dropdown panel's id,
replace the hardcoded label with the localized string used elsewhere (e.g.
t('notifications.buttonLabel') or the component's i18n key), and remove the
unconditional focus:outline-none in favor of a visible keyboard focus style such
as focus-visible:ring or focus-visible:outline to preserve keyboard focus
indication; apply the same aria and focus-visible changes to the other trigger
button in the file (the one around line 45).
In `@frontend/src/components/ScrollToTop.tsx`:
- Around line 7-9: The current useEffect in ScrollToTop calls window.scrollTo({
top: 0, behavior: "smooth" }) on pathname changes causing slow smooth scroll;
change it to an instant scroll by using window.scrollTo({ top: 0, behavior:
"auto" }) or simply window.scrollTo(0, 0) inside the same useEffect that depends
on pathname (refer to the useEffect hook and the pathname variable) so route
changes jump immediately to the top.
In `@frontend/src/components/ui/LanguageSwitcher.tsx`:
- Around line 36-40: The motion.div in LanguageSwitcher uses a fixed layoutId
("activeLangBg") which causes Framer Motion conflicts when multiple
LanguageSwitcher instances render; update the component to generate or accept a
unique id (e.g., via React's useId() or an instanceId prop) and interpolate it
into the layoutId (e.g., `activeLangBg-${id}`) so each LanguageSwitcher instance
supplies a unique layoutId for the motion.div to avoid shared animation
contexts.
In `@frontend/src/components/ui/Ticket3D.tsx`:
- Around line 168-188: The key for the animated member rows uses the unstable
array index in members.map (key={i}) which can break reconciliation/animations;
change the key on the motion.div to a stable identifier from the member object
such as m.name or a composite like `${m.name}-${i}` (use members.map((m, i) =>
...) and replace key={i} with key={m.name} or key={`${m.name}-${i}`}) so React
can correctly track additions/removals during edits.
In `@frontend/src/hooks/useClickOutside.ts`:
- Around line 7-22: The effect re-registers listeners whenever the handler
identity changes; instead capture the latest handler in a stable ref (e.g.,
create handlerRef, update handlerRef.current = handler inside an effect) and
have listener call handlerRef.current(event) so the useEffect for
adding/removing listeners depends only on ref (and not handler), or—if you can
target React 19.2—replace handler with useEffectEvent to get a stable event
callback and keep the effect dependency minimal; update the hook so useEffect
registers/removes listener only when ref changes and the listener delegates to
the stable handlerRef/useEffectEvent.
In `@frontend/src/locales/en/common.json`:
- Line 49: The aria-label string for the language switcher is grammatically
incorrect; update the "aria_label" value inside the "language_switcher" object
in frontend/src/locales/en/common.json (the "language_switcher" -> "aria_label"
key) to a correct phrase such as "Choose language" or "Language selection" so
the accessibility label reads properly.
In `@frontend/src/locales/en/tournaments.json`:
- Around line 19-22: The English locale file currently defines unnecessary
Slavic plural keys (tournaments_zero, tournaments_few, tournaments_many);
replace these with a single plural entry using only "one" and "other" (e.g., a
"tournaments" key with nested "one" and "other" forms) so i18next can handle
English plurals, and update callers to use i18next's pluralization API like
t('results.tournaments', { count: n }) where relevant; target the tournament
keys in frontend/src/locales/en/tournaments.json (remove
tournaments_zero/tournaments_few/tournaments_many and keep tournaments_one plus
tournaments_other under a single "tournaments" namespace).
In `@frontend/src/pages/Auth/AuthPage.tsx`:
- Line 181: The rendered error messages currently cast
errors.displayName.message (and likewise errors.*.message at the other
occurrences) directly to string which is unsafe; update the rendering to guard
the value before calling t() by checking that errors.displayName?.message is a
non-empty string (e.g. typeof message === "string" && message.trim()) and
otherwise pass a fallback key or safe string to t(); apply the same change for
the other occurrences referenced (the other errors.*.message uses around the
t(...) calls on the same file).
- Line 289: Wrap the direct call to signInWithPopup(auth, google) in a try/catch
or promise .catch inside the AuthPage onClick handler so sign-in failures (popup
blocked, user cancelled, network errors) are handled; locate the onClick that
currently calls signInWithPopup and replace it with an async handler (or attach
.catch) that logs the error and surfaces a user-friendly message (e.g., set an
error state or call showToast) using existing UI error-handling utilities so the
promise rejection is no longer unhandled.
In `@frontend/src/pages/Contact/Contact.tsx`:
- Around line 174-176: The footer text in the Contact component is hardcoded;
replace the literal "Star for Life Ukraine © 2026" in the div with a localized
string (e.g., use the existing i18n system: import and use useTranslation in the
Contact component and replace the content with t('contact.footerCopyright', {
year: new Date().getFullYear() }) or similar), and add the corresponding key
(contact.footerCopyright) to your locale files for English and Ukrainian (use a
template like "Star for Life Ukraine © {{year}}"). Ensure you update the JSX in
the div with className "mt-12 pt-6 border-t border-white/10 text-slate-400
text-sm italic" to render the t(...) result.
In `@frontend/src/pages/FaqPage/FaqPage.tsx`:
- Line 11: The faqIndexes array is hardcoded to 10; change it to derive its
length from the translations or a config and validate keys exist: fetch the FAQ
items via react-i18next (e.g. const items = i18n.t('faq.items', { returnObjects:
true }) || []) and then build faqIndexes = items.map((_, i) => i + 1) (or import
FAQ_CONFIG.itemCount if you prefer a config approach), and add a simple guard
that skips or logs missing question/answer entries when rendering (reference
faqIndexes, 'faq.items' translation key and any render function/component that
maps over faqIndexes).
In `@frontend/src/pages/Home/components/TournamentSlider.tsx`:
- Around line 228-237: The left navigation button in the TournamentSlider
component is an icon-only control and lacks accessible labeling and an explicit
button type; update the <button> that calls handlePrev (the one rendering
ArrowLeft) to include type="button" and a descriptive aria-label (e.g.,
aria-label="Previous tournament"), and make the same change for the matching
right navigation button (the one rendering ArrowRight/handleNext) so both are
keyboard/screen-reader accessible.
- Around line 118-177: The startRoulette function schedules an interval and
requestAnimationFrame but doesn't track or clear them on unmount; add two refs
rouletteIntervalRef and rouletteRafRef, assign the interval ID returned by
setInterval to rouletteIntervalRef.current and assign the requestAnimationFrame
id returned by requestAnimationFrame to rouletteRafRef.current, and ensure you
clearInterval(rouletteIntervalRef.current) and
cancelAnimationFrame(rouletteRafRef.current) whenever you stop the roulette
(after clearing the interval and when the final animation completes) and inside
a useEffect cleanup to avoid stale updates; also handle the early exit when
cards.length === 0 by clearing any timers and calling setIsSpinning(false) so
the component doesn't stay stuck spinning.
In `@frontend/src/pages/Home/Home.tsx`:
- Around line 8-15: The NotoEmoji image has no error/loading handling and uses a
non-descriptive alt; update the NotoEmoji component to accept an optional
descriptive label prop (e.g., altText) or derive a human-readable alt from the
code, render a loading state (e.g., small spinner or placeholder) while the
image loads, and add an onError handler to swap the src to a local/fallback SVG
or render a fallback element when the CDN fails; ensure the img uses the
descriptive alt (or aria-hidden if purely decorative). Locate the NotoEmoji
function and update its props, add onLoad/onError handlers and fallback logic,
and then update any calls to NotoEmoji/badge usages to pass the new
altText/label where appropriate.
In `@frontend/src/pages/Page404/Page404.tsx`:
- Around line 59-68: The continuous sticker animation in the useEffect that
creates the interval should respect the user's reduced-motion preference: detect
prefers-reduced-motion (e.g., via window.matchMedia('(prefers-reduced-motion:
reduce)').matches or a custom hook) before starting the interval in the effect
that calls setStickers and generateRandomSticker, and skip creating the interval
entirely when reduced motion is requested (returning a no-op cleanup). Apply the
same guard to the other effect block (the one around setStickers at the 77-84
area) so neither interval-driven sticker churn nor repeated updates run for
users who opt out of animations.
- Around line 7-11: The emoji <img> in Page404 (the element rendering
src={`https://fonts.gstatic.com/s/e/notoemoji/latest/${code}/emoji.svg`} with
className="w-6 h-6 select-none") is decorative and should be hidden from
assistive tech; remove the meaningful alt text and mark it as presentation by
setting an empty alt and adding aria-hidden="true" (and optionally
role="presentation") so screen readers do not announce it.
In `@frontend/src/pages/RegistrationPage/components/Icons.tsx`:
- Around line 16-31: Replace the repetitive explicit property assignments in the
Icon object with ES6 property shorthand for properties where the key and value
identifiers are identical (e.g., change "Check: Check" to "Check"). Keep
explicit mappings where the key differs from the imported identifier (e.g.,
"Warning: AlertTriangle", "Team: Users", "Solo: User", "Org: Home", "Telegram:
Send") so only entries like Check, ChevronRight, ChevronLeft, ChevronDown, Plus,
X, Info, User, and Users use shorthand in the exported Icon object.
In `@frontend/src/pages/RegistrationPage/components/StepGeneral.tsx`:
- Around line 36-39: handleNextClick currently opens the accordion when
errors.customFields exists but still calls onNext(), allowing navigation despite
validation errors; update handleNextClick so that if errors.customFields is
truthy and optOpen is false you call setOptOpen(true) and immediately return
(preventing onNext from running), otherwise proceed to call onNext(); reference
the handleNextClick function and the variables/errors.customFields, optOpen,
setOptOpen, and onNext when making the change.
- Line 227: Remove the unsafe "as any" on the register call for customFields and
replace it with a type-safe path: define the form value type that includes
customFields (e.g., RegistrationFormValues with customFields keyed by a union of
known IDs), then cast the template literal to the proper Path type from
react-hook-form (or to a string literal union like `CustomFieldPath =
\`customFields.${KnownCustomFields}\``) when calling
register(`customFields.${field.id}`) in StepGeneral.tsx so you preserve
type-safety for register, field.id, and the customFields shape.
In `@frontend/src/pages/RegistrationPage/components/StepMembers.tsx`:
- Around line 36-38: The teammate bounds logic in StepMembers.tsx sets
maxTeammates = Math.max(0, cfg.maxMembers - 1) but forces minTeammates =
Math.max(1, cfg.minMembers - 1), which breaks when cfg.maxMembers === 1 (min >
max and progress/division by zero). Update both places (the initial definition
and the other occurrences around lines 92-93 and 241-247) to compute safe
non-negative bounds: use Math.max(0, cfg.minMembers - 1) and Math.max(0,
cfg.maxMembers - 1), then clamp minTeammates = Math.min(minTeammates,
maxTeammates) (or vice versa) so min ≤ max and handle the zero-teammate case
gracefully (disable Add/Next or treat range as 0..0) in the UI logic in
StepMembers component.
- Around line 39-63: handleNextClick currently bypasses form validation and can
call onNext() with invalid fields; make it asynchronous and explicitly trigger
form validation for the member group and captainEmail before proceeding. Inside
handleNextClick, await the form library's trigger (e.g., trigger("members") and
trigger("captainEmail") or a single trigger for all relevant fields), and if
trigger returns false, setMembersError with a suitable message and return; only
run the existing duplicate/count checks and call onNext() when validation
passed. Ensure you update the function signature to async and reference existing
symbols: handleNextClick, trigger, getValues, fields, captainEmail,
minTeammates, setMembersError, and onNext.
In `@frontend/src/pages/RegistrationPage/components/StepSuccess.tsx`:
- Line 1: Remove the unused React namespace import and any usage of the React.FC
type: delete the top-level `import React from "react";`, update the component
definition `StepSuccess` to a plain function or arrow function without
`React.FC` (e.g., `export default function StepSuccess(...) { ... }` or `const
StepSuccess = (...) => { ... }; export default StepSuccess;`), and ensure any
JSX remains unchanged so the file relies on the new JSX transform.
In `@frontend/src/pages/RegistrationPage/RegistrationPage.tsx`:
- Around line 24-31: The hardcoded user object in RegistrationPage.tsx (variable
user) should be replaced with the authenticated Firebase user: read
auth.currentUser (same pattern used in Profile, JuryPanel, GetRole) and map its
properties (displayName, email, uid, etc.) into the form payload, falling back
to existing fields (telegram, github, discord) from local state or inputs if
needed; update any references to the old user variable to use this derived
currentUser object and handle null/unauthenticated cases (redirect or show login
prompt) accordingly.
In `@frontend/src/pages/RegistrationPage/types.ts`:
- Around line 24-32: The Zod calls in customFieldsShape[field.id] (e.g.,
z.string().min(1, ...) and any z.string().email(...) uses) are using the
deprecated legacy string error-parameter; update them to Zod 4's unified error
format by replacing the second positional string argument with an options object
(e.g., { error: ... } or { error: (issue) => t(...) } as needed) for the
z.string().min and z.string().email invocations so all custom field validators
use the new error parameter shape.
In `@frontend/src/pages/TournamentPage/TournamentPage.tsx`:
- Around line 394-396: The value prop in TournamentPage using a hardcoded
Ukrainian prefix ("До ") breaks localization; replace the string interpolation
value={`До ${tournament.max_teams}`} with a translated string that accepts the
number (e.g., use the t function with an interpolation key such as
t("stats.upTo", { count: tournament.max_teams }) or a suitable
pluralized/templated key) so the prefix is produced by the i18n lookup rather
than hardcoded; update the value expression to call t(...) and pass
tournament.max_teams as the interpolation variable.
In `@frontend/src/pages/TournamentsPage/TournamentsPage.tsx`:
- Around line 121-127: Replace the manual ternary that chooses singular/plural
with i18next pluralization: pass filteredData.length as the count to t (e.g.,
t("results.tournaments", { count: filteredData.length })) in the TournamentsPage
JSX instead of using filteredData.length === 1 ? ... : ...; also update the
translations to provide plural forms for the key "results.tournaments" using the
_one and _other suffixes (e.g., "results.tournaments_one" and
"results.tournaments_other") so i18next can resolve the correct form.
---
Outside diff comments:
In `@frontend/src/App.tsx`:
- Around line 86-97: The onIdTokenChanged callback can race and crash; introduce
a local run counter (e.g., authRunId incremented each callback) and capture the
current runId at start of the async handler to ignore stale completions, wrap
the await user.getIdToken() in try/catch to handle/recover from token fetch
failures (log or return early without leaving socket disconnected), and before
creating a new socket with io(...) check that the captured runId still matches
the latest counter and that the auth state hasn't changed; also ensure setSocket
and currentSocket manipulation happen only when the runId is still current to
avoid overwriting a newer socket.
In `@frontend/src/pages/Auth/AuthPage.tsx`:
- Around line 23-41: The Zod schema uses deprecated v3 error syntax; update the
validators to Zod v4's error option object: change email(...) to email({
message: "errors.email_invalid" }) and password.min(8, "errors.pass_short") to
password.min(8, { message: "errors.pass_short" }); keep the superRefine custom
issue for displayName as-is. Apply the same update for the email validator in
frontend/src/pages/Auth/ForgotPassword.tsx (the email z.string() there) so all
three instances use the { message: ... } form.
---
Duplicate comments:
In `@frontend/src/pages/Auth/ForgotPassword.tsx`:
- Around line 17-19: The Zod schema resetSchema uses deprecated v3-style error
strings; update the email validator to Zod v4 unified error syntax by replacing
the positional string with an options object (set
message:"errors.email_invalid") on the z.string().email() call so resetSchema =
z.object({ email: z.string().email({ message: "errors.email_invalid" }) });
ensure any other validators in this file follow the same pattern.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: aac7d6ce-ec9c-4fc9-bc5e-1219602f4e1c
⛔ Files ignored due to path filters (3)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/src/pages/Auth/__snapshots__/Auth.test.tsx.snapis excluded by!**/*.snapfrontend/src/pages/Auth/__snapshots__/ForgotPassword.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (69)
frontend/index.htmlfrontend/package.jsonfrontend/src/App.tsxfrontend/src/components/Footer.tsxfrontend/src/components/Header.tsxfrontend/src/components/Hero.tsxfrontend/src/components/MainLayout.tsxfrontend/src/components/NotificationsDropdown.tsxfrontend/src/components/ProfileDropdown.tsxfrontend/src/components/ScrollToTop.tsxfrontend/src/components/ThemeToggle.tsxfrontend/src/components/TournamentCard.tsxfrontend/src/components/icons/BrandIcons.tsxfrontend/src/components/layouts/AuthLayout.tsxfrontend/src/components/ui/BrandingPanel.tsxfrontend/src/components/ui/Button.tsxfrontend/src/components/ui/LanguageSwitcher.tsxfrontend/src/components/ui/Ticket3D.tsxfrontend/src/components/ui/index.tsfrontend/src/config/navigation.tsfrontend/src/data/mockTournaments.tsfrontend/src/firebase.tsfrontend/src/hooks/useClickOutside.tsfrontend/src/i18n/config.tsfrontend/src/index.cssfrontend/src/locales/en/about.jsonfrontend/src/locales/en/auth.jsonfrontend/src/locales/en/common.jsonfrontend/src/locales/en/contact.jsonfrontend/src/locales/en/faq.jsonfrontend/src/locales/en/home.jsonfrontend/src/locales/en/registration.jsonfrontend/src/locales/en/rules.jsonfrontend/src/locales/en/support.jsonfrontend/src/locales/en/tournaments.jsonfrontend/src/locales/uk/about.jsonfrontend/src/locales/uk/auth.jsonfrontend/src/locales/uk/common.jsonfrontend/src/locales/uk/contact.jsonfrontend/src/locales/uk/faq.jsonfrontend/src/locales/uk/home.jsonfrontend/src/locales/uk/registration.jsonfrontend/src/locales/uk/rules.jsonfrontend/src/locales/uk/support.jsonfrontend/src/locales/uk/tournaments.jsonfrontend/src/main.tsxfrontend/src/pages/AboutUs/AboutUs.tsxfrontend/src/pages/Auth/AuthPage.tsxfrontend/src/pages/Auth/ForgotPassword.tsxfrontend/src/pages/Contact/Contact.tsxfrontend/src/pages/FaqPage/FaqPage.tsxfrontend/src/pages/Home/Home.tsxfrontend/src/pages/Home/components/TournamentSlider.tsxfrontend/src/pages/Page404/Page404.tsxfrontend/src/pages/RegistrationPage/RegistrationPage.tsxfrontend/src/pages/RegistrationPage/components/FormUI.tsxfrontend/src/pages/RegistrationPage/components/Icons.tsxfrontend/src/pages/RegistrationPage/components/StepConfirm.tsxfrontend/src/pages/RegistrationPage/components/StepGeneral.tsxfrontend/src/pages/RegistrationPage/components/StepMembers.tsxfrontend/src/pages/RegistrationPage/components/StepSuccess.tsxfrontend/src/pages/RegistrationPage/components/StepsNav.tsxfrontend/src/pages/RegistrationPage/types.tsfrontend/src/pages/Rules/Rules.tsxfrontend/src/pages/SupportPage/SupportPage.tsxfrontend/src/pages/TournamentPage/TournamentPage.tsxfrontend/src/pages/TournamentsPage/TournamentsPage.tsxfrontend/src/routers/Router.tsxfrontend/src/utils/cn.ts
💤 Files with no reviewable changes (1)
- frontend/src/firebase.ts
| "clsx": "^2.1.1", | ||
| "firebase": "^12.10.0", | ||
| "framer-motion": "^12.38.0", | ||
| "i18next": "^26.0.5", | ||
| "i18next-browser-languagedetector": "^8.2.1", | ||
| "i18next-http-backend": "^3.0.4", | ||
| "lottie-react": "^2.4.1", | ||
| "lucide-react": "^1.7.0", | ||
| "lucide-react": "^1.8.0", | ||
| "react": "^19.2.0", | ||
| "react-dom": "^19.2.0", | ||
| "react-hook-form": "^7.72.0", | ||
| "react-hot-toast": "^2.6.0", | ||
| "react-i18next": "^17.0.3", | ||
| "react-icons": "^5.6.0", | ||
| "react-redux": "^9.2.0", | ||
| "react-router-dom": "^7.13.1", | ||
| "react-toastify": "^11.1.0", | ||
| "socket.io-client": "^4.8.3", | ||
| "sonner": "^2.0.7", | ||
| "tailwind-merge": "^3.5.0", | ||
| "tailwindcss": "^4.2.1", | ||
| "zod": "^4.3.6" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify package versions exist on npm and check for security advisories
packages=(
"clsx@2.1.1"
"i18next@26.0.5"
"i18next-browser-languagedetector@8.2.1"
"i18next-http-backend@3.0.4"
"lucide-react@1.8.0"
"react-i18next@17.0.3"
"react-toastify@11.1.0"
"tailwind-merge@3.5.0"
)
echo "Checking package versions..."
for pkg in "${packages[@]}"; do
name=$(echo "$pkg" | cut -d'@' -f1)
version=$(echo "$pkg" | cut -d'@' -f2)
echo "Checking $name@$version..."
# Check if version exists
npm view "$name@$version" version 2>/dev/null
if [ $? -ne 0 ]; then
echo " ❌ Version $version not found for $name"
else
echo " ✅ Version exists"
fi
done
echo -e "\nChecking for security advisories..."
for pkg in "${packages[@]}"; do
name=$(echo "$pkg" | cut -d'@' -f1)
echo "Checking $name..."
gh api graphql -f query="
{
securityVulnerabilities(first: 5, ecosystem: NPM, package: \"$name\") {
nodes {
advisory {
summary
severity
publishedAt
}
vulnerableVersionRange
firstPatchedVersion {
identifier
}
}
}
}
" 2>/dev/null | jq '.data.securityVulnerabilities.nodes[] | select(.vulnerableVersionRange)' || echo " No advisories found"
doneRepository: ugalaxy-team/Project
Length of output: 1608
Upgrade i18next-http-backend to version 3.0.5 or later—path traversal vulnerability detected.
The package i18next-http-backend@3.0.4 has a Path Traversal & URL Injection vulnerability via unsanitized lng/ns parameters (MODERATE severity). This is fixed in version 3.0.5. All other verified package versions exist and have no active security advisories.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/package.json` around lines 25 - 46, Update the i18next-http-backend
dependency in package.json from "i18next-http-backend": "^3.0.4" to at least
"^3.0.5" to pull in the patch that fixes the path traversal/URL injection issue;
run your package manager (npm/yarn/pnpm) to update the lockfile
(package-lock.json / yarn.lock / pnpm-lock.yaml) and ensure CI installs the
updated version before merging.
| useEffect(() => { | ||
| let currentSocket: Socket | null = null; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "App.tsx" -type f | head -20Repository: ugalaxy-team/Project
Length of output: 87
🏁 Script executed:
cat -n frontend/src/App.tsx | head -150Repository: ugalaxy-team/Project
Length of output: 5954
Decouple socket lifecycle from translation function identity.
The socket effect re-runs whenever t changes (on language switch), causing unnecessary disconnection and reconnection of the socket. Keep socket lifecycle stable by reading translations via a ref instead.
Suggested fix
export const App = () => {
const { t } = useTranslation("common");
+ const tRef = useRef(t);
+ useEffect(() => {
+ tRef.current = t;
+ }, [t]);
useEffect(() => {
let currentSocket: Socket | null = null;
const unsubscribeAuth = onIdTokenChanged(auth, async (user) => {
// ...
currentSocket.on("connect_error", () => {
toast.error(
<div>
<div className="font-bold mb-1">
- {t("errors.socket", "Проблеми з сервером :(")}
+ {tRef.current("errors.socket", "Проблеми з сервером :(")}
</div>
{/* ... */}
</div>,
);
});
});
return () => {
unsubscribeAuth();
if (currentSocket) currentSocket.disconnect();
};
- }, [t]);
+ }, []);Also applies to: line 141
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/App.tsx` around lines 83 - 84, The socket effect currently
depends on the translation function `t`, causing socket tear-down/reconnect on
language changes; decouple them by storing `t` in a ref (e.g., `const tRef =
useRef(t)`) and updating `tRef.current = t` in a separate effect, then remove
`t` from the socket `useEffect` dependency array so `useEffect(() => { let
currentSocket... }, [...deps without t])` manages socket lifecycle stably;
update any places inside the socket handlers to call `tRef.current(...)` instead
of `t(...)`; apply the same ref approach to the other effect mentioned (the one
at line ~141) that currently depends on `t`.
|
|
||
| <button | ||
| className="lg:hidden text-white p-2 focus:outline-none" | ||
| className="lg:hidden text-white p-1.5 focus:outline-none hover:text-accent transition-colors" |
There was a problem hiding this comment.
Restore visible keyboard focus on the mobile menu button.
focus:outline-none removes the only default focus indicator and no focus-visible replacement is provided.
Suggested fix
- className="lg:hidden text-white p-1.5 focus:outline-none hover:text-accent transition-colors"
+ className="lg:hidden text-white p-1.5 hover:text-accent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg-header-bg"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| className="lg:hidden text-white p-1.5 focus:outline-none hover:text-accent transition-colors" | |
| className="lg:hidden text-white p-1.5 hover:text-accent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg-header-bg" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Header.tsx` at line 126, The mobile menu button in
Header.tsx currently removes the keyboard focus indicator via
"focus:outline-none"; restore an accessible focus style by removing that utility
and adding a visible focus replacement (e.g., Tailwind focus-visible:ring or
focus:ring and focus-visible:ring-offset classes) on the button element so
keyboard users see a clear focus outline when interacting with the mobile menu
button.
| <div onClick={() => setIsMobileMenuOpen(false)}> | ||
| <ProfileDropdown | ||
| userRoles={user?.roles || []} | ||
| isMobile={true} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
Mobile profile actions are effectively unreachable due to parent click-close.
The wrapping <div onClick={() => setIsMobileMenuOpen(false)}> closes the whole mobile menu on any interaction with ProfileDropdown, so users can’t reliably open/use its internal menu on mobile.
Suggested fix
- {user?.uid ? (
- <div onClick={() => setIsMobileMenuOpen(false)}>
- <ProfileDropdown
- userRoles={user?.roles || []}
- isMobile={true}
- />
- </div>
+ {user?.uid ? (
+ <ProfileDropdown
+ userRoles={user?.roles || []}
+ isMobile={true}
+ />
) : (📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div onClick={() => setIsMobileMenuOpen(false)}> | |
| <ProfileDropdown | |
| userRoles={user?.roles || []} | |
| isMobile={true} | |
| /> | |
| </div> | |
| <ProfileDropdown | |
| userRoles={user?.roles || []} | |
| isMobile={true} | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Header.tsx` around lines 175 - 180, The mobile menu's
parent wrapper currently closes the menu on any click because of the div with
onClick={() => setIsMobileMenuOpen(false)}, which prevents interacting with
ProfileDropdown; fix this by preventing event propagation for the dropdown area
or moving the close handler to the backdrop/explicit close control: replace the
wrapping div around ProfileDropdown with a container that stops propagation
(e.g., onClick={e => e.stopPropagation()}) or remove the onClick entirely and
attach setIsMobileMenuOpen(false) to the backdrop/close button instead; update
references around setIsMobileMenuOpen and the ProfileDropdown usage in
Header.tsx accordingly.
| import { LanguageSwitcher } from "../ui/LanguageSwitcher"; | ||
|
|
||
| import { BrandingPanel } from "../ui/BrandingPanel"; | ||
| import starAnimation from "../../../public/star.json"; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify the star.json file location and how it's referenced elsewhere
# Find star.json file
fd -t f "star.json"
# Check how star.json is imported in other files
rg -n "star\.json" --type=ts --type=tsx -C 2Repository: ugalaxy-team/Project
Length of output: 173
🏁 Script executed:
# Check if this is a Vite project
cat frontend/package.json | head -50Repository: ugalaxy-team/Project
Length of output: 1538
🏁 Script executed:
# Search for how public assets are imported in the codebase
rg -n "from.*public" frontend/src --max-count=20Repository: ugalaxy-team/Project
Length of output: 169
🏁 Script executed:
# Look for vite config
fd -t f "vite.config" frontend/Repository: ugalaxy-team/Project
Length of output: 88
🏁 Script executed:
# Check if there are other JSON imports from public directory
rg -n "\.json" frontend/src/components --max-count=15Repository: ugalaxy-team/Project
Length of output: 169
🏁 Script executed:
cat frontend/vite.config.tsRepository: ugalaxy-team/Project
Length of output: 634
🏁 Script executed:
# Check how starAnimation is used in AuthLayout.tsx
sed -n '1,50p' frontend/src/components/layouts/AuthLayout.tsxRepository: ugalaxy-team/Project
Length of output: 1909
🏁 Script executed:
# Look for any TypeScript or build configuration that might affect JSON imports
cat frontend/tsconfig.json | head -60Repository: ugalaxy-team/Project
Length of output: 183
Consider moving the Lottie JSON to src/assets or using path alias for better maintainability.
The import path ../../../public/star.json works correctly and resolves to frontend/public/star.json, but it's not idiomatic to Vite. The relative path creates fragility—if AuthLayout.tsx moves to a different directory depth, the path breaks. Instead:
- Move
star.jsontosrc/assets/and import with a relative path, or - Use the configured
@alias (e.g.,import starAnimation from "@/assets/star.json") for consistency with the rest of the codebase
This aligns with Vite conventions and makes imports more maintainable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/layouts/AuthLayout.tsx` at line 10, The import in
AuthLayout.tsx uses a brittle relative path for starAnimation (import
starAnimation from "../../../public/star.json"); move star.json into src/assets
and update the import to reference the new location or use the project alias
(e.g., "@/assets/star.json") so the component imports starAnimation from
src/assets via the alias; update any build/tsconfig paths if needed and ensure
AuthLayout.tsx's import statement references the new path.
| @@ -0,0 +1,48 @@ | |||
| import React from "react"; | |||
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Remove unnecessary React import.
In React 19, the React namespace import is no longer required for JSX or component definitions. The React.FC type is also no longer recommended. You can remove this import entirely.
♻️ Simplify component definition for React 19
-import React from "react";
import { motion } from "framer-motion";
import { useTranslation } from "react-i18next";
import { useFormContext } from "react-hook-form";
import { Icon } from "./Icons";
import { BtnNext } from "./FormUI";
import type { RegFormData } from "../types";
interface StepSuccessProps {
onHome: () => void;
}
-export const StepSuccess: React.FC<StepSuccessProps> = ({ onHome }) => {
+export const StepSuccess = ({ onHome }: StepSuccessProps) => {
const { t } = useTranslation("registration");
const { watch } = useFormContext<RegFormData>();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/RegistrationPage/components/StepSuccess.tsx` at line 1,
Remove the unused React namespace import and any usage of the React.FC type:
delete the top-level `import React from "react";`, update the component
definition `StepSuccess` to a plain function or arrow function without
`React.FC` (e.g., `export default function StepSuccess(...) { ... }` or `const
StepSuccess = (...) => { ... }; export default StepSuccess;`), and ensure any
JSX remains unchanged so the file relies on the new JSX transform.
| const user = { | ||
| displayName: "izachoc", | ||
| full_name: "Андрій Іванченко", | ||
| email: "andriy.test@gmail.com", | ||
| telegram: "@izachoc_dev", | ||
| github: "izachoc-code", | ||
| discord: "izachoc#1234", | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for auth context or user hook in the codebase
echo "=== Searching for auth context providers ==="
rg -n "AuthContext|UserContext|useAuth|useUser" --type=ts --type=tsx frontend/src -A 2
echo -e "\n=== Checking if Firebase auth user is available ==="
rg -n "auth.currentUser|onAuthStateChanged" --type=ts --type=tsx frontend/srcRepository: ugalaxy-team/Project
Length of output: 236
🏁 Script executed:
#!/bin/bash
# Corrected search without problematic --type flags
echo "=== Searching for auth-related patterns ==="
rg -n "useAuth|AuthContext|UserContext" frontend/src -A 2
echo -e "\n=== Searching for Firebase auth ==="
rg -n "firebase.*auth|auth.*currentUser" frontend/src
echo -e "\n=== Checking RegistrationPage file ==="
fd "RegistrationPage.tsx" frontend/srcRepository: ugalaxy-team/Project
Length of output: 3232
🏁 Script executed:
#!/bin/bash
# Read the RegistrationPage.tsx file
file_path=$(fd "RegistrationPage.tsx" frontend/src 2>/dev/null | head -1)
if [ -n "$file_path" ]; then
echo "=== Content of $file_path ==="
cat -n "$file_path"
else
echo "File not found, searching with broader pattern..."
fd "RegistrationPage" frontend/src
fiRepository: ugalaxy-team/Project
Length of output: 7746
🏁 Script executed:
#!/bin/bash
# Search for auth providers, hooks, context setup
echo "=== Looking for auth hook implementations ==="
fd "useAuth|auth" frontend/src --type f | head -20
echo -e "\n=== Looking for auth-related directories ==="
fd --type d "auth|hooks" frontend/srcRepository: ugalaxy-team/Project
Length of output: 269
Use authenticated user from Firebase auth instead of hardcoded test data.
The hardcoded test user will cause all registrations to be attributed to "izachoc" instead of the actual authenticated user. Firebase auth is already set up and used throughout the codebase (Profile, JuryPanel, GetRole pages); apply the same pattern here by accessing auth.currentUser directly.
🔧 Proposed fix: Use Firebase auth
import React, { useState } from "react";
import { useParams, Navigate, useNavigate, Link } from "react-router-dom";
import { useForm, FormProvider } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { motion, AnimatePresence } from "framer-motion";
import { useTranslation } from "react-i18next";
+import { auth } from "../../firebase";
import { TOURNAMENTS_DATA } from "../../data/mockTournaments";
import { BrandingPanel } from "../../components/ui/BrandingPanel";
import { Ticket3D } from "../../components/ui/Ticket3D";
import { makeSchema, type RegFormData, type TournamentConfig } from "./types";
import { StepsNav } from "./components/StepsNav";
import { StepGeneral } from "./components/StepGeneral";
import { StepMembers } from "./components/StepMembers";
import { StepConfirm } from "./components/StepConfirm";
import { StepSuccess } from "./components/StepSuccess";
export const RegistrationPage = () => {
const { t } = useTranslation("registration");
const navigate = useNavigate();
const { id } = useParams();
-
- const user = {
- displayName: "izachoc",
- full_name: "Андрій Іванченко",
- email: "andriy.test@gmail.com",
- telegram: "@izachoc_dev",
- github: "izachoc-code",
- discord: "izachoc#1234",
- };
+
+ const fbUser = auth.currentUser;
+ if (!fbUser) return <Navigate to="/auth" replace />;
+
+ const user = {
+ displayName: fbUser.displayName || "",
+ full_name: fbUser.displayName || "",
+ email: fbUser.email || "",
+ telegram: "",
+ github: "",
+ discord: "",
+ };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const user = { | |
| displayName: "izachoc", | |
| full_name: "Андрій Іванченко", | |
| email: "andriy.test@gmail.com", | |
| telegram: "@izachoc_dev", | |
| github: "izachoc-code", | |
| discord: "izachoc#1234", | |
| }; | |
| import React, { useState } from "react"; | |
| import { useParams, Navigate, useNavigate, Link } from "react-router-dom"; | |
| import { useForm, FormProvider } from "react-hook-form"; | |
| import { zodResolver } from "@hookform/resolvers/zod"; | |
| import { motion, AnimatePresence } from "framer-motion"; | |
| import { useTranslation } from "react-i18next"; | |
| import { auth } from "../../firebase"; | |
| import { TOURNAMENTS_DATA } from "../../data/mockTournaments"; | |
| import { BrandingPanel } from "../../components/ui/BrandingPanel"; | |
| import { Ticket3D } from "../../components/ui/Ticket3D"; | |
| import { makeSchema, type RegFormData, type TournamentConfig } from "./types"; | |
| import { StepsNav } from "./components/StepsNav"; | |
| import { StepGeneral } from "./components/StepGeneral"; | |
| import { StepMembers } from "./components/StepMembers"; | |
| import { StepConfirm } from "./components/StepConfirm"; | |
| import { StepSuccess } from "./components/StepSuccess"; | |
| export const RegistrationPage = () => { | |
| const { t } = useTranslation("registration"); | |
| const navigate = useNavigate(); | |
| const { id } = useParams(); | |
| const fbUser = auth.currentUser; | |
| if (!fbUser) return <Navigate to="/auth" replace />; | |
| const user = { | |
| displayName: fbUser.displayName || "", | |
| full_name: fbUser.displayName || "", | |
| email: fbUser.email || "", | |
| telegram: "", | |
| github: "", | |
| discord: "", | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/RegistrationPage/RegistrationPage.tsx` around lines 24 -
31, The hardcoded user object in RegistrationPage.tsx (variable user) should be
replaced with the authenticated Firebase user: read auth.currentUser (same
pattern used in Profile, JuryPanel, GetRole) and map its properties
(displayName, email, uid, etc.) into the form payload, falling back to existing
fields (telegram, github, discord) from local state or inputs if needed; update
any references to the old user variable to use this derived currentUser object
and handle null/unauthenticated cases (redirect or show login prompt)
accordingly.
| customFieldsShape[field.id] = z | ||
| .string() | ||
| .min( | ||
| 1, | ||
| t( | ||
| "validation.requiredCustom", | ||
| `Поле "${field.label}" є обов'язковим`, | ||
| ), | ||
| ); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Verify Zod 4 error message syntax compatibility.
The code uses the legacy error message syntax:
z.string().min(1, "Error message")
z.string().email("Invalid email")Zod 4 introduced a unified error parameter that accepts a callback. While Zod may maintain backward compatibility, you should verify that the legacy string syntax still works in Zod 4, or migrate to the new format:
z.string().min(1, {
error: (issue) => t("validation.requiredCustom", `Поле обов'язкове`)
})Migrate to Zod 4 unified error syntax.
The message parameter is still supported but deprecated in Zod 4. The code uses the legacy error message syntax which still works but should be migrated to the new unified error parameter:
Current (deprecated):
z.string().min(1, "Error message")
z.string().email("Invalid email")Recommended Zod 4 syntax:
z.string().min(1, { error: "Error message" })
z.string().email({ error: "Invalid email" })♻️ Proposed migration to Zod 4 syntax
if (field.required) {
customFieldsShape[field.id] = z
.string()
- .min(
- 1,
- t(
- "validation.requiredCustom",
- `Поле "${field.label}" є обов'язковим`,
- ),
- );
+ .min(1, {
+ error: t(
+ "validation.requiredCustom",
+ `Поле "${field.label}" є обов'язковим`,
+ ),
+ });
} else { z.object({
name: z
.string()
- .min(2, t("validation.nameRequired", "Введіть ПІБ або нікнейм")),
+ .min(2, {
+ error: t("validation.nameRequired", "Введіть ПІБ або нікнейм"),
+ }),
email: z
.string()
- .email(t("validation.invalidEmail", "Невірний email")),
+ .email({
+ error: t("validation.invalidEmail", "Невірний email"),
+ }),
customFields: z.object(customFieldsShape).optional(),
}),Also applies to: 49-52
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/RegistrationPage/types.ts` around lines 24 - 32, The Zod
calls in customFieldsShape[field.id] (e.g., z.string().min(1, ...) and any
z.string().email(...) uses) are using the deprecated legacy string
error-parameter; update them to Zod 4's unified error format by replacing the
second positional string argument with an options object (e.g., { error: ... }
or { error: (issue) => t(...) } as needed) for the z.string().min and
z.string().email invocations so all custom field validators use the new error
parameter shape.
| <div className="mb-6 text-[15px] font-bold text-text-muted transition-colors duration-300 px-1"> | ||
| {t("results.found")}{" "} | ||
| <strong className="text-primary">{filteredData.length}</strong>{" "} | ||
| турнірів | ||
| {filteredData.length === 1 | ||
| ? t("results.tournaments_one") | ||
| : t("results.tournaments_many")} | ||
| </div> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Use i18next's built-in pluralization instead of manual ternary logic.
The current implementation uses a simple ternary to choose between singular and plural forms. react-i18next provides built-in pluralization support that handles more complex plural rules (including languages with multiple plural forms) and is more maintainable.
♻️ Refactor to use i18next pluralization
Update the translation files to use _one and _other suffixes:
frontend/src/locales/en/tournaments.json:
{
"results": {
"found": "Found",
"tournaments_one": "tournament",
"tournaments_other": "tournaments"
}
}Then simplify the component code:
<div className="mb-6 text-[15px] font-bold text-text-muted transition-colors duration-300 px-1">
- {t("results.found")}{" "}
- <strong className="text-primary">{filteredData.length}</strong>{" "}
- {filteredData.length === 1
- ? t("results.tournaments_one")
- : t("results.tournaments_many")}
+ {t("results.found")} <strong className="text-primary">{filteredData.length}</strong> {t("results.tournaments", { count: filteredData.length })}
</div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/TournamentsPage/TournamentsPage.tsx` around lines 121 -
127, Replace the manual ternary that chooses singular/plural with i18next
pluralization: pass filteredData.length as the count to t (e.g.,
t("results.tournaments", { count: filteredData.length })) in the TournamentsPage
JSX instead of using filteredData.length === 1 ? ... : ...; also update the
translations to provide plural forms for the key "results.tournaments" using the
_one and _other suffixes (e.g., "results.tournaments_one" and
"results.tournaments_other") so i18next can resolve the correct form.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
frontend/src/App.tsx (1)
116-116:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSocket reconnects unnecessarily on language change.
The socket
useEffectdepends ont(line 116), so changing the app language causes the socket to disconnect and reconnect. This creates unnecessary network overhead and brief service interruption.🔧 Proposed fix using a ref
export const App = () => { const { t } = useTranslation("common"); + const tRef = useRef(t); + + useEffect(() => { + tRef.current = t; + }, [t]); // ... (socket setup remains the same until the handlers) useEffect(() => { let currentSocket: Socket | null = null; const unsubscribeAuth = onIdTokenChanged(auth, async (user) => { // ... existing code ... currentSocket.on("connect_error", () => { // ... existing code ... toast.error( <div> - <div className="font-bold mb-1">{t("errors.socket")}</div> + <div className="font-bold mb-1">{tRef.current("errors.socket")}</div> <div className="text-[13px] opacity-90 leading-tight"> Сповіщення тимчасово не працюють </div> </div>, { toastId: "socket-error" } ); }); // ... rest of socket setup ... }); return () => { unsubscribeAuth(); if (currentSocket) currentSocket.disconnect(); }; - }, [t]); + }, []);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/App.tsx` at line 116, The socket useEffect in App.tsx is incorrectly depending on the i18n translator `t`, causing the socket to disconnect/reconnect on language changes; update the effect that creates/manages the socket (the useEffect which currently has dependency array [t]) to remove `t` from its dependencies and instead use a stable ref or state for any values that must persist across language changes (e.g., store the socket instance in a useRef like socketRef and read/write from socketRef inside other effects or callbacks), ensuring socket creation/cleanup only runs on mount/unmount (empty deps or relevant stable deps) while keeping language-driven UI updates separate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/package.json`:
- Around line 35-36: Remove the duplicate "lucide-react" entry in package.json
so there is only one dependency for lucide-react (choose the intended version,
e.g., keep "^1.14.0" and delete the "^1.8.0" line), then run your package
manager (npm install / yarn install / pnpm install) to update lockfiles and
ensure the resolved version is consistent; verify that no other files reference
the removed version.
In `@frontend/src/App.tsx`:
- Line 87: The App component currently uses hardcoded Ukrainian strings in the
toast calls (notifications) which bypass i18n; add the missing keys
("errors.socket", "errors.socket_detail", "errors.socket_restored") to your
locale files (e.g., locales/en/common.json and locales/uk/common.json) and
update the toast usages inside the App component to use the i18n translator
(e.g., t('errors.socket') / t('errors.socket_detail') /
t('errors.socket_restored')) instead of the literal Ukrainian text so messages
respect the user's selected language.
---
Duplicate comments:
In `@frontend/src/App.tsx`:
- Line 116: The socket useEffect in App.tsx is incorrectly depending on the i18n
translator `t`, causing the socket to disconnect/reconnect on language changes;
update the effect that creates/manages the socket (the useEffect which currently
has dependency array [t]) to remove `t` from its dependencies and instead use a
stable ref or state for any values that must persist across language changes
(e.g., store the socket instance in a useRef like socketRef and read/write from
socketRef inside other effects or callbacks), ensuring socket creation/cleanup
only runs on mount/unmount (empty deps or relevant stable deps) while keeping
language-driven UI updates separate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3f24ca0d-4131-45d1-bbc2-a5c2af6a73cb
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (11)
frontend/package.jsonfrontend/src/App.tsxfrontend/src/components/Header.tsxfrontend/src/components/ui/Ticket3D.tsxfrontend/src/config/navigation.tsfrontend/src/locales/en/common.jsonfrontend/src/locales/en/registration.jsonfrontend/src/locales/uk/common.jsonfrontend/src/locales/uk/registration.jsonfrontend/src/pages/Page404/Page404.tsxfrontend/src/routers/Router.tsx
Fundi1330
left a comment
There was a problem hiding this comment.
Merge as fast as possible, because we have other work to do
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/ui/DateTimePicker.tsx (1)
70-78:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMinute adjustments ignore hour rollover/borrow.
When minutes cross
59→00or00→59, the hour is not updated, so resulting time is incorrect (e.g.,10:58 + 5becomes10:03instead of11:03).💡 Suggested fix
const adjustTime = (type: "hours" | "minutes", amount: number) => { let nextDate = new Date(dateValue); if (type === "hours") { const newHours = (nextDate.getHours() + amount + 24) % 24; nextDate = setHours(nextDate, newHours); } else { - const newMinutes = (nextDate.getMinutes() + amount + 60) % 60; - nextDate = setMinutes(nextDate, newMinutes); + nextDate = new Date(nextDate.getTime() + amount * 60_000); } updateDateTime(nextDate); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/ui/DateTimePicker.tsx` around lines 70 - 78, The adjustTime function currently updates minutes without adjusting hours on rollover; modify adjustTime (referencing adjustTime, dateValue, setMinutes, setHours) so when type === "minutes" compute the total minutes (currentMinutes + amount), derive newMinutes = ((totalMinutes % 60) + 60) % 60 and hourDelta = Math.floor(totalMinutes / 60) (or -1 for negative wrap), then call setMinutes(nextDate, newMinutes) and update hours with setHours(nextDate, (nextDate.getHours() + hourDelta + 24) % 24) to correctly handle both forward and backward minute rollovers.
♻️ Duplicate comments (4)
frontend/src/locales/en/common.json (1)
54-54:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix aria-label grammar
Line 54 uses
"Choice language", which is ungrammatical for an accessibility label. Use"Choose language"(or"Language selection").✏️ Proposed fix
- "aria_label": "Choice language" + "aria_label": "Choose language"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/locales/en/common.json` at line 54, The aria-label value for the localization key "aria_label" is ungrammatical; update the string value in frontend/src/locales/en/common.json for the "aria_label" key from "Choice language" to a proper accessibility label such as "Choose language" (or "Language selection") so screen readers convey the intended action; ensure you only modify the value for the "aria_label" key and keep JSON formatting intact.frontend/src/pages/Auth/AuthPage.tsx (1)
198-198:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid unsafe casting of validation messages before translation
At Lines 198, 227, and 273,
errors.*.message as stringcan pass non-string/undefined values intot(...). Guard and normalize before translating.🔧 Proposed fix pattern
- {t(errors.email.message as string)} + {t(typeof errors.email.message === "string" ? errors.email.message : "errors.unknown")}Also applies to: 227-227, 273-273
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/Auth/AuthPage.tsx` at line 198, The three places using unsafe casts (errors.displayName.message as string at lines referenced) should guard and normalize the message before passing to the i18n translate function `t`: in the AuthPage component locate the usages of `errors.displayName.message`, `errors.email.message`, and `errors.password.message` and replace the cast with a safe extraction such as checking existence and type (e.g. use a ternary or nullish coalescing to fallback to an empty string or String(...) conversion) so `t(...)` always receives a string; ensure you reference the same `t` call sites and preserve existing behavior for empty/undefined messages by supplying a sensible default.frontend/package.json (1)
33-33:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpgrade or remove vulnerable
i18next-http-backendversionLine 33 still pins
i18next-http-backendto^3.0.4, which was previously flagged for a path traversal/URL injection advisory. Please bump to a patched version (>=3.0.5) or remove it if unused.🔧 Proposed minimal fix
- "i18next-http-backend": "^3.0.4", + "i18next-http-backend": "^3.0.5",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/package.json` at line 33, Update the vulnerable dependency declaration for i18next-http-backend in package.json: either bump the version string for "i18next-http-backend" from "^3.0.4" to a patched release ">=3.0.5" (e.g. "^3.0.5") or remove the dependency entry if it's not used; after changing package.json, run your package manager to update the lockfile (npm/yarn/pnpm install) and run the frontend tests/build to verify nothing breaks. Ensure any imports/usages of i18next-http-backend in the codebase are updated/removed accordingly if you choose to remove the package.frontend/src/App.tsx (1)
55-137:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSocket effect still depends on translation function.
The socket
useEffectcontinues to depend ont(line 137), causing the socket to disconnect and reconnect whenever the user switches languages. This was previously flagged and marked as addressed, but the issue persists.🔄 Recommended fix using a ref to decouple lifecycle
export const App = () => { const { t } = useTranslation("common"); + const tRef = useRef(t); const dispatch = useDispatch(); const [socket, setSocket] = useState<Socket | null>(null); const [toastTheme, setToastTheme] = useState<"colored" | "dark">("colored"); const lastErrorTime = useRef(0); const wasError = useRef(false); + useEffect(() => { + tRef.current = t; + }, [t]); // ... theme effect ... useEffect(() => { let currentSocket: Socket | null = null; const unsubscribeAuth = onIdTokenChanged(auth, async (user) => { // ... existing logic ... currentSocket.on("connect_error", () => { const now = Date.now(); if ( now - lastErrorTime.current > COOLDOWN_TIME || !wasError.current ) { toast.error( <div> - <div className="font-bold mb-1">{t("errors.socket")}</div> + <div className="font-bold mb-1">{tRef.current("errors.socket")}</div> <div className="text-[13px] opacity-90 leading-tight"> - {t("errors.socket_desc")}{" "} + {tRef.current("errors.socket_desc")}{" "} </div> </div>, { toastId: "socket-error" }, ); lastErrorTime.current = now; wasError.current = true; } }); currentSocket.on("connect", () => { toast.dismiss("socket-error"); if (wasError.current) { - toast.success(t("success.socket_restored"), { + toast.success(tRef.current("success.socket_restored"), { toastId: "socket-success", }); wasError.current = false; lastErrorTime.current = 0; } }); setSocket(currentSocket); // ... rest of logic ... }); return () => { unsubscribeAuth(); if (currentSocket) currentSocket.disconnect(); }; - }, [t, dispatch]); + }, [dispatch]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/App.tsx` around lines 55 - 137, The effect that manages the socket connection (the useEffect containing onIdTokenChanged, currentSocket, setSocket, and handlers using lastErrorTime.current and wasError.current) wrongly lists the translation function t in its dependency array, causing reconnects when language changes; remove t from the dependency list and instead access translations only inside handlers via a stable ref or by calling t at the point you show notifications through a separate helper (e.g., create a toastMessagesRef or getToastMessage function outside the effect and reference that), ensuring the effect depends only on stable values (dispatch, auth) and that socket logic (currentSocket, connect/connect_error handlers, unsubscribeAuth, setSocket) is not re-run on language switch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/App.tsx`:
- Around line 112-113: Remove the empty JSX expression block `{ }` found in the
App component's render/return JSX (the stray empty block around lines 112-113);
simply delete that empty expression so the JSX has no redundant empty JS block
and the component's returned JSX remains unchanged otherwise.
- Line 125: Replace the hardcoded Ukrainian console message in the App
component's error handler with an i18n lookup: add "errors.profile_sync" to your
locale files (frontend/src/locales/en/common.json and
frontend/src/locales/uk/common.json) and change the console.error call in App
(where "Помилка синхронізації профілю" is used) to use the i18n translator
(e.g., t('errors.profile_sync')) obtained from your i18n hook/instance
(useTranslation or equivalent) so the logged message follows the user's language
setting and still includes the original error object.
In `@frontend/src/components/ui/DateTimePicker.tsx`:
- Around line 135-136: The code in DateTimePicker.tsx hardcodes Ukrainian
strings and a fixed 'uk' locale (e.g., the "Виберіть дату" string and uses
format(dateValue, "dd.MM.yyyy", { locale: uk })) which prevents runtime language
switching; update the component (DateTimePicker) to use the app's i18n
translation function (e.g., t('selectDate')) instead of the literal "Виберіть
дату" wherever value is falsy, and pass a dynamic locale object to
date-fns/format (derive the locale from the current app language/locale prop or
context) so calls like format(dateValue, "dd.MM.yyyy", { locale: dynamicLocale
}) replace any hardcoded uk usage (also fix the other occurrences around lines
161, 181-187, 195 that mirror this pattern).
- Around line 135-136: The DateTimePicker component is calling format(dateValue,
...) without ensuring dateValue is a valid Date, causing runtime throws when
parseISO(value) is invalid; update the component (references: DateTimePicker,
parseISO, dateValue, value, and every format(...) call at the mentioned render
locations) to first parse the value, check isValid(parsedDate) (from date-fns)
and only call format when valid, otherwise render a safe fallback string (e.g.,
the placeholder "Виберіть дату" or the raw value) so format() is never invoked
with an invalid date.
In `@frontend/src/i18n/config.ts`:
- Around line 67-78: The i18n namespace list `ns` is missing the "profile"
namespace that is included in the bundled resources; update the `ns` array (in
config.ts where `ns: [...]` is declared) to include "profile" alongside
"common", "auth", "registration", etc., so the `profile` resource is initialized
correctly.
In `@frontend/src/locales/uk/common.json`:
- Around line 40-44: The Ukrainian errors.404 object is missing the
errors.404.back translation key present in English; update the uk common
translations by adding the errors.404.back entry inside the existing "404"
object (the same place as "title", "description", "go_home") with an appropriate
Ukrainian string (e.g., "Повернутися назад") so the 404 page doesn't render an
untranslated key.
In `@frontend/src/pages/Auth/AuthPage.tsx`:
- Around line 263-269: The password visibility toggle button in AuthPage.tsx
(the button using showPassword, setShowPassword and icons Eye/EyeOff) lacks an
accessible name; add an aria-label that updates based on showPassword (e.g.,
"Hide password" when showPassword is true, "Show password" when false) so screen
readers can describe the action, and optionally include
aria-pressed={showPassword} to convey state.
In `@frontend/src/pages/Profile/EditProfileModal.tsx`:
- Around line 19-24: EditProfileModalProps currently types currentUser as
any—replace that with a concrete user interface that includes the fields used by
the component (displayName, full_name, telegram, github, discord) or import and
reuse the existing UserData interface from Profile.tsx; update the
EditProfileModalProps definition to reference this interface (e.g., currentUser:
UserData) and adjust any local usages if necessary to satisfy TypeScript.
In `@frontend/src/pages/Profile/Profile.tsx`:
- Around line 72-77: The map callback in userTournaments is shadowing the
translation function t from useTranslation("profile"); rename the map parameter
(e.g., from t to tournament or createdTournament) and update its uses inside the
callback (t.title || t.name -> tournament.title || tournament.name) so that
calls to t("tournament_unnamed") and t("no_tournaments") refer to the
translation function, not the mapped object.
---
Outside diff comments:
In `@frontend/src/components/ui/DateTimePicker.tsx`:
- Around line 70-78: The adjustTime function currently updates minutes without
adjusting hours on rollover; modify adjustTime (referencing adjustTime,
dateValue, setMinutes, setHours) so when type === "minutes" compute the total
minutes (currentMinutes + amount), derive newMinutes = ((totalMinutes % 60) +
60) % 60 and hourDelta = Math.floor(totalMinutes / 60) (or -1 for negative
wrap), then call setMinutes(nextDate, newMinutes) and update hours with
setHours(nextDate, (nextDate.getHours() + hourDelta + 24) % 24) to correctly
handle both forward and backward minute rollovers.
---
Duplicate comments:
In `@frontend/package.json`:
- Line 33: Update the vulnerable dependency declaration for i18next-http-backend
in package.json: either bump the version string for "i18next-http-backend" from
"^3.0.4" to a patched release ">=3.0.5" (e.g. "^3.0.5") or remove the dependency
entry if it's not used; after changing package.json, run your package manager to
update the lockfile (npm/yarn/pnpm install) and run the frontend tests/build to
verify nothing breaks. Ensure any imports/usages of i18next-http-backend in the
codebase are updated/removed accordingly if you choose to remove the package.
In `@frontend/src/App.tsx`:
- Around line 55-137: The effect that manages the socket connection (the
useEffect containing onIdTokenChanged, currentSocket, setSocket, and handlers
using lastErrorTime.current and wasError.current) wrongly lists the translation
function t in its dependency array, causing reconnects when language changes;
remove t from the dependency list and instead access translations only inside
handlers via a stable ref or by calling t at the point you show notifications
through a separate helper (e.g., create a toastMessagesRef or getToastMessage
function outside the effect and reference that), ensuring the effect depends
only on stable values (dispatch, auth) and that socket logic (currentSocket,
connect/connect_error handlers, unsubscribeAuth, setSocket) is not re-run on
language switch.
In `@frontend/src/locales/en/common.json`:
- Line 54: The aria-label value for the localization key "aria_label" is
ungrammatical; update the string value in frontend/src/locales/en/common.json
for the "aria_label" key from "Choice language" to a proper accessibility label
such as "Choose language" (or "Language selection") so screen readers convey the
intended action; ensure you only modify the value for the "aria_label" key and
keep JSON formatting intact.
In `@frontend/src/pages/Auth/AuthPage.tsx`:
- Line 198: The three places using unsafe casts (errors.displayName.message as
string at lines referenced) should guard and normalize the message before
passing to the i18n translate function `t`: in the AuthPage component locate the
usages of `errors.displayName.message`, `errors.email.message`, and
`errors.password.message` and replace the cast with a safe extraction such as
checking existence and type (e.g. use a ternary or nullish coalescing to
fallback to an empty string or String(...) conversion) so `t(...)` always
receives a string; ensure you reference the same `t` call sites and preserve
existing behavior for empty/undefined messages by supplying a sensible default.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 88ca65f3-e26e-4eb5-8e62-92e6fc4ac4cd
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (15)
frontend/package.jsonfrontend/src/App.tsxfrontend/src/components/ui/DateTimePicker.tsxfrontend/src/firebase.tsfrontend/src/i18n/config.tsfrontend/src/locales/en/common.jsonfrontend/src/locales/en/profile.jsonfrontend/src/locales/uk/common.jsonfrontend/src/locales/uk/profile.jsonfrontend/src/pages/Auth/AuthPage.tsxfrontend/src/pages/Profile/EditProfileModal.test.tsxfrontend/src/pages/Profile/EditProfileModal.tsxfrontend/src/pages/Profile/Profile.test.tsxfrontend/src/pages/Profile/Profile.tsxfrontend/src/slices/user.ts
💤 Files with no reviewable changes (1)
- frontend/src/firebase.ts
| { | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Remove empty code block.
Lines 112-113 contain an empty block { } that serves no purpose and should be removed.
🧹 Cleanup
toast.success(t("success.socket_restored"), {
toastId: "socket-success",
});
- {
- }
wasError.current = false;
lastErrorTime.current = 0;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { | |
| } | |
| toast.success(t("success.socket_restored"), { | |
| toastId: "socket-success", | |
| }); | |
| wasError.current = false; | |
| lastErrorTime.current = 0; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/App.tsx` around lines 112 - 113, Remove the empty JSX expression
block `{ }` found in the App component's render/return JSX (the stray empty
block around lines 112-113); simply delete that empty expression so the JSX has
no redundant empty JS block and the component's returned JSX remains unchanged
otherwise.
| window.location.replace("/"); | ||
| } | ||
| } catch (error) { | ||
| console.error("Помилка синхронізації профілю:", error); |
There was a problem hiding this comment.
Replace hardcoded Ukrainian error message with i18n key.
The console error at line 125 contains a hardcoded Ukrainian string that bypasses the i18n system and will always display in Ukrainian regardless of the user's language preference.
🌐 Proposed fix
First, add the missing key to your locale files:
frontend/src/locales/en/common.json:
{
"errors": {
"profile_sync": "Profile synchronization error"
}
}frontend/src/locales/uk/common.json:
{
"errors": {
"profile_sync": "Помилка синхронізації профілю"
}
}Then update the error handler:
} catch (error) {
- console.error("Помилка синхронізації профілю:", error);
+ console.error(t("errors.profile_sync"), error);
dispatch(clearUser());
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.error("Помилка синхронізації профілю:", error); | |
| console.error(t("errors.profile_sync"), error); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/App.tsx` at line 125, Replace the hardcoded Ukrainian console
message in the App component's error handler with an i18n lookup: add
"errors.profile_sync" to your locale files (frontend/src/locales/en/common.json
and frontend/src/locales/uk/common.json) and change the console.error call in
App (where "Помилка синхронізації профілю" is used) to use the i18n translator
(e.g., t('errors.profile_sync')) obtained from your i18n hook/instance
(useTranslation or equivalent) so the logged message follows the user's language
setting and still includes the original error object.
| {value ? format(dateValue, "dd.MM.yyyy") : "Виберіть дату"} | ||
| </span> |
There was a problem hiding this comment.
Hardcoded Ukrainian text/locale blocks runtime language switching.
These labels and the forced uk locale make the picker non-translatable in English mode.
💡 Suggested fix
- {value ? format(dateValue, "dd.MM.yyyy") : "Виберіть дату"}
+ {value ? format(dateValue, "dd.MM.yyyy") : t("common.selectDate")}
- {format(currentMonth, "LLLL yyyy", { locale: uk })}
+ {format(currentMonth, "LLLL yyyy", { locale: dateFnsLocale })}
- {["Пн", "Вв", "Ср", "Чт", "Пт", "Сб", "Нд"].map((d) => (
+ {weekDayLabels.map((d) => (
- Час
+ {t("common.time")}Also applies to: 161-161, 181-187, 195-195
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/DateTimePicker.tsx` around lines 135 - 136, The
code in DateTimePicker.tsx hardcodes Ukrainian strings and a fixed 'uk' locale
(e.g., the "Виберіть дату" string and uses format(dateValue, "dd.MM.yyyy", {
locale: uk })) which prevents runtime language switching; update the component
(DateTimePicker) to use the app's i18n translation function (e.g.,
t('selectDate')) instead of the literal "Виберіть дату" wherever value is falsy,
and pass a dynamic locale object to date-fns/format (derive the locale from the
current app language/locale prop or context) so calls like format(dateValue,
"dd.MM.yyyy", { locale: dynamicLocale }) replace any hardcoded uk usage (also
fix the other occurrences around lines 161, 181-187, 195 that mirror this
pattern).
🧩 Analysis chain
🏁 Script executed:
cd frontend/src/components/ui && cat -n DateTimePicker.tsx | sed -n '1,150p'Repository: ugalaxy-team/Project
Length of output: 5582
🌐 Web query:
date-fns format() invalid date throws error
💡 Result:
Yes, the date-fns format function throws a RangeError: "Invalid time value" when passed an invalid date.[1] This occurs because format internally calls toDate on the provided argument to normalize it to a Date instance.[2][1] If the resulting date is invalid (i.e., isValid returns false), it throws the error.[1] Common causes include: - Passing a string directly instead of a Date object or timestamp. date-fns v2+ does not automatically parse strings (unlike v1); use new Date or parseISO first.[3][4][5] - new Date receiving an invalid string or value, producing an Invalid Date.[6] - Invalid numerical timestamps.[2] Example of error: import { format } from 'date-fns'; format('invalid', 'yyyy-MM-dd'); // RangeError: Invalid time value Fixes: const date = new Date('2023-01-01'); if (isValid(date)) { format(date, 'yyyy-MM-dd'); // Works } For ISO strings, use parseISO: import { parseISO } from 'date-fns'; format(parseISO('2023-01-01'), 'yyyy-MM-dd'); This behavior is consistent in current versions (v3+).[1][2]
Citations:
- 1: https://github.com/date-fns/date-fns/blob/main/src/format/index.ts
- 2: https://github.com/date-fns/date-fns/blob/main/src/toDate/index.ts
- 3: https://stackoverflow.com/questions/76077602/invalid-time-value-when-using-date-fns-package
- 4: Error on I send a Date String in format() date-fns/date-fns#3203
- 5: Invalid date error date-fns/date-fns#1421
- 6: https://stackoverflow.com/questions/70727103/date-fns-rangeerror-invalid-time-value
🏁 Script executed:
cd frontend/src/components/ui && grep -n "dateValue" DateTimePicker.tsxRepository: ugalaxy-team/Project
Length of output: 714
🏁 Script executed:
cd frontend/src/components/ui && wc -l DateTimePicker.tsx && cat -n DateTimePicker.tsx | sed -n '200,250p'Repository: ugalaxy-team/Project
Length of output: 2681
Guard all format() calls against invalid value input.
If value is malformed, parseISO(value) yields an invalid date and format() throws at render time. This affects multiple render locations (lines 135, 140, 208, 230).
Suggested fix
import {
format,
+ isValid,
addMonths,
subMonths,
startOfMonth,
endOfMonth,
startOfWeek,
endOfWeek,
isSameMonth,
isSameDay,
addDays,
setHours,
setMinutes,
parseISO,
} from "date-fns";
const DateTimePicker: React.FC<DateTimePickerProps> = ({
value,
onChange,
label,
}) => {
- const dateValue = value ? parseISO(value) : new Date();
+ const parsed = value ? parseISO(value) : new Date();
+ const dateValue = isValid(parsed) ? parsed : new Date();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/DateTimePicker.tsx` around lines 135 - 136, The
DateTimePicker component is calling format(dateValue, ...) without ensuring
dateValue is a valid Date, causing runtime throws when parseISO(value) is
invalid; update the component (references: DateTimePicker, parseISO, dateValue,
value, and every format(...) call at the mentioned render locations) to first
parse the value, check isValid(parsedDate) (from date-fns) and only call format
when valid, otherwise render a safe fallback string (e.g., the placeholder
"Виберіть дату" or the raw value) so format() is never invoked with an invalid
date.
| ns: [ | ||
| "common", | ||
| "auth", | ||
| "registration", | ||
| "home", | ||
| "about", | ||
| "faq", | ||
| "support", | ||
| "contact", | ||
| "tournaments", | ||
| "rules", | ||
| ], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "config.ts" | grep -i i18nRepository: ugalaxy-team/Project
Length of output: 94
🏁 Script executed:
cat -n frontend/src/i18n/config.ts | head -90Repository: ugalaxy-team/Project
Length of output: 2941
Add profile to the namespace list
The profile namespace is bundled in resources (lines 43 and 56) but missing from the ns array (lines 67-78). Add it to ensure proper namespace initialization.
ns: [
"common",
"auth",
"registration",
"home",
"about",
"faq",
"support",
"contact",
"tournaments",
"rules",
+ "profile",
],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ns: [ | |
| "common", | |
| "auth", | |
| "registration", | |
| "home", | |
| "about", | |
| "faq", | |
| "support", | |
| "contact", | |
| "tournaments", | |
| "rules", | |
| ], | |
| ns: [ | |
| "common", | |
| "auth", | |
| "registration", | |
| "home", | |
| "about", | |
| "faq", | |
| "support", | |
| "contact", | |
| "tournaments", | |
| "rules", | |
| "profile", | |
| ], |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/i18n/config.ts` around lines 67 - 78, The i18n namespace list
`ns` is missing the "profile" namespace that is included in the bundled
resources; update the `ns` array (in config.ts where `ns: [...]` is declared) to
include "profile" alongside "common", "auth", "registration", etc., so the
`profile` resource is initialized correctly.
| "404": { | ||
| "title": "Упс, пусто!", | ||
| "description": "Схоже, цієї сторінки не існує. Можливо, вона переїхала або ви відлетіли занадто далеко в космос.", | ||
| "go_home": "На головну" | ||
| }, |
There was a problem hiding this comment.
Add missing errors.404.back translation key
en/common.json includes errors.404.back, but Ukrainian errors.404 does not. This can cause untranslated key output on the 404 page for uk.
🌐 Proposed fix
"errors": {
"404": {
"title": "Упс, пусто!",
"description": "Схоже, цієї сторінки не існує. Можливо, вона переїхала або ви відлетіли занадто далеко в космос.",
+ "back": "Назад",
"go_home": "На головну"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "404": { | |
| "title": "Упс, пусто!", | |
| "description": "Схоже, цієї сторінки не існує. Можливо, вона переїхала або ви відлетіли занадто далеко в космос.", | |
| "go_home": "На головну" | |
| }, | |
| "404": { | |
| "title": "Упс, пусто!", | |
| "description": "Схоже, цієї сторінки не існує. Можливо, вона переїхала або ви відлетіли занадто далеко в космос.", | |
| "back": "Назад", | |
| "go_home": "На головну" | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/locales/uk/common.json` around lines 40 - 44, The Ukrainian
errors.404 object is missing the errors.404.back translation key present in
English; update the uk common translations by adding the errors.404.back entry
inside the existing "404" object (the same place as "title", "description",
"go_home") with an appropriate Ukrainian string (e.g., "Повернутися назад") so
the 404 page doesn't render an untranslated key.
| <button | ||
| type="button" | ||
| className="absolute right-4 text-text-muted hover:text-text-main transition-colors p-1" | ||
| onClick={() => setShowPassword(!showPassword)} | ||
| > | ||
| {showPassword ? <EyeOff size={20} /> : <Eye size={20} />} | ||
| </button> |
There was a problem hiding this comment.
Add accessible name to password visibility toggle
The icon-only button at Line 263 has no aria-label, so screen-reader users can’t identify its action.
♿ Proposed fix
<button
type="button"
className="absolute right-4 text-text-muted hover:text-text-main transition-colors p-1"
onClick={() => setShowPassword(!showPassword)}
+ aria-label={showPassword ? t("fields.password.hide") : t("fields.password.show")}
+ aria-pressed={showPassword}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| type="button" | |
| className="absolute right-4 text-text-muted hover:text-text-main transition-colors p-1" | |
| onClick={() => setShowPassword(!showPassword)} | |
| > | |
| {showPassword ? <EyeOff size={20} /> : <Eye size={20} />} | |
| </button> | |
| <button | |
| type="button" | |
| className="absolute right-4 text-text-muted hover:text-text-main transition-colors p-1" | |
| onClick={() => setShowPassword(!showPassword)} | |
| aria-label={showPassword ? t("fields.password.hide") : t("fields.password.show")} | |
| aria-pressed={showPassword} | |
| > | |
| {showPassword ? <EyeOff size={20} /> : <Eye size={20} />} | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Auth/AuthPage.tsx` around lines 263 - 269, The password
visibility toggle button in AuthPage.tsx (the button using showPassword,
setShowPassword and icons Eye/EyeOff) lacks an accessible name; add an
aria-label that updates based on showPassword (e.g., "Hide password" when
showPassword is true, "Show password" when false) so screen readers can describe
the action, and optionally include aria-pressed={showPassword} to convey state.
| // Додали currentUser, оскільки ми передаємо його з Profile.tsx | ||
| interface EditProfileModalProps { | ||
| isOpen: boolean; | ||
| onClose: () => void; | ||
| currentUser: any; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Replace any type with proper interface for currentUser prop.
The currentUser prop is typed as any, which defeats TypeScript's type safety. Since the component uses fields like displayName, full_name, telegram, github, and discord, define a proper interface or reuse the UserData interface from the parent component.
🔧 Suggested fix
+interface CurrentUserData {
+ uid: string;
+ displayName?: string;
+ full_name?: string;
+ telegram?: string;
+ github?: string;
+ discord?: string;
+}
+
interface EditProfileModalProps {
isOpen: boolean;
onClose: () => void;
- currentUser: any;
+ currentUser: CurrentUserData;
}Alternatively, if UserData from Profile.tsx can be shared, import and reuse it:
+import type { UserData } from './Profile';
+
interface EditProfileModalProps {
isOpen: boolean;
onClose: () => void;
- currentUser: any;
+ currentUser: UserData;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Profile/EditProfileModal.tsx` around lines 19 - 24,
EditProfileModalProps currently types currentUser as any—replace that with a
concrete user interface that includes the fields used by the component
(displayName, full_name, telegram, github, discord) or import and reuse the
existing UserData interface from Profile.tsx; update the EditProfileModalProps
definition to reference this interface (e.g., currentUser: UserData) and adjust
any local usages if necessary to satisfy TypeScript.
| const userTournaments = | ||
| user.created_tournaments && user.created_tournaments.length > 0 | ||
| ? user.created_tournaments.map( | ||
| (t) => t.title || t.name || t("tournament_unnamed"), | ||
| ) | ||
| : [t("no_tournaments")]; |
There was a problem hiding this comment.
Fix variable shadowing bug in tournament mapping.
Line 75 contains a critical bug: the map callback parameter t shadows the translation function t from useTranslation("profile"). When the code tries to call t("tournament_unnamed"), it's actually calling the tournament object as a function, which will cause a runtime error.
🐛 Fix the variable name collision
const userTournaments =
user.created_tournaments && user.created_tournaments.length > 0
? user.created_tournaments.map(
- (t) => t.title || t.name || t("tournament_unnamed"),
+ (tournament) => tournament.title || tournament.name || t("tournament_unnamed"),
)
: [t("no_tournaments")];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const userTournaments = | |
| user.created_tournaments && user.created_tournaments.length > 0 | |
| ? user.created_tournaments.map( | |
| (t) => t.title || t.name || t("tournament_unnamed"), | |
| ) | |
| : [t("no_tournaments")]; | |
| const userTournaments = | |
| user.created_tournaments && user.created_tournaments.length > 0 | |
| ? user.created_tournaments.map( | |
| (tournament) => tournament.title || tournament.name || t("tournament_unnamed"), | |
| ) | |
| : [t("no_tournaments")]; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Profile/Profile.tsx` around lines 72 - 77, The map
callback in userTournaments is shadowing the translation function t from
useTranslation("profile"); rename the map parameter (e.g., from t to tournament
or createdTournament) and update its uses inside the callback (t.title || t.name
-> tournament.title || tournament.name) so that calls to t("tournament_unnamed")
and t("no_tournaments") refer to the translation function, not the mapped
object.








Чучуть оновив дизайн фронтенду, якщо коротко то я забув що міняв давно, але загалом перекопав майже все. Ось що тут є:
Відрефакторив сторінки турнірів,додав динамічні статуси і плавні таби. А рулетка на головній тепер розумна, крутить і видає тільки активні івенти.Коротше, стало швидше, темніше і красивіше і загалом краса неписана (вроді, я надіюсь)
Ну я попрацював над адаптивністю під різні пристрої і іншим, надіюсь гарно :)
Summary by CodeRabbit
New Features
Style
Tests