Conventions¶
This page captures the coding conventions and patterns used in Workbench AI. Treat it as the default playbook when adding or modifying code.
Language & framework¶
-
TypeScript first
- Use strict types; avoid
anyunless absolutely necessary (and annotate why). - Prefer explicit interfaces/types over ad-hoc object shapes.
- Use strict types; avoid
-
React + hooks
- Function components only (no class components).
- Prefer hooks + composition over heavy component inheritance.
-
React Router v6
- All routing is declared in
App.tsx. - Use
React.lazyandSuspensefor route-based code-splitting.
- All routing is declared in
-
State management
- Global:
AuthContext,ThemeContext, and React Query caches. - Local/feature: component state + custom hooks.
- Global:
Naming conventions¶
Files & folders¶
-
Components
- React components:
PascalCase.tsx(e.g.InventoryTable.tsx). - Folders by domain:
components/inventory/,components/orders/, etc.
- React components:
-
Hooks
useXYZ.ts(e.g.useInventory.ts,useRepairSettings.ts).- One main hook per file; helper functions can live alongside if tightly related.
-
Contexts
AuthContext.tsx,ThemeContext.tsxincontexts/.- Hook alias:
useAuth.ts,useTheme.ts.
-
Lib & utils
- Domain logic:
lib/(e.g.subscription-utils.ts,repair-calculations.ts). - Generic helpers:
utils/utils.tsor small modules underutils/.
- Domain logic:
-
Types
types/<domain>.ts(e.g.types/clients.ts,types/inventory.ts).
Code-level naming¶
- Components:
PascalCase. - Hooks:
useCamelCase. - Functions:
camelCase. - Enums / unions: descriptive names (
SubscriptionStatus,RepairType). - Booleans: prefix with
is/has/should/canwhen it improves clarity (isOwner,hasSubscription,shouldShowUpgrade).
React Query patterns¶
React Query is used throughout for data fetching and caching.
Basic rules¶
- Use stable query keys:
const queryKey = ['inventory', tenantId]
const { data, isLoading, error } = useQuery(queryKey, fetchInventoryItems)
- Keep query functions pure and focused on data fetching (no side effects).
- Use
selectto derive view-specific data instead of post-processing in components when feasible.
Mutations¶
- Use
useMutationfor create/update/delete operations. -
On success, prefer:
queryClient.invalidateQueries(['inventory', tenantId]), orqueryClient.setQueryDatafor small, predictable updates.
-
Mutations should:
- Surface errors via
toast. - Return typed responses (no
any).
- Surface errors via
Loading states¶
- Prefer explicit
loadingstates from hooks (isLoading,isFetching,isPending). - For route-level data, show skeletons or loading placeholders in the page component.
Error handling¶
UI-level¶
- Use
ErrorBoundary(seecomponents/ErrorBoundary.tsx) for global fallback. - For expected errors (validation, business rules):
- Show inline messages near the relevant fields.
- Use
toast({ title, description, variant: 'destructive' })for more global errors.
Hook-level¶
- Catch and normalize errors in hooks:
try {
// ...
} catch (err) {
const error = err instanceof Error ? err : new Error('Unknown error')
setError(error)
toast({ title: 'Error', description: error.message, variant: 'destructive' })
throw error
}
- Avoid swallowing errors silently. Either:
- Handle with a toast and return
null/[]explicitly, or - Re-throw to let the caller decide.
- Handle with a toast and return
Edge functions¶
- Always log structured context:
console.error('[Invoice Creation] context', {
error: error.message,
tenant_id: tenantId,
invoice_number: input.invoice_number,
})
- Do not log full secrets or tokens.
- Return JSON with a generic error message to clients; keep details in logs.
Logging¶
Frontend¶
- Use
console.errorfor unexpected errors during development; keep noise low in production. - Wrap logging in helpers when context matters (e.g.,
logErrorinuseInvoicesandusePayLinks). - Avoid
console.logfor “just checking”; either remove or convert tologErrorwith context.
Edge functions¶
- Use structured logs (objects) instead of raw strings.
- Mask or omit sensitive data (tokens, keys, PII).
- Log key lifecycle events:
- Webhook received, verified, skipped, applied.
- Subscription sync runs, counts, and errors.
- Nameplate usage checks and limit hits.
React & component conventions¶
- Prefer controlled components for forms (with sensible defaults).
- Lean on
components/uiprimitives for consistency (buttons, cards, dialogs, tables, etc.). - Keep components focused:
- If a component does too many things, split into smaller pieces (e.g.,
InventoryTable+InventoryFilters).
- If a component does too many things, split into smaller pieces (e.g.,
- Use
DashboardLayoutfor authenticated app sections; keep public pages separate in layout.
Adding & updating documentation¶
Docs live under docs-site/docs/ and are built with MkDocs Material.
When to update docs¶
- You add a new feature → add a page under
flows/and/or extendarchitecture/. - You change data models → update
api/supabase-tables.mdand edge function docs if relevant. - You introduce a new pattern or convention → update this page or
dev/onboarding.md.
How to run docs locally¶
From docs-site/:
Open the printed URL (usually http://127.0.0.1:8000) and verify:
- New pages show up correctly in the nav.
- Mermaid diagrams render.
- Cross-links are valid.
Style¶
- Use short, direct sentences.
- Prefer lists and small sections over dense paragraphs.
- Add code snippets when explaining patterns.
- Use Material admonitions for emphasis:
Pull requests & reviews¶
- Keep PRs focused (one concern per PR where possible).
- Include:
- Summary of changes.
- Screenshots/GIFs for UI changes.
- Notes about new env vars or migrations.
- If you touch migrations or edge functions, explicitly mention it in the PR description.
Following these conventions keeps the codebase consistent and easier to work in for everyone.