feat: Fixed footer (check App.tsx&components/MainLayout.tsx) and adde…#8
feat: Fixed footer (check App.tsx&components/MainLayout.tsx) and adde…#8
Conversation
…d tournaments page
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/Profile/Profile.tsx (1)
63-83:⚠️ Potential issue | 🟠 MajorReplace hardcoded contact information with actual user data.
The contact section displays placeholder values (
hacker777@example.com,@hacker777) instead of the authenticated user's actual contact information. This defeats the purpose of a user profile page.Consider retrieving and displaying the user's real contact information from the user object or Firebase profile. At minimum, the email should come from
user.emailorauth.currentUser.email.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/Profile/Profile.tsx` around lines 63 - 83, The contact section currently uses hardcoded placeholders; replace those strings in the JSX (elements with className "contact-section" / "contact-chip") to read from the real user object (e.g. use the authenticated user prop or auth.currentUser) and render user.email (for mailto href and visible text) and the user's telegram/github/discord fields (e.g. user.telegram, user.github, user.discord) with sensible fallbacks (empty string or "-" if missing); ensure mailto uses user.email or auth.currentUser.email, and that links for Telegram/GitHub/Discord point to the correct URLs if available or render as plain text when not.
🧹 Nitpick comments (1)
frontend/src/pages/Profile/Profile.tsx (1)
21-23: Consider displaying error feedback to the user.The error handler only logs to the console. Users won't receive feedback if account deletion fails. Consider showing a toast notification or alert to inform the user of the error.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/Profile/Profile.tsx` around lines 21 - 23, The onError callback in Profile.tsx currently only logs the error (onError: (e) => { console.log(...e.message) }), so surface failures to the user by showing a UI notification; update the onError handler to call your app's notification utility (e.g., toast.error, enqueueSnackbar, or set state that renders an Alert) with a clear message including e.message, and ensure the UI component that renders toasts/alerts is available in this page; keep the console.log for debugging if desired but add the user-facing notification in the onError handler of the delete account mutation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/TournamentCard.tsx`:
- Around line 28-40: The CTA button in TournamentCard is rendered inert for the
"completed" state (the completed config object and the final CTA render in
TournamentCard), so wire it to the real tournament page: import Link from
react-router-dom and wrap the CTA/button output (where btnText and btnClass are
used) with <Link to={`/tournament/${tournament.id}`}> (or attach an onClick that
navigates programmatically) for both places using the completed variant (the
object named completed and the JSX that renders the button), and remove the
disabled/cursor-default styling so the Link is clickable; if the tournament has
no id or should be non-navigable, render plain text instead.
- Around line 62-78: The gradId is currently derived from title (const gradId =
`grad-${title.replace(...)}`) which breaks when titles contain appended tokens
like “#1”; change it to use the stable tournament id instead (e.g., const gradId
= `grad-${id}` or `grad-${tournament.id}` depending on the prop name) and update
the linearGradient id and the SVG fill usage (id={gradId} and
fill={`url(#${gradId})` remain) so the gradient id is deterministic and safe;
keep the same "grad-" prefix to avoid collisions if needed.
In `@frontend/src/pages/Profile/Profile.tsx`:
- Line 22: Fix the spelling mistake in the console log message: change "An error
occured while trying to delete account" to "An error occurred while trying to
delete account" where the console.log is called (inside the Profile component's
delete-account error handler / the console.log call shown).
- Around line 56-58: The delete button currently invokes handleDeleteUser
directly causing immediate account deletion; update the Profile component so
clicking the "Видалити профіль" button opens a confirmation dialog (browser
confirm() or a modal) and only calls handleDeleteUser if the user explicitly
confirms; locate the button and the handleDeleteUser function in Profile.tsx and
wrap the call in a confirmation check (e.g., if (confirm('Are you sure...'))
handleDeleteUser()) or trigger your modal and call handleDeleteUser from the
modal's confirm action.
In `@frontend/src/pages/TournamentsPage/TournamentsPage.tsx`:
- Around line 67-73: The search input currently lacks an accessible label; add a
labeled association by giving the input an id (e.g., "tournament-search" or
similar) and adding a <label> element bound via htmlFor that id (the label can
be visually hidden for design); update the JSX around the <input> in
TournamentsPage to include this label (referencing the existing value={query}
and onChange={handleSearch}) so the field has a stable accessible name for
screen readers.
- Around line 76-103: The filter buttons (using handleFilterChange and reading
filter) convey selection only via color; add accessible state by setting
aria-pressed on each button to true when filter === "<status>" and false
otherwise (e.g., aria-pressed={filter === "all"} for the "Всі події" button) so
assistive tech can announce the active filter; apply the same aria-pressed
pattern to the buttons tied to "registration", "active", and "completed" states.
---
Outside diff comments:
In `@frontend/src/pages/Profile/Profile.tsx`:
- Around line 63-83: The contact section currently uses hardcoded placeholders;
replace those strings in the JSX (elements with className "contact-section" /
"contact-chip") to read from the real user object (e.g. use the authenticated
user prop or auth.currentUser) and render user.email (for mailto href and
visible text) and the user's telegram/github/discord fields (e.g. user.telegram,
user.github, user.discord) with sensible fallbacks (empty string or "-" if
missing); ensure mailto uses user.email or auth.currentUser.email, and that
links for Telegram/GitHub/Discord point to the correct URLs if available or
render as plain text when not.
---
Nitpick comments:
In `@frontend/src/pages/Profile/Profile.tsx`:
- Around line 21-23: The onError callback in Profile.tsx currently only logs the
error (onError: (e) => { console.log(...e.message) }), so surface failures to
the user by showing a UI notification; update the onError handler to call your
app's notification utility (e.g., toast.error, enqueueSnackbar, or set state
that renders an Alert) with a clear message including e.message, and ensure the
UI component that renders toasts/alerts is available in this page; keep the
console.log for debugging if desired but add the user-facing notification in the
onError handler of the delete account mutation.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 60eb4f30-78d8-4978-94b3-187896b5e8ae
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (7)
frontend/src/App.tsxfrontend/src/components/MainLayout.tsxfrontend/src/components/TournamentCard.tsxfrontend/src/data/mockTournaments.tsfrontend/src/pages/Home/components/TournamentSlider.tsxfrontend/src/pages/Profile/Profile.tsxfrontend/src/pages/TournamentsPage/TournamentsPage.tsx
| completed: { | ||
| label: "Завершено", | ||
| badgeBg: "bg-slate-100", | ||
| badgeText: "text-slate-500", | ||
| dot: "bg-slate-400", | ||
| gradFrom: "#94a3b8", | ||
| gradTo: "#475569", | ||
| icon: ( | ||
| <path d="M12 2a5 5 0 100 10A5 5 0 0012 2zm0 12c-5.33 0-8 2.67-8 4v2h16v-2c0-1.33-2.67-4-8-4z" /> | ||
| ), | ||
| btnText: "Переглянути результати", | ||
| btnClass: "bg-bg-body text-slate-400 cursor-default", | ||
| }, |
There was a problem hiding this comment.
Wire the CTA to an actual destination.
Lines 152-156 render a live control with no onClick, and the "Переглянути результати" state is even disabled. That leaves the card CTA inert, although frontend/src/App.tsx, Line 29 already defines a /tournament/:id route. Use a Link or real handler here; otherwise render plain text instead of a button.
Proposed fix
import React from "react";
+import { Link } from "react-router-dom";
import { type Tournament } from "../data/mockTournaments";
@@
completed: {
@@
btnText: "Переглянути результати",
- btnClass: "bg-bg-body text-slate-400 cursor-default",
+ btnClass: "bg-bg-body text-slate-500 hover:bg-slate-100",
},
};
@@
export const TournamentCard = ({
+ id,
status,
title,
tags,
desc,
teams,
max,
deadline,
}: Tournament) => {
@@
- <button
- disabled={status === "completed"}
- className={`w-full py-3 rounded-xl font-quicksand text-[16px] font-extrabold transition-all duration-200 ${cfg.btnClass}`}
- >
- {cfg.btnText}
- </button>
+ <Link
+ to={`/tournament/${id}`}
+ className={`w-full py-3 rounded-xl font-quicksand text-[16px] font-extrabold transition-all duration-200 text-center ${cfg.btnClass}`}
+ >
+ {cfg.btnText}
+ </Link>Also applies to: 50-58, 152-156
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/TournamentCard.tsx` around lines 28 - 40, The CTA
button in TournamentCard is rendered inert for the "completed" state (the
completed config object and the final CTA render in TournamentCard), so wire it
to the real tournament page: import Link from react-router-dom and wrap the
CTA/button output (where btnText and btnClass are used) with <Link
to={`/tournament/${tournament.id}`}> (or attach an onClick that navigates
programmatically) for both places using the completed variant (the object named
completed and the JSX that renders the button), and remove the
disabled/cursor-default styling so the Link is clickable; if the tournament has
no id or should be non-navigable, render plain text instead.
| const gradId = `grad-${title.replace(/\s+/g, "-")}`; | ||
|
|
||
| return ( | ||
| <div | ||
| className={`w-full h-full rounded-[32px] p-8 flex flex-col relative shadow-[0_10px_30px_-10px_rgba(0,0,0,0.08)] transition-shadow duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:shadow-[0_20px_40px_-10px_rgba(0,0,0,0.12)] ${ | ||
| isDefaultWhiteCard ? "bg-bg-card border border-slate-200" : "" | ||
| }`} | ||
| style={cardStyle} | ||
| > | ||
| <div className="flex-grow flex flex-col"> | ||
| <div className="flex gap-2 mb-5 flex-wrap"> | ||
| <div className="bg-white rounded-[28px] border-[1.5px] border-slate-200 shadow-sm flex flex-col overflow-hidden transition-all duration-300 ease-out hover:-translate-y-1 hover:shadow-[0_16px_32px_rgba(15,23,42,0.08)]"> | ||
| <div className="p-7 flex flex-col flex-grow"> | ||
| <div className="flex items-start gap-3 mb-3"> | ||
| <div className="shrink-0 w-11 h-11 flex items-center justify-center relative drop-shadow-md group-hover:rotate-6 transition-transform duration-300"> | ||
| <svg className="absolute inset-0 w-full h-full" viewBox="0 0 24 24"> | ||
| <defs> | ||
| <linearGradient id={gradId} x1="0%" y1="0%" x2="100%" y2="100%"> | ||
| <stop offset="0%" stopColor={cfg.gradFrom} /> | ||
| <stop offset="100%" stopColor={cfg.gradTo} /> | ||
| </linearGradient> | ||
| </defs> | ||
| <path | ||
| fill={`url(#${gradId})`} | ||
| d="M12 2l2.4 2.3 3.3-.4.8 3.2 2.9 1.7-1.8 2.8 1.8 2.8-2.9 1.7-.8 3.2-3.3-.4L12 22l-2.4-2.3-3.3.4-.8-3.2-2.9-1.7 1.8-2.8-1.8-2.8 2.9-1.7.8-3.2 3.3.4L12 2z" |
There was a problem hiding this comment.
Don’t derive gradId from title.
Line 62 can now produce values like grad-...-#1 because frontend/src/data/mockTournaments.ts, Line 115 appends #<n> to mock titles. Reusing raw titles inside url(#...) is brittle; use a safe token such as the tournament id instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/TournamentCard.tsx` around lines 62 - 78, The gradId
is currently derived from title (const gradId = `grad-${title.replace(...)}`)
which breaks when titles contain appended tokens like “#1”; change it to use the
stable tournament id instead (e.g., const gradId = `grad-${id}` or
`grad-${tournament.id}` depending on the prop name) and update the
linearGradient id and the SVG fill usage (id={gradId} and
fill={`url(#${gradId})` remain) so the gradient id is deterministic and safe;
keep the same "grad-" prefix to avoid collisions if needed.
| <input | ||
| type="text" | ||
| value={query} | ||
| onChange={handleSearch} | ||
| placeholder="Пошук турніру…" | ||
| className="w-full md:w-[240px] pl-[38px] pr-4 py-2.5 bg-slate-50 border-[1.5px] border-slate-200 rounded-xl text-[14px] font-bold outline-none focus:border-primary focus:bg-white transition-colors placeholder:text-slate-300 placeholder:font-semibold" | ||
| /> |
There was a problem hiding this comment.
Add a real label to the search field.
Line 67 currently relies on placeholder text only, so the input has no stable accessible name. Add a bound <label> here; a visually hidden label is fine.
Proposed fix
+ <label htmlFor="tournament-search" className="sr-only">
+ Пошук турніру
+ </label>
<input
+ id="tournament-search"
type="text"
value={query}
onChange={handleSearch}
placeholder="Пошук турніру…"📝 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.
| <input | |
| type="text" | |
| value={query} | |
| onChange={handleSearch} | |
| placeholder="Пошук турніру…" | |
| className="w-full md:w-[240px] pl-[38px] pr-4 py-2.5 bg-slate-50 border-[1.5px] border-slate-200 rounded-xl text-[14px] font-bold outline-none focus:border-primary focus:bg-white transition-colors placeholder:text-slate-300 placeholder:font-semibold" | |
| /> | |
| <label htmlFor="tournament-search" className="sr-only"> | |
| Пошук турніру | |
| </label> | |
| <input | |
| id="tournament-search" | |
| type="text" | |
| value={query} | |
| onChange={handleSearch} | |
| placeholder="Пошук турніру…" | |
| className="w-full md:w-[240px] pl-[38px] pr-4 py-2.5 bg-slate-50 border-[1.5px] border-slate-200 rounded-xl text-[14px] font-bold outline-none focus:border-primary focus:bg-white transition-colors placeholder:text-slate-300 placeholder:font-semibold" | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/pages/TournamentsPage/TournamentsPage.tsx` around lines 67 - 73,
The search input currently lacks an accessible label; add a labeled association
by giving the input an id (e.g., "tournament-search" or similar) and adding a
<label> element bound via htmlFor that id (the label can be visually hidden for
design); update the JSX around the <input> in TournamentsPage to include this
label (referencing the existing value={query} and onChange={handleSearch}) so
the field has a stable accessible name for screen readers.
| <div className="flex gap-1 flex-wrap"> | ||
| <button | ||
| onClick={() => handleFilterChange("all")} | ||
| className={`px-4 py-2 rounded-xl text-[14px] font-bold transition-all flex items-center gap-1.5 ${filter === "all" ? "bg-primary text-white shadow-md shadow-primary/20" : "text-slate-500 hover:bg-slate-50 hover:text-slate-900"}`} | ||
| > | ||
| Всі події | ||
| </button> | ||
| <button | ||
| onClick={() => handleFilterChange("registration")} | ||
| className={`px-4 py-2 rounded-xl text-[14px] font-bold transition-all flex items-center gap-1.5 ${filter === "registration" ? "bg-primary text-white shadow-md shadow-primary/20" : "text-slate-500 hover:bg-slate-50 hover:text-slate-900"}`} | ||
| > | ||
| <span className="w-1.5 h-1.5 rounded-full bg-green-500" /> | ||
| Реєстрація | ||
| </button> | ||
| <button | ||
| onClick={() => handleFilterChange("active")} | ||
| className={`px-4 py-2 rounded-xl text-[14px] font-bold transition-all flex items-center gap-1.5 ${filter === "active" ? "bg-primary text-white shadow-md shadow-primary/20" : "text-slate-500 hover:bg-slate-50 hover:text-slate-900"}`} | ||
| > | ||
| <span className="w-1.5 h-1.5 rounded-full bg-pink-500" /> В | ||
| процесі | ||
| </button> | ||
| <button | ||
| onClick={() => handleFilterChange("completed")} | ||
| className={`px-4 py-2 rounded-xl text-[14px] font-bold transition-all flex items-center gap-1.5 ${filter === "completed" ? "bg-primary text-white shadow-md shadow-primary/20" : "text-slate-500 hover:bg-slate-50 hover:text-slate-900"}`} | ||
| > | ||
| <span className="w-1.5 h-1.5 rounded-full bg-slate-400" /> Архів | ||
| </button> | ||
| </div> |
There was a problem hiding this comment.
Expose the active filter state semantically.
These controls behave like a toggle group, but the selected state is only conveyed by color. Add aria-pressed to each button (or model them as radios) so assistive tech can announce which filter is active.
Suggested change
<button
onClick={() => handleFilterChange("all")}
+ aria-pressed={filter === "all"}
className={`px-4 py-2 rounded-xl text-[14px] font-bold transition-all flex items-center gap-1.5 ${filter === "all" ? "bg-primary text-white shadow-md shadow-primary/20" : "text-slate-500 hover:bg-slate-50 hover:text-slate-900"}`}
>Repeat the same pattern for the other status buttons.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/pages/TournamentsPage/TournamentsPage.tsx` around lines 76 -
103, The filter buttons (using handleFilterChange and reading filter) convey
selection only via color; add accessible state by setting aria-pressed on each
button to true when filter === "<status>" and false otherwise (e.g.,
aria-pressed={filter === "all"} for the "Всі події" button) so assistive tech
can announce the active filter; apply the same aria-pressed pattern to the
buttons tied to "registration", "active", and "completed" states.
Summary
Implemented the Tournaments page with full filtering and pagination support.
Main Changes
TournamentsPagewith search and category filters.UI Updates
TournamentCardstyling and layout.Summary by CodeRabbit
Release Notes
New Features
Refactor