173 lines
9.9 KiB
Markdown
173 lines
9.9 KiB
Markdown
# 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`
|
||
```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>;
|
||
}
|
||
```
|
||
- `displayFormat` replaces the legacy `displayField`. 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`, `toGridValueOptions` convert enum definitions into MUI‑compatible arrays.
|
||
- **Refactor idea**: Move the `displayFormat` resolution logic from `EnhancedTable`/`FilterBar` into a dedicated helper (`formatDisplay(item, field)`), reducing duplication.
|
||
|
||
### 2.3 `react-openapi/components/EnhancedTable.tsx`
|
||
- **Core responsibilities**
|
||
1. Build column definitions from `config.fields`.
|
||
2. Render each cell via `FieldRenderer`.
|
||
3. Provide server‑side or client‑side pagination.
|
||
4. Add a static "Actions" column.
|
||
- **Key functions**
|
||
- `getFormattedDisplayValue(item, displayFormat?, enumValue?)` – now uses `resolveTemplate` and falls back to generic fields.
|
||
- `FieldRenderer` – decides how to render a cell based on `field.type`, `field.relation`, custom renderers, and `displayFormat`.
|
||
- **Duplication**: Both `EnhancedTable` and `FilterBar` perform very similar `displayFormat` extraction. 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 `extractOptions` to populate autocomplete lists, falling back to `displayFormat` for label generation.
|
||
- **Opportunity**: Replace the inline `pull` helper with the shared formatter from `utils/options`.
|
||
|
||
### 2.5 Authentication (`src/auth`)
|
||
- `AuthContext.tsx` – provides `user`, `accessToken`, `isAuthenticated` plus actions.
|
||
- `useAuth.ts` – thin wrapper exposing the context values.
|
||
- `ProtectedRoute.tsx` – guards routes, redirects to `/login` when unauthenticated.
|
||
- `api.ts` – thin Axios wrapper (`login`, `logout`, `refresh`).
|
||
- **Refactor suggestions**
|
||
- Consolidate token storage (localStorage ↔ sessionStorage) behind a small `tokenStore` service.
|
||
- 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.
|
||
|
||
### 2.6 Application Core (`src/pages`, `src/main.tsx`)
|
||
- `ResourceList.tsx` – reads `resource` param, loads the related `ResourceConfig` from a central map, fetches data, and renders `EnhancedTable` + `FilterBar`.
|
||
- `ResourceForm.tsx` – builds a dynamic form based on `ResourceField` definitions; uses `displayFormat` for default values on relation fields.
|
||
- `main.tsx` – wraps the app with `AuthProvider`, `QueryClientProvider`, and MUI `ThemeProvider`.
|
||
- **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
|
||
1. **Create utility** `src/react-openapi/utils/formatDisplay.ts`
|
||
```ts
|
||
export 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);
|
||
};
|
||
```
|
||
2. Replace *all* inline calls to `getFormattedDisplayValue` in `EnhancedTable` and `FilterBar` with `formatDisplay`.
|
||
3. Remove `getFormattedDisplayValue` from `EnhancedTable.tsx` (or keep it as a thin wrapper for backward compatibility).
|
||
4. Update imports accordingly.
|
||
5. Run TypeScript check – no errors.
|
||
|
||
### Phase 2 – Decouple UI from Config Loading
|
||
- Introduce **`configLoader.ts`** under `src/react-openapi/utils` that reads a JSON file (or fetches a remote spec) and produces a `Record<string, ResourceConfig>`.
|
||
- Replace hard‑coded imports in `src/pages/ResourceList.tsx` with a call to `useResourceConfig(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.tsx` and `src/components/ErrorToast.tsx`.
|
||
- Wrap all data‑fetching hooks (`useResource`, `useAuth` actions) with a HOC that automatically displays these components.
|
||
- Migrate the scattered `if (loading) …` checks into the new components.
|
||
|
||
### Phase 4 – Theming & Dark Mode
|
||
1. Add a `ThemeContext` that stores `mode: 'light' | 'dark'` and persists the preference.
|
||
2. Expose a toggle button (e.g., in the top‑right corner of `App.tsx`).
|
||
3. Update component styles to use theme‑aware colors (via `theme.palette`), ensuring the `Chip` variants already respect the palette.
|
||
|
||
### Phase 5 – Testing & CI
|
||
- **Unit tests** using `vitest` for:
|
||
- `formatDisplay` utility (various template & array cases).
|
||
- `AuthProvider` behavior (login, logout, token refresh).
|
||
- **Component tests** (`@testing-library/react`) for `EnhancedTable` and `FilterBar` verifying that `displayFormat` rendering matches expectations.
|
||
- Add a GitHub Actions workflow that runs `npm run lint && npx tsc --noEmit && vitest run` on 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 `displayField` remain (search `\.displayField`).
|
||
- [ ] `formatDisplay` correctly 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 lint` if 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.
|