Files
khata-ui/REFRACTOR_GUIDE.md

173 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Refactor Guide Deep Dive into the KhataUI Codebase
> This document walks through the entire repository, explains the current architecture, and provides a stepbystep refactor plan that will improve maintainability, type safety, and UI/UX while preserving the existing functional behavior.
---
## 1. Repository Layout (highlevel)
```
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. |
| **OpenAPIdriven 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 Walkthrough
### 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 MUIcompatible 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 serverside or clientside 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 StepbyStep
### Phase1 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.
### Phase2 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 hardcoded 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.
### Phase3 Centralize Error & Loading UI
- Create `src/components/LoadingSpinner.tsx` and `src/components/ErrorToast.tsx`.
- Wrap all datafetching hooks (`useResource`, `useAuth` actions) with a HOC that automatically displays these components.
- Migrate the scattered `if (loading) …` checks into the new components.
### Phase4 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 topright corner of `App.tsx`).
3. Update component styles to use themeaware colors (via `theme.palette`), ensuring the `Chip` variants already respect the palette.
### Phase5 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.
### Phase6 Documentation (the files you will publish)
- **DESIGN.md** highlevel architecture (already present).
- **IMPLEMENTATION.md** detailed filebyfile breakdown (already present).
- **CONCEPT.md** why the metadatadriven 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 serverside.
- [ ] Mobile layout (card view) still renders correctly.
- [ ] Darkmode 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 celllevel edit mode using MUI `TextField`. |
| **GraphQL fallback** | Flexibility for backends | Abstract data fetching behind an adapter interface (`useDataProvider`). |
| **Internationalisation** | Multilanguage 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 handoff to the **lovable** UI platform while retaining its lowcode advantage.