Skip to content

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 any unless absolutely necessary (and annotate why).
    • Prefer explicit interfaces/types over ad-hoc object shapes.
  • 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.lazy and Suspense for route-based code-splitting.
  • State management

    • Global: AuthContext, ThemeContext, and React Query caches.
    • Local/feature: component state + custom hooks.

Naming conventions

Files & folders

  • Components

    • React components: PascalCase.tsx (e.g. InventoryTable.tsx).
    • Folders by domain: components/inventory/, components/orders/, etc.
  • 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.tsx in contexts/.
    • Hook alias: useAuth.ts, useTheme.ts.
  • Lib & utils

    • Domain logic: lib/ (e.g. subscription-utils.ts, repair-calculations.ts).
    • Generic helpers: utils/utils.ts or small modules under utils/.
  • 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/can when 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 select to derive view-specific data instead of post-processing in components when feasible.

Mutations

  • Use useMutation for create/update/delete operations.
  • On success, prefer:

    • queryClient.invalidateQueries(['inventory', tenantId]), or
    • queryClient.setQueryData for small, predictable updates.
  • Mutations should:

    • Surface errors via toast.
    • Return typed responses (no any).

Loading states

  • Prefer explicit loading states from hooks (isLoading, isFetching, isPending).
  • For route-level data, show skeletons or loading placeholders in the page component.

Error handling

UI-level

  • Use ErrorBoundary (see components/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.

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.error for unexpected errors during development; keep noise low in production.
  • Wrap logging in helpers when context matters (e.g., logError in useInvoices and usePayLinks).
  • Avoid console.log for “just checking”; either remove or convert to logError with 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/ui primitives 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).
  • Use DashboardLayout for 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 extend architecture/.
  • You change data models → update api/supabase-tables.md and 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/:

mkdocs serve

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:
!!! tip "Best Practice"
    Explanation goes here.

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.