5.3 KiB
5.3 KiB
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.tsandsrc/dashboard.ts:- Add both the URL builder (e.g.
submissionDetails(userId, submissionId)) and the:parampath. - Wrap pages in
PageWrapperindashboard.tsx.
- Add both the URL builder (e.g.
- Use real pages for complex views (details, stats) instead of large modals:
- Submissions details:
SubmissionDetailsPagewith URL/submissions/:userId/:submissionId. - User stats:
UserStatsPagewith URL/users/:userId.
- Submissions details:
- 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.
- Use route params for
Don’t
- Don’t hardcode paths in components (e.g.
'/submissions/...'); always useURLs.*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<DialogRoot>.
- If you use
- 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.
- This causes
- 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.
- Either a true modal (
Data Safety & Types
Do
- Assume backend fields can be either object or ID string, per
ChallengeSubmissiontypes:- Example safe access in submissions:
- Guard before reading
user.nicknameortask.title.
- Guard before reading
- Derive strings like:
const nickname = typeof rawUser === 'object' && 'nickname' in rawUser ? rawUser.nickname ?? '' : typeof rawUser === 'string' ? rawUser : ''.
- Example safe access in submissions:
- Normalize strings before calling
.toLowerCase():const normalized = (value ?? '').toLowerCase().
- When filtering/searching, never call string methods on possibly
undefinedor non-object values.
Don’t
- Don’t cast blindly (
as ChallengeUser) and then access.nicknameor.titlewithout 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:
SubmissionDetailsPagereturns toURLs.submissionswith?userId=..., andSubmissionsPagereadsuserIdfromuseSearchParamsto preselect the user.
- Prefer semantic back actions over bare
navigate(-1)when the previous page/state is known:- Use
navigate(URLs.submissions + '?userId=...')ornavigate(URLs.users)when appropriate.
- Use
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.jsonanden.jsonin sync for any new keys:- When adding a key under
challenge.admin.*in one file, add the corresponding entry in the other.
- When adding a key under
- For status enums, ensure all possible values have translations:
challenge.admin.users.stats.status.*must cover all values oftaskStat.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.
- Example:
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
Progressbars, - Minimal text (percentage + small counters).
- Truncated names (
- Sort by progress to surface lagging participants.
- Use compact cards or rows with:
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.
- New URL added to
- Check data safety:
- No unchecked property access on union/nullable types.
- Check i18n:
- New keys exist in both
ru.jsonanden.json.
- New keys exist in both
- Check Chakra usage:
- No
Dialog*components outside a proper<DialogRoot>or on standalone pages.
- No