9.9 KiB
9.9 KiB
Refactor Guide – Deep Dive into the Khata‑UI Codebase
This document walks through the entire repository, explains the current architecture, and provides a step‑by‑step refactor plan that will improve maintainability, type safety, and UI/UX while preserving the existing functional behavior.
1. Repository Layout (high‑level)
khata-ui/
├─ react-openapi/ # Core UI generated from OpenAPI configs
│ ├─ components/ # UI pieces: EnhancedTable, FilterBar, etc.
│ ├─ types/ # TypeScript interfaces (config, overrides)
│ └─ utils/ # Helper utilities (options, template resolution)
├─ src/ # Application entry point and pages
│ ├─ auth/ # Authentication context, hooks, and protected routes
│ ├─ pages/ # Dynamic resources (list, form)
│ └─ main.tsx # React root, providers, theming
├─ public/ # Static assets (favicon, index.html)
├─ index.html
├─ package.json
└─ tsconfig.json
Key Concepts
| Area | Responsibility |
|---|---|
| Auth | Central JWT handling, AuthProvider, useAuth, route guarding. |
| OpenAPI‑driven UI | Describes each resource via ResourceConfig/ResourceField. Generates tables, filters, and forms automatically. |
| Data Layer | TanStack Query (useQuery) fetches data; Axios instance carries auth token via interceptor. |
| Theming | MUI theme with light/dark mode toggle (future). |
| Extensibility | components prop on EnhancedTable / FilterBar lets callers inject custom cell renderers, filter widgets, or action buttons. |
2. Detailed Module Walk‑through
2.1 react-openapi/types/config.ts
export interface ResourceField {
displayFormat: string; // <- single source of truth for rendering
type: FieldType;
label: string;
required?: boolean;
options?: string[];
readOnly?: boolean;
schema?: Record<string, ResourceField>;
formatter?: (value: any) => string;
relation?: string;
filterType?: "autocomplete" | "multiselect" | "number-range" | "date-range";
enumOption?: EnumOption;
enumLabels?: Record<string, string>;
}
displayFormatreplaces the legacydisplayField. It can be a template string ("{{first}} {{last}}") or an array of keys for concatenation.- All UI components now rely exclusively on this field.
2.2 react-openapi/utils/options.ts
resolveTemplate(format: string, item: any)– interpolates{{key}}placeholders.getFieldOptions,toGridValueOptionsconvert enum definitions into MUI‑compatible arrays.- Refactor idea: Move the
displayFormatresolution logic fromEnhancedTable/FilterBarinto a dedicated helper (formatDisplay(item, field)), reducing duplication.
2.3 react-openapi/components/EnhancedTable.tsx
- Core responsibilities
- Build column definitions from
config.fields. - Render each cell via
FieldRenderer. - Provide server‑side or client‑side pagination.
- Add a static "Actions" column.
- Build column definitions from
- Key functions
getFormattedDisplayValue(item, displayFormat?, enumValue?)– now usesresolveTemplateand falls back to generic fields.FieldRenderer– decides how to render a cell based onfield.type,field.relation, custom renderers, anddisplayFormat.
- Duplication: Both
EnhancedTableandFilterBarperform very similardisplayFormatextraction. Extracting this into a shared utility will shrink the component size and make testing easier.
2.4 react-openapi/components/FilterBar.tsx
- Generates filter controls for each filterable field.
- Uses
extractOptionsto populate autocomplete lists, falling back todisplayFormatfor label generation. - Opportunity: Replace the inline
pullhelper with the shared formatter fromutils/options.
2.5 Authentication (src/auth)
AuthContext.tsx– providesuser,accessToken,isAuthenticatedplus actions.useAuth.ts– thin wrapper exposing the context values.ProtectedRoute.tsx– guards routes, redirects to/loginwhen unauthenticated.api.ts– thin Axios wrapper (login,logout,refresh).- Refactor suggestions
- Consolidate token storage (localStorage ↔ sessionStorage) behind a small
tokenStoreservice. - Add automatic token refresh using an interceptor that retries the original request.
- Provide a hook (
useAuthorizedQuery) that injects the auth token into TanStack Query automatically.
- Consolidate token storage (localStorage ↔ sessionStorage) behind a small
2.6 Application Core (src/pages, src/main.tsx)
ResourceList.tsx– readsresourceparam, loads the relatedResourceConfigfrom a central map, fetches data, and rendersEnhancedTable+FilterBar.ResourceForm.tsx– builds a dynamic form based onResourceFielddefinitions; usesdisplayFormatfor default values on relation fields.main.tsx– wraps the app withAuthProvider,QueryClientProvider, and MUIThemeProvider.- Future work: Extract the “resource loader” into a hook (
useResourceConfig(resourceName)) that also validates the config at runtime.
3. Refactor Roadmap – Step‑by‑Step
Phase 1 – Consolidate Formatting Logic
- Create utility
src/react-openapi/utils/formatDisplay.tsexport const formatDisplay = (item: any, field: ResourceField, enumValue?: string) => { if (enumValue) return resolveTemplate(enumValue, item); const fmt = field.displayFormat; if (!fmt) return item.name ?? item.title ?? item.label ?? item.id ?? JSON.stringify(item); if (Array.isArray(fmt)) { return fmt.map(k => item[k]).filter(Boolean).join(' '); } return resolveTemplate(fmt, item) || item.id || JSON.stringify(item); }; - Replace all inline calls to
getFormattedDisplayValueinEnhancedTableandFilterBarwithformatDisplay. - Remove
getFormattedDisplayValuefromEnhancedTable.tsx(or keep it as a thin wrapper for backward compatibility). - Update imports accordingly.
- Run TypeScript check – no errors.
Phase 2 – Decouple UI from Config Loading
- Introduce
configLoader.tsundersrc/react-openapi/utilsthat reads a JSON file (or fetches a remote spec) and produces aRecord<string, ResourceConfig>. - Replace hard‑coded imports in
src/pages/ResourceList.tsxwith a call touseResourceConfig(resourceName). - Add runtime validation (e.g., using
zod) to ensure required fields (displayFormat,type,label) are present; surface errors via a toast.
Phase 3 – Centralize Error & Loading UI
- Create
src/components/LoadingSpinner.tsxandsrc/components/ErrorToast.tsx. - Wrap all data‑fetching hooks (
useResource,useAuthactions) with a HOC that automatically displays these components. - Migrate the scattered
if (loading) …checks into the new components.
Phase 4 – Theming & Dark Mode
- Add a
ThemeContextthat storesmode: 'light' | 'dark'and persists the preference. - Expose a toggle button (e.g., in the top‑right corner of
App.tsx). - Update component styles to use theme‑aware colors (via
theme.palette), ensuring theChipvariants already respect the palette.
Phase 5 – Testing & CI
- Unit tests using
vitestfor:formatDisplayutility (various template & array cases).AuthProviderbehavior (login, logout, token refresh).
- Component tests (
@testing-library/react) forEnhancedTableandFilterBarverifying thatdisplayFormatrendering matches expectations. - Add a GitHub Actions workflow that runs
npm run lint && npx tsc --noEmit && vitest runon each PR.
Phase 6 – Documentation (the files you will publish)
- DESIGN.md – high‑level architecture (already present).
- IMPLEMENTATION.md – detailed file‑by‑file breakdown (already present).
- CONCEPT.md – why the metadata‑driven approach works (already present).
- REFRACTOR_GUIDE.md – the detailed guide you are reading now (this file).
- Keep these files in the repo root; they can be exported to the lovable platform directly.
4. Migration Checklist (what to verify after refactor)
- All UI components compile with TypeScript (
npx tsc --noEmit). - No runtime references to
displayFieldremain (search\.displayField). formatDisplaycorrectly resolves:- Template strings with multiple placeholders.
- Array of keys.
- Fallback to generic fields.
- Auth flow works (login ➜ token stored ➜ API requests succeed, protected routes guarded).
- Pagination works both client‑ and server‑side.
- Mobile layout (card view) still renders correctly.
- Dark‑mode toggle persists across reloads.
- Lint passes (
npm run lintif configured) and tests pass.
5. Potential Future Enhancements
| Feature | Benefit | Rough Implementation |
|---|---|---|
| Bulk actions (delete, export) | Improves admin productivity | Add a toolbar with selection model in EnhancedTable. |
| Inline editing | Faster data tweaks | Replace onEdit dialog with cell‑level edit mode using MUI TextField. |
| GraphQL fallback | Flexibility for back‑ends | Abstract data fetching behind an adapter interface (useDataProvider). |
| Internationalisation | Multi‑language UI | Wrap all static strings with i18n.t() and provide locale files. |
| Performance profiling | Identify render bottlenecks | Use React Profiler and memoize expensive formatters (useMemo). |
Closing Note
The current codebase already demonstrates a powerful pattern: declare once, render everywhere. By consolidating the display logic, adding a small utility layer, and strengthening the authentication and theming foundations, the project will become easier to extend, test, and hand‑off to the lovable UI platform while retaining its low‑code advantage.