# riu_company_admin — Claude Code guide

React admin frontend (CRA / react-scripts 4, **React 16.14** — no `useId`, no automatic batching outside events). Companion backend: `../riu-company-admin-backend` (NestJS + TypeORM, has its own CLAUDE.md).

## Commands

```bash
npm start                                            # dev server
CI=true npx react-scripts test --watchAll=false      # one-off test run
NODE_OPTIONS=--openssl-legacy-provider npx react-scripts build   # build (needed on Node 17+)
```

## Architecture conventions

Feature folders, not god files. A page lives in `src/component/<Area>/<Feature>/` with:
- `index.jsx` — page component (folder resolves the old import path)
- `<feature>Api.js` — ALL endpoint calls as named functions wrapping `_services/apiCall`; components never assemble URLs
- `use<Feature>Data.js` — data-fetching hook; other hooks for page-level state machines
- `helpers.js` — pure domain logic (unit-tested in `__tests__/`)
- `<Feature>.css` — scoped styles
- Reference example: `src/component/Settings/KpiAssets/`

When refactoring or moving code, move its tests with it (and their import paths); update or extend them to match the new structure — never leave tests pointing at deleted files or drop them silently.

## React standards (enforced in review)

- **SOLID always**, adapted to React: one responsibility per component/hook (a page composes hooks + presentational components; a hook owns one concern like data or a selection state machine); depend on props/callbacks, not sibling internals; extend by adding variants/props rather than forking near-copies.

- **No prop→state mirroring.** Render from props; refresh data in the background after mutations instead of copying props into local state and syncing with effects.
- **Effects are for external systems only** (timers, subscriptions). Logic that reacts to a user action (clear staged input on scope change, prune selections) belongs in the event handler.
- **Data fetching**: race-guard with a fetch-id (stale responses dropped); blocking spinner only for initial load / context switch; background `refresh()` after mutations so rows keep expanded/edit state; expose `error` + retry, never a silent empty page.
- **Keys**: stable server ids. Never `Math.random()`/index keys for persisted rows.
- **Memoize list rows** (`React.memo`) and pass primitives + stable `useCallback` handlers — no fresh object literals as props to memoized components.
- **Styling**: scoped CSS file with a feature prefix (e.g. `.kpa-`) + CSS-variable tokens. No global selectors (`*`, bare `select`) in component `<style>` tags. Hover/focus via `:hover`/`:focus-visible`, never `onMouseEnter` mutating `e.currentTarget.style`. One-off layout styles may stay inline.
- **A11y**: clickable divs get `role="button"`, `tabIndex={0}`, keyboard handling (guard `e.target === e.currentTarget` when the row contains inner controls); labels use `htmlFor` (React 16: per-instance id counter, no `useId`); icon-only buttons get `aria-label`; real `<button>`/`<input type="radio">` over styled spans; never remove focus outlines.
- **localStorage**: read once in a `useState` initializer via a `safeParseJSON` helper — never bare `JSON.parse(localStorage.getItem(...))`, never in `useMemo`.
- Reuse over duplication: for a UI variant of existing markup, extract/parameterize the existing component instead of copy-pasting JSX.
- **Comments are JSDoc only.** Document via `/** … */` blocks attached to the declaration (function, component, hook, or `const`) — no inline `//` comments inside bodies and no `{/* section */}` markers in JSX. A note that can't attach to a declaration gets folded into the enclosing function's JSDoc or dropped.

## Domain gotchas

- MySQL `tinyint` booleans arrive as `0/1` — use truthy/falsy checks, never `=== true/false`.
- Duplicate/uniqueness rules must have a single helper as source of truth (see `KpiAssets/helpers.js` `inBucket`/`titleExistsIn`) — don't fork the logic per call site.
- Same rule for API payloads and mutation flows: when two components call the same endpoint, the request body is built by one pure helper (`KpiAssets/helpers.js` `buildMapping` — owns conventions like `null` vs `undefined` fields) and the save lifecycle (saving flag, error message, post-save refresh/close) lives in one shared hook (`KpiAssets/useSaveMappings.js`), never copy-pasted per panel.
- `apiCall` returns `{ isSuccess, data, message }` and shows a global alert itself (suppress with the 6th arg when aggregating bulk results).

## UX preferences

- Don't hard-block users on partial conflicts: warn, skip the conflicting subset, proceed with the rest, and report what was skipped.
- Prefer inline page modes (checkboxes/panels in place) over modals for bulk selection flows.
- Bulk operations call ONE bulk endpoint (add it to the backend if missing) — never loop single-item API calls.

## Git

- Stage with `git add -u` (tracked files only); ask before adding untracked files.
- When swapping/reverting a mechanism, make only the surgical change — don't drop related params or restructure.
- Don't commit or push unless asked.
