## Overview This document summarizes the recent changes around submissions/users pages and records guardrails to avoid similar issues in the future. We: - Reworked submissions and user stats UIs to use real routes/pages instead of modals. - Added compact progress overview for participants. - Introduced deep-linked details pages for submissions and users. - Fixed Chakra UI dialog misuse and type/translation issues. ## Routing & Page Structure ### Do - **Define all routes centrally** in `src/__data__/urls.ts` and `src/dashboard.ts`: - Add both the **URL builder** (e.g. `submissionDetails(userId, submissionId)`) and the **`:param` path**. - Wrap pages in `PageWrapper` in `dashboard.tsx`. - **Use real pages for complex views** (details, stats) instead of large modals: - Submissions details: `SubmissionDetailsPage` with URL `/submissions/:userId/:submissionId`. - User stats: `UserStatsPage` with URL `/users/:userId`. - **Pass IDs via URL**, not only component state: - Use route params for `userId`, `submissionId`, etc. - For “return and keep selection”, encode it as a query param (e.g. `?userId=...`) and read it on the list page. ### Don’t - **Don’t hardcode paths in components** (e.g. `'/submissions/...'`); always use `URLs.*` helpers. - **Don’t rely solely on local React state for deep links**: - If a view must be shareable/bookmarkable or restorable on reload, it must be addressable by URL. ## Chakra UI Dialogs & Layout ### Do - **Use dialog subcomponents only inside a dialog root**: - If you use `DialogBody`, `DialogContent`, etc., they must be wrapped in ``. - For **standalone pages**, use plain layout components: - `Box`, `Heading`, `VStack`, `Grid`, `Progress`, etc. - No `Dialog*` components on normal routed pages. ### Don’t - **Don’t import or use `DialogBody`, `DialogContent`, `DialogHeader`, etc. on regular pages**: - This causes `useDialogStyles returned 'undefined'` runtime errors. - **Don’t mix modal patterns and page patterns**: - Either a true modal (`DialogRoot` + `DialogContent`) over an existing page, - Or a full page route with normal layout — not both at the same time. ## Data Safety & Types ### Do - Assume backend fields can be **either object or ID string**, per `ChallengeSubmission` types: - Example safe access in submissions: - Guard before reading `user.nickname` or `task.title`. - Derive strings like: - `const nickname = typeof rawUser === 'object' && 'nickname' in rawUser ? rawUser.nickname ?? '' : typeof rawUser === 'string' ? rawUser : ''`. - Normalize strings before calling `.toLowerCase()`: - `const normalized = (value ?? '').toLowerCase()`. - When filtering/searching, **never call string methods on possibly `undefined` or non-object values**. ### Don’t - **Don’t cast blindly** (`as ChallengeUser`) and then access `.nickname` or `.title` without guards. - **Don’t call `.toLowerCase()` directly on untrusted values** from API or union-typed fields. ## “Back” Navigation & State Restoration ### Do - For **details pages that should restore list state**: - Encode the necessary selection into the URL when navigating _to_ details. - Example: `SubmissionDetailsPage` returns to `URLs.submissions` with `?userId=...`, and `SubmissionsPage` reads `userId` from `useSearchParams` to preselect the user. - Prefer **semantic back actions** over bare `navigate(-1)` when the previous page/state is known: - Use `navigate(URLs.submissions + '?userId=...')` or `navigate(URLs.users)` when appropriate. ### Don’t - **Don’t rely on `navigate(-1)`** when: - The previous page might not be the canonical list page, - You need a specific state (e.g. selected user) restored. ## i18n / Locales ### Do - **Keep `ru.json` and `en.json` in sync** for any new keys: - When adding a key under `challenge.admin.*` in one file, add the corresponding entry in the other. - For **status enums**, ensure all possible values have translations: - `challenge.admin.users.stats.status.*` must cover all values of `taskStat.status`. - `challenge.admin.submissions.status.*` must cover all submission statuses. - Use **consistent key naming patterns**: - Example: `challenge.admin.users.stats.status.accepted`, `...status.needs_revision`, etc. ### Don’t - **Don’t introduce new `t('...')` keys in code without adding them to both locale files**. - **Don’t reuse unrelated keys** just to avoid adding translations — create clear, specific keys. ## UI Patterns for High-Density Overviews ### Do - For high-density screens (e.g. 100 participants at once): - Use **compact cards or rows** with: - Truncated names (`truncate`), - Thin `Progress` bars, - Minimal text (percentage + small counters). - Sort by progress to surface lagging participants. ### Don’t - **Don’t use wide tables** when many rows must fit on one screen; prefer grids or narrow rows with fixed-width text columns and flexible progress area. ## When Adding New Features Before merging: - **Check routing**: - New URL added to `URLs`. - Route wired in `dashboard.tsx`. - **Check data safety**: - No unchecked property access on union/nullable types. - **Check i18n**: - New keys exist in both `ru.json` and `en.json`. - **Check Chakra usage**: - No `Dialog*` components outside a proper `` _or_ on standalone pages.