diff --git a/.claude/openspec/architecture/adr-001-data-layer.md b/.claude/openspec/architecture/adr-001-data-layer.md deleted file mode 100644 index f1709eeb..00000000 --- a/.claude/openspec/architecture/adr-001-data-layer.md +++ /dev/null @@ -1,146 +0,0 @@ -- ALL domain data → OpenRegister objects. NO custom Entity/Mapper for domain data. -- App config → `IAppConfig`. NOT OpenRegister. -- Schemas: PascalCase, schema.org vocabulary where equivalent exists, explicit types. -- Cross-entity references: OpenRegister relations (register+schema+objectId). NO foreign keys. -- Register templates: `lib/Settings/{app}_register.json` (OpenAPI 3.0 + x-openregister). -- Seed data: 3-5 realistic objects per schema using `@self` envelope (`register`, `schema`, `slug`). - Use general org data (municipality/consultancy), NOT context-specific. Include in design.md. -- Breaking schema changes → new migration in repair step. NEVER modify existing migrations. - -### OpenRegister + @conduction/nextcloud-vue — DO NOT REBUILD - -The platform provides 258+ backend methods and 69+ frontend components. Apps ONLY build -custom logic for domain-specific business rules. Everything below is provided for FREE. - -**CRUD & Data Management** (use ObjectService + CnIndexPage + CnDetailPage): -- Single & bulk create, read, update, delete — `ObjectService.saveObject()`, `deleteObject()` -- List with pagination, sorting, filtering — `ObjectService.findAll()` + `CnDataTable` -- Schema-driven forms — `CnFormDialog` (auto-generates from schema) or `CnAdvancedFormDialog` -- Detail views — `CnDetailPage` with `CnDetailGrid`, `CnDetailCard` sections -- Record merging/deduplication — `ObjectService.mergeObjects()` -- Object locking — `ObjectService.lockObject()` / `unlockObject()` - -**Import & Export** (use ImportService/ExportService + CnMassImportDialog/CnMassExportDialog): -- CSV, Excel, JSON import with intelligent field mapping — `ImportService` -- CSV, Excel, JSON export with column selection — `ExportService` -- Bulk import with validation and progress — `CnMassImportDialog` -- Filtered export with format picker — `CnMassExportDialog` -- NO custom import dialogs, parsers, upload handlers, or export controllers - -**Search & Discovery** (use IndexService + CnFilterBar + CnFacetSidebar): -- Full-text search with field weighting — `IndexService` -- Faceted navigation with counts — `FacetBuilder` + `CnFacetSidebar` -- Semantic search with embeddings — `VectorizationService` -- Hybrid search (keyword + semantic) — automatic -- Search analytics — `SearchTrailService` (popular terms, activity) -- NO custom search endpoints, query builders, or search pages - -**File Management** (use FileService + CnObjectSidebar): -- Upload (single/multipart), download, share links — `FileService` -- File tagging, public/private toggle — `FileService` -- Bulk download as ZIP — `createObjectFilesZip()` -- Text extraction from PDFs/Office docs — `TextExtractionService` -- File tab in object sidebar — `CnObjectSidebar` → `CnFilesTab` -- NO custom file upload components, file controllers, or download handlers - -**Audit & Compliance** (use AuditTrailService + CnObjectSidebar): -- Full change tracking with before/after snapshots — automatic -- Audit trail tab — `CnObjectSidebar` → `CnAuditTrailTab` -- GDPR data subject access requests — `inzageverzoek()`, `verwerkingsregister()` -- Audit export and analytics — `AuditTrailController` -- NO custom audit logging, change tracking, or compliance controllers - -**Dashboard & Analytics** (use CnDashboardPage + CnChartWidget + CnStatsBlock): -- Drag-drop widget dashboard — `CnDashboardPage` with GridStack -- KPI cards — `CnKpiGrid`, `CnStatsBlock`, `CnStatsPanel` -- Charts (line/bar/pie/donut) — `CnChartWidget` (ApexCharts) -- Data tables as widgets — `CnTableWidget` -- Editable data grids — `CnObjectDataWidget` -- NO custom dashboard layouts, chart components, or KPI cards - -**Forms & Dialogs** (use CnFormDialog + schema-driven generation): -- Auto-generated create/edit forms — `CnFormDialog` reads schema → generates fields -- JSON/metadata editing — `CnAdvancedFormDialog` with Properties/Data/Metadata tabs -- Schema editor — `CnSchemaFormDialog` -- Delete/Copy/Mass operations — `CnDeleteDialog`, `CnCopyDialog`, `CnMassDeleteDialog` -- NO custom form components, validation logic, or dialog wrappers - -**Navigation & Pagination** (use CnPagination + CnActionsBar + useListView): -- Pagination control with size selector — `CnPagination` -- Action bar (add, search, toggle views) — `CnActionsBar` -- List state management — `useListView` composable (handles search, filter, sort, page) -- Detail state management — `useDetailView` composable -- NO custom pagination logic, debounced search, or list state management - -**Authorization & RBAC** (use AuthorizationService + PropertyRbacHandler): -- Role-based access control — `AuthorizationService` -- Field-level permissions — `PropertyRbacHandler` -- Object-level restrictions — `PermissionHandler` -- Authorization audit — `AuthorizationAuditService` -- NO custom permission checks, role systems, or access control middleware - -**Webhooks & Events** (use WebhookService): -- Create, test, retry webhooks — `WebhookService` -- CloudEvents format — automatic -- Event subscriptions — selective per schema/action -- NO custom webhook controllers or event dispatchers - -**Notifications & Activity** (use NotificationService + ActivityService): -- Nextcloud notifications — `NotificationService` -- Activity feed — `ActivityService` -- Calendar events — `CalendarEventService` -- Deck/Kanban cards — `DeckCardService` - -**Store & State** (use createObjectStore + plugins): -- Object stores — `createObjectStore(name)` generates Pinia CRUD store -- Store plugins: `auditTrails`, `files`, `lifecycle`, `relations`, `search`, `selection` -- Column/field/filter generation from schema — `columnsFromSchema()`, `fieldsFromSchema()` -- NO custom Pinia stores for CRUD, Vuex, or manual API call management - -**Chat & AI** (use ChatService): -- Multi-turn conversation — `ChatService` -- RAG-based knowledge retrieval — `ContextRetrievalHandler` -- LLM response generation — `ResponseGenerationHandler` - -**Data Retention & Archival** (use ArchivalService): -- Legal hold — `LegalHoldService` -- Destruction schedules — `DestructionService` -- Retention policies — `RetentionService` - -**Semantic & Hybrid Search** (use SolrController + SettingsController): -- Semantic search via vector embeddings — `SettingsController.semanticSearch()` -- Hybrid search (keyword + semantic combined) — `SolrController.hybridSearch()` -- Vector embedding generation — `VectorizationService` -- NO custom search algorithms — configure via OpenRegister settings - -**GraphQL API** (use GraphQLController): -- Query objects across schemas via GraphQL — `GraphQLController.execute()` -- Alternative to REST for complex cross-entity queries - -**Organization / Multi-Tenancy** (use OrganisationController): -- Organization CRUD — `OrganisationController` -- Tenant-scoped data isolation — automatic via `TenantLifecycleService` -- NO custom multi-tenancy logic - -**Task & Workflow Management** (use TasksController + WorkflowEngineController): -- Task creation and tracking — `TasksController` -- Workflow orchestration — `WorkflowEngineRegistry` -- Scheduled workflows — `ScheduledWorkflowController` -- NO custom task/workflow systems - -**Text Extraction** (use FileTextController): -- Extract text from PDFs and Office docs — `TextExtractionService` -- Entity recognition (PII detection) — `EntityRecognitionHandler` -- Content anonymization — automatic - -**Timeline & Stages** (use CnTimelineStages): -- Workflow progression visualization — `CnTimelineStages` component -- Stage tracking with status colors - -### What apps SHOULD build (custom business logic only): -- External API integrations (SAP, Peppol, TenderNed, etc.) -- PDF/document generation with business-specific templates -- Workflow triggers and business rules specific to the domain -- Notification dispatch with app-specific event types -- Custom settings pages with app-specific configuration -- Background jobs for domain-specific processing diff --git a/.claude/openspec/architecture/adr-002-api.md b/.claude/openspec/architecture/adr-002-api.md deleted file mode 100644 index 4f956593..00000000 --- a/.claude/openspec/architecture/adr-002-api.md +++ /dev/null @@ -1,6 +0,0 @@ -- URL pattern: `/index.php/apps/{app}/api/{resource}` — lowercase plural, hyphens. -- Methods: GET=read, POST=create, PUT=update, DELETE=remove. No custom methods. -- Pagination: support `_page` + `_limit`. Response includes `total`, `page`, `pages`. -- Errors: appropriate HTTP status + `message` field. NO stack traces in responses. -- Auth: Nextcloud built-in only. NO custom login/session/token flows. -- Public endpoints: annotate `#[PublicPage]` + `#[NoCSRFRequired]`. Register CORS OPTIONS route. diff --git a/.claude/openspec/architecture/adr-003-backend.md b/.claude/openspec/architecture/adr-003-backend.md deleted file mode 100644 index 82abe764..00000000 --- a/.claude/openspec/architecture/adr-003-backend.md +++ /dev/null @@ -1,14 +0,0 @@ -- **Controller → Service → Mapper** (strict 3-layer). Controllers NEVER call mappers directly. -- Controllers: thin (<10 lines/method). Routing + validation + response only. -- Services: ALL business logic. Stateless — no instance state between requests. -- Mappers: DB CRUD only. No business logic. -- DI: constructor injection with `private readonly`. NO `\OC::$server` or static locators. -- Entity setters: POSITIONAL args only. `$e->setName('val')` — NEVER `$e->setName(name: 'val')`. - (`__call` passes `['name' => val]` but `setter()` uses `$args[0]`.) -- Routes: `appinfo/routes.php`. Specific routes BEFORE wildcard `{slug}` routes. -- Config: `IAppConfig` with sensitive flag for secrets. NEVER read DB directly. -- Lifecycle: schema init via repair steps (`IRepairStep`), background via job queue, events via dispatcher. -- **Spec traceability**: every class and public method MUST have `@spec` PHPDoc tag(s) linking to - the OpenSpec change that caused it: `@spec openspec/changes/{name}/tasks.md#task-N`. - Multiple `@spec` tags allowed (code touched by multiple changes). File-level `@spec` in header docblock. - This enables: code → docblock → spec traceability alongside code → git blame → commit → issue → spec. diff --git a/.claude/openspec/architecture/adr-004-frontend.md b/.claude/openspec/architecture/adr-004-frontend.md deleted file mode 100644 index 1c92146c..00000000 --- a/.claude/openspec/architecture/adr-004-frontend.md +++ /dev/null @@ -1,97 +0,0 @@ -- **Vue 2 + Pinia + @nextcloud/vue + @conduction/nextcloud-vue**. NO Vuex. Options API only. -- State: Pinia stores in `src/store/modules/`. Use `createObjectStore` for OpenRegister CRUD. -- `fetch()` for API calls — NOT axios. Loading state with `try/finally`. -- Translations: ALL user-visible strings via `t(appName, 'text')`. NO hardcoded strings. -- CSS: ONLY Nextcloud CSS variables. NO hardcoded colors. NEVER reference `--nldesign-*`. -- Router: history mode, base `/index.php/apps/{app}/`, catch-all `*` redirects to `/`. -- OpenRegister dependency: settings returns `openRegisters` (bool) + `isAdmin`. - Show empty state if OR missing. NEVER use `OC.isAdmin` — get from backend. - -### @conduction/nextcloud-vue — ALWAYS check before building custom - -**Pages & Layout:** - `CnIndexPage` (schema-driven list+CRUD) | `CnDetailPage` (detail+sidebar) | - `CnPageHeader` (title+icon) | `CnActionsBar` (add+search+toggle) - -**Data Display:** - `CnDataTable` (sortable+paginated) | `CnCardGrid` + `CnObjectCard` (card views) | - `CnDetailGrid` (label-value pairs) | `CnFilterBar` (search+filters) | - `CnFacetSidebar` (faceted filters) | `CnPagination` | `CnCellRenderer` (type-aware) - -**Forms & Dialogs:** - `CnFormDialog` (schema-driven create/edit) | `CnAdvancedFormDialog` (properties+JSON+metadata) | - `CnSchemaFormDialog` (JSON Schema editor) | `CnTabbedFormDialog` (tabbed form framework) | - `CnDeleteDialog` | `CnCopyDialog` - -**Mass Actions:** - `CnMassDeleteDialog` | `CnMassCopyDialog` | `CnMassExportDialog` (CSV/JSON/XML) | - `CnMassImportDialog` (upload+summary) | `CnMassActionBar` (floating selection bar) - -**Dashboard & Widgets:** - `CnDashboardPage` (GridStack drag-drop layout) | `CnDashboardGrid` (layout engine) | - `CnWidgetWrapper` (widget shell) | `CnWidgetRenderer` (NC Dashboard API v1/v2) | - `CnChartWidget` (ApexCharts: area/line/bar/pie/donut/radial) | - `CnTableWidget` (data table widget) | `CnTileWidget` (quick-access tile) | - `CnInfoWidget` (label-value grid) | `CnKpiGrid` (responsive KPI layout) | - `CnStatsBlock` (metric card) | `CnStatsPanel` (stats sections) | `CnProgressBar` | - `CnObjectDataWidget` (schema-driven editable data grid, inline edit + save via objectStore) | - `CnObjectMetadataWidget` (read-only object metadata display) - -**UI Elements:** - `CnStatusBadge` | `CnEmptyState` | `CnIcon` (MDI) | `CnCard` | `CnDetailCard` | - `CnRowActions` | `CnTimelineStages` (workflow progression) | - `CnUserActionMenu` (user context menu) | `CnJsonViewer` (CodeMirror) - -**Detail Sidebar:** - `CnObjectSidebar` (Files/Notes/Tags/Tasks/Audit tabs) | `CnIndexSidebar` | - `CnNotesCard` (inline notes) | `CnTasksCard` (inline tasks) - -**Settings:** - `CnSettingsSection` + `CnVersionInfoCard` (MUST be first on admin pages) | - `CnSettingsCard` | `CnConfigurationCard` | `CnRegisterMapping` - User settings: `NcAppSettingsDialog` (NOT `NcDialog`) - -**Composables:** - `useListView` (search/filter/sort/pagination) | `useDetailView` (load/edit/delete) | - `useSubResource` (related items) | `useDashboardView` (widgets/layout/edit) - -**Store Plugins:** - `auditTrailsPlugin` | `relationsPlugin` | `filesPlugin` | `lifecyclePlugin` | - `selectionPlugin` | `searchPlugin` | `registerMappingPlugin` - -**Utilities:** - `columnsFromSchema()` | `filtersFromSchema()` | `fieldsFromSchema()` | - `formatValue()` | `buildHeaders()` | `buildQueryString()` - -### Page Construction Patterns (follow these recipes) - -**App.vue:** `NcContent` → 3 states: loading (`NcLoadingIcon`), no-OpenRegister (`NcEmptyContent`), - ready (`MainMenu` + `NcAppContent` + `router-view` + optional `CnIndexSidebar`). - Inject `sidebarState` for child components. `created()` calls `initializeStores()`. - -**MainMenu:** `NcAppNavigation` with `NcAppNavigationItem` per route (icon + name + `:to`). - Footer: settings link via `NcAppNavigationSettings`. - -**Dashboard:** `CnDashboardPage` with `CnStatsBlock` KPIs (4 cards: open/overdue/value/completed), - status distribution chart, "My Work" list (grouped: overdue → due this week → rest). - Fetch all collections in parallel via `Promise.all`. Widget templates via `#widget-{id}` slots. - -**Index page:** `CnIndexPage` with `useListView(entityType, { sidebarState, objectStore })`. - Inject sidebarState. Row click → `$router.push({ name: 'EntityDetail', params: { id } })`. - Add button → new entity detail with id='new'. - -**Detail page:** Two modes — edit (form component) / view (`CnDetailPage` + `CnDetailCard` sections). - Header actions: Edit + Delete buttons. Related entities in table inside `CnDetailCard`. - Props: `entityId` from route. `isNew = entityId === 'new'`. Sidebar via `CnObjectSidebar`. - -**Settings:** `CnVersionInfoCard` (FIRST, always) → `CnRegisterMapping` → `CnSettingsSection` per feature. - Load settings from `GET /api/settings`. Save via `POST /api/settings`. - Re-import button calls `POST /api/settings/load`. - -**Router:** Flat routes (no nesting), all named, props via arrow function for params. - Routes: `/` (Dashboard), `/{entities}` (list), `/{entities}/:id` (detail), `/settings`. - -**Store init:** `initializeStores()` in `store/store.js` — fetches settings, then calls - `objectStore.registerObjectType(name, schemaSlug, registerSlug)` for each entity. - Object store uses `createObjectStore` with plugins (files, auditTrails, relations). - Settings store: Pinia `defineStore` with `fetchSettings()` and `saveSettings()`. diff --git a/.claude/openspec/architecture/adr-005-security.md b/.claude/openspec/architecture/adr-005-security.md deleted file mode 100644 index 771da8e1..00000000 --- a/.claude/openspec/architecture/adr-005-security.md +++ /dev/null @@ -1,6 +0,0 @@ -- Auth: Nextcloud built-in ONLY. NO custom login, sessions, tokens, password storage. -- Admin check: `IGroupManager::isAdmin()` on BACKEND. Frontend-only checks = vulnerability. -- Multi-tenant isolation: enforce at API/service level, not UI only. -- NO PII in logs, error responses, or debug output. -- File uploads: validate type + size before storage. -- API responses: NO stack traces, SQL, or internal paths. diff --git a/.claude/openspec/architecture/adr-006-metrics.md b/.claude/openspec/architecture/adr-006-metrics.md deleted file mode 100644 index 58a9bf8e..00000000 --- a/.claude/openspec/architecture/adr-006-metrics.md +++ /dev/null @@ -1,3 +0,0 @@ -- Every app: `GET /api/metrics` (Prometheus text, admin auth) + `GET /api/health` (JSON, public). -- Metric names: `{app}_` prefix. MUST include `{app}_health_status` and `{app}_info`. -- Health check MUST verify OpenRegister connectivity (for apps that depend on it). diff --git a/.claude/openspec/architecture/adr-007-i18n.md b/.claude/openspec/architecture/adr-007-i18n.md deleted file mode 100644 index f428d680..00000000 --- a/.claude/openspec/architecture/adr-007-i18n.md +++ /dev/null @@ -1,4 +0,0 @@ -- Minimum: Dutch (nl) + English (en) translations. -- PHP: `$this->l->t('key')`. JS: `t(appName, 'key')`. -- API field names: English. Date/number formatting: respect user locale. -- Each app with OpenRegister: define `register-i18n` spec listing translatable fields. diff --git a/.claude/openspec/architecture/adr-008-testing.md b/.claude/openspec/architecture/adr-008-testing.md deleted file mode 100644 index 983fa88d..00000000 --- a/.claude/openspec/architecture/adr-008-testing.md +++ /dev/null @@ -1,5 +0,0 @@ -- Every new PHP service/controller → PHPUnit tests in `tests/Unit/` (≥3 methods). -- Every new Vue component → test file (if test framework exists). -- Every new API endpoint → Newman/Postman collection in `tests/integration/`. -- Every spec scenario → browser test (GIVEN/WHEN/THEN verified via Playwright). -- All tests MUST pass in `composer check:strict`. diff --git a/.claude/openspec/architecture/adr-009-docs.md b/.claude/openspec/architecture/adr-009-docs.md deleted file mode 100644 index 372cfacf..00000000 --- a/.claude/openspec/architecture/adr-009-docs.md +++ /dev/null @@ -1,2 +0,0 @@ -- Every user-facing feature → docs in `docs/` with screenshots from running app. -- English primary, Dutch recommended. Update docs when behavior changes. diff --git a/.claude/openspec/architecture/adr-010-nl-design.md b/.claude/openspec/architecture/adr-010-nl-design.md deleted file mode 100644 index 6f0450d5..00000000 --- a/.claude/openspec/architecture/adr-010-nl-design.md +++ /dev/null @@ -1,8 +0,0 @@ -- ALL UI: CSS custom properties from NL Design System tokens. NO hardcoded colors, fonts, spacing. -- Theme switching: support `nldesign` app's token sets (Rijkshuisstijl, Utrecht, municipality-specific). -- Components: `@nextcloud/vue` primary. Custom components styled via NL Design tokens only. -- Scoped styles: ALL `