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 — claim your Tome and manage your conversions.
1---2name: frontend-dev-23description: 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.4---56The 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.78## Component architecture910- 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).11- Import pure UI components (`Card`, `Button`, `Badge`, `Sheet`, etc.) from `@/components/ui/`.12- Domain configuration (deal stages, note statuses, task types, company sectors) comes from `useConfigurationContext()`, never hardcoded.1314## Resource (CRUD) conventions1516Each resource follows this file structure (e.g. `contacts/`):1718- `ContactList.tsx` — list page (desktop + mobile variants)19- `ContactShow.tsx` — detail view20- `ContactEdit.tsx` / `ContactCreate.tsx` — form pages21- `ContactInputs.tsx` — shared form fields reused between create and edit22- `index.tsx` — exports `{ list, show, edit, create, recordRepresentation }`2324Resources are registered in `root/CRM.tsx` via `<Resource name="contacts" {...contacts} />`.2526## Data fetching2728- For standard CRUD, use ra-core hooks: `useListContext()`, `useShowContext()`, `useGetList()`, `useGetOne()`, `useGetIdentity()`.29- 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`).3031## Forms3233- Forms use `Form` from ra-core + `FormToolbar` for submit/cancel actions.34- Ra-core's `Form` uses React Hook Form under the hood. Use `useFormContext()` for imperative operations (`setValue`, `reset`, `getValues`).35- Top-level resource forms use full-page `CreateBase`/`EditBase` with `Card` (e.g. contacts), or `Dialog` (e.g. deals).36- On mobile, inline/sub-resource forms use `CreateSheet`/`EditSheet` from `misc/` (e.g. notes, tasks).37- Split form fields into semantic sub-components (e.g. `ContactIdentityInputs`, `ContactPositionInputs`).3839## Filters4041- Use `ToggleFilterButton` / `ActiveFilterButton` components for filter UI.42- Filters apply immediately, no "apply" button.4344## Responsive design4546- Major pages have desktop and mobile variants. Use `useIsMobile()` to branch.47- Desktop: 2-column grid layouts. Mobile: single column with `MobileHeader`/`MobileContent`.48- Mobile lists use `InfiniteListBase` for scroll pagination.4950---51> Converted and distributed by [TomeVault](https://tomevault.io/claim/marmelab) — claim your Tome and manage your conversions.52<!-- tomevault:4.0:skill_md:2026-04-11 -->