# Frontend Dev

> Coding practices for frontend development in Atomic CRM. Use when creating or modifying React components, forms, list pages, detail views, filters, data fetching, or responsive layouts. Use when this capability is needed.

- Skill: `tomevault-io/frontend-dev-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/frontend-dev-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/frontend-dev-2/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/frontend-dev-2

---


The frontend uses ra-core (react-admin headless) for data fetching, routing, and CRUD logic, with shadcn-admin-kit and shadcn/ui for the UI layer.

## Component architecture

- Import form inputs (`TextInput`, `SelectInput`, `ReferenceInput`, etc.) from `@/components/admin/`, not from shadcn/ui directly. The admin layer wraps shadcn with ra-core integration (labels, validation, data binding).
- Import pure UI components (`Card`, `Button`, `Badge`, `Sheet`, etc.) from `@/components/ui/`.
- Domain configuration (deal stages, note statuses, task types, company sectors) comes from `useConfigurationContext()`, never hardcoded.

## Resource (CRUD) conventions

Each resource follows this file structure (e.g. `contacts/`):

- `ContactList.tsx` — list page (desktop + mobile variants)
- `ContactShow.tsx` — detail view
- `ContactEdit.tsx` / `ContactCreate.tsx` — form pages
- `ContactInputs.tsx` — shared form fields reused between create and edit
- `index.tsx` — exports `{ list, show, edit, create, recordRepresentation }`

Resources are registered in `root/CRM.tsx` via `<Resource name="contacts" {...contacts} />`.

## Data fetching

- For standard CRUD, use ra-core hooks: `useListContext()`, `useShowContext()`, `useGetList()`, `useGetOne()`, `useGetIdentity()`.
- When a query or mutation isn't covered by ra-core hooks, add a custom dataProvider method and call it via `useQuery`/`useMutation` with `useDataProvider<CrmDataProvider>()` (e.g. `dataProvider.getActivityLog()` in `ActivityLog.tsx`, `dataProvider.salesCreate()` in `SalesCreate.tsx`).

## Forms

- Forms use `Form` from ra-core + `FormToolbar` for submit/cancel actions.
- Ra-core's `Form` uses React Hook Form under the hood. Use `useFormContext()` for imperative operations (`setValue`, `reset`, `getValues`).
- Top-level resource forms use full-page `CreateBase`/`EditBase` with `Card` (e.g. contacts), or `Dialog` (e.g. deals).
- On mobile, inline/sub-resource forms use `CreateSheet`/`EditSheet` from `misc/` (e.g. notes, tasks).
- Split form fields into semantic sub-components (e.g. `ContactIdentityInputs`, `ContactPositionInputs`).

## Filters

- Use `ToggleFilterButton` / `ActiveFilterButton` components for filter UI.
- Filters apply immediately, no "apply" button.

## Responsive design

- Major pages have desktop and mobile variants. Use `useIsMobile()` to branch.
- Desktop: 2-column grid layouts. Mobile: single column with `MobileHeader`/`MobileContent`.
- Mobile lists use `InfiniteListBase` for scroll pagination.

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/marmelab) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

