mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 13:01:10 +00:00
510df68bcf
Captures the full output of autodev existing-code Phase A through Step 4 (Code Testability Revision) for the Azaion UI workspace: - Step 1 Document: _docs/02_document/ (FINAL_report, architecture, glossary, components/, modules/, diagrams/, system-flows, module-layout) plus _docs/00_problem/ + _docs/01_solution/ + _docs/legacy/ + _docs/how_to_test + README. - Step 2 Architecture Baseline: architecture_compliance_baseline.md. - Step 3 Test Spec: _docs/02_document/tests/ (environment, test-data, blackbox/performance/resilience/security/ resource-limit tests, traceability-matrix), enum_spec_snapshot, expected_results/results_report.md (98 rows), plus the run-tests.sh + run-performance-tests.sh runners. - Step 4 Code Testability Revision: 01-testability-refactoring/ run dir (list-of-changes C01-C07, deferred_to_refactor, analysis/research_findings + refactoring_roadmap) and the 7 child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/ plus _dependencies_table.md. - _docs/_autodev_state.md pins the cursor at Step 4 / refactor Phase 4 entry so /autodev resumes cleanly. Epic AZ-447 (UI testability gates) tracks the 7 child tasks that will land in subsequent commits. Co-authored-by: Cursor <cursoragent@cursor.com>
218 lines
16 KiB
Markdown
218 lines
16 KiB
Markdown
# Module Layout
|
||
|
||
**Status**: derived-from-code
|
||
**Language**: typescript (React 19 + Vite + Tailwind)
|
||
**Layout Convention**: custom (flat-features under `src/`; no per-component barrels)
|
||
**Root**: `src/`
|
||
**Last Updated**: 2026-05-10
|
||
|
||
> Authoritative file-ownership map for the React UI workspace. Derived from
|
||
> `_docs/02_document/00_discovery.md` (dependency graph) and the Step 2
|
||
> component specs at `_docs/02_document/components/`. Consumed by
|
||
> `/implement` Step 4 (file ownership), `/code-review` Phase 7
|
||
> (architecture violations), and `/refactor` discovery.
|
||
|
||
## Layout Rules
|
||
|
||
1. Each component owns ONE OR MORE top-level directories (or top-level files) under `src/`. The mapping is NOT 1:1 — `00_foundation` owns three sibling directories (`src/types/`, `src/hooks/`, `src/i18n/`), `05_flights` spans `src/features/flights/` AND a separate `mission-planner/` port-source root, and `10_app-shell` owns top-level files (`App.tsx`, `main.tsx`, `index.css`, `vite-env.d.ts`).
|
||
2. Shared code does **not** live under `src/shared/` today — there is no `shared/` directory. Two helper modules (`11_class-colors/classColors.ts` and `06_annotations/CanvasEditor.tsx`) are physically misplaced and consumed across components; both are flagged in the `## Verification Needed` block. A `src/shared/` directory is a Step 4 testability candidate.
|
||
3. Public API per component: NO barrel `index.ts` exists at any component root. The only `index.ts` files are `src/types/index.ts` (a re-export hub for type aliases — used as the de-facto public API for `00_foundation` types) and `mission-planner/src/types/index.ts`. Until Step 4 introduces barrels, Public API is approximated as "every named export from any file under the component's owned directories". Cross-component imports ARE happening at file-name granularity (`import { api } from '../api/client'`, `import { CanvasEditor } from '../annotations/CanvasEditor'`).
|
||
4. Cross-cutting concerns (logging, config, error handling, telemetry): no dedicated infrastructure today. `console.error` / silent catches are the closest thing — recorded in module findings.
|
||
5. Tests: there are **zero tests** under `src/`. The only test file is `mission-planner/src/test/jsonImport.test.ts`, which can't run because Jest isn't installed (00_discovery.md §11.5). Test layout is therefore TBD; suggest `src/<component>/__tests__/` per the standard React convention when tests are added (autodev Step 5–6).
|
||
|
||
## Per-Component Mapping
|
||
|
||
### Component: `00_foundation`
|
||
|
||
- **Epic**: TBD (set during autodev Step 4 / Decompose)
|
||
- **Directories**: `src/types/`, `src/hooks/`, `src/i18n/`
|
||
- **Public API** (de-facto, no barrel):
|
||
- `src/types/index.ts` — every exported type alias (`Detection`, `Flight`, `MediaItem`, `User`, etc.)
|
||
- `src/hooks/useDebounce.ts` — `useDebounce`
|
||
- `src/hooks/useResizablePanel.ts` — `useResizablePanel`
|
||
- `src/i18n/i18n.ts` — default export (i18n instance)
|
||
- **Internal**: `src/i18n/en.json`, `src/i18n/ua.json` (data; consumed only by `i18n.ts`)
|
||
- **Owns** (exclusive write): `src/types/**`, `src/hooks/**`, `src/i18n/**`
|
||
- **Imports from**: (none — Layer 0)
|
||
- **Consumed by**: every other component
|
||
|
||
### Component: `11_class-colors`
|
||
|
||
- **Epic**: TBD
|
||
- **Directories**: (none today — physical file lives at `src/features/annotations/classColors.ts`, which is owned by `06_annotations` on disk). Logical owner is this component; physical move to `src/shared/classColors.ts` (or `src/components/detection/classColors.ts`) is a Step 4 testability task.
|
||
- **Public API**: `src/features/annotations/classColors.ts` exports `getClassColor`, `getClassNameFallback`, `getPhotoModeSuffix`, `FALLBACK_CLASS_NAMES`.
|
||
- **Internal**: module-private `CLASS_COLORS` constant.
|
||
- **Owns**: pending — see Verification Needed item #1.
|
||
- **Imports from**: (none — Layer 0/1, no internal imports)
|
||
- **Consumed by**: `03_shared-ui` (DetectionClasses), `06_annotations` (CanvasEditor, AnnotationsPage, AnnotationsSidebar)
|
||
|
||
### Component: `01_api-transport`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/api/`
|
||
- **Public API** (de-facto): `src/api/client.ts` exports `api` (fetch wrapper); `src/api/sse.ts` exports `subscribeSSE` / equivalent helper.
|
||
- **Internal**: none (both files are externally consumed)
|
||
- **Owns**: `src/api/**`
|
||
- **Imports from**: `00_foundation` (types)
|
||
- **Consumed by**: `02_auth`, `03_shared-ui`, every feature page (04, 05, 06, 07, 08, 09)
|
||
|
||
### Component: `02_auth`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/auth/`
|
||
- **Public API**: `src/auth/AuthContext.tsx` exports `AuthProvider`, `useAuth`. `src/auth/ProtectedRoute.tsx` exports `ProtectedRoute`.
|
||
- **Internal**: none
|
||
- **Owns**: `src/auth/**`
|
||
- **Imports from**: `00_foundation`, `01_api-transport`
|
||
- **Consumed by**: `03_shared-ui` (Header reads `useAuth`), `04_login`, `10_app-shell` (mounts `AuthProvider` + `ProtectedRoute`)
|
||
|
||
### Component: `03_shared-ui`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/components/`
|
||
- **Public API** (de-facto, all are externally consumed):
|
||
- `Header.tsx` → `Header`
|
||
- `HelpModal.tsx` → `HelpModal`
|
||
- `ConfirmDialog.tsx` → `ConfirmDialog`
|
||
- `DetectionClasses.tsx` → `DetectionClasses`
|
||
- `FlightContext.tsx` → `FlightProvider`, `useFlight`
|
||
- **Internal**: none — every file in `src/components/` is consumed externally today
|
||
- **Owns**: `src/components/**`
|
||
- **Imports from**: `00_foundation`, `11_class-colors` (physical: `../features/annotations/classColors`), `01_api-transport`, `02_auth`
|
||
- **Consumed by**: `10_app-shell` (mounts `Header` + `FlightProvider`), every feature page (consumes `useFlight`, `ConfirmDialog`, `DetectionClasses`)
|
||
|
||
### Component: `04_login`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/features/login/`
|
||
- **Public API**: `LoginPage.tsx` → `LoginPage`
|
||
- **Internal**: none (single-page component)
|
||
- **Owns**: `src/features/login/**`
|
||
- **Imports from**: `00_foundation`, `01_api-transport`, `02_auth`
|
||
- **Consumed by**: `10_app-shell` (route)
|
||
|
||
### Component: `05_flights`
|
||
|
||
- **Epic**: TBD (this is the merged Flights & Mission Planning component)
|
||
- **Directories** (TWO physical roots):
|
||
- `src/features/flights/` — deployed target tree (15 modules)
|
||
- `mission-planner/` — port-source, NOT deployed (37 modules under `mission-planner/src/`). Documented inside this component per the user's Step 2 BLOCKING-gate decision (`_docs/02_document/state.json::component_05_flights_merge_2026-05-10`). The port direction is `mission-planner/` → `src/features/flights/`; module-layout treats both trees as owned by this component but only the target tree is in the layering table below.
|
||
- **Public API** (target tree, de-facto): `FlightsPage.tsx` → `FlightsPage` (route component). Internal sub-components (`FlightMap`, `FlightParamsPanel`, `FlightListSidebar`, `WaypointList`, `AltitudeChart`, `AltitudeDialog`, `WindEffect`, `MiniMap`, `MapPoint`, `DrawControl`, `JsonEditorDialog`, `mapIcons`, `flightPlanUtils`, `types`) are NOT consumed outside the component.
|
||
- **Public API** (port-source `mission-planner/`): not consumed at all by `src/` today (separate Vite entrypoint, `main.tsx` of its own). Effectively a private vendored sibling.
|
||
- **Internal** (target tree): every file under `src/features/flights/` except `FlightsPage.tsx`
|
||
- **Internal** (port-source): every file under `mission-planner/`
|
||
- **Owns**: `src/features/flights/**`, `mission-planner/**`
|
||
- **Imports from** (target tree): `00_foundation`, `01_api-transport`, `02_auth` (via `ProtectedRoute` from shell), `03_shared-ui` (uses `ConfirmDialog`, `useFlight`)
|
||
- **Consumed by**: `10_app-shell` (route)
|
||
|
||
### Component: `06_annotations`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/features/annotations/`
|
||
- **Public API** (de-facto):
|
||
- `AnnotationsPage.tsx` → `AnnotationsPage` (route component)
|
||
- `CanvasEditor.tsx` → `CanvasEditor` — **also imported by `07_dataset`** (cross-feature edge, see Verification Needed #3)
|
||
- **Internal**: `MediaList.tsx`, `VideoPlayer.tsx`, `AnnotationsSidebar.tsx`
|
||
- **Owns**: `src/features/annotations/**` EXCEPT `classColors.ts` (logically owned by `11_class-colors`; physical home pending refactor)
|
||
- **Imports from**: `00_foundation`, `11_class-colors`, `01_api-transport`, `03_shared-ui`
|
||
- **Consumed by**: `10_app-shell` (route); `07_dataset` (imports `CanvasEditor` directly — see Verification Needed)
|
||
|
||
### Component: `07_dataset`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/features/dataset/`
|
||
- **Public API**: `DatasetPage.tsx` → `DatasetPage`
|
||
- **Internal**: none (single-page)
|
||
- **Owns**: `src/features/dataset/**`
|
||
- **Imports from**: `00_foundation`, `11_class-colors` (only when class-distribution chart is added — not in code yet), `01_api-transport`, `03_shared-ui`, **`06_annotations` (CanvasEditor cross-feature edge)**
|
||
- **Consumed by**: `10_app-shell` (route)
|
||
|
||
### Component: `08_admin`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/features/admin/`
|
||
- **Public API**: `AdminPage.tsx` → `AdminPage`
|
||
- **Internal**: none (single-page)
|
||
- **Owns**: `src/features/admin/**`
|
||
- **Imports from**: `00_foundation`, `01_api-transport`, `03_shared-ui`
|
||
- **Consumed by**: `10_app-shell` (route)
|
||
|
||
### Component: `09_settings`
|
||
|
||
- **Epic**: TBD
|
||
- **Directory**: `src/features/settings/`
|
||
- **Public API**: `SettingsPage.tsx` → `SettingsPage`
|
||
- **Internal**: none (single-page)
|
||
- **Owns**: `src/features/settings/**`
|
||
- **Imports from**: `00_foundation`, `01_api-transport`, `03_shared-ui`
|
||
- **Consumed by**: `10_app-shell` (route)
|
||
|
||
### Component: `10_app-shell`
|
||
|
||
- **Epic**: TBD
|
||
- **Files** (no dedicated directory): `src/App.tsx`, `src/main.tsx`, `src/index.css`, `src/vite-env.d.ts`
|
||
- **Public API**: `main.tsx` is the Vite entrypoint (no symbols are externally imported). `App.tsx` exports `App`.
|
||
- **Internal**: `index.css` (global Tailwind base + `az-*` design-token CSS variables), `vite-env.d.ts` (type shim)
|
||
- **Owns**: `src/App.tsx`, `src/main.tsx`, `src/index.css`, `src/vite-env.d.ts`
|
||
- **Imports from**: every other component (it is the composition root)
|
||
- **Consumed by**: (none — top of the graph; bundled by Vite)
|
||
|
||
## Shared / Cross-Cutting
|
||
|
||
> No `src/shared/` directory exists today. Two cross-cutting concerns are tracked here as **proposed** shared modules; they require a physical file move scheduled for Step 4 (testability) or Step 8 (refactor).
|
||
|
||
### shared/class-colors (proposed; current physical location: `src/features/annotations/classColors.ts`)
|
||
|
||
- **Owner component**: `11_class-colors`
|
||
- **Purpose**: Detection-class fallback color, fallback name, PhotoMode suffix.
|
||
- **Owned by**: pending move task — current physical file is under `06_annotations`'s owns-glob, which makes it ambiguous. Workaround: until moved, treat `classColors.ts` as `OWNED` by tasks targeting `11_class-colors` and `READ-ONLY` to all other tasks (including those targeting `06_annotations`).
|
||
- **Consumed by**: `03_shared-ui/DetectionClasses`, `06_annotations` (CanvasEditor, AnnotationsPage, AnnotationsSidebar)
|
||
|
||
### shared/canvas-editor (proposed; current physical location: `src/features/annotations/CanvasEditor.tsx`)
|
||
|
||
- **Owner component**: still `06_annotations` for now (it's the dominant consumer)
|
||
- **Purpose**: Bounding-box draw / move / resize layer; reused by Dataset's inline editor.
|
||
- **Status**: cross-feature edge (07_dataset imports it). The proper home is a future `src/components/canvas/` directory. Decision deferred to Step 2 architecture baseline scan; the implement skill should treat this as a `READ-ONLY` for `07_dataset` tasks.
|
||
|
||
## Allowed Dependencies (layering)
|
||
|
||
Read top-to-bottom; an upper layer may import from a lower layer but NEVER the reverse. Same-layer imports are permitted only for explicit cross-feature edges listed in the table footnotes.
|
||
|
||
| Layer | Components | May import from |
|
||
|-------|------------|-----------------|
|
||
| 4. App Shell / Entry | `10_app-shell` | 0, 1, 2, 3 (and any feature in Layer 3) |
|
||
| 3. Application / Features | `04_login`, `05_flights`, `06_annotations`, `07_dataset`<sup>†</sup>, `08_admin`, `09_settings` | 0, 1, 2, 3<sup>†</sup> |
|
||
| 2. Composition | `02_auth`, `03_shared-ui` | 0, 1 |
|
||
| 1. Transport | `01_api-transport` | 0 |
|
||
| 0. Foundation / Shared kernel | `00_foundation`, `11_class-colors` | (none) |
|
||
|
||
<sup>†</sup> `07_dataset` imports `06_annotations/CanvasEditor.tsx` — same-layer cross-feature edge. Permitted today; flagged as a refactor target. The implement skill grants `07_dataset` tasks **READ-ONLY** access to that one file specifically.
|
||
|
||
Violations of this table are **Architecture** findings in code-review Phase 7 and are High severity.
|
||
|
||
## Verification Needed
|
||
|
||
The following inferences could not be made cleanly from code alone. They are surfaced for the user to confirm or override at the Step 2.5 BLOCKING gate.
|
||
|
||
1. **Physical home of `11_class-colors`**. The component is logically Layer 0/1 shared kernel, but its physical file lives inside `06_annotations`'s owns-glob (`src/features/annotations/classColors.ts`). Until the file is moved (proposed: `src/shared/classColors.ts`), the implement skill must apply the special-case rule documented under `shared/class-colors` above (READ-ONLY for `06_annotations` tasks even though the file is inside that component's directory). **Decision needed**: schedule the file move at Step 4 / Step 8, or accept the special-case rule indefinitely?
|
||
|
||
2. **Physical home of `CanvasEditor.tsx`**. Same shape: it lives under `06_annotations` and is consumed cross-feature by `07_dataset`. Proposed: `src/components/canvas/CanvasEditor.tsx` (or a new `06b_canvas` component). **Decision needed**: keep the same-layer cross-feature edge, or schedule the lift?
|
||
|
||
3. **No barrel exports anywhere**. The codebase imports cross-component at file-name granularity (`import { api } from '../api/client'`). This means every internal file is *de-facto* Public API. Recommendation: Step 4 testability task to add `src/<component>/index.ts` barrels per component, locking the public surface. **Decision needed**: add barrels now or stay file-import?
|
||
|
||
4. **`mission-planner/` is owned by `05_flights` but lives at the repo root** (not under `src/`). Layout rule #1 says one component owns one or more top-level directories — this satisfies the rule (it owns two: `src/features/flights/` AND `mission-planner/`). Implement-skill consumers must include `mission-planner/**` in `05_flights`'s OWNED glob. **Decision needed**: confirm the implement skill should treat `mission-planner/**` as OWNED by 05_flights (otherwise it's FORBIDDEN by default).
|
||
|
||
5. **`05_flights` cycle inside the port-source**. `mission-planner/src/flightPlanning/MapView.tsx ↔ MiniMap.tsx` form a circular import (named-handle, see `00_discovery.md` §7 footnote). They were analyzed together in batch MP-B6. The cycle is internal to the component and does not cross component boundaries; flagged here for completeness.
|
||
|
||
6. **`00_foundation` owns three sibling directories** (`types/`, `hooks/`, `i18n/`). Layout rule #1 permits this. Future option: split into `00a_types`, `00b_hooks`, `00c_i18n` if the directories grow. **Decision needed**: keep the multi-directory component, or split now?
|
||
|
||
7. **`10_app-shell` owns top-level files instead of a directory** (`src/App.tsx`, `src/main.tsx`, etc.). Layout rule #1 permits this. The implement-skill OWNED glob for app-shell tasks must therefore be the explicit file list, not a directory glob.
|
||
|
||
8. **Test layout is undefined** (no tests exist). When Steps 5–6 of autodev produce tests, recommended layout is `src/<component-dir>/__tests__/` per React convention; for `05_flights` cross-tree tests, prefer `src/features/flights/__tests__/` (target tree only).
|
||
|
||
## Layout Conventions (reference)
|
||
|
||
| Language | Root | Per-component path | Public API file | Test path |
|
||
|----------|------|-------------------|-----------------|-----------|
|
||
| TypeScript / React | `src/` | `src/<component>/` (this codebase deviates: features under `src/features/<feature>/`, shared chrome under `src/components/`) | `src/<component>/index.ts` (barrel — none exist today) | `src/<component>/__tests__/` (none exist today) |
|