Skip to content

feat: Fixed footer (check App.tsx&components/MainLayout.tsx) and adde…#8

Open
izachoc wants to merge 1 commit intodevfrom
tournaments-page
Open

feat: Fixed footer (check App.tsx&components/MainLayout.tsx) and adde…#8
izachoc wants to merge 1 commit intodevfrom
tournaments-page

Conversation

@izachoc
Copy link
Copy Markdown
Member

@izachoc izachoc commented Mar 26, 2026

Summary

Implemented the Tournaments page with full filtering and pagination support.

Main Changes

  • Added TournamentsPage with search and category filters.
  • Mock Data with test

UI Updates

  • Refined TournamentCard styling and layout.
  • Improved general layout responsiveness and fixed footer alignment

Summary by CodeRabbit

Release Notes

  • New Features

    • Tournaments listing page with search, status filtering, and pagination controls.
    • Tournament cards now display status badges, team counts with progress indicators, and deadline dates.
    • Improved application layout with consistent header and footer placement across pages.
  • Refactor

    • Code style standardization and formatting improvements.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: de445534-4d0f-48cc-be05-d8711b135270

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

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tournaments-page

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.

❤️ Share

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

@izachoc izachoc requested review from AnnPoshtak and Fundi1330 and removed request for Fundi1330 March 26, 2026 17:03
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 | 🟠 Major

Replace 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.email or auth.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

📥 Commits

Reviewing files that changed from the base of the PR and between 9589105 and 17bd6ec.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • frontend/src/App.tsx
  • frontend/src/components/MainLayout.tsx
  • frontend/src/components/TournamentCard.tsx
  • frontend/src/data/mockTournaments.ts
  • frontend/src/pages/Home/components/TournamentSlider.tsx
  • frontend/src/pages/Profile/Profile.tsx
  • frontend/src/pages/TournamentsPage/TournamentsPage.tsx

Comment on lines +28 to +40
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",
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +62 to +78
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"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +67 to +73
<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"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<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.

Comment on lines +76 to +103
<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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant