feat: upcoming contests tracker for Codeforces with in-app reminders - #269
Conversation
|
@ida-jemi is attempting to deploy a commit to the Kunal Verma's projects Team on Vercel. A member of the Team first needs to authorize it. |
🎉 Welcome to CodeLens — Thank You for Your Contribution!Hey @ida-jemi! 👋 We are genuinely excited to have you here. Every single PR — big or small — makes CodeLens better, and yours is no exception. Take a moment to review the checklist below to help us merge your work quickly and smoothly. ✅ Before Requesting a Review
💬 Join Our Community Channel — This is MandatoryBeing part of our communication channel is compulsory for all contributors, not optional. Why join? This is where all important announcements, PR review updates, contribution discussions, and maintainer decisions happen in real time. Contributors who are not in the channel regularly miss critical context and updates, which often leads to duplicated or misaligned work. Staying connected here is what keeps the community strong and your contributions impactful. We are rooting for you! If you have any questions, drop them in the channel or comment right here on this PR. Let's build something great together. 🚀✨ |
|
Important Review skippedNo new commits to review since the last review. ⚙️ 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:
📝 WalkthroughWalkthroughThis PR adds Codeforces upcoming contests support with cached contest data, reminder persistence and APIs, an hourly sync job, and frontend contest display and notification UI wired into the dashboard, contest page, navbar, and layout. ChangesContest Tracker Feature
Estimated code review effort: 4 (Complex) | ~60 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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: 3
🧹 Nitpick comments (4)
server/models/ContestReminder.js (1)
9-14: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueRedundant single-field index on
user.The compound unique index at Line 27-30 already starts with
user, so it can satisfy any query filtering byuseralone. Theindex: trueon theuserfield (Line 13) creates a second, unnecessary index that adds write/storage overhead without new query coverage.♻️ Proposed fix
user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, - index: true, },Also applies to: 27-30
🤖 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 `@server/models/ContestReminder.js` around lines 9 - 14, The `ContestReminder` schema has a redundant standalone index on `user`; remove the `index: true` from the `user` field and keep the compound unique index in the schema so queries by `user` are still covered without duplicating index maintenance. Update the `mongoose.Schema` definition in `ContestReminder.js` by adjusting the `user` field and leaving the compound index declaration as the single source of indexing for this key.server/modules/contests/repository.js (1)
79-93: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
pruneStaleRemindersscans all historical finished contests instead of scoping to active reminders.This query fetches every
FINISHEDcontest document in the collection on every hourly sync, which grows unbounded with the total number of Codeforces contests ever cached, not with the (much smaller) number of active reminders. Scoping the lookup to contest IDs that actually have reminders first is more efficient and scales with reminder count instead of total contest history.♻️ Proposed fix
static async pruneStaleReminders(platform = "codeforces") { - const finishedContestIds = await Contest.find({ - platform, - phase: "FINISHED", - }) - .select("contestId -_id") - .lean(); - - if (!finishedContestIds.length) return; - - await ContestReminder.deleteMany({ - platform, - contestId: { $in: finishedContestIds.map((c) => c.contestId) }, - }); + const reminderContestIds = await ContestReminder.find({ platform }) + .distinct("contestId"); + + if (!reminderContestIds.length) return; + + const finishedContestIds = await Contest.find({ + platform, + contestId: { $in: reminderContestIds }, + phase: "FINISHED", + }) + .select("contestId -_id") + .lean(); + + if (!finishedContestIds.length) return; + + await ContestReminder.deleteMany({ + platform, + contestId: { $in: finishedContestIds.map((c) => c.contestId) }, + }); }🤖 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 `@server/modules/contests/repository.js` around lines 79 - 93, `pruneStaleReminders` is loading every finished contest, which makes the hourly cleanup scale with contest history instead of reminder usage. Update the `Contest` lookup in `ContestReminder` pruning to first collect the contest IDs that currently have reminders for the given platform, then query only matching `FINISHED` contests and delete reminders for that intersection. Keep the behavior inside `pruneStaleReminders` and use the existing `Contest` and `ContestReminder` models to preserve the same deletion result with a smaller scoped lookup.frontend/src/components/contests/ContestReminderBell.jsx (1)
13-38: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDuplicate polling of
getMyActiveRemindersalongsideContestReminderNotifier.This component polls
/contests/reminders/activeevery 60s whilefrontend/src/components/shared/ContestReminderNotifier.jsx(mounted globally inMainLayout) independently polls the same endpoint every 30s. Since both are rendered simultaneously for every authenticated page, this results in two redundant, uncoordinated network requests to the same backend endpoint. Consider extracting a shared hook (e.g.useActiveReminders) or a small context provider that both components consume, so there's a single polling source of truth.🤖 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/contests/ContestReminderBell.jsx` around lines 13 - 38, The `ContestReminderBell` effect is duplicating the same active-reminders polling already done by `ContestReminderNotifier`, causing redundant requests to `/contests/reminders/active`. Refactor the polling logic in `ContestReminderBell` and `ContestReminderNotifier` to consume a shared source of truth, such as a `useActiveReminders` hook or a small context/provider, so only one polling loop calls `getMyActiveReminders` and both components read from the same reminder state.frontend/src/components/contests/UpcomingContestsList.jsx (1)
38-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReminder intent lost on redirect to
/login.Unauthenticated users clicking "Remind Me" are redirected to
/loginwith no way to resume the reminder toggle after authenticating.🤖 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/contests/UpcomingContestsList.jsx` around lines 38 - 49, The reminder action in handleReminderClick is lost when unauthenticated users are redirected to login. Update the UpcomingContestsList flow so the clicked contestId and reminder intent are preserved before navigating to /login, then have the post-login path or login page recover that state and retry toggleReminder(contestId) after authentication. Use the existing handleReminderClick, navigate, and toggleReminder symbols to keep the reminder request resumable.
🤖 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/components/contests/ContestReminderBell.jsx`:
- Around line 22-26: The due-soon calculation in ContestReminderBell.jsx only
applies an upper-bound window, so started or past contests can still be counted;
update the filter in the reminder badge logic to require the contest start time
to be in the future as well as within DUE_SOON_WINDOW_MS. Use the same
lower-bound check pattern already used in ContestReminderNotifier.jsx
(msUntilStart > 0) and apply it around the existing Date.now() /
startTimeSeconds comparison before setDueSoonCount runs.
In `@frontend/src/components/contests/UpcomingContestsList.jsx`:
- Around line 19-23: The duration formatter can render invalid values like “1h
60m” because formatDuration rounds the minute remainder. Update formatDuration
to normalize the remainder so minutes never reach 60, either by flooring the
minutes or by carrying any rounded overflow into hours, and keep the display
logic consistent for UpcomingContestsList.
In `@server/modules/contests/validation.js`:
- Around line 4-18: Update the validate middleware in validate(schema) so it
uses result.error.issues instead of result.error.errors when building the 400
response payload. The current mapping over errors breaks on Zod 4 invalid input,
so change the error collection inside the safeParse failure branch to read from
the ZodError issues array and keep the existing field/message shape in the JSON
response.
---
Nitpick comments:
In `@frontend/src/components/contests/ContestReminderBell.jsx`:
- Around line 13-38: The `ContestReminderBell` effect is duplicating the same
active-reminders polling already done by `ContestReminderNotifier`, causing
redundant requests to `/contests/reminders/active`. Refactor the polling logic
in `ContestReminderBell` and `ContestReminderNotifier` to consume a shared
source of truth, such as a `useActiveReminders` hook or a small
context/provider, so only one polling loop calls `getMyActiveReminders` and both
components read from the same reminder state.
In `@frontend/src/components/contests/UpcomingContestsList.jsx`:
- Around line 38-49: The reminder action in handleReminderClick is lost when
unauthenticated users are redirected to login. Update the UpcomingContestsList
flow so the clicked contestId and reminder intent are preserved before
navigating to /login, then have the post-login path or login page recover that
state and retry toggleReminder(contestId) after authentication. Use the existing
handleReminderClick, navigate, and toggleReminder symbols to keep the reminder
request resumable.
In `@server/models/ContestReminder.js`:
- Around line 9-14: The `ContestReminder` schema has a redundant standalone
index on `user`; remove the `index: true` from the `user` field and keep the
compound unique index in the schema so queries by `user` are still covered
without duplicating index maintenance. Update the `mongoose.Schema` definition
in `ContestReminder.js` by adjusting the `user` field and leaving the compound
index declaration as the single source of indexing for this key.
In `@server/modules/contests/repository.js`:
- Around line 79-93: `pruneStaleReminders` is loading every finished contest,
which makes the hourly cleanup scale with contest history instead of reminder
usage. Update the `Contest` lookup in `ContestReminder` pruning to first collect
the contest IDs that currently have reminders for the given platform, then query
only matching `FINISHED` contests and delete reminders for that intersection.
Keep the behavior inside `pruneStaleReminders` and use the existing `Contest`
and `ContestReminder` models to preserve the same deletion result with a smaller
scoped lookup.
🪄 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: faa3cc58-c5a6-4adc-b307-37bc44f5d65e
⛔ Files ignored due to path filters (1)
server/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (22)
frontend/src/components/contests/ContestCountdown.jsxfrontend/src/components/contests/ContestReminderBell.jsxfrontend/src/components/contests/UpcomingContestsList.jsxfrontend/src/components/dashboard/UpcomingContestsWidget.jsxfrontend/src/components/shared/ContestReminderNotifier.jsxfrontend/src/components/shared/Navbar.jsxfrontend/src/hooks/useContests.jsfrontend/src/layouts/MainLayout.jsxfrontend/src/pages/ContestCodeforcesPage.jsxfrontend/src/pages/DashboardPage.jsxfrontend/src/services/contestService.jsserver/app.jsserver/jobs/contestSync.jsserver/models/Contest.jsserver/models/ContestReminder.jsserver/modules/contests/controller.jsserver/modules/contests/repository.jsserver/modules/contests/routes.jsserver/modules/contests/service.jsserver/modules/contests/validation.jsserver/package.jsonserver/server.js
- fix due-soon badge counting started/past contests - fix duration formatter rounding overflow (60m -> next hour) - use ZodError.issues instead of removed .errors (Zod v4)
|
Hi @ida-jemi, I finally had the opportunity to go through your implementation, and I must say I'm genuinely impressed. This is a remarkably comprehensive full-stack contribution. It's evident that you approached the feature with a strong engineering mindset rather than simply making it work. What stood out the most was how meticulously you've organized the implementation. The project structure, module separation, folder hierarchy, reusable components, hooks, services, and overall code organization demonstrate that you invested time in understanding the existing architecture before building on top of it. That level of discipline is something I deeply appreciate because it shows a solid understanding of the project instead of treating it as an isolated feature. Contributors who can architect and deliver end to end features like this are quite rare. This kind of ownership and execution is exactly what makes an exceptional open source contributor. I genuinely hope you continue taking on ambitious features like this because I believe you can make a significant impact on CodeLens. I'd really appreciate it if you could record another demo video showcasing the entire feature from an end user's perspective. I'd love to see the complete workflow from viewing upcoming contests, setting reminders, the dashboard widget, navbar badge, reminder notifications, and how everything behaves in real world usage. It would also be great if you could use your own voice while explaining the implementation, the architecture, and the reasoning behind your design decisions. Think of it as a short technical presentation where you walk me through both the user experience and the codebase. I'd also love to have a short Google Meet with you to discuss this feature in more detail. It's an exceptional contribution, and I think a quick technical discussion would be valuable before we move forward. Let me know a time that works for you. Also, if you haven't already, please join our communication channel on Matrix. It'll make it much easier for us to discuss this feature and a few additional ideas going forward. |
|
Hi @kunalverma2512 Thank you for the review and feedback! Here's a video of a complete walkthrough: CodeLens.1.mp4Note: The bell is a simple link to the tracker rather than a dropdown, since the toast and dashboard widget already surface reminder details, this keeps the navbar footprint minimal for v1. Explanation of the architechture and reasoning:
Also, I couldn't find a link to the Matrix channel anywhere in the README/CONTRIBUTING docs, it would be great if you could help me with that. Thank you!! |
|
@ida-jemi Good and the presentation was so much beautiful because its rarely contributors shows inspect , changes in DB to show future actions and other stuffs you showed for which I appreciate your whole work Click here to visit our matrix channel (😄 btw when you make PR then in Welcome message Chatbot it shows out communication matrix channel) Apart from this I see their some work more to do before merging it Some work I can tell here but some need dedicated discussion for which try to schedule a google meet as possible (text me on communication channel)
complete till point 3 and then further discussion as said in point 4 above |
… layout, fix contest page hero background
There was a problem hiding this comment.
♻️ Duplicate comments (1)
frontend/src/components/contests/UpcomingContestsList.jsx (1)
20-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
formatDurationstill produces invalid "60m" display — past fix not applied.
Math.round((durationSeconds % 3600) / 60)can yield60(e.g.,durationSeconds = 7199→"1h 60m"). This was flagged in a prior review and marked addressed, but the current code still has the same pattern.🐛 Proposed fix
function formatDuration(durationSeconds) { - const hours = Math.floor(durationSeconds / 3600); - const minutes = Math.round((durationSeconds % 3600) / 60); - return minutes ? `${hours}h ${minutes}m` : `${hours}h`; + const totalMinutes = Math.round(durationSeconds / 60); + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return minutes ? `${hours}h ${minutes}m` : `${hours}h`; }🤖 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/contests/UpcomingContestsList.jsx` around lines 20 - 24, `formatDuration` in UpcomingContestsList still rounds minutes in a way that can produce an invalid 60m display. Update the duration formatting logic inside `formatDuration` so the minutes value is derived without rounding up into 60, and handle any carry into hours before returning the formatted string; use the existing `formatDuration` helper to locate the fix.
🧹 Nitpick comments (1)
frontend/src/components/contests/UpcomingContestsList.jsx (1)
166-187: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
aria-pressedto the reminder toggle button for screen reader accessibility.The button's visual styling changes based on
contest.hasReminder, but the state isn't conveyed to assistive technology. Addingaria-pressed={contest.hasReminder}lets screen readers announce the toggle state.♿ Proposed fix
<button + aria-pressed={contest.hasReminder} onClick={() => handleReminderClick(contest.contestId)} className={`mt-auto w-full flex items-center justify-center gap-2 border-4 border-black font-black uppercase tracking-widest transition-colors ${🤖 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/contests/UpcomingContestsList.jsx` around lines 166 - 187, The reminder button in UpcomingContestsList should expose its toggle state to assistive tech by adding an aria-pressed attribute driven by contest.hasReminder. Update the button element in the reminder toggle block so the accessibility state matches the existing visual state changes, using the same contest.hasReminder value that already controls the label and styling.
🤖 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.
Duplicate comments:
In `@frontend/src/components/contests/UpcomingContestsList.jsx`:
- Around line 20-24: `formatDuration` in UpcomingContestsList still rounds
minutes in a way that can produce an invalid 60m display. Update the duration
formatting logic inside `formatDuration` so the minutes value is derived without
rounding up into 60, and handle any carry into hours before returning the
formatted string; use the existing `formatDuration` helper to locate the fix.
---
Nitpick comments:
In `@frontend/src/components/contests/UpcomingContestsList.jsx`:
- Around line 166-187: The reminder button in UpcomingContestsList should expose
its toggle state to assistive tech by adding an aria-pressed attribute driven by
contest.hasReminder. Update the button element in the reminder toggle block so
the accessibility state matches the existing visual state changes, using the
same contest.hasReminder value that already controls the label and styling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0ecc4d15-88c1-441c-867a-18027497d88a
📒 Files selected for processing (6)
frontend/src/components/contests/ContestCountdown.jsxfrontend/src/components/contests/ContestReminderBell.jsxfrontend/src/components/contests/UpcomingContestsList.jsxfrontend/src/components/dashboard/UpcomingContestsWidget.jsxfrontend/src/components/shared/ContestReminderNotifier.jsxfrontend/src/pages/ContestCodeforcesPage.jsx
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/src/components/dashboard/UpcomingContestsWidget.jsx
- frontend/src/pages/ContestCodeforcesPage.jsx
- frontend/src/components/contests/ContestReminderBell.jsx
- frontend/src/components/shared/ContestReminderNotifier.jsx
|
@kunalverma2512 I've addressed all three:
|
|
@ida-jemi Thanks and I'll going to review asap and get in conversation on matrix with you soon |
|
Please perform a COMPLETE production-level review of this PR. This PR introduces an entirely new feature with backend, frontend, database models, APIs, scheduled jobs, hooks, services, layouts, pages, and shared components while also modifying existing parts of the codebase. Do NOT perform a surface-level review. Review every changed file and every changed line of code. I want this review to be as if you are the lead maintainer responsible for approving a production release. Please verify all of the following: 1. Code Correctness
2. Existing Codebase Compatibility
3. Architecture ReviewReview whether the implementation follows good software engineering principles. Check:
Point out any architectural smells or future maintenance risks. 4. Frontend ReviewInspect every React component. Check:
5. Backend ReviewReview every backend file. Check:
6. Database ReviewReview models and persistence. Check:
7. Performance ReviewIdentify:
Suggest optimizations wherever possible. 8. Security ReviewCheck for:
9. Production ReadinessIdentify anything that could fail in production including:
10. Open Source QualityReview whether the implementation reflects professional open-source standards. Comment on:
11. Testing ReviewPoint out scenarios that are currently untested. List:
12. Review Output FormatFor EVERY issue found, provide:
Do not only report syntax or style issues. Focus primarily on correctness, architecture, reliability, maintainability, scalability, security, and production stability. Finally, provide a summary with:
Then answer these questions explicitly:
Please be extremely thorough. I would rather receive a long review than miss a production bug. |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
gh pr view 269 --repo kunalverma2512/CodeLens --json headRefName,baseRefName,number -q '.'
gh pr checkout 269 --repo kunalverma2512/CodeLens || true
echo "---FILES---"
fd . server/models server/modules/contests server/jobs -e js
echo "---"
fd . frontend/src/components/contests frontend/src/components/dashboard frontend/src/components/shared frontend/src/hooks frontend/src/services frontend/src/pages frontend/src/layouts -e jsx -e jsLength of output: 3509 🏁 Script executed: #!/bin/bash
set -euo pipefail
for f in server/models/Contest.js server/models/ContestReminder.js \
server/modules/contests/controller.js server/modules/contests/repository.js \
server/modules/contests/routes.js server/modules/contests/service.js \
server/modules/contests/validation.js server/jobs/contestSync.js \
server/server.js server/app.js; do
echo "===== $f ====="
cat -n "$f"
echo
doneLength of output: 19753 🏁 Script executed: #!/bin/bash
set -euo pipefail
for f in frontend/src/components/contests/ContestCountdown.jsx \
frontend/src/components/contests/ContestReminderBell.jsx \
frontend/src/components/contests/UpcomingContestsList.jsx \
frontend/src/components/dashboard/UpcomingContestsWidget.jsx \
frontend/src/components/shared/ContestReminderNotifier.jsx \
frontend/src/hooks/useContests.js \
frontend/src/services/contestService.js \
frontend/src/services/api.js \
frontend/src/pages/ContestCodeforcesPage.jsx \
frontend/src/pages/DashboardPage.jsx; do
echo "===== $f ====="
cat -n "$f"
echo
done
echo "===== server/utils/codeforcesApi.js ====="
cat -n server/utils/codeforcesApi.js 2>/dev/null || fd codeforcesApi.js -x cat -n {}
echo "===== Navbar diff context ====="
rg -n "ContestReminderBell" frontend/src/components/shared/Navbar.jsx -B3 -A3
echo "===== MainLayout diff context ====="
rg -n "ContestReminderNotifier" frontend/src/layouts/MainLayout.jsx -B5 -A5Length of output: 48146
Issues Found🟠 HighH1 — Cron job has no multi-instance guard (duplicate execution risk)
🟡 MediumM1 — Contests in system-testing phases go stale in DB
M2 — Reminder bell and reminder notifier poll the same endpoint independently
M3 — "Top 20 most recent finished contests" window can permanently orphan a stale document
M4 — Multi-tab duplicate toast risk
M5 — Division filter list is incomplete
🟢 Low
What's particularly well designed
Summary
(One nit for future PRs, not this one specifically): please continue documenting design rationale in comments as you did with ٩(◕‿◕)۶ ✅ Action performedReview finished.
|
|
@ida-jemi I see this PR will not produce any bugs in production but theirs some high,medium,low issues that coderabbitai has mentioned I want a confirmation that if I merge this then will you be working on the issues mentioned by coderabbitai ? |
|
Hi @kunalverma2512 yes sure, I will work on all of the issues! According to your preference either before merging or after merging, I'll make sure to fix the issues tomorrow. |
|
@coderabbitai please make 3 separate issues for HIGH category, ALL MEDIUM category and ALL LOW category issues mentioned by coderabbitai in its recent comment and make sure you write a very insane and detailed description about each issues that why its needed and what the problem and how things are working without this work and how it will resolve issue and where it improves codebase, understanding and in terms of industry standards and reality also write few learnings about the concepts needed to be learned by contributor during working on this and what concepts,topics to go through before working on this PR so that contributor can increase his/her learnings and become more better developer |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
repo="kunalverma2512/CodeLens"
assignee="kunalverma2512"
pr_url="https://github.com/kunalverma2512/CodeLens/pull/269"
review_url="https://github.com/kunalverma2512/CodeLens/pull/269#issuecomment-4934958050"
high_body="$(mktemp)"
medium_body="$(mktemp)"
low_body="$(mktemp)"
trap 'rm -f "$high_body" "$medium_body" "$low_body"' EXIT
cat >"$high_body" <<EOF
## Summary
Make the Codeforces contest synchronization scheduler safe when the backend runs in more than one process or deployment instance.
This follow-up tracks the **High**-severity finding from the production review of `#269`: the scheduler created in \`server/jobs/contestSync.js\` runs inside every application process. The current implementation is correct for a single server process, but it has no leader-election or distributed-lock mechanism.
**Backlinks**
- Source PR: $pr_url
- Source review: $review_url
- Requested by: @$assignee
## Current behavior
On successful database connection, \`server/server.js\` calls \`startContestSyncJob()\`. That function:
1. performs an initial Codeforces sync; and
2. registers a \`node-cron\` task for the top of every hour.
Because this happens in-process, each independently running server instance registers and executes its own schedule. Examples include PM2 cluster mode, container replicas, autoscaling hosts, rolling deployments with overlap, or multiple dynos.
The persistence writes are largely idempotent because contest records are upserted, so this is not expected to corrupt data immediately. However, idempotency does not make duplicated work safe or desirable at production scale.
## Why this is needed
A horizontally scalable web application must not assume that a scheduled task is executed by only one process. Without coordination, a deployment with N replicas produces N initial syncs and N hourly syncs at the same time.
### Production impact without this change
- **External API pressure:** every replica calls the public Codeforces API at the same cadence, multiplying outbound traffic and increasing the chance of rate limiting or temporary blocks.
- **Database pressure:** every replica performs the same bulk upserts and stale-reminder cleanup, producing avoidable write load and lock contention.
- **Operational noise:** logs report multiple “synced” executions for one intended schedule, complicating incident investigation and monitoring.
- **Scaling regression:** increasing availability by adding instances unintentionally increases background-work volume instead of keeping it constant.
- **Future correctness risk:** today’s sync is mostly idempotent; a later non-idempotent side effect (email, push notification, analytics event, cleanup) added to the job could become duplicated immediately.
This is a standard distributed-systems concern: an in-memory scheduler coordinates only one process, not a fleet.
## Scope and affected areas
- \`server/jobs/contestSync.js\`
- \`server/server.js\`
- Potentially a small persistence abstraction/model for job locks or job-run leases
- Potentially configuration/deployment documentation for the selected scheduling strategy
- Automated tests for lock/lease acquisition and release behavior
## Required changes
Choose and document one production-appropriate single-execution strategy. Suitable approaches include:
1. **Dedicated scheduler/worker deployment**
- Run the sync only from one explicitly configured worker process or managed platform scheduler.
- Keep web instances free of cron ownership.
2. **Database-backed lease / distributed lock**
- Atomically acquire a short-lived lease before starting a sync.
- Only the lock holder performs the work.
- Use an expiry/TTL or lease timestamp so a crashed process cannot block future syncs indefinitely.
- Release or expire the lease safely after the run.
3. **Queue-based repeatable job**
- Use a queue system with a documented single scheduler/worker ownership model.
The implementation must avoid a check-then-act race. For example, “read lock document, then insert it” is not sufficient under concurrent startup; lease acquisition must be atomic at the database/queue layer. Preserve the current startup sync and hourly refresh intent, but ensure only one actor executes each logical run.
Also define observable behavior:
- log whether an instance acquired or skipped the lease;
- log failures with enough context to distinguish lock acquisition from Codeforces API/sync failure;
- avoid holding a lock forever if a process crashes;
- ensure a slow sync cannot overlap with another run unintentionally.
## Acceptance criteria
- [ ] Starting two or more backend instances results in **one** logical contest sync per scheduled interval.
- [ ] The initial sync is similarly protected from concurrent process startup.
- [ ] Lock/leader acquisition is atomic and safe against concurrent attempts.
- [ ] A crash, timeout, or abandoned lock cannot permanently prevent later syncs.
- [ ] A long-running sync cannot cause unintended concurrent work from another instance.
- [ ] The approach is documented in code/configuration so deployment owners know which process owns scheduled work.
- [ ] Logs/metrics make it possible to tell whether a process ran or intentionally skipped a scheduled sync.
- [ ] Tests cover concurrent acquisition, lease expiry/recovery, successful sync, failed sync, and skipped execution.
- [ ] The existing single-instance development workflow remains simple and functional.
## Suggested validation scenarios
1. Launch two application processes against the same database and verify that only one makes the Codeforces request for the same interval.
2. Simulate a process terminating after acquiring the lock and verify another instance can run after the lease expires.
3. Simulate a Codeforces/API failure and confirm the next scheduled run is not permanently suppressed.
4. Simulate a sync taking longer than the normal interval and verify the chosen policy prevents unsafe overlap.
5. Verify normal one-instance startup still runs exactly one initial synchronization.
## Contributor learning guide
Before implementation, review these topics:
- **Distributed locks and leader election:** why process-local state is insufficient in a replicated deployment.
- **Leases versus mutexes:** leases expire and recover from crashes; understand clock/TTL implications.
- **Atomic compare-and-set operations:** MongoDB conditional updates, unique constraints, and why “find then create” races.
- **At-least-once execution and idempotency:** scheduled jobs may run more than once; design writes and side effects accordingly.
- **Job observability:** structured logs, run IDs, duration/error metrics, and diagnosing skipped versus failed work.
- **Deployment topology:** PM2 cluster mode, containers, autoscaling, rolling releases, and managed cron/worker services.
- **Failure-mode design:** API outage, database outage, process crash, slow work, and overlapping schedules.
## Definition of done
The contest sync is safe to deploy across multiple backend replicas without multiplying scheduled work, recovers automatically from failed owners, and is covered by focused automated tests and clear operational documentation.
EOF
cat >"$medium_body" <<EOF
## Summary
Harden the contest tracker’s lifecycle handling, reminder data flow, and filtering behavior by resolving the five **Medium**-severity findings from the production review of `#269`.
**Backlinks**
- Source PR: $pr_url
- Source review: $review_url
- Requested by: @$assignee
## Goals
The tracker should display an accurate contest lifecycle, avoid avoidable per-tab network/database work, remain correct after delayed synchronization, prevent duplicate in-app reminders across browser tabs where feasible, and expose every backend-derived division/category in the UI.
## Findings and required work
### 1. Persist system-testing lifecycle phases so contests do not remain falsely “live”
**Affected area:** \`server/modules/contests/service.js\`, especially \`ContestService.syncCodeforcesContests()\`; downstream repository/frontend display behavior.
#### Current behavior
The sync selection persists only \`BEFORE\`, \`CODING\`, and a bounded group of \`FINISHED\` contests. Codeforces contests can transition through phases such as \`PENDING_SYSTEM_TEST\` and \`SYSTEM_TEST\` after submissions close and before they become \`FINISHED\`.
When those intermediate phases are excluded, the cached document can retain its previous \`CODING\` phase. The frontend treats \`CODING\` as running, so it can show “Live Now” even though the contest is no longer accepting submissions.
#### Production impact
Users receive inaccurate real-time status. A contest can look active for up to the next successful sync—or longer if combined with the finished-window issue below. This undermines the core promise of a live contest tracker and can cause users to set expectations around a contest that has ended.
#### Required change
Persist all Codeforces phases that are necessary to transition an already cached upcoming/running contest to a non-runnable state. Define a clear UI/domain mapping for intermediate testing phases—e.g., show “System testing” or a non-live completed state—and ensure upcoming queries and reminder eligibility match that mapping.
---
### 2. Eliminate duplicate polling of active reminders within one authenticated tab
**Affected area:** \`frontend/src/components/contests/ContestReminderBell.jsx\`, \`frontend/src/components/shared/ContestReminderNotifier.jsx\`, \`frontend/src/hooks/useContests.js\`, and possibly a shared context/provider or query cache.
#### Current behavior
The navbar bell polls \`getMyActiveReminders()\` every 60 seconds, while the global reminder notifier independently polls the same endpoint every 30 seconds. Both are mounted on authenticated pages.
#### Production impact
Each active browser tab produces duplicate network requests, authentication work, and database reads for identical information. As user traffic grows, this costs capacity without improving freshness and creates two independent sources of state that can disagree momentarily.
#### Required change
Introduce one shared reminder-data owner per browser tab (for example, a context/provider, a dedicated hook with a shared cache, or an existing application query layer). The bell and notifier should consume the same cached state. Preserve cleanup on auth changes/unmounts and define a single polling/refetch cadence appropriate for reminder urgency.
---
### 3. Ensure delayed or bursty syncs cannot leave contests permanently stale
**Affected area:** \`server/modules/contests/service.js\` and relevant repository queries/tests.
#### Current behavior
Only the 20 most recently started \`FINISHED\` contests are selected for persistence on each sync. Under normal hourly operation, a contest usually gets recorded as finished. But after downtime, API failure, a burst of contest events, or an unusual data ordering, a previously cached \`BEFORE\`/\`CODING\` contest may fall outside that 20-item window before its \`FINISHED\` phase is ever persisted.
#### Production impact
A stale document may remain eligible for the “upcoming/running” query indefinitely, showing users an event that has ended and retaining stale reminders. This is a data-lifecycle correctness issue, not merely a display delay.
#### Required change
Make phase reconciliation complete for records the application already tracks. Options include:
- persist the entire relevant Codeforces response when reasonably bounded; or
- explicitly update every locally cached non-finished contest from the latest API response, regardless of a historical finished-contest display/cache cap.
Keep any retention cap separate from lifecycle reconciliation. If old finished records need pruning, perform that as an explicit retention policy rather than relying on a limited fetch subset to communicate state transitions.
---
### 4. Coordinate reminder toasts across browser tabs
**Affected area:** \`frontend/src/components/shared/ContestReminderNotifier.jsx\`; potentially reminder API semantics and cross-tab browser coordination.
#### Current behavior
The notifier’s \`shownThisSession\` set exists only inside one mounted component instance. If the same authenticated user has multiple tabs open, both can fetch a reminder with \`notifiedAt: null\` before either fire-and-forget \`markReminderNotified()\` request completes. Both tabs can show the toast.
#### Production impact
Users can receive duplicate “starting soon” notifications. The impact is mostly user-experience noise, but reminders are a core feature and duplicate alerts diminish trust in their reliability. The risk grows for users who keep dashboards/trackers open in multiple tabs.
#### Required change
Provide cross-tab coordination where supported, such as \`BroadcastChannel\` or a carefully scoped \`localStorage\` event/key, while retaining server-side \`notifiedAt\` as the durable source of truth. Consider making the server’s mark-notified operation expose clear idempotent/conditional semantics so clients can tell whether they won the notification claim. The implementation must degrade safely when browser storage/channel APIs are unavailable.
---
### 5. Make all backend-generated division/category values reachable in the UI
**Affected area:** \`server/modules/contests/service.js\` (\`parseDivision\`) and \`frontend/src/components/contests/UpcomingContestsList.jsx\` (\`DIVISIONS\` and filtering).
#### Current behavior
The backend can classify contests into values such as \`Div. 1 + 2\`, \`Kotlin Heroes\`, \`ICPC\`, and \`Other\`. The UI’s explicit filter list includes only \`Div. 1\` through \`Div. 4\`, \`Educational\`, and \`Global\`.
#### Production impact
Contests in the omitted categories are displayed only under the unfiltered “all” view and cannot be selected through a dedicated category filter. This is inconsistent behavior between the API’s domain vocabulary and the UI’s navigation model.
#### Required change
Establish one canonical category vocabulary or mapping contract. Either include every backend value in the filter UI, deliberately group them into documented categories, or generate filters from returned data while preserving a stable and accessible order. Make sure filtering labels, badge labels, and empty states remain understandable on small screens.
## Acceptance criteria
- [ ] Intermediate Codeforces testing phases are persisted and never shown as “Live Now” after submissions close.
- [ ] Cached contests that are already known locally are reliably reconciled to their terminal phase after outages, delayed runs, or high event volume.
- [ ] Reminder bell and global notifier use one shared active-reminder data flow per authenticated tab, with no parallel duplicate polling loops.
- [ ] The shared polling mechanism has correct cancellation/cleanup on logout, unmount, and request replacement.
- [ ] Multiple open tabs do not normally show duplicate reminder toasts for the same contest; fallback behavior is safe and documented.
- [ ] Reminder notification remains one-time and durable across reloads through the backend’s \`notifiedAt\` state.
- [ ] Every category produced by backend parsing is either filterable or intentionally mapped into an explicit visible grouping.
- [ ] Frontend loading, error, empty, and unauthenticated states still work correctly.
- [ ] Tests cover each item below.
## Test plan
### Backend/service and repository tests
- Phase transitions: \`BEFORE → CODING → PENDING_SYSTEM_TEST/SYSTEM_TEST → FINISHED\`.
- A locally cached running contest that is absent from the “latest 20 finished” window is still reconciled to finished.
- Reminder eligibility and active-reminder queries for all lifecycle states.
- API response and ordering edge cases, including delayed syncs and temporary Codeforces failures.
### Frontend tests
- Exactly one request owner/cadence is used when both bell and notifier render.
- Logout/unmount cancels polling and does not update stale component state.
- Bell count and toast use the same reminder snapshot.
- Two simulated tabs coordinate notification display; the losing tab suppresses its duplicate toast.
- Every parsed division/category is represented or grouped correctly by the UI filter.
## Contributor learning guide
Review these topics before starting:
- **State-machine modeling:** model contest lifecycle phases explicitly rather than treating state as a few independent strings.
- **Cache reconciliation:** distinguish data retention/display limits from authoritative state synchronization.
- **Client-side data ownership:** shared query caches, context providers, subscription lifecycles, polling deduplication, and stale-while-revalidate patterns.
- **React effect correctness:** dependency arrays, cancellation, stale async responses, interval cleanup, and avoiding duplicated side effects.
- **Cross-tab web coordination:** \`BroadcastChannel\`, \`storage\` events, atomicity limitations, and progressive enhancement.
- **Distributed/idempotent notifications:** server-side durable state, conditional updates, and why fire-and-forget client requests can race.
- **Domain contracts:** keep backend enum/parser outputs and frontend filters/types synchronized.
- **User-facing time/status semantics:** distinguish “running,” “ended,” and “system testing” instead of deriving status only from timestamps.
## Definition of done
The tracker reports the correct contest lifecycle, performs no duplicate reminder polling within a tab, remains correct after synchronization gaps, avoids multi-tab duplicate alerts in supported browsers, and presents a complete consistent division/category filter model with automated coverage.
EOF
cat >"$low_body" <<EOF
## Summary
Improve validation consistency, index hygiene, React async cleanup, accessibility, and automated coverage for the contest tracker introduced in `#269`.
This issue consolidates the **Low**-severity findings from the production review. None is expected to block a single-instance release independently, but resolving them increases maintainability, accessibility, performance clarity, and confidence in future changes.
**Backlinks**
- Source PR: $pr_url
- Source review: $review_url
- Requested by: @$assignee
## Findings and required work
### 1. Use a consistent validation strategy for reminder route parameters
**Affected area:** \`server/modules/contests/controller.js\`, \`server/modules/contests/routes.js\`, \`server/modules/contests/validation.js\`.
#### Current behavior
\`POST /reminders\` validates its body through the shared Zod \`validate()\` middleware. In contrast, \`DELETE /reminders/:contestId\` and \`POST /reminders/:contestId/notified\` manually call \`parseInt\` in controller methods and only reject \`NaN\`.
#### Why improve this
The current behavior is functional for common inputs, but validation rules are split across layers. \`parseInt\` has permissive parsing behavior for strings such as \`"12abc"\`, and the two parameter routes do not enforce the same positive-integer contract as creation.
A unified schema prevents divergent error payloads and makes future validation changes (bounds, integer-only semantics, reusable error messages) safer.
#### Required change
Create/reuse a Zod schema and middleware for \`req.params.contestId\`, apply it at the route boundary, and have controllers consume the normalized validated value. Maintain consistent 400 error responses across reminder endpoints.
---
### 2. Review and remove redundant contest indexes
**Affected area:** \`server/models/Contest.js\`, database migration/index-management procedure.
#### Current behavior
The model has a standalone \`contestId\` index and a compound unique index on \`{ platform, contestId }\`. With only the \`codeforces\` platform currently permitted, these indexes substantially overlap for the known query patterns.
#### Why improve this
Every extra MongoDB index consumes disk/RAM and must be updated on inserts/upserts. Redundant indexes increase write amplification and operational overhead. Indexes should correspond to actual query predicates/sorts, not merely be added defensively.
#### Required change
Inspect repository query shapes and MongoDB index usage. Retain only indexes justified by current queries and anticipated multi-platform requirements. If the standalone index is unnecessary, remove it using the project’s safe index deployment process; do not assume changing the schema automatically drops an existing production index.
Document the selected index/query rationale.
---
### 3. Prevent stale async hook requests from updating state after unmount or replacement
**Affected area:** \`frontend/src/hooks/useContests.js\`.
#### Current behavior
The hook’s contest/reminder fetch functions update React state after awaiting requests, but do not use cancellation/sequence guards. The bell and notifier components already employ cancellation flags, so behavior is inconsistent.
#### Why improve this
React 18 no longer always emits the historical warning for post-unmount state updates, but stale responses can still overwrite newer state after auth changes, navigation, retry/refetch, or slow network requests. Guarding request ownership makes the hook deterministic and prevents needless work.
#### Required change
Use an appropriate cancellation/identity strategy—such as \`AbortController\` support in the API client, a mounted flag plus request sequence token, or a query library—so only the latest active request can update state. Ensure interval cleanup and errors follow the same guard.
---
### 4. Provide an accessible name for the contest reminder bell control
**Affected area:** \`frontend/src/components/contests/ContestReminderBell.jsx\`.
#### Current behavior
The icon-only reminder-bell link relies on \`title\` text.
#### Why improve this
A tooltip title is not a reliable accessible name for screen-reader users and may not be consistently exposed across assistive technology. Icon-only interactive elements need an explicit programmatic label, and a visible/semantic indication of the badge count where useful.
#### Required change
Add a descriptive \`aria-label\` to the link/button, including the number of due reminders when applicable. Mark decorative icon SVGs appropriately and verify keyboard focus and contrast remain clear.
---
### 5. Add automated coverage for the new contest/reminder feature
**Affected area:** all newly introduced contest backend/frontend modules, plus the existing test configuration and CI scripts.
#### Current behavior
The feature adds models, routes, validation, controllers, services, repositories, a cron job, a frontend service, hook, countdown, list, bell, notifier, widget, and page/layout integrations without automated tests.
#### Why improve this
The feature’s most important behavior is time-, lifecycle-, authorization-, and async-dependent. Manual testing is valuable but cannot reliably protect phase transitions, duplicate-reminder prevention, polling cleanup, validation contracts, or future refactors. Tests turn the feature’s expected behavior into an executable contract for contributors.
#### Required change
Add a proportionate test suite aligned with existing project tooling. Avoid introducing a second testing stack if the repository already has one. Prioritize behavior over implementation details.
## Acceptance criteria
- [ ] All reminder endpoints enforce a single positive-integer \`contestId\` contract at the route boundary and return consistent validation errors.
- [ ] Contest indexes are justified by documented query patterns; unused/redundant production indexes are safely removed or retained with rationale.
- [ ] \`useContests\` does not apply results from an unmounted, cancelled, or superseded request.
- [ ] The icon-only reminder navigation control has an explicit accessible name and communicates its state/count appropriately.
- [ ] The project contains focused automated tests for the contest feature, integrated into the established test command/CI path.
- [ ] Existing behavior remains backward compatible for anonymous users, authenticated users, empty data, failures, and normal navigation.
## Minimum recommended test coverage
### Backend
- Zod validation for valid IDs, zero, negatives, decimals, non-numeric strings, and permissive-looking strings such as \`"12abc"\`.
- Route integration tests confirming authentication requirements and response shapes.
- \`ContestService\` tests for add/remove/mark reminder behavior, missing contest handling, invalid lifecycle state handling, and sync normalization.
- Repository tests for unique reminder idempotency, upcoming sorting/filtering, active reminder joins, and stale-reminder cleanup.
- Scheduler tests for startup invocation/error logging boundaries where feasible.
### Frontend
- \`useContests\` tests for optimistic add/remove and rollback on request failure.
- Tests that slow/stale fetches cannot overwrite newer data or update after unmount.
- \`ContestCountdown\` tests for upcoming, running, ended, and multi-day displays.
- \`UpcomingContestsList\` tests for loading/error/empty/filter/auth redirect states.
- Reminder bell accessibility tests: accessible name, count state, keyboard navigation.
- Notifier tests for one-time notification, dismissal, and mark-notified request behavior.
## Contributor learning guide
Before implementation, study:
- **Input validation at trust boundaries:** schema validation, coercion pitfalls, and why \`parseInt\` is not full input validation.
- **REST API consistency:** route-level contracts, normalized error payloads, and controller responsibilities.
- **MongoDB index design:** compound index ordering, selectivity, query plans, write amplification, \`explain()\`, and safe index migrations.
- **React async effects:** AbortController, stale-response races, cleanup patterns, and request sequencing.
- **Web accessibility:** accessible names, ARIA usage for icon-only controls, focus management, and testing with role/name queries.
- **Testing strategy:** unit versus integration versus component tests; fake timers for countdown/polling; API mocking; database isolation; and testing observable behavior rather than private implementation.
- **Regression prevention:** every fixed bug or clarified contract should get a focused test where practical.
## Definition of done
The contest module has consistent input validation, intentional database indexing, safe async client state handling, accessible icon controls, and an automated test baseline that protects the feature’s core lifecycle, reminder, and UI behavior.
EOF
high_url=$(gh issue create --repo "$repo" \
--title "High: Make Codeforces contest sync single-execution safe across multiple backend instances" \
--body-file "$high_body" \
--assignee "$assignee")
medium_url=$(gh issue create --repo "$repo" \
--title "Medium: Harden contest lifecycle reconciliation, reminder polling, and category filtering" \
--body-file "$medium_body" \
--assignee "$assignee")
low_url=$(gh issue create --repo "$repo" \
--title "Low: Improve contest validation, index hygiene, async cleanup, accessibility, and test coverage" \
--body-file "$low_body" \
--assignee "$assignee")
printf 'HIGH=%s\nMEDIUM=%s\nLOW=%s\n' "$high_url" "$medium_url" "$low_url"Length of output: 27790
Each issue includes the rationale, current behavior, real production impact, affected modules, actionable requirements, acceptance criteria, recommended test coverage, and a contributor learning guide covering the relevant engineering concepts. |
Amazing @ida-jemi TIP : @ida-jemi since your work is so much good and adds value and also a source for more growth to see in future thats why I think if you can make a blog plus a good message in community that how you worked, what challenges you faced and how you adopted this challenging environment , what lessons you got by working on this issue and special topics , concepts you learned while contributing NOTE : PLEASE UPDATE YOU WORK ON OUT MATRIX CHANNEL SO AS TO MAKE COMMUNITY ACTIVE, STRONGER AND FULL OF CURIOSITY AND A BETTER DEVELOPER COMMUNITY |
|
@kunalverma2512 Thank you so much!! |



📌 Pull Request Summary
🔗 Related Issue
Closes #253
📝 Description
Adds a real, live Upcoming Contests Tracker for Codeforces with in-app reminders, replacing the static placeholder content on
/contests/codeforceswith real data and wiring reminders through the dashboard and navbar.Changes Made
Backend
Contestmodel - caches Codeforces'contest.listresponse (id, name, phase, parsed division, start time, duration).ContestRemindermodel - one doc per(user, contest)opt-in, with anotifiedAtflag so in-app toasts never repeat.contestsmodule (routes/controller/service/repository/validation) following the existing feature-module pattern used bycodeforces:GET /api/contests/codeforces/upcoming- public, returns cached upcoming/running contests.GET /api/contests/reminders- a user's reminder contest ids.GET /api/contests/reminders/active- full contest details for reminders (powers the badge/toast).POST /api/contests/reminders/DELETE /api/contests/reminders/:contestId- set/unset a reminder.POST /api/contests/reminders/:contestId/notified- mark a reminder's toast as shown.server/jobs/contestSync.js-node-cronjob that syncs the Codeforces contest list on startup and hourly, so the frontend never calls the CF API directly (avoids rate limits, keeps the page fast).node-crondependency.Frontend
useContestshook - fetches cached contests, ticks a shared 1s clock for live countdowns, manages optimistic reminder toggling.UpcomingContestsList- division-filterable contest cards with live countdown + "Remind Me" toggle (used both full-size on the contest page and incompactmode elsewhere).ContestCountdown- pure countdown formatter (dd:hh:mm:ss / "Live Now").UpcomingContestsWidget- compact top-3 dashboard widget, added toDashboardPage.ContestReminderBell- navbar badge showing reminders due within 24h, linking to the tracker.ContestReminderNotifier- mounted once inMainLayout; polls reminders every 30s and pops an in-app toast when a reminder-contest starts within 15 minutes (persisted server-side so it survives reloads/navigation).ContestCodeforcesPagenow leads with the live tracker, with the existing editorial archive section kept below it.Motivation
Issue #253 asked for a way to see upcoming Codeforces contests and get reminded about them without leaving CodeLens. This wires up real Codeforces data (cached and refreshed hourly server-side) end-to-end: dashboard widget → dedicated tracker page → reminder opt-in → navbar badge → in-app toast when a contest is about to start.
🚀 Type of Change
🧪 Testing
Verification
Test Details
node --checkon every new/modified backend file, and smoke-importedapp.js/ the newcontestsroutes module to confirm they load without errors.npm run lint(ESLint) on all new/modified frontend files - zero new warnings/errors introduced (pre-existing, unrelatedNavbar.jsxissues onmainare untouched by this PR).npm run build(Vite) - production build succeeds.📸 Screenshots / Demo (If Applicable)
Screen.Recording.2026-07-07.194633.mp4
✅ Checklist
📚 Additional Notes
contest.listendpoint already wrapped inserver/utils/codeforcesApi.js.Contestmodel is intentionally keyed byplatformso CodeChef/LeetCode/AtCoder trackers can reuse the same collection/module shape in a follow-up issue instead of duplicating it.server/package-lock.jsonis updated becausenode-cronwas added as a dependency.Summary by CodeRabbit
hh:mm:ssformatting, plus a compact display mode.