dodil/erp-ui

README.md

erp-ui

The core ERP UI components — the data-heavy building blocks every brick's screens are made of: tables, filters, record pages, timelines. Built on Radix (Radix Themes + primitives) in a Dodil-inspired default theme, and delivered as source a project owns, so a client can change any of it without forking a library.

ItemWhat it does
themeErpTheme: Radix Themes, purple accent, slate gray. Rebrand with one prop or the --accent-* variables
formatMoney as exact text — separators inserted, never through a float; exact decimal comparison and addition (sumDecimal, for totals beside rows the API computed); dates
jsonparseJsonLossless / quoteUnsafeIntegers: JSON where a 64-bit id (bp_id) past 2^53 arrives exactly, as a string — JSON.parse turns it into another partner's id
data-tableLists by column kind (text, number, money, date, status): exact money sorting, pagination, loading / empty / error states, open a row by click or Enter. cell is the escape hatch
filter-barControlled search + select filters, Clear
detail-pageOne record: header, status, actions that are shown disabled with the reason (ActionButton, usable anywhere), a toolbar for dialog triggers, sections of fields, and panels other modules add through an extension point
timelineWhat was said and done, newest first, grouped by day
status-badgeA state as a coloured badge, with brick-specific tones
statesLoadingState, EmptyState (with the next step), ErrorState (the API's reason, Try again), NotFoundState (the 404 answer)
formFormDialog: fields as data, required/shape checks before sending, the API's refusal shown in place with what was typed kept; opened by a trigger or from code
money-inputAn amount typed by a person, kept as exact decimal text ("12,000.5" → "12000.50"); a third decimal is an error, never rounded
confirm-dialogConfirm a step that is recorded or cannot be undone; asks for the reason when the API needs one
toastToaster / useToast: confirms a step that worked. A refusal is never a toast — it stays where the person acted
kanban-boardRecords in columns by state; every move has a keyboard path (a "Move to" menu per card), dragging is the mouse shortcut, a column or card can say why a move is refused
statStat / StatGrid: the numbers a person starts the day from
app-shellThe frame: the modules' navigation by section, the account menu, light/dark, a skip link; a sidebar at a desk, a menu on a phone
contractThe module UI contract every brick's UI exports — screens, extension points, extensions — and checkModules(), which refuses route collisions and panels aimed at a point nobody offers

Use it

The components install as source into a project (shadcn's registry format; no Tailwind needed):

// components.json in the consuming project
{ "registries": { "@erp-ui": "<where public/r is served>/{name}.json" } }
npx shadcn add @erp-ui/data-table     # brings @erp-ui/format and @erp-ui/status-badge with it

Wrap the app in <ErpTheme>. Every component draws colour, spacing and radius from Radix tokens only. theme.css raises Radix's placeholder text to --gray-11 and StatusBadge uses high-contrast text: both were under 4.5:1 in a real browser (axe on the CRM screens, light and dark, 2026-10-17).

How a product's UI is assembled

Each brick ships its own UI (web/ in the brick repo) exporting a ModuleUI. The composition's shell assembles them — by default into one bundle served by the composed app (ui: monolith); the same contract also allows runtime federation, or each brick serving its own UI. A brick never imports another brick's UI: it links by URL and plugs panels into extension points, the UI's version of a seam.

Develop

npm install
npm run dev              # the gallery: every component in the default theme, with CRM data
npm test                 # 30 tests incl. an axe accessibility check per component
npm run typecheck
npx shadcn build --output public/r      # rebuild the registry after changing a component

Money sorting is tested with amounts past a float's precision: a float sort sees them as equal and fails the test (checked by putting one in).

Used by

The CRM's screens (erp-compositions crm/web) vendor these items from public/r with scripts/vendor-erp-ui.mjs and record the commit they came from.

Not in 0.2

Charts, column visibility, URL state helpers, server-side pagination, a responsive card layout for wide tables on a phone (they scroll inside themselves today).

License: Apache-2.0, see LICENSE