NextGraph integration (WIP), broker banner, and feature-based architecture

- Add NextGraph data layer with @ng-org/orm, SHEX shapes (Event, UserProfile,
  Participation), session management, and FestipodDataContext with dual-mode
  operation (connected via NextGraph or local seed data)
- Add BrokerBanner and NgStatus components showing connection status
- Refactor to feature-based architecture: organize code by business domain
  (event, user, home, auth, workshop, meeting, notification) instead of
  technical layer. Modules only import from shared/, never from each other
- Collocate BDD features and step definitions with their modules: event-specific
  steps in event/steps/, user steps in user/steps/, shared generic steps remain
  in shared/steps/
- Set up multi-layer BDD structure (frontend/backend/e2e steps per module)
- Add project documentation (AGENTS.md, .project/knowledge/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-03-11 12:19:45 +01:00
parent c9bc957d2a
commit 901fd659df
128 changed files with 5738 additions and 2885 deletions
+84
View File
@@ -0,0 +1,84 @@
# Architecture
Feature-based architecture where code is organized by business domain (module), not by technical layer.
## Module Structure
```
src/modules/
event/ # 7 screens, 5 features — events CRUD, discovery, participants, meeting points
user/ # 5 screens, 11 features — profiles, friends, sharing
home/ # 2 screens — dashboard, settings
auth/ # 2 screens — login, welcome/onboarding
workshop/ # 0 screens, 6 features — workshop/atelier specs (future)
meeting/ # 0 screens, 1 feature — meeting point specs
notification/ # 0 screens, 3 features — notification specs
```
Each module can contain:
- `screens/` — React screen components
- `features/` — Gherkin `.feature` files (BDD specs)
- `steps/{frontend,backend,e2e}/` — Cucumber step definitions by layer
## Import Rules
**Modules only import from `shared/` — never from each other.**
```
src/modules/event/screens/EventDetailScreen.tsx
✅ import from '../../../shared/components/sketchy'
✅ import from '../../../shared/context/FestipodDataContext'
✅ import from '../../../screens' (registry types)
❌ import from '../../user/screens/...'
```
## Shared Layer
`src/shared/` contains everything reusable across modules:
| Directory | Contents |
|-----------|----------|
| `components/sketchy/` | Hand-drawn UI library (Button, Card, Avatar, Header, NavBar, etc.) |
| `components/ui/` | Shadcn/Radix components (used only in prototyping tool) |
| `context/` | ThemeContext, NextGraphContext, FestipodDataContext |
| `data/` | User stories (`index.ts`), auto-generated `features.ts`, `testResults.ts`, `seedData.ts`, `types.ts` |
| `hooks/` | `useShapeWithDefaults` (NextGraph) |
| `shapes/` | SHEX definitions + ORM TypeScript bindings |
| `utils/` | `ngSession.ts`, `ngBootstrap.ts` |
| `steps/frontend/` | Shared BDD step definitions (navigation, screen, form) |
| `support/` | Cucumber `world.ts`, `hooks.ts` |
| `types/` | `gherkin.ts` (ParsedFeature, ParsedScenario types) |
| `lib/` | `utils.ts` (cn helper for Tailwind) |
## App Shell
`src/app/` is the prototyping tool — not part of the Festipod app itself:
- `App.tsx` — Root: ThemeProvider > NextGraphProvider > FestipodDataProvider > RouterProvider
- `router.tsx` — Hash-based routing: `#/` (gallery), `#/demo/{screenId}`, `#/specs/{featureId}`
- `frontend.tsx` — React entry point (referenced from `src/index.html`)
- `components/Gallery.tsx` — Screen preview grid
- `components/DemoMode.tsx` — Interactive mockup viewer with sidebar navigation
- `components/specs/` — BDD specs browser (SpecsPage, FeatureView, GherkinHighlighter)
## Screen Registry
`src/screens/index.ts` is the central registry that imports all screens from all modules and exports:
- `screenGroups` — Grouped by domain (Accueil, Evenements, Utilisateur, General)
- `screens` — Flat list
- `getScreen(id)` — Lookup by ID
- `ScreenProps` interface — `{ navigate: (screenId: string) => void }`
## Entry Points
| File | Purpose |
|------|---------|
| `src/index.ts` | Bun.serve() — HTTP server, serves index.html + cucumber report |
| `src/index.html` | HTML entry, loads `src/app/frontend.tsx` |
| `src/app/frontend.tsx` | React root, renders `<App />` |
## Build
- Dev: `bun --hot src/index.ts` (via `bun run dev`)
- Prod: `bun run build.ts` — Bun bundler + Tailwind plugin → `dist/`
- Path alias: `@/*``./src/*` (tsconfig)
+101
View File
@@ -0,0 +1,101 @@
# BDD Testing
Cucumber/Gherkin BDD specs in French with multi-layer step definitions.
## Overview
- 26 feature files (US-1 to US-26), all in French
- Categories: EVENT, WORKSHOP, USER, MEETING, NOTIF
- Priorities: 0 (Impossible), 1 (Haute), 2 (Moyenne), 3 (Basse)
- Current results: 51 passed, 7 failed, 75 skipped (133 scenarios total)
## Multi-Layer BDD
Each module has step directories for three test layers:
```
src/modules/event/steps/
frontend/ # UI/screen assertions (active)
backend/ # Data layer assertions (planned)
e2e/ # Full integration (planned)
```
Shared steps (cross-domain) live in `src/shared/steps/frontend/`.
## Feature Files
Collocated with their module:
```
src/modules/event/features/us-13-creer-evenement.feature
src/modules/user/features/us-23-connexion-utilisateurs.feature
src/modules/workshop/features/us-1-visualiser-atelier-termine.feature
...
```
Tagged with `@CATEGORY @priority-N` for filtering.
## Step Definitions
### Shared Steps (`src/shared/steps/frontend/`)
| File | Purpose |
|------|---------|
| `navigation.steps.ts` | Screen navigation, authentication, click/select actions, section/button/field assertions |
| `form.steps.ts` | Form field validation, required fields, import/duplicate detection |
| `screen.steps.ts` | Screen content assertions (participants, events, profiles, QR codes) |
### How Frontend Steps Work
Steps analyze screen **source code** (not rendered DOM):
1. `world.ts` loads screen `.tsx` file content via `loadScreenSource()`
2. Steps use regex patterns on JSX source to verify UI elements
3. `screenFileMap` in `world.ts` maps screen IDs to file paths (e.g., `'home'``'src/modules/home/screens/HomeScreen.tsx'`)
4. `screenFieldDetectors` define per-screen regex patterns for field verification
5. `screenExpectedContent` lists expected text content per screen
### Screen Name Resolution
French names in `.feature` files map to screen IDs via `screenNameMap`:
- `"accueil"``home`
- `"détail événement"``event-detail`
- `"mon profil"``profile`
- `"relayer un événement"``create-event`
## Cucumber Configuration
`cucumber.json`:
```json
{
"default": {
"import": [
"src/shared/support/**/*.ts",
"src/shared/steps/**/*.ts",
"src/modules/*/steps/**/*.ts"
],
"paths": ["src/modules/*/features/**/*.feature"],
"language": "fr"
}
}
```
Requires `tsx` loader: `node --import tsx/esm node_modules/.bin/cucumber-js`
## Auto-Generated Files
Scripts in `scripts/` parse features and steps into TypeScript data files consumed by the prototyping tool:
| Script | Input | Output |
|--------|-------|--------|
| `parse-features.ts` | `src/modules/*/features/*.feature` | `src/shared/data/features.ts` |
| `parse-test-results.ts` | `reports/cucumber-report.json` | `src/shared/data/testResults.ts` |
| `extract-step-definitions.ts` | `src/shared/steps/frontend/*.ts` | `src/shared/data/stepDefinitions.ts` |
Run all: `bun run test:cucumber`
## Adding New Steps
1. **Module-specific**: Create in `src/modules/{module}/steps/frontend/`
2. **Cross-domain**: Add to `src/shared/steps/frontend/`
3. Import `FestipodWorld` type from `../../support/world` (shared) or adjust relative path
4. Run `bun run steps:extract` to regenerate tooltip data
+67
View File
@@ -0,0 +1,67 @@
# Data Layer
NextGraph-backed local-first data with fallback to local state for demo/disconnected mode.
## Overview
The app has two data modes:
1. **Connected** — NextGraph ORM shapes (P2P, encrypted, local-first)
2. **Disconnected/Demo** — Local React state seeded from `seedData.ts`
All screens use `useFestipodData()` hook regardless of mode.
## NextGraph Stack
```
@ng-org/web # Browser WASM runtime
@ng-org/orm # RDF shape-based ORM
@ng-org/shex-orm # SHEX → TypeScript code generation
@ng-org/alien-deepsignals # Reactive signals bridge
```
Packages installed from local tarballs in `.ng-tarballs/`.
## SHEX Shapes
`src/shared/shapes/shex/festipodShapes.shex` defines:
- **Event** — title, description, dates, location, themes, participants
- **UserProfile** — name, username, bio, city, visibility
- **Participation** — links event + user, confirmation status
ORM bindings in `src/shared/shapes/orm/`:
- `festipodShapes.schema.ts` — Schema registration
- `festipodShapes.shapeTypes.ts` — Shape type constants
- `festipodShapes.typings.ts` — TypeScript interfaces
Regenerate with `bun run build:orm`.
## Context Providers
### NextGraphContext (`src/shared/context/NextGraphContext.tsx`)
- Connection lifecycle: `disconnected``connecting``connected` | `error`
- Auto-initializes via `initNg()` from `src/shared/utils/ngSession.ts`
- Provides session with store IDs (private, protected, public)
### FestipodDataContext (`src/shared/context/FestipodDataContext.tsx`)
- Wraps NextGraph shapes with `useShapeWithDefaults()` hook
- CRUD: `createEvent()`, `updateEvent()`, `joinEvent()`, `leaveEvent()`, etc.
- Exposes `useFestipodData()` hook consumed by all screens
- `selectedEventId` state for cross-screen event navigation
- Falls back to seed data when disconnected
## Data Types
`src/shared/data/types.ts`:
- `FpEventData` — id, title, date, location, distance, themes, etc.
- `FpUserData` — id, name, username, bio, city, counts
- `FpParticipationData` — eventId + userId + confirmed
- `FpMeetingPointData` — eventId, location, time, host (local-only)
- `FpFriendshipData` — userId + friendId (local-only)
## Seed Data
`src/shared/data/seedData.ts`:
- 10 users (Marie Dupont = current user, `user-1`)
- Multiple events with dates, locations, themes
- Participations, meeting points, friendships
- `CURRENT_USER_ID = 'user-1'`
+94
View File
@@ -0,0 +1,94 @@
# Screens
16 mobile mockup screens using the sketchy hand-drawn component library.
## Screen Inventory
### Home Module (`src/modules/home/screens/`)
| ID | Name | File | Description |
|----|------|------|-------------|
| `welcome` | Bienvenue | WelcomeScreen.tsx | Onboarding/welcome page |
| `home` | Accueil | HomeScreen.tsx | Dashboard with upcoming events, quick actions |
| `settings` | Parametres | SettingsScreen.tsx | Notifications, privacy, location settings |
### Event Module (`src/modules/event/screens/`)
| ID | Name | File | Description |
|----|------|------|-------------|
| `events` | Decouvrir | EventsScreen.tsx | Event discovery/search |
| `event-detail` | Detail evenement | EventDetailScreen.tsx | Event info, participants, join/leave |
| `create-event` | Relayer evenement | CreateEventScreen.tsx | Create/relay event, import from Mobilizon/Transiscope |
| `update-event` | Modifier evenement | UpdateEventScreen.tsx | Edit existing event |
| `invite` | Inviter des amis | InviteScreen.tsx | Invite contacts to event |
| `participants-list` | Liste des participants | ParticipantsListScreen.tsx | Event participant list |
| `meeting-points` | Points de rencontre | MeetingPointsScreen.tsx | Carpooling/meeting coordination |
### User Module (`src/modules/user/screens/`)
| ID | Name | File | Description |
|----|------|------|-------------|
| `profile` | Mon profil | ProfileScreen.tsx | Current user profile |
| `update-profile` | Modifier mon profil | UpdateProfileScreen.tsx | Edit profile form |
| `user-profile` | Profil d'un utilisateur | UserProfileScreen.tsx | View another user's profile |
| `friends-list` | Mon reseau | FriendsListScreen.tsx | Network/friends list |
| `share-profile` | Partager mon profil | ShareProfileScreen.tsx | QR code + link sharing |
### Auth Module (`src/modules/auth/screens/`)
| ID | Name | File | Description |
|----|------|------|-------------|
| `login` | Connexion | LoginScreen.tsx | Login (NextGraph + email fallback) |
## Screen Registry
`src/screens/index.ts` imports all screens and exports:
```typescript
export interface ScreenProps {
navigate: (screenId: string) => void;
}
export const screenGroups: ScreenGroup[] // Grouped: home, events, user, general
export const screens: Screen[] // Flat list
export function getScreen(id: string): Screen | undefined
```
## Sketchy Component Library
`src/shared/components/sketchy/` — hand-drawn UI with custom font:
| Component | Usage |
|-----------|-------|
| `Header` | Screen header with back button |
| `NavBar` | Bottom tab navigation |
| `Button` | Action buttons |
| `Card` | Content cards |
| `Input` | Text inputs |
| `Title`, `Subtitle`, `Text` | Typography |
| `Avatar` | User avatars with initials |
| `Badge` | Status/category badges |
| `Toggle`, `Checkbox` | Form controls |
| `ListItem` | List row items |
| `Divider` | Section separators |
| `Placeholder` | Image/content placeholders |
| `PhoneFrame` | Phone device frame wrapper |
| `BrokerBanner` | NextGraph connection status banner |
| `NgStatus` | Connection indicator dot |
## Screen Patterns
All screens follow the same pattern:
```typescript
import { Header, Button, ... } from '../../../shared/components/sketchy';
import { useFestipodData } from '../../../shared/context/FestipodDataContext';
import type { ScreenProps } from '../../../screens';
export function MyScreen({ navigate }: ScreenProps) {
const { events, currentUser, ... } = useFestipodData();
// render with sketchy components
}
```
Navigation between screens uses `navigate(screenId)` — the prototyping tool intercepts this to switch the displayed screen.