Files
challenge-admin-pl/CLAUDE.MD

117 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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.
### Dont
- **Dont hardcode paths in components** (e.g. `'/submissions/...'`); always use `URLs.*` helpers.
- **Dont 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>`.
- For **standalone pages**, use plain layout components:
- `Box`, `Heading`, `VStack`, `Grid`, `Progress`, etc.
- No `Dialog*` components on normal routed pages.
### Dont
- **Dont import or use `DialogBody`, `DialogContent`, `DialogHeader`, etc. on regular pages**:
- This causes `useDialogStyles returned 'undefined'` runtime errors.
- **Dont 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**.
### Dont
- **Dont cast blindly** (`as ChallengeUser`) and then access `.nickname` or `.title` without guards.
- **Dont 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.
### Dont
- **Dont 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.
### Dont
- **Dont introduce new `t('...')` keys in code without adding them to both locale files**.
- **Dont 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.
### Dont
- **Dont 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 `<DialogRoot>` _or_ on standalone pages.