|
| 1 | +# apps/web — Frontend Development Plan & Roadmap |
| 2 | + |
| 3 | +> Complete plan for building the **ObjectOS Admin Console & Business App Shell** (`apps/web`). |
| 4 | +
|
| 5 | +## 1. Executive Summary |
| 6 | + |
| 7 | +`apps/web` is a **Vite + React 19 SPA** that serves two purposes: |
| 8 | + |
| 9 | +1. **Admin Console** (`/settings/*`) — System administration pages for managing organizations, users, permissions, plugins, jobs, metrics, and more. |
| 10 | +2. **Business App Shell** (`/apps/:appId/*`) — A metadata-driven workspace that renders ObjectStack business applications (CRM, HRM, Finance, etc.) by assembling ObjectUI controls. |
| 11 | + |
| 12 | +### Current State (Baseline) |
| 13 | + |
| 14 | +| Area | Status | |
| 15 | +|---|---| |
| 16 | +| Auth (sign-in, sign-up, 2FA, OAuth) | ✅ Complete (6 pages) | |
| 17 | +| Settings / Admin Console | ✅ Complete (16 pages) | |
| 18 | +| Business App Shell | 🟡 Placeholder only — single `app.tsx` stub | |
| 19 | +| Object List View | ❌ Not started | |
| 20 | +| Object Record View | ❌ Not started | |
| 21 | +| API Client Layer | ❌ Not started | |
| 22 | +| Metadata-driven Navigation | ❌ Not started | |
| 23 | +| ObjectUI Integration | ❌ Not started | |
| 24 | + |
| 25 | +### Goal |
| 26 | + |
| 27 | +Build the **Business App Shell** layer so that any ObjectStack app registered via the plugin manifest can be rendered in the browser with: |
| 28 | + |
| 29 | +- Dynamic sidebar navigation derived from object metadata |
| 30 | +- Object list pages with sortable/filterable tables |
| 31 | +- Object record detail pages with form views |
| 32 | +- Proper loading, empty, and error states |
| 33 | +- API integration via TanStack Query → `/api/v1/*` |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## 2. Architecture |
| 38 | + |
| 39 | +``` |
| 40 | +┌─────────────────────────────────────────────────────────┐ |
| 41 | +│ apps/web (SPA) │ |
| 42 | +│ │ |
| 43 | +│ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │ |
| 44 | +│ │ Auth │ │ Admin Console│ │ Business App Shell│ │ |
| 45 | +│ │ Pages │ │ /settings/* │ │ /apps/:appId/* │ │ |
| 46 | +│ └──────────┘ └──────────────┘ └──────────────────┘ │ |
| 47 | +│ │ │ |
| 48 | +│ ┌────┴────┐ │ |
| 49 | +│ │ AppLayout│ │ |
| 50 | +│ │ + Object │ │ |
| 51 | +│ │ Nav │ │ |
| 52 | +│ └────┬────┘ │ |
| 53 | +│ ┌──────────┼──────────┐ │ |
| 54 | +│ │ │ │ │ |
| 55 | +│ ObjectList ObjectRecord AppHome│ |
| 56 | +│ (table) (form/detail) (dash) │ |
| 57 | +│ │ |
| 58 | +│ ┌─────────────────────────────────────────────────┐ │ |
| 59 | +│ │ Shared Infrastructure │ │ |
| 60 | +│ │ api.ts │ use-metadata.ts │ use-records.ts │ │ |
| 61 | +│ │ types/metadata.ts │ app-registry.ts │ │ |
| 62 | +│ └─────────────────────────────────────────────────┘ │ |
| 63 | +└──────────────────────┬──────────────────────────────────┘ |
| 64 | + │ HTTP (TanStack Query) |
| 65 | + ▼ |
| 66 | + ObjectStack Hono Server |
| 67 | + /api/v1/* (REST + GraphQL) |
| 68 | +``` |
| 69 | + |
| 70 | +### Key Principles |
| 71 | + |
| 72 | +1. **Metadata-Driven**: Object definitions (fields, labels, icons) drive the UI — no hardcoded business logic in the frontend. |
| 73 | +2. **API-First**: All data flows through `/api/v1/*` endpoints via TanStack Query. |
| 74 | +3. **Progressive Enhancement**: Start with basic table/form views, later integrate ObjectUI controls. |
| 75 | +4. **Lazy Loading**: All page components are lazy-loaded for optimal code splitting. |
| 76 | +5. **Type Safety**: Full TypeScript types for metadata, records, and API responses. |
| 77 | + |
| 78 | +--- |
| 79 | + |
| 80 | +## 3. Type System |
| 81 | + |
| 82 | +### Core Metadata Types (`src/types/metadata.ts`) |
| 83 | + |
| 84 | +```typescript |
| 85 | +// Object field definition — mirrors @objectstack/spec |
| 86 | +interface FieldDefinition { |
| 87 | + name: string; |
| 88 | + type: 'text' | 'number' | 'boolean' | 'datetime' | 'select' | 'reference' | 'textarea' | 'email' | 'url' | 'phone' | 'currency' | 'percent' | 'object'; |
| 89 | + label: string; |
| 90 | + required?: boolean; |
| 91 | + readonly?: boolean; |
| 92 | + defaultValue?: unknown; |
| 93 | + options?: { label: string; value: string }[]; // for 'select' type |
| 94 | + referenceTo?: string; // for 'reference' type |
| 95 | + group?: string; // field grouping |
| 96 | +} |
| 97 | + |
| 98 | +// Object definition — describes a business entity |
| 99 | +interface ObjectDefinition { |
| 100 | + name: string; |
| 101 | + label: string; |
| 102 | + pluralLabel: string; |
| 103 | + icon?: string; |
| 104 | + description?: string; |
| 105 | + fields: Record<string, FieldDefinition>; |
| 106 | + primaryField?: string; // which field is the "name" field |
| 107 | + listFields?: string[]; // which fields to show in list view |
| 108 | +} |
| 109 | + |
| 110 | +// App definition — describes a business application |
| 111 | +interface AppDefinition { |
| 112 | + id: string; |
| 113 | + name: string; |
| 114 | + description: string; |
| 115 | + icon?: string; |
| 116 | + objects: string[]; // object names belonging to this app |
| 117 | + defaultObject?: string; // landing object |
| 118 | + status: 'active' | 'paused'; |
| 119 | + category: 'system' | 'business' | 'custom'; |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +--- |
| 124 | + |
| 125 | +## 4. Phase Roadmap |
| 126 | + |
| 127 | +### Phase 1: Foundation (Current Sprint) ✅ |
| 128 | + |
| 129 | +**Goal**: Build the core infrastructure for metadata-driven business apps. |
| 130 | + |
| 131 | +| Deliverable | File(s) | Description | |
| 132 | +|---|---|---| |
| 133 | +| API client | `src/lib/api.ts` | `@objectstack/client` SDK with mock fallback | |
| 134 | +| Metadata types | `src/types/metadata.ts` | TypeScript interfaces for objects, fields, apps | |
| 135 | +| Metadata hooks | `src/hooks/use-metadata.ts` | TanStack Query hooks: `useAppObjects`, `useObjectDefinition` | |
| 136 | +| Record hooks | `src/hooks/use-records.ts` | TanStack Query hooks: `useRecords`, `useRecord`, mutations | |
| 137 | +| Object list page | `src/pages/apps/object-list.tsx` | Table view with sorting, pagination | |
| 138 | +| Object record page | `src/pages/apps/object-record.tsx` | Detail view with field rendering | |
| 139 | +| App home page | `src/pages/apps/app.tsx` | Enhanced dashboard with object cards | |
| 140 | +| Dynamic app navigation | `AppLayout.tsx` | Sidebar nav items from object metadata | |
| 141 | +| Routing update | `App.tsx` | Add `/:objectName` and `/:objectName/:recordId` routes | |
| 142 | + |
| 143 | +### Phase 2: Rich Data Views (Next Sprint) |
| 144 | + |
| 145 | +| Deliverable | Description | |
| 146 | +|---|---| |
| 147 | +| Inline editing | Click-to-edit fields in record detail | |
| 148 | +| Column configuration | Users choose which columns to display | |
| 149 | +| Search & filters | Global search bar + field-level filters on list view | |
| 150 | +| Bulk actions | Multi-select rows for batch operations | |
| 151 | +| Related lists | Show child/related records on detail page | |
| 152 | +| Pagination controls | Page size selector, prev/next, jump-to-page | |
| 153 | + |
| 154 | +### Phase 3: ObjectUI Integration |
| 155 | + |
| 156 | +| Deliverable | Description | |
| 157 | +|---|---| |
| 158 | +| `@objectui/form` | Metadata-driven form control | |
| 159 | +| `@objectui/grid` | Advanced data grid with virtual scrolling | |
| 160 | +| `@objectui/kanban` | Kanban board view for pipeline objects | |
| 161 | +| `@objectui/chart` | Dashboard chart widgets | |
| 162 | +| View switcher | Toggle between Table / Kanban / Calendar views | |
| 163 | +| Layout builder | Drag-and-drop page layout configuration | |
| 164 | + |
| 165 | +### Phase 4: Workflow & Automation UI |
| 166 | + |
| 167 | +| Deliverable | Description | |
| 168 | +|---|---| |
| 169 | +| Workflow status badges | Show current workflow state on records | |
| 170 | +| Approval buttons | Approve/reject actions on pending records | |
| 171 | +| Workflow visualizer | BPMN-lite flow diagram for workflow definitions | |
| 172 | +| Automation rules UI | Visual rule builder for triggers and actions | |
| 173 | +| Activity timeline | Show audit log / activity feed on records | |
| 174 | + |
| 175 | +### Phase 5: Offline & Sync |
| 176 | + |
| 177 | +| Deliverable | Description | |
| 178 | +|---|---| |
| 179 | +| Service Worker | PWA support for offline access | |
| 180 | +| Local SQLite (WASM) | Client-side data cache via `@objectos/browser` | |
| 181 | +| Sync engine | Push mutations / pull deltas from server | |
| 182 | +| Conflict resolution UI | Manual conflict resolution for sync conflicts | |
| 183 | +| Optimistic updates | Instant UI feedback before server confirmation | |
| 184 | + |
| 185 | +### Phase 6: Polish & Performance |
| 186 | + |
| 187 | +| Deliverable | Description | |
| 188 | +|---|---| |
| 189 | +| Virtual scrolling | Handle 10K+ rows in list views | |
| 190 | +| Keyboard navigation | Full keyboard shortcut support | |
| 191 | +| Accessibility audit | WCAG 2.1 AA compliance | |
| 192 | +| i18n integration | Multi-locale support via `@objectos/i18n` | |
| 193 | +| Theme system | Light/dark mode + custom brand colors | |
| 194 | +| Performance budget | Bundle size limits, Lighthouse CI | |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +## 5. API Contract |
| 199 | + |
| 200 | +The frontend uses the official `@objectstack/client` SDK to interact with the server. |
| 201 | + |
| 202 | +### Client SDK Usage |
| 203 | + |
| 204 | +```typescript |
| 205 | +import { ObjectStackClient } from '@objectstack/client'; |
| 206 | + |
| 207 | +const client = new ObjectStackClient({ baseUrl: '/api/v1' }); |
| 208 | + |
| 209 | +// Metadata |
| 210 | +client.meta.getObject('lead') // → ObjectDefinition |
| 211 | +client.meta.getItems('object') // → { type: 'object', items: ObjectDefinition[] } |
| 212 | +client.meta.getItems('app') // → { type: 'app', items: AppDefinition[] } |
| 213 | +client.meta.getItem('app', 'crm') // → AppDefinition |
| 214 | + |
| 215 | +// Data |
| 216 | +client.data.find('lead', { top: 20 }) // → { records: T[], total: number } |
| 217 | +client.data.get('lead', 'lead-001') // → { record: T } |
| 218 | +client.data.create('lead', { ... }) // → { record: T } |
| 219 | +client.data.update('lead', 'id', { }) // → { record: T } |
| 220 | +client.data.delete('lead', 'id') // → { deleted: true } |
| 221 | +``` |
| 222 | + |
| 223 | +> When the server is unreachable, hooks fall back to mock data from `lib/mock-data.ts`. |
| 224 | +> This allows the UI to be developed independently of the backend. |
| 225 | +
|
| 226 | +--- |
| 227 | + |
| 228 | +## 6. File Structure (Target) |
| 229 | + |
| 230 | +``` |
| 231 | +apps/web/src/ |
| 232 | +├── App.tsx # Root router |
| 233 | +├── main.tsx # Entry point |
| 234 | +├── index.css # Global styles |
| 235 | +│ |
| 236 | +├── types/ |
| 237 | +│ └── metadata.ts # Object, Field, App type definitions |
| 238 | +│ |
| 239 | +├── lib/ |
| 240 | +│ ├── api.ts # @objectstack/client SDK singleton |
| 241 | +│ ├── auth-client.ts # Better-Auth client |
| 242 | +│ ├── app-registry.ts # App registry (mock → API) |
| 243 | +│ ├── mock-data.ts # Mock metadata & records for dev |
| 244 | +│ └── utils.ts # cn() utility |
| 245 | +│ |
| 246 | +├── hooks/ |
| 247 | +│ ├── use-metadata.ts # Object/app metadata queries |
| 248 | +│ ├── use-records.ts # CRUD record queries & mutations |
| 249 | +│ └── use-mobile.ts # Mobile breakpoint detection |
| 250 | +│ |
| 251 | +├── components/ |
| 252 | +│ ├── layouts/ |
| 253 | +│ │ ├── AppLayout.tsx # Business app shell + dynamic nav |
| 254 | +│ │ └── SettingsLayout.tsx # Admin console layout |
| 255 | +│ ├── auth/ # Auth guards & layouts |
| 256 | +│ ├── dashboard/ # App switcher, nav user, org switcher |
| 257 | +│ ├── records/ |
| 258 | +│ │ ├── RecordTable.tsx # Reusable data table |
| 259 | +│ │ ├── FieldRenderer.tsx # Render field value by type |
| 260 | +│ │ └── RecordForm.tsx # (Phase 2) Editable record form |
| 261 | +│ └── ui/ # shadcn/ui primitives |
| 262 | +│ |
| 263 | +├── pages/ |
| 264 | +│ ├── home.tsx |
| 265 | +│ ├── sign-in.tsx / sign-up.tsx / ... |
| 266 | +│ ├── settings/ # 16 admin pages |
| 267 | +│ └── apps/ |
| 268 | +│ ├── app.tsx # App home dashboard |
| 269 | +│ ├── object-list.tsx # Object list view (table) |
| 270 | +│ └── object-record.tsx # Object record detail |
| 271 | +│ |
| 272 | +└── test/ |
| 273 | + └── setup.ts |
| 274 | +``` |
| 275 | + |
| 276 | +--- |
| 277 | + |
| 278 | +## 7. Development Workflow |
| 279 | + |
| 280 | +```bash |
| 281 | +# Terminal 1: ObjectStack API server |
| 282 | +pnpm objectstack:serve # → http://localhost:5320 |
| 283 | + |
| 284 | +# Terminal 2: Vite dev server (HMR) |
| 285 | +pnpm web:dev # → http://localhost:5321 |
| 286 | + |
| 287 | +# Vite proxies /api/v1/* → localhost:5320 automatically |
| 288 | +``` |
| 289 | + |
| 290 | +### Testing |
| 291 | + |
| 292 | +```bash |
| 293 | +# Unit tests (Vitest + jsdom) |
| 294 | +cd apps/web && npx vitest run |
| 295 | + |
| 296 | +# Type checking |
| 297 | +cd apps/web && npx tsc --noEmit |
| 298 | + |
| 299 | +# Build |
| 300 | +pnpm web:build |
| 301 | +``` |
| 302 | + |
| 303 | +--- |
| 304 | + |
| 305 | +## 8. Success Criteria |
| 306 | + |
| 307 | +### Phase 1 (Foundation) — Definition of Done |
| 308 | + |
| 309 | +- [ ] User can navigate to `/apps/crm` and see a sidebar with object links |
| 310 | +- [ ] Clicking an object link navigates to `/apps/crm/leads` (object list) |
| 311 | +- [ ] Object list page shows a table with columns derived from metadata |
| 312 | +- [ ] Clicking a row navigates to `/apps/crm/leads/:id` (record detail) |
| 313 | +- [ ] Record detail page renders field values by type (text, number, date, select, etc.) |
| 314 | +- [ ] Loading spinners shown while data is fetching |
| 315 | +- [ ] Empty states shown when no records exist |
| 316 | +- [ ] Error states shown when API calls fail |
| 317 | +- [ ] All existing tests continue to pass |
| 318 | +- [ ] Build succeeds with no TypeScript errors |
0 commit comments