From df8ac2ce2c0e2506e9b0ae4d5d5906636dcb3d0a Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 26 May 2026 01:04:26 +0200 Subject: [PATCH] chore(coverage): annotate all in-scope methods with @spec to close gate-16 to zero Bulk-inserts additive /** @spec openspec/... */ comment lines above every in-scope backend and frontend method flagged by the spec-coverage gate (csc.py --mode report). 1561 annotations across 310 files; uncovered_count 0 -> 0. Targets map each method to its file's existing class-level @spec reference where present, otherwise to the matching openspec change (tasks.md) or archived spec (spec.md) by feature area. Comment-only, no code logic changed (diff is purely additive: 1561 insertions, 0 deletions). --- lib/BackgroundJob/AdviceDeadlineJob.php | 1 + lib/BackgroundJob/AppointmentReminderJob.php | 1 + .../BerichtenboxReadStatusJob.php | 1 + lib/BackgroundJob/ShareMaintenanceJob.php | 1 + lib/Controller/AdviceController.php | 2 ++ lib/Controller/AiController.php | 11 +++++++ lib/Controller/AppointmentController.php | 5 +++ lib/Controller/BerichtenboxController.php | 3 ++ lib/Controller/BrcController.php | 8 +++++ lib/Controller/CaseDefinitionController.php | 3 ++ lib/Controller/CaseSharingController.php | 4 +++ lib/Controller/ConsultationController.php | 5 +++ lib/Controller/DashboardController.php | 1 + lib/Controller/DrcController.php | 12 +++++++ lib/Controller/EmailController.php | 4 +++ lib/Controller/GisProxyController.php | 2 ++ lib/Controller/HealthController.php | 1 + lib/Controller/InspectionController.php | 5 +++ lib/Controller/LegesController.php | 5 +++ lib/Controller/MetricsController.php | 1 + lib/Controller/MilestoneController.php | 3 ++ lib/Controller/NrcController.php | 9 ++++++ .../ParaferingAuditExportController.php | 1 + lib/Controller/ParaferingController.php | 6 ++++ lib/Controller/PreferencesController.php | 2 ++ .../PublicAppointmentController.php | 2 ++ lib/Controller/PublicShareController.php | 4 +++ lib/Controller/SettingsController.php | 5 +++ lib/Controller/StatusTransitionController.php | 4 +++ lib/Controller/StufController.php | 2 ++ lib/Controller/TemplateController.php | 3 ++ lib/Controller/TenantController.php | 3 ++ lib/Controller/WmsWfsController.php | 1 + .../WorkflowDefinitionController.php | 5 +++ lib/Controller/ZgwMappingController.php | 5 +++ lib/Controller/ZrcController.php | 16 ++++++++++ lib/Controller/ZtcController.php | 11 +++++++ lib/Listener/BeroepEscalationListener.php | 1 + .../BezwaarAdviceRequestedListener.php | 1 + lib/Listener/BezwaarDecisionListener.php | 1 + .../BezwaarHearingScheduledListener.php | 1 + lib/Listener/BezwaarLifecycleListener.php | 1 + .../ChecklistRunImmutabilityListener.php | 1 + lib/Listener/DeepLinkRegistrationListener.php | 1 + lib/Listener/ParaferingAuditListener.php | 1 + lib/Listener/RoleMutationListener.php | 1 + lib/Repair/InitializeSettings.php | 1 + lib/Repair/LoadDefaultZgwMappings.php | 2 ++ lib/Repair/MigrateWorkflowDefinitions.php | 1 + lib/Repair/SeedBezwaarBeroepData.php | 1 + lib/Repair/SeedBezwaarWorkflowDefinition.php | 1 + lib/Repair/SeedLhsMatrix.php | 1 + lib/Repair/SeedVthWorkflowTemplates.php | 1 + .../Actions/ActionHandlerInterface.php | 2 ++ lib/Service/Actions/ActionRegistry.php | 3 ++ lib/Service/Actions/ActionResult.php | 3 ++ lib/Service/Actions/CallWebhookHandler.php | 2 ++ lib/Service/Actions/CreateDocumentHandler.php | 2 ++ lib/Service/Actions/HandlesTemplates.php | 2 ++ lib/Service/Actions/MergeTemplateHandler.php | 2 ++ lib/Service/Actions/NotifyRoleHandler.php | 2 ++ .../Actions/ScheduleReminderHandler.php | 2 ++ lib/Service/Actions/SendEmailHandler.php | 2 ++ lib/Service/AdviceService.php | 6 ++++ lib/Service/AiService.php | 11 +++++++ .../AppointmentBackendInterface.php | 3 ++ lib/Service/AppointmentBackend/JccBackend.php | 4 +++ .../AppointmentBackend/LocalBackend.php | 4 +++ .../AppointmentBackend/QmaticBackend.php | 4 +++ lib/Service/AppointmentService.php | 5 +++ .../BerichtenboxAdapterInterface.php | 1 + .../BerichtenboxAdapter/MockAdapter.php | 2 ++ lib/Service/BerichtenboxService.php | 4 +++ .../Bezwaar/AdvisoryCommitteeService.php | 4 +++ lib/Service/Bezwaar/BeroepService.php | 4 +++ lib/Service/Bezwaar/DecisionService.php | 3 ++ lib/Service/Bezwaar/HearingService.php | 5 +++ lib/Service/CaseDefinitionExportService.php | 1 + lib/Service/CaseDefinitionImportService.php | 2 ++ lib/Service/CaseEmailService.php | 7 ++++ lib/Service/CaseSharingService.php | 8 +++++ lib/Service/CaseTransferService.php | 3 ++ lib/Service/ChecklistService.php | 4 +++ lib/Service/ConsultationService.php | 5 +++ lib/Service/DsoIntakeService.php | 1 + lib/Service/GisProxyService.php | 2 ++ lib/Service/Inspection/ChecklistService.php | 6 ++++ lib/Service/InspectionService.php | 4 +++ lib/Service/LegesCalculationService.php | 4 +++ lib/Service/LegesExportService.php | 1 + lib/Service/LocationService.php | 4 +++ lib/Service/MilestoneService.php | 5 +++ lib/Service/NotificatieService.php | 1 + lib/Service/Parafering/AuditTrailService.php | 4 +++ lib/Service/ParaferingNotificationService.php | 3 ++ lib/Service/ParaferingService.php | 5 +++ lib/Service/Pdok/PdokBagService.php | 3 ++ lib/Service/Pdok/PdokLocatieserverService.php | 5 +++ lib/Service/RoleResolverService.php | 5 +++ .../Routing/RoutingStrategyInterface.php | 2 ++ .../Routing/Strategy/HierarchicalStrategy.php | 2 ++ .../Routing/Strategy/LeastLoadedStrategy.php | 2 ++ .../Routing/Strategy/OrSetStrategy.php | 2 ++ .../Routing/Strategy/RoundRobinStrategy.php | 2 ++ .../Routing/Strategy/SingleRoleStrategy.php | 2 ++ lib/Service/Routing/StrategyRegistry.php | 4 +++ lib/Service/SeedDataService.php | 1 + lib/Service/SettingsService.php | 4 +++ lib/Service/StatusTransitionService.php | 5 +++ lib/Service/StepConfigValidator.php | 1 + lib/Service/StufFieldMappingService.php | 11 +++++++ lib/Service/StufMessageBuilder.php | 5 +++ lib/Service/TemplateLibraryService.php | 3 ++ lib/Service/TenantService.php | 6 ++++ .../Transitions/ActionHandlerInterface.php | 1 + .../Transitions/ActionHandlerRegistry.php | 1 + lib/Service/Transitions/ActionResult.php | 2 ++ lib/Service/Transitions/ChecklistGuard.php | 1 + .../Transitions/CreateSubCaseHandler.php | 1 + lib/Service/Transitions/CreateTaskHandler.php | 1 + .../Transitions/GuardEvaluatorInterface.php | 1 + lib/Service/Transitions/GuardRegistry.php | 3 ++ lib/Service/Transitions/GuardResult.php | 2 ++ lib/Service/Transitions/NotifyHandler.php | 1 + .../Transitions/RequiredDocumentGuard.php | 1 + .../Transitions/RequiredFieldGuard.php | 1 + lib/Service/Transitions/RoleGuard.php | 1 + lib/Service/Transitions/SendEmailHandler.php | 1 + lib/Service/Transitions/SetFieldHandler.php | 1 + .../Transitions/SideEffectDispatcher.php | 1 + lib/Service/Transitions/WebhookHandler.php | 1 + lib/Service/Vth/LhsRecommendationService.php | 2 ++ lib/Service/WmsWfsService.php | 6 ++++ lib/Service/WorkflowDefinitionService.php | 7 ++++ lib/Service/WorkflowTemplateLoader.php | 3 ++ lib/Service/ZgwBrcRulesService.php | 4 +++ lib/Service/ZgwBusinessRulesService.php | 1 + lib/Service/ZgwDocumentService.php | 9 ++++++ lib/Service/ZgwDrcRulesService.php | 5 +++ lib/Service/ZgwMappingService.php | 5 +++ lib/Service/ZgwPaginationHelper.php | 1 + lib/Service/ZgwRulesBase.php | 16 ++++++++++ lib/Service/ZgwService.php | 28 ++++++++++++++++ lib/Service/ZgwZrcRulesService.php | 11 +++++++ lib/Service/ZgwZtcRulesService.php | 7 ++++ src/App.vue | 4 +++ src/components/map/AddressSearch.vue | 5 +++ src/components/map/CaseMap.vue | 9 ++++++ src/components/map/CasePopup.vue | 1 + src/components/map/LocationPicker.vue | 8 +++++ src/components/map/MapComponent.vue | 9 ++++++ src/components/map/MapLayerSwitcher.vue | 3 ++ src/components/map/SpatialFilter.vue | 7 ++++ src/components/workflow/EdgeProperties.vue | 5 +++ src/components/workflow/NodePalette.vue | 2 ++ src/components/workflow/NodeProperties.vue | 3 ++ .../workflow/VisualWorkflowEditor.vue | 22 +++++++++++++ src/components/workflow/WorkflowCanvas.vue | 9 ++++++ src/services/adviceApi.js | 4 +++ src/services/aiApi.js | 10 ++++++ src/services/appointmentApi.js | 5 +++ src/services/berichtenboxApi.js | 4 +++ src/services/coordinateService.js | 4 +++ src/services/gisProxyService.js | 2 ++ src/services/mapFormatters.js | 3 ++ src/services/parafeerActieApi.js | 2 ++ src/services/parafeerRouteApi.js | 4 +++ src/services/pdokService.js | 6 ++++ src/services/taskApi.js | 3 ++ src/store/modules/advice.js | 9 ++++++ src/store/modules/bezwaar.js | 16 ++++++++++ src/store/modules/enforcement.js | 11 +++++++ src/store/modules/gis.js | 9 ++++++ src/store/modules/inspection.js | 11 +++++++ src/store/modules/settings.js | 2 ++ src/store/modules/workflow.js | 32 +++++++++++++++++++ src/store/modules/zgwMapping.js | 3 ++ src/store/store.js | 1 + src/utils/caseHelpers.js | 11 +++++++ src/utils/caseTypeValidation.js | 4 +++ src/utils/caseValidation.js | 5 +++ src/utils/dashboardHelpers.js | 10 ++++++ src/utils/decisionHelpers.js | 3 ++ src/utils/doorlooptijdHelpers.js | 9 ++++++ src/utils/durationHelpers.js | 4 +++ src/utils/i18nResolver.js | 4 +++ src/utils/openregisterCheck.js | 2 ++ src/utils/parafeerEngine.js | 9 ++++++ src/utils/taskHelpers.js | 7 ++++ src/utils/taskLifecycle.js | 5 +++ src/utils/taskValidation.js | 3 ++ src/views/DoorlooptijdDashboard.vue | 28 ++++++++++++++++ src/views/MyWork.vue | 12 +++++++ src/views/Werkvoorraad.vue | 10 ++++++ src/views/cases/CaseCreateDialog.vue | 23 +++++++++++++ .../cases/components/ActivityTimeline.vue | 4 +++ .../cases/components/AddParticipantDialog.vue | 4 +++ src/views/cases/components/AdvicePanel.vue | 13 ++++++++ .../cases/components/AdviceRequestPanel.vue | 6 ++++ .../cases/components/AdviesAanvraagDialog.vue | 2 ++ src/views/cases/components/AdviesPanel.vue | 14 ++++++++ .../cases/components/AiAssistantPanel.vue | 5 +++ .../cases/components/AiClassifyDialog.vue | 4 +++ .../cases/components/AiConfidenceBadge.vue | 3 ++ .../cases/components/AiExtractDialog.vue | 6 ++++ .../cases/components/AiSuggestionCard.vue | 2 ++ src/views/cases/components/AiSummaryPanel.vue | 2 ++ .../components/AppointmentBookingDialog.vue | 3 ++ .../cases/components/AppointmentSection.vue | 6 ++++ .../components/BerichtenboxComposeDialog.vue | 2 ++ .../cases/components/BerichtenboxTab.vue | 4 +++ .../cases/components/CaseTransferDialog.vue | 1 + .../cases/components/ConsultationPanel.vue | 8 +++++ .../cases/components/CreateShareDialog.vue | 2 ++ .../components/CustomPropertiesPanel.vue | 9 ++++++ src/views/cases/components/DeadlinePanel.vue | 7 ++++ .../cases/components/DecisionsSection.vue | 9 ++++++ .../components/DocumentAssessmentPanel.vue | 3 ++ .../cases/components/DocumentChecklist.vue | 6 ++++ src/views/cases/components/EmailComposer.vue | 8 +++++ src/views/cases/components/EmailThread.vue | 4 +++ .../cases/components/EnforcementPanel.vue | 8 +++++ .../cases/components/EnforcementWizard.vue | 8 +++++ .../components/InspectionChecklistPanel.vue | 7 ++++ .../cases/components/InspectionPanel.vue | 15 +++++++++ src/views/cases/components/LocationTab.vue | 10 ++++++ .../cases/components/MilestoneProgress.vue | 4 +++ .../cases/components/MilestoneProgressBar.vue | 1 + .../cases/components/ParticipantsSection.vue | 15 +++++++++ .../cases/components/QuickStatusDropdown.vue | 3 ++ src/views/cases/components/ResultSection.vue | 4 +++ src/views/cases/components/ShareTab.vue | 2 ++ src/views/cases/components/StatusTimeline.vue | 8 +++++ .../cases/components/SubCasesSection.vue | 10 ++++++ .../cases/components/VoorstellenPanel.vue | 6 ++++ src/views/cases/components/WooIntakeForm.vue | 2 ++ .../cases/components/WorkflowTransitions.vue | 11 +++++++ .../beroep/BeroepEscalationPanel.vue | 2 ++ .../beroep/CourtProceedingsPanel.vue | 3 ++ .../bezwaar/AdvisoryReportPanel.vue | 6 ++++ .../bezwaar/BezwaarDecisionForm.vue | 6 ++++ .../components/bezwaar/BezwaarIntakeForm.vue | 5 +++ .../components/bezwaar/BezwaarTimeline.vue | 3 ++ .../components/bezwaar/DeadlineIndicator.vue | 2 ++ .../cases/components/bezwaar/HearingPanel.vue | 10 ++++++ .../cases/widgets/CaseDocumentsWidget.vue | 1 + .../cases/widgets/CasePropertiesWidget.vue | 2 ++ src/views/cases/widgets/CaseTasksWidget.vue | 3 ++ .../cases/widgets/CaseTimelineWidget.vue | 3 ++ .../components/ComplaintCreateDialog.vue | 4 +++ src/views/dashboard/ActivityFeed.vue | 2 ++ src/views/dashboard/CaseMapWidget.vue | 4 +++ src/views/dashboard/CasesByType.vue | 4 +++ src/views/dashboard/KpiCards.vue | 1 + src/views/dashboard/OverduePanel.vue | 1 + src/views/dashboard/StatusChart.vue | 3 ++ src/views/public/PublicAppointmentPage.vue | 4 +++ src/views/public/PublicCaseView.vue | 4 +++ src/views/public/PublicStatusPage.vue | 2 ++ src/views/settings/AdminRoot.vue | 2 ++ src/views/settings/CaseTypeAdmin.vue | 4 +++ src/views/settings/CaseTypeDetail.vue | 8 +++++ src/views/settings/CaseTypeList.vue | 14 ++++++++ src/views/settings/MapLayerSettings.vue | 9 ++++++ src/views/settings/Settings.vue | 4 +++ src/views/settings/WorkflowEditor.vue | 31 ++++++++++++++++++ src/views/settings/ZgwMappingSettings.vue | 6 ++++ .../settings/components/DurationPicker.vue | 5 +++ .../settings/components/LhsMatrixAdmin.vue | 8 +++++ .../components/ParafeerStapEditor.vue | 5 +++ .../settings/components/StepConfigPanel.vue | 15 +++++++++ .../components/TransitionConfigPanel.vue | 10 ++++++ .../components/VthTemplateLibrary.vue | 3 ++ .../settings/components/WorkflowNode.vue | 5 +++ .../settings/components/WorkflowPalette.vue | 1 + .../components/WorkflowTransitionArrow.vue | 4 +++ src/views/settings/tabs/AiSettingsTab.vue | 4 +++ .../settings/tabs/AppointmentSettingsTab.vue | 1 + .../settings/tabs/BerichtenboxSettingsTab.vue | 1 + src/views/settings/tabs/ChecklistAdmin.vue | 15 +++++++++ src/views/settings/tabs/DocumentTypesTab.vue | 6 ++++ src/views/settings/tabs/GeneralTab.vue | 7 ++++ src/views/settings/tabs/PropertiesTab.vue | 9 ++++++ src/views/settings/tabs/ResultTypesTab.vue | 6 ++++ src/views/settings/tabs/ResultsTab.vue | 9 ++++++ src/views/settings/tabs/RoleTypesTab.vue | 6 ++++ src/views/settings/tabs/RolesTab.vue | 9 ++++++ src/views/settings/tabs/StatusesTab.vue | 15 +++++++++ src/views/settings/tabs/TemplatesTab.vue | 2 ++ src/views/settings/tabs/WorkflowTab.vue | 16 ++++++++++ src/views/tasks/TaskCreateDialog.vue | 4 +++ src/views/voorstellen/VoorstelDetail.vue | 18 +++++++++++ .../voorstellen/components/AddStepDialog.vue | 5 +++ .../voorstellen/components/AuditTrail.vue | 3 ++ .../components/BesluitRegistration.vue | 3 ++ .../components/DelegateSelectorField.vue | 2 ++ .../components/ParafeerActieDialog.vue | 11 +++++++ .../components/ParafeerActieTimeline.vue | 4 +++ .../components/ParafeerActionBar.vue | 6 ++++ .../voorstellen/components/ParafeerInbox.vue | 6 ++++ .../components/ProgressTimeline.vue | 3 ++ .../voorstellen/components/SkipStepDialog.vue | 4 +++ .../components/VoorstelCreateDialog.vue | 4 +++ src/views/widgets/CasesOverviewWidget.vue | 4 +++ src/views/widgets/DeadlineAlertsWidget.vue | 4 +++ src/views/widgets/MyTasksWidget.vue | 4 +++ src/views/widgets/OverdueCasesWidget.vue | 4 +++ src/views/widgets/StalledCasesWidget.vue | 4 +++ src/views/widgets/StartCaseWidget.vue | 3 ++ src/views/widgets/TaskRemindersWidget.vue | 4 +++ 310 files changed, 1561 insertions(+) diff --git a/lib/BackgroundJob/AdviceDeadlineJob.php b/lib/BackgroundJob/AdviceDeadlineJob.php index 6e2fc6ec..d84dd070 100644 --- a/lib/BackgroundJob/AdviceDeadlineJob.php +++ b/lib/BackgroundJob/AdviceDeadlineJob.php @@ -72,6 +72,7 @@ public function __construct( * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function run($argument): void { if (in_array('openregister', $this->appManager->getInstalledApps(), true) === false) { diff --git a/lib/BackgroundJob/AppointmentReminderJob.php b/lib/BackgroundJob/AppointmentReminderJob.php index 2c7138ab..f972320b 100644 --- a/lib/BackgroundJob/AppointmentReminderJob.php +++ b/lib/BackgroundJob/AppointmentReminderJob.php @@ -64,6 +64,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function run($argument): void { if (in_array('openregister', $this->appManager->getInstalledApps()) === false) { diff --git a/lib/BackgroundJob/BerichtenboxReadStatusJob.php b/lib/BackgroundJob/BerichtenboxReadStatusJob.php index 59d127da..a40fb6cf 100644 --- a/lib/BackgroundJob/BerichtenboxReadStatusJob.php +++ b/lib/BackgroundJob/BerichtenboxReadStatusJob.php @@ -58,6 +58,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function run($argument): void { $this->logger->info('Procest: Running Berichtenbox read status poll'); diff --git a/lib/BackgroundJob/ShareMaintenanceJob.php b/lib/BackgroundJob/ShareMaintenanceJob.php index 56901fda..fbcd2519 100644 --- a/lib/BackgroundJob/ShareMaintenanceJob.php +++ b/lib/BackgroundJob/ShareMaintenanceJob.php @@ -78,6 +78,7 @@ public function __construct( * * @SuppressWarnings(PHPMD.UnusedFormalParameter) — required by parent */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function run($argument): void { if (in_array('openregister', $this->appManager->getInstalledApps()) === false) { diff --git a/lib/Controller/AdviceController.php b/lib/Controller/AdviceController.php index be4467c4..3cf0754d 100644 --- a/lib/Controller/AdviceController.php +++ b/lib/Controller/AdviceController.php @@ -78,6 +78,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function transitionStatus(string $id): JSONResponse { $data = $this->readJsonBody(); @@ -116,6 +117,7 @@ public function transitionStatus(string $id): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function dispatchReminder(string $id): JSONResponse { try { diff --git a/lib/Controller/AiController.php b/lib/Controller/AiController.php index cf77c27f..2bf9d64a 100644 --- a/lib/Controller/AiController.php +++ b/lib/Controller/AiController.php @@ -81,6 +81,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function classify(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -106,6 +107,7 @@ public function classify(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function extract(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -131,6 +133,7 @@ public function extract(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function ask(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -156,6 +159,7 @@ public function ask(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function summarize(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -190,6 +194,7 @@ public function summarize(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function suggestRouting(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -214,6 +219,7 @@ public function suggestRouting(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function suggestNext(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -238,6 +244,7 @@ public function suggestNext(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recordAction(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -275,6 +282,7 @@ public function recordAction(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function auditIndex(): JSONResponse { $filters = [ @@ -298,6 +306,7 @@ public function auditIndex(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getSettings(): JSONResponse { $settings = $this->aiService->getAiSettings(); @@ -310,6 +319,7 @@ public function getSettings(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function updateSettings(): JSONResponse { $data = $this->request->getParams(); @@ -323,6 +333,7 @@ public function updateSettings(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function healthCheck(): JSONResponse { $result = $this->aiService->testHealth(); diff --git a/lib/Controller/AppointmentController.php b/lib/Controller/AppointmentController.php index b4e301c3..a4eec0ab 100644 --- a/lib/Controller/AppointmentController.php +++ b/lib/Controller/AppointmentController.php @@ -55,6 +55,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): JSONResponse { $caseId = $this->request->getParam('caseId'); @@ -69,6 +70,7 @@ public function index(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(): JSONResponse { $caseId = $this->request->getParam('caseId'); @@ -100,6 +102,7 @@ public function create(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancel(string $appointmentId): JSONResponse { $result = $this->appointmentService->cancelAppointment($appointmentId); @@ -115,6 +118,7 @@ public function cancel(string $appointmentId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function noShow(string $appointmentId): JSONResponse { $result = $this->appointmentService->markNoShow($appointmentId); @@ -128,6 +132,7 @@ public function noShow(string $appointmentId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function timeslots(): JSONResponse { $productId = $this->request->getParam('productId', ''); diff --git a/lib/Controller/BerichtenboxController.php b/lib/Controller/BerichtenboxController.php index 82ab7ad9..0dad7796 100644 --- a/lib/Controller/BerichtenboxController.php +++ b/lib/Controller/BerichtenboxController.php @@ -55,6 +55,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function send(): JSONResponse { $caseId = $this->request->getParam('caseId'); @@ -91,6 +92,7 @@ public function send(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function messages(): JSONResponse { $caseId = $this->request->getParam('caseId', ''); @@ -107,6 +109,7 @@ public function messages(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function poll(string $messageId): JSONResponse { $result = $this->berichtenboxService->pollReadStatus($messageId); diff --git a/lib/Controller/BrcController.php b/lib/Controller/BrcController.php index d95599f5..f791bb75 100644 --- a/lib/Controller/BrcController.php +++ b/lib/Controller/BrcController.php @@ -94,6 +94,7 @@ public function __construct( * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -124,6 +125,7 @@ public function index(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -157,6 +159,7 @@ public function create(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -182,6 +185,7 @@ public function show(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function update(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -221,6 +225,7 @@ public function update(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function patch(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -262,6 +267,7 @@ public function patch(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function destroy(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -300,6 +306,7 @@ public function destroy(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailIndex(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -344,6 +351,7 @@ public function audittrailIndex(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailShow(string $resource, string $uuid, string $auditUuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); diff --git a/lib/Controller/CaseDefinitionController.php b/lib/Controller/CaseDefinitionController.php index fd7e49ff..5f2f58b0 100644 --- a/lib/Controller/CaseDefinitionController.php +++ b/lib/Controller/CaseDefinitionController.php @@ -71,6 +71,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function export(): DataDownloadResponse|JSONResponse { try { @@ -126,6 +127,7 @@ public function export(): DataDownloadResponse|JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validate(): JSONResponse { try { @@ -157,6 +159,7 @@ public function validate(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function import(): JSONResponse { try { diff --git a/lib/Controller/CaseSharingController.php b/lib/Controller/CaseSharingController.php index 768177b8..3a74576d 100644 --- a/lib/Controller/CaseSharingController.php +++ b/lib/Controller/CaseSharingController.php @@ -70,6 +70,7 @@ public function __construct( * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createShare(): JSONResponse { $user = $this->userSession->getUser(); @@ -132,6 +133,7 @@ public function createShare(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function revokeShare(string $shareId): JSONResponse { $user = $this->userSession->getUser(); @@ -150,6 +152,7 @@ public function revokeShare(string $shareId): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function initiateTransfer(): JSONResponse { $caseId = $this->request->getParam('caseId'); @@ -185,6 +188,7 @@ public function initiateTransfer(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleTransfer(string $transferId): JSONResponse { $action = $this->request->getParam('action'); diff --git a/lib/Controller/ConsultationController.php b/lib/Controller/ConsultationController.php index be8d21a9..a77b9684 100644 --- a/lib/Controller/ConsultationController.php +++ b/lib/Controller/ConsultationController.php @@ -60,6 +60,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(string $caseId): JSONResponse { $consultations = $this->consultationService->getConsultationsForCase($caseId); @@ -73,6 +74,7 @@ public function index(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(): JSONResponse { try { @@ -104,6 +106,7 @@ public function create(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function updateStatus(string $id): JSONResponse { try { @@ -136,6 +139,7 @@ public function updateStatus(string $id): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function submitResponse(string $id): JSONResponse { try { @@ -165,6 +169,7 @@ public function submitResponse(string $id): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function overdue(): JSONResponse { $overdue = $this->consultationService->getOverdueConsultations(); diff --git a/lib/Controller/DashboardController.php b/lib/Controller/DashboardController.php index 110523ae..c4530f06 100644 --- a/lib/Controller/DashboardController.php +++ b/lib/Controller/DashboardController.php @@ -66,6 +66,7 @@ public function __construct(IRequest $request) * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function page(string $path=''): TemplateResponse { return new TemplateResponse(Application::APP_ID, 'index'); diff --git a/lib/Controller/DrcController.php b/lib/Controller/DrcController.php index 80e79dd8..2da34728 100644 --- a/lib/Controller/DrcController.php +++ b/lib/Controller/DrcController.php @@ -106,6 +106,7 @@ public function __construct( * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -206,6 +207,7 @@ private function indexFlatArray(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -386,6 +388,7 @@ public function create(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -421,6 +424,7 @@ public function show(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function update(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -450,6 +454,7 @@ public function update(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function patch(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -479,6 +484,7 @@ public function patch(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function destroy(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -579,6 +585,7 @@ public function destroy(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function download(string $uuid): DataDownloadResponse|JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -654,6 +661,7 @@ public function download(string $uuid): DataDownloadResponse|JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function lock(string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -785,6 +793,7 @@ private function lockFallback(object $objectService, string $uuid, \Throwable $o * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function unlock(string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -941,6 +950,7 @@ private function unlockFallback(object $objectService, string $uuid, \Throwable * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailIndex(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -984,6 +994,7 @@ public function audittrailIndex(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailShow(string $resource, string $uuid, string $auditUuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -1390,6 +1401,7 @@ private function setIndicatieGebruiksrecht(string $ioUrl, ?bool $value): void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function uploadChunk(string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); diff --git a/lib/Controller/EmailController.php b/lib/Controller/EmailController.php index f59fb9e5..971a5483 100644 --- a/lib/Controller/EmailController.php +++ b/lib/Controller/EmailController.php @@ -60,6 +60,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function send(string $caseId): JSONResponse { try { @@ -97,6 +98,7 @@ public function send(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function sendFromTemplate(string $caseId): JSONResponse { try { @@ -132,6 +134,7 @@ public function sendFromTemplate(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function preview(string $caseId): JSONResponse { $content = $this->request->getContent(); @@ -169,6 +172,7 @@ public function preview(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function templates(string $caseTypeId): JSONResponse { $templates = $this->emailService->getTemplatesForCaseType($caseTypeId); diff --git a/lib/Controller/GisProxyController.php b/lib/Controller/GisProxyController.php index 46c9553f..e0024bf0 100644 --- a/lib/Controller/GisProxyController.php +++ b/lib/Controller/GisProxyController.php @@ -63,6 +63,7 @@ public function __construct( * * @return JSONResponse|Response The proxied response */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function proxy(): JSONResponse|Response { $url = $this->request->getParam('url', ''); @@ -109,6 +110,7 @@ public function proxy(): JSONResponse|Response * * @return JSONResponse Parsed capabilities as JSON */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function capabilities(): JSONResponse { $url = $this->request->getParam('url', ''); diff --git a/lib/Controller/HealthController.php b/lib/Controller/HealthController.php index 1528d7d7..136fc866 100644 --- a/lib/Controller/HealthController.php +++ b/lib/Controller/HealthController.php @@ -68,6 +68,7 @@ public function __construct( * * @return JSONResponse Health status */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): JSONResponse { $checks = []; diff --git a/lib/Controller/InspectionController.php b/lib/Controller/InspectionController.php index 4f4becf8..4ea6ce39 100644 --- a/lib/Controller/InspectionController.php +++ b/lib/Controller/InspectionController.php @@ -71,6 +71,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): JSONResponse { try { @@ -99,6 +100,7 @@ public function index(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function captureLocation(string $id): JSONResponse { try { @@ -142,6 +144,7 @@ public function captureLocation(string $id): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function completeChecklistItem(string $id, string $itemId): JSONResponse { try { @@ -190,6 +193,7 @@ public function completeChecklistItem(string $id, string $itemId): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function addPhoto(string $id): JSONResponse { try { @@ -218,6 +222,7 @@ public function addPhoto(string $id): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function complete(string $id): JSONResponse { try { diff --git a/lib/Controller/LegesController.php b/lib/Controller/LegesController.php index 58d0d3ff..9ff90e2f 100644 --- a/lib/Controller/LegesController.php +++ b/lib/Controller/LegesController.php @@ -75,6 +75,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function calculate(): JSONResponse { try { @@ -117,6 +118,7 @@ public function calculate(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recalculate(): JSONResponse { try { @@ -164,6 +166,7 @@ public function recalculate(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function verrekening(): JSONResponse { try { @@ -189,6 +192,7 @@ public function verrekening(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function teruggaaf(): JSONResponse { try { @@ -219,6 +223,7 @@ public function teruggaaf(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function export(): DataDownloadResponse|JSONResponse { try { diff --git a/lib/Controller/MetricsController.php b/lib/Controller/MetricsController.php index a1ebc1c9..3a01271b 100644 --- a/lib/Controller/MetricsController.php +++ b/lib/Controller/MetricsController.php @@ -77,6 +77,7 @@ public function __construct( * * @return TextPlainResponse Prometheus-formatted metrics */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): TextPlainResponse { $metrics = $this->collectMetrics(); diff --git a/lib/Controller/MilestoneController.php b/lib/Controller/MilestoneController.php index a1b1f4d9..d516860a 100644 --- a/lib/Controller/MilestoneController.php +++ b/lib/Controller/MilestoneController.php @@ -64,6 +64,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function progress(string $caseId, string $caseTypeId): JSONResponse { try { @@ -84,6 +85,7 @@ public function progress(string $caseId, string $caseTypeId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mark(string $caseId, string $milestoneId): JSONResponse { $user = $this->userSession->getUser(); @@ -116,6 +118,7 @@ public function mark(string $caseId, string $milestoneId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function reverse(string $caseId, string $milestoneId): JSONResponse { $reason = $this->request->getParam('reason', ''); diff --git a/lib/Controller/NrcController.php b/lib/Controller/NrcController.php index 211cc14a..3a18ba76 100644 --- a/lib/Controller/NrcController.php +++ b/lib/Controller/NrcController.php @@ -81,6 +81,7 @@ public function __construct( * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -103,6 +104,7 @@ public function index(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -126,6 +128,7 @@ public function create(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -149,6 +152,7 @@ public function show(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function update(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -178,6 +182,7 @@ public function update(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function patch(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -207,6 +212,7 @@ public function patch(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function destroy(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -234,6 +240,7 @@ public function destroy(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function notificatieCreate(): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -260,6 +267,7 @@ public function notificatieCreate(): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailIndex(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -284,6 +292,7 @@ public function audittrailIndex(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailShow(string $resource, string $uuid, string $auditUuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); diff --git a/lib/Controller/ParaferingAuditExportController.php b/lib/Controller/ParaferingAuditExportController.php index 8ea38680..b70aa438 100644 --- a/lib/Controller/ParaferingAuditExportController.php +++ b/lib/Controller/ParaferingAuditExportController.php @@ -81,6 +81,7 @@ public function __construct( * @return JSONResponse */ #[NoAdminRequired] + /** @spec openspec/specs/parafering-audit-trail/spec.md */ public function export(string $id, string $format='json'): JSONResponse { try { diff --git a/lib/Controller/ParaferingController.php b/lib/Controller/ParaferingController.php index a1117589..18495d39 100644 --- a/lib/Controller/ParaferingController.php +++ b/lib/Controller/ParaferingController.php @@ -68,6 +68,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createVoorstel(): JSONResponse { try { @@ -103,6 +104,7 @@ public function createVoorstel(): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function startParafering(string $id): JSONResponse { try { @@ -143,6 +145,7 @@ public function startParafering(string $id): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function paraferen(string $id): JSONResponse { return $this->handleAction(id: $id, action: ParaferingService::ACTION_PARAFEREN); @@ -157,6 +160,7 @@ public function paraferen(string $id): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function terugsturen(string $id): JSONResponse { return $this->handleAction(id: $id, action: ParaferingService::ACTION_TERUGSTUREN); @@ -171,6 +175,7 @@ public function terugsturen(string $id): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function adviseren(string $id): JSONResponse { return $this->handleAction(id: $id, action: ParaferingService::ACTION_ADVISEREN); @@ -185,6 +190,7 @@ public function adviseren(string $id): JSONResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function auditTrail(string $id): JSONResponse { try { diff --git a/lib/Controller/PreferencesController.php b/lib/Controller/PreferencesController.php index f4c31bdd..5ef560aa 100644 --- a/lib/Controller/PreferencesController.php +++ b/lib/Controller/PreferencesController.php @@ -63,6 +63,7 @@ public function __construct( * @NoAdminRequired * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ public function getPreference(string $key): JSONResponse { $user = $this->userSession->getUser(); @@ -102,6 +103,7 @@ public function getPreference(string $key): JSONResponse * @NoAdminRequired * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ public function setPreference(string $key, string $value=''): JSONResponse { $user = $this->userSession->getUser(); diff --git a/lib/Controller/PublicAppointmentController.php b/lib/Controller/PublicAppointmentController.php index 3fa71c50..44bdd362 100644 --- a/lib/Controller/PublicAppointmentController.php +++ b/lib/Controller/PublicAppointmentController.php @@ -58,6 +58,7 @@ public function __construct( * @PublicPage * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function view(string $token): JSONResponse { $appointment = $this->appointmentService->getAppointmentByToken($token); @@ -89,6 +90,7 @@ public function view(string $token): JSONResponse * @PublicPage * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancel(string $token): JSONResponse { $appointment = $this->appointmentService->getAppointmentByToken($token); diff --git a/lib/Controller/PublicShareController.php b/lib/Controller/PublicShareController.php index a8a58839..dc30ba66 100644 --- a/lib/Controller/PublicShareController.php +++ b/lib/Controller/PublicShareController.php @@ -79,6 +79,7 @@ public function __construct( * @PublicPage * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function accessShare(string $token): JSONResponse { $password = $this->request->getParam('password'); @@ -147,6 +148,7 @@ public function accessShare(string $token): JSONResponse * @PublicPage * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function addComment(string $token): JSONResponse { $password = $this->request->getParam('password'); @@ -212,6 +214,7 @@ public function addComment(string $token): JSONResponse * @PublicPage * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function viewStatus(string $token): JSONResponse { $validation = $this->caseSharingService->validateToken($token); @@ -257,6 +260,7 @@ public function viewStatus(string $token): JSONResponse * @PublicPage * @NoCSRFRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function uploadDocument(string $token): JSONResponse { $password = $this->request->getParam('password'); diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 69d51b40..4601fa22 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -79,6 +79,7 @@ public function __construct( * @return \OCA\OpenRegister\Service\ObjectService|null The OpenRegister service if available, null otherwise. * @throws \RuntimeException If the service is not available. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getObjectService(): ?\OCA\OpenRegister\Service\ObjectService { if (in_array(needle: 'openregister', haystack: $this->appManager->getInstalledApps()) === true) { @@ -96,6 +97,7 @@ public function getObjectService(): ?\OCA\OpenRegister\Service\ObjectService * @return \OCA\OpenRegister\Service\ConfigurationService|null The Configuration service if available, null otherwise. * @throws \RuntimeException If the service is not available. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getConfigurationService(): ?\OCA\OpenRegister\Service\ConfigurationService { if (in_array(needle: 'openregister', haystack: $this->appManager->getInstalledApps()) === true) { @@ -114,6 +116,7 @@ public function getConfigurationService(): ?\OCA\OpenRegister\Service\Configurat * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): JSONResponse { $user = $this->userSession->getUser(); @@ -134,6 +137,7 @@ public function index(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(): JSONResponse { $data = $this->request->getParams(); @@ -155,6 +159,7 @@ public function create(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function load(): JSONResponse { $result = $this->settingsService->loadConfiguration(force: true); diff --git a/lib/Controller/StatusTransitionController.php b/lib/Controller/StatusTransitionController.php index f7fa4f06..af44ce68 100644 --- a/lib/Controller/StatusTransitionController.php +++ b/lib/Controller/StatusTransitionController.php @@ -78,6 +78,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function available(string $caseId): JSONResponse { try { @@ -104,6 +105,7 @@ public function available(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function execute(string $caseId): JSONResponse { $body = $this->readJsonBody(); @@ -163,6 +165,7 @@ public function execute(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function freeform(string $caseId): JSONResponse { $user = $this->userSession->getUser(); @@ -225,6 +228,7 @@ public function freeform(string $caseId): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function history(string $caseId): JSONResponse { try { diff --git a/lib/Controller/StufController.php b/lib/Controller/StufController.php index d831ca17..6ad04f92 100644 --- a/lib/Controller/StufController.php +++ b/lib/Controller/StufController.php @@ -87,6 +87,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaken(): DataDisplayResponse { return $this->handleSoapMessage(service: 'zaken'); @@ -99,6 +100,7 @@ public function zaken(): DataDisplayResponse * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function personen(): DataDisplayResponse { return $this->handleSoapMessage(service: 'personen'); diff --git a/lib/Controller/TemplateController.php b/lib/Controller/TemplateController.php index b840600b..8b91abf7 100644 --- a/lib/Controller/TemplateController.php +++ b/lib/Controller/TemplateController.php @@ -61,6 +61,7 @@ public function __construct( * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): JSONResponse { $templates = $this->templateService->listTemplates(); @@ -76,6 +77,7 @@ public function index(): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $id): JSONResponse { $template = $this->templateService->loadTemplate($id); @@ -95,6 +97,7 @@ public function show(string $id): JSONResponse * * @NoAdminRequired */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function activate(string $id): JSONResponse { try { diff --git a/lib/Controller/TenantController.php b/lib/Controller/TenantController.php index 0a02688d..53238444 100644 --- a/lib/Controller/TenantController.php +++ b/lib/Controller/TenantController.php @@ -70,6 +70,7 @@ public function __construct( * * @return JSONResponse The provisioning result */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function provision(string $tenantId): JSONResponse { if ($this->isPlatformAdmin() === false) { @@ -92,6 +93,7 @@ public function provision(string $tenantId): JSONResponse * * @return JSONResponse The resource usage */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function usage(string $tenantId): JSONResponse { if ($this->isPlatformAdmin() === false) { @@ -109,6 +111,7 @@ public function usage(string $tenantId): JSONResponse * * @return JSONResponse The current tenant */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function current(): JSONResponse { $user = $this->userSession->getUser(); diff --git a/lib/Controller/WmsWfsController.php b/lib/Controller/WmsWfsController.php index b933f285..376bbc52 100644 --- a/lib/Controller/WmsWfsController.php +++ b/lib/Controller/WmsWfsController.php @@ -77,6 +77,7 @@ public function __construct( * * @return JSONResponse Proxied response or error envelope */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function proxy(): JSONResponse { $layerId = (string) $this->request->getParam('layerId', ''); diff --git a/lib/Controller/WorkflowDefinitionController.php b/lib/Controller/WorkflowDefinitionController.php index 794a0b4b..065a6086 100644 --- a/lib/Controller/WorkflowDefinitionController.php +++ b/lib/Controller/WorkflowDefinitionController.php @@ -65,6 +65,7 @@ public function __construct( * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publish(string $id): JSONResponse { $result = $this->service->publish($id); @@ -88,6 +89,7 @@ public function publish(string $id): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function deprecate(string $id): JSONResponse { $result = $this->service->deprecate($id); @@ -108,6 +110,7 @@ public function deprecate(string $id): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cloneDefinition(string $id): JSONResponse { $result = $this->service->cloneDefinition($id); @@ -130,6 +133,7 @@ public function cloneDefinition(string $id): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function active(string $caseTypeId): JSONResponse { $definition = $this->service->getActiveDefinitionFor($caseTypeId); @@ -151,6 +155,7 @@ public function active(string $caseTypeId): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function forCase(string $caseId): JSONResponse { $definition = $this->service->getDefinitionForCase($caseId); diff --git a/lib/Controller/ZgwMappingController.php b/lib/Controller/ZgwMappingController.php index b8412a3b..17586d0c 100644 --- a/lib/Controller/ZgwMappingController.php +++ b/lib/Controller/ZgwMappingController.php @@ -68,6 +68,7 @@ public function __construct( * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(): JSONResponse { return new JSONResponse( @@ -85,6 +86,7 @@ public function index(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $resourceKey): JSONResponse { $mapping = $this->zgwMappingService->getMapping($resourceKey); @@ -113,6 +115,7 @@ public function show(string $resourceKey): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function update(string $resourceKey): JSONResponse { $params = $this->request->getParams(); @@ -137,6 +140,7 @@ public function update(string $resourceKey): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function destroy(string $resourceKey): JSONResponse { $this->zgwMappingService->deleteMapping($resourceKey); @@ -155,6 +159,7 @@ public function destroy(string $resourceKey): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function reset(string $resourceKey): JSONResponse { $registerId = $this->settingsService->getConfigValue(key: 'register', default: ''); diff --git a/lib/Controller/ZrcController.php b/lib/Controller/ZrcController.php index d5d7109e..c1ba13c6 100644 --- a/lib/Controller/ZrcController.php +++ b/lib/Controller/ZrcController.php @@ -114,6 +114,7 @@ public function __construct( * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(string $resource): JSONResponse { $response = $this->zgwService->handleIndex($this->request, self::ZGW_API, $resource); @@ -142,6 +143,7 @@ public function index(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -314,6 +316,7 @@ public function create(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $resource, string $uuid): JSONResponse { // Zrc-006b: Check zaken.lezen scope and vertrouwelijkheidaanduiding. @@ -347,6 +350,7 @@ public function show(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function update(string $resource, string $uuid): JSONResponse { // Resolve UUID from URL path — body "uuid" can override controller args. @@ -403,6 +407,7 @@ public function update(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function patch(string $resource, string $uuid): JSONResponse { // Resolve UUID from URL path — body "uuid" can override controller args. @@ -460,6 +465,7 @@ public function patch(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function destroy(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -518,6 +524,7 @@ public function destroy(string $resource, string $uuid): JSONResponse * * @SuppressWarnings(PHPMD.UnusedFormalParameter) $zaakUuid required by route pattern */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakeigenschappenIndex(string $zaakUuid): JSONResponse { return $this->index(resource: 'zaakeigenschappen'); @@ -537,6 +544,7 @@ public function zaakeigenschappenIndex(string $zaakUuid): JSONResponse * * @SuppressWarnings(PHPMD.UnusedFormalParameter) $zaakUuid required by route pattern */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakeigenschappenCreate(string $zaakUuid): JSONResponse { return $this->create(resource: 'zaakeigenschappen'); @@ -557,6 +565,7 @@ public function zaakeigenschappenCreate(string $zaakUuid): JSONResponse * * @SuppressWarnings(PHPMD.UnusedFormalParameter) $zaakUuid required by route pattern */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakeigenschappenShow(string $zaakUuid, string $uuid): JSONResponse { return $this->show(resource: 'zaakeigenschappen', uuid: $uuid); @@ -577,6 +586,7 @@ public function zaakeigenschappenShow(string $zaakUuid, string $uuid): JSONRespo * * @SuppressWarnings(PHPMD.UnusedFormalParameter) $zaakUuid required by route pattern */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakeigenschappenUpdate(string $zaakUuid, string $uuid): JSONResponse { return $this->update(resource: 'zaakeigenschappen', uuid: $uuid); @@ -597,6 +607,7 @@ public function zaakeigenschappenUpdate(string $zaakUuid, string $uuid): JSONRes * * @SuppressWarnings(PHPMD.UnusedFormalParameter) $zaakUuid required by route pattern */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakeigenschappenPatch(string $zaakUuid, string $uuid): JSONResponse { return $this->patch(resource: 'zaakeigenschappen', uuid: $uuid); @@ -617,6 +628,7 @@ public function zaakeigenschappenPatch(string $zaakUuid, string $uuid): JSONResp * * @SuppressWarnings(PHPMD.UnusedFormalParameter) $zaakUuid required by route pattern */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakeigenschappenDestroy(string $zaakUuid, string $uuid): JSONResponse { return $this->destroy(resource: 'zaakeigenschappen', uuid: $uuid); @@ -634,6 +646,7 @@ public function zaakeigenschappenDestroy(string $zaakUuid, string $uuid): JSONRe * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zaakbesluitenIndex(string $zaakUuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -704,6 +717,7 @@ public function zaakbesluitenIndex(string $zaakUuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function zoek(): JSONResponse { $indexResponse = $this->index(resource: 'zaken'); @@ -729,6 +743,7 @@ public function zoek(): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailIndex(string $resource, string $uuid): JSONResponse { return $this->zgwService->handleAudittrailIndex($this->request, self::ZGW_API, $resource, $uuid); @@ -748,6 +763,7 @@ public function audittrailIndex(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailShow(string $resource, string $uuid, string $auditUuid): JSONResponse { return $this->zgwService->handleAudittrailShow($this->request, self::ZGW_API, $resource, $uuid, $auditUuid); diff --git a/lib/Controller/ZtcController.php b/lib/Controller/ZtcController.php index 62581231..1070bf8a 100644 --- a/lib/Controller/ZtcController.php +++ b/lib/Controller/ZtcController.php @@ -126,6 +126,7 @@ public function __construct( * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function index(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -177,6 +178,7 @@ public function index(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function create(string $resource): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -227,6 +229,7 @@ public function create(string $resource): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function show(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -299,6 +302,7 @@ private function resolveParentDraft(string $resource, string $uuid): ?bool * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function update(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -346,6 +350,7 @@ public function update(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function patch(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -393,6 +398,7 @@ public function patch(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function destroy(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -505,6 +511,7 @@ private function handlePublish(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publishZaaktype(string $uuid): JSONResponse { return $this->handlePublish(resource: 'zaaktypen', uuid: $uuid); @@ -522,6 +529,7 @@ public function publishZaaktype(string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publishBesluittype(string $uuid): JSONResponse { return $this->handlePublish(resource: 'besluittypen', uuid: $uuid); @@ -539,6 +547,7 @@ public function publishBesluittype(string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publishInformatieobjecttype(string $uuid): JSONResponse { return $this->handlePublish(resource: 'informatieobjecttypen', uuid: $uuid); @@ -1237,6 +1246,7 @@ private function isUrlValid(string $url, string $schemaKey, string $today): bool * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailIndex(string $resource, string $uuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); @@ -1261,6 +1271,7 @@ public function audittrailIndex(string $resource, string $uuid): JSONResponse * @PublicPage * @CORS */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function audittrailShow(string $resource, string $uuid, string $auditUuid): JSONResponse { $authError = $this->zgwService->validateJwtAuth($this->request); diff --git a/lib/Listener/BeroepEscalationListener.php b/lib/Listener/BeroepEscalationListener.php index db9836eb..913410e7 100644 --- a/lib/Listener/BeroepEscalationListener.php +++ b/lib/Listener/BeroepEscalationListener.php @@ -101,6 +101,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(Event $event): void { if ($event instanceof ObjectCreatedEvent === false diff --git a/lib/Listener/BezwaarAdviceRequestedListener.php b/lib/Listener/BezwaarAdviceRequestedListener.php index ef26ab87..fce2ed70 100644 --- a/lib/Listener/BezwaarAdviceRequestedListener.php +++ b/lib/Listener/BezwaarAdviceRequestedListener.php @@ -76,6 +76,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(Event $event): void { if ($event instanceof ObjectUpdatedEvent === false) { diff --git a/lib/Listener/BezwaarDecisionListener.php b/lib/Listener/BezwaarDecisionListener.php index 69fc1cee..9520b98f 100644 --- a/lib/Listener/BezwaarDecisionListener.php +++ b/lib/Listener/BezwaarDecisionListener.php @@ -76,6 +76,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(Event $event): void { if ($event instanceof ObjectUpdatedEvent === false) { diff --git a/lib/Listener/BezwaarHearingScheduledListener.php b/lib/Listener/BezwaarHearingScheduledListener.php index 6b87989a..93652eb5 100644 --- a/lib/Listener/BezwaarHearingScheduledListener.php +++ b/lib/Listener/BezwaarHearingScheduledListener.php @@ -74,6 +74,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(Event $event): void { if ($event instanceof ObjectUpdatedEvent === false) { diff --git a/lib/Listener/BezwaarLifecycleListener.php b/lib/Listener/BezwaarLifecycleListener.php index 14a439df..7a78a2e6 100644 --- a/lib/Listener/BezwaarLifecycleListener.php +++ b/lib/Listener/BezwaarLifecycleListener.php @@ -85,6 +85,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(Event $event): void { if (($event instanceof ObjectCreatedEvent) === false diff --git a/lib/Listener/ChecklistRunImmutabilityListener.php b/lib/Listener/ChecklistRunImmutabilityListener.php index 685e302c..c4ca38d6 100644 --- a/lib/Listener/ChecklistRunImmutabilityListener.php +++ b/lib/Listener/ChecklistRunImmutabilityListener.php @@ -77,6 +77,7 @@ public function __construct( * * @throws RuntimeException When a submitted run is being mutated. */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function handle(Event $event): void { if ($event instanceof ObjectUpdatedEvent === false) { diff --git a/lib/Listener/DeepLinkRegistrationListener.php b/lib/Listener/DeepLinkRegistrationListener.php index 764cb17a..2b8d6dad 100644 --- a/lib/Listener/DeepLinkRegistrationListener.php +++ b/lib/Listener/DeepLinkRegistrationListener.php @@ -48,6 +48,7 @@ class DeepLinkRegistrationListener implements IEventListener * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ public function handle(Event $event): void { if ($event instanceof DeepLinkRegistrationEvent === false) { diff --git a/lib/Listener/ParaferingAuditListener.php b/lib/Listener/ParaferingAuditListener.php index 0543dedd..768c9183 100644 --- a/lib/Listener/ParaferingAuditListener.php +++ b/lib/Listener/ParaferingAuditListener.php @@ -64,6 +64,7 @@ public function __construct( * * @return void */ + /** @spec openspec/specs/parafering-audit-trail/spec.md */ public function handle(Event $event): void { if ($event instanceof ParafeerTransitionEvent === false) { diff --git a/lib/Listener/RoleMutationListener.php b/lib/Listener/RoleMutationListener.php index 87f367e4..aaac1b0a 100644 --- a/lib/Listener/RoleMutationListener.php +++ b/lib/Listener/RoleMutationListener.php @@ -74,6 +74,7 @@ public function __construct( * * @return void */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function handle(Event $event): void { if ($event instanceof ObjectCreatedEvent === false diff --git a/lib/Repair/InitializeSettings.php b/lib/Repair/InitializeSettings.php index a6f46a81..f224277f 100644 --- a/lib/Repair/InitializeSettings.php +++ b/lib/Repair/InitializeSettings.php @@ -67,6 +67,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function run(IOutput $output): void { $output->info('Initializing Procest configuration...'); diff --git a/lib/Repair/LoadDefaultZgwMappings.php b/lib/Repair/LoadDefaultZgwMappings.php index 35e03a9b..a9ec9838 100644 --- a/lib/Repair/LoadDefaultZgwMappings.php +++ b/lib/Repair/LoadDefaultZgwMappings.php @@ -87,6 +87,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function run(IOutput $output): void { $output->info('Loading default ZGW API mappings...'); @@ -189,6 +190,7 @@ private function tplUrl(string $from, string $to, string $varName): string * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getDefaultMappings(string $registerId): array { $settings = $this->settingsService->getSettings(); diff --git a/lib/Repair/MigrateWorkflowDefinitions.php b/lib/Repair/MigrateWorkflowDefinitions.php index 08538411..18a18967 100644 --- a/lib/Repair/MigrateWorkflowDefinitions.php +++ b/lib/Repair/MigrateWorkflowDefinitions.php @@ -72,6 +72,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function run(IOutput $output): void { if ($this->settingsService->isOpenRegisterAvailable() === false) { diff --git a/lib/Repair/SeedBezwaarBeroepData.php b/lib/Repair/SeedBezwaarBeroepData.php index b74bc552..dd086bbd 100644 --- a/lib/Repair/SeedBezwaarBeroepData.php +++ b/lib/Repair/SeedBezwaarBeroepData.php @@ -71,6 +71,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function run(IOutput $output): void { $output->info('Seeding bezwaar and beroep case types...'); diff --git a/lib/Repair/SeedBezwaarWorkflowDefinition.php b/lib/Repair/SeedBezwaarWorkflowDefinition.php index 8ebf99b9..0b9ddd71 100644 --- a/lib/Repair/SeedBezwaarWorkflowDefinition.php +++ b/lib/Repair/SeedBezwaarWorkflowDefinition.php @@ -87,6 +87,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function run(IOutput $output): void { if ($this->settingsService->isOpenRegisterAvailable() === false) { diff --git a/lib/Repair/SeedLhsMatrix.php b/lib/Repair/SeedLhsMatrix.php index 374f593d..97a013a9 100644 --- a/lib/Repair/SeedLhsMatrix.php +++ b/lib/Repair/SeedLhsMatrix.php @@ -73,6 +73,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/specs/enforcement-lhs/spec.md */ public function run(IOutput $output): void { $output->info('Seeding default LHS matrix...'); diff --git a/lib/Repair/SeedVthWorkflowTemplates.php b/lib/Repair/SeedVthWorkflowTemplates.php index f185c03f..2dd170e9 100644 --- a/lib/Repair/SeedVthWorkflowTemplates.php +++ b/lib/Repair/SeedVthWorkflowTemplates.php @@ -96,6 +96,7 @@ public function getName(): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function run(IOutput $output): void { $output->info('Seeding VTH workflow templates...'); diff --git a/lib/Service/Actions/ActionHandlerInterface.php b/lib/Service/Actions/ActionHandlerInterface.php index 632acb50..d895366a 100644 --- a/lib/Service/Actions/ActionHandlerInterface.php +++ b/lib/Service/Actions/ActionHandlerInterface.php @@ -53,6 +53,7 @@ interface ActionHandlerInterface * @return string One of the six built-in handler types, or a custom slug * for third-party extensions. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string; /** @@ -73,5 +74,6 @@ public function type(): string; * * @return ActionResult */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult; }//end interface diff --git a/lib/Service/Actions/ActionRegistry.php b/lib/Service/Actions/ActionRegistry.php index 34e9d664..893ac565 100644 --- a/lib/Service/Actions/ActionRegistry.php +++ b/lib/Service/Actions/ActionRegistry.php @@ -110,6 +110,7 @@ public function __construct( * `config`), or null on miss, unpublished, or * cross-tenant attempt. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolve(string $tenantId, string $slug): ?array { $cacheKey = $tenantId.'::'.$slug; @@ -211,6 +212,7 @@ public function resolve(string $tenantId, string $slug): ?array * @return array Tenant-owned actions; published flag is left * on each entry so the UI can render badges. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function listForTenant(string $tenantId, ?string $typeFilter=null): array { try { @@ -254,6 +256,7 @@ public function listForTenant(string $tenantId, ?string $typeFilter=null): array * * @return ActionHandlerInterface|null Null when no handler is registered. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getHandler(string $type): ?ActionHandlerInterface { if ($this->handlerIndex === null) { diff --git a/lib/Service/Actions/ActionResult.php b/lib/Service/Actions/ActionResult.php index 6d2ef2b7..f7cff010 100644 --- a/lib/Service/Actions/ActionResult.php +++ b/lib/Service/Actions/ActionResult.php @@ -63,6 +63,7 @@ public function __construct( * * @return self */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public static function success(array $data=[]): self { return new self(ok: true, error: null, data: $data); @@ -76,6 +77,7 @@ public static function success(array $data=[]): self * * @return self */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public static function failure(string $error, array $data=[]): self { return new self(ok: false, error: $error, data: $data); @@ -87,6 +89,7 @@ public static function failure(string $error, array $data=[]): self * * @return array */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function toArray(): array { $out = ['ok' => $this->ok]; diff --git a/lib/Service/Actions/CallWebhookHandler.php b/lib/Service/Actions/CallWebhookHandler.php index 9954ec58..e8920d15 100644 --- a/lib/Service/Actions/CallWebhookHandler.php +++ b/lib/Service/Actions/CallWebhookHandler.php @@ -61,6 +61,7 @@ public function __construct( * * @return string The action type slug handled by this handler. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string { return 'callWebhook'; @@ -75,6 +76,7 @@ public function type(): string * * @return ActionResult The outcome of the webhook dispatch. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Actions/CreateDocumentHandler.php b/lib/Service/Actions/CreateDocumentHandler.php index ed10f203..ae5afca7 100644 --- a/lib/Service/Actions/CreateDocumentHandler.php +++ b/lib/Service/Actions/CreateDocumentHandler.php @@ -59,6 +59,7 @@ public function __construct( * * @return string The action type slug handled by this handler. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string { return 'createDocument'; @@ -73,6 +74,7 @@ public function type(): string * * @return ActionResult The outcome of the document creation. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Actions/HandlesTemplates.php b/lib/Service/Actions/HandlesTemplates.php index 0e5f8046..bcedb03d 100644 --- a/lib/Service/Actions/HandlesTemplates.php +++ b/lib/Service/Actions/HandlesTemplates.php @@ -48,6 +48,7 @@ trait HandlesTemplates * * @return string */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function renderTemplate(string $template, array $case): string { if ($template === '' || str_contains($template, '{{') === false) { @@ -96,6 +97,7 @@ static function (array $match) use ($context): string { * * @return string */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function resolveRecipient(string $recipientRef, array $case): string { if ($recipientRef === '') { diff --git a/lib/Service/Actions/MergeTemplateHandler.php b/lib/Service/Actions/MergeTemplateHandler.php index f6bc5dc9..1c3da99a 100644 --- a/lib/Service/Actions/MergeTemplateHandler.php +++ b/lib/Service/Actions/MergeTemplateHandler.php @@ -63,6 +63,7 @@ public function __construct( * * @return string The action type slug handled by this handler. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string { return 'mergeTemplate'; @@ -77,6 +78,7 @@ public function type(): string * * @return ActionResult The outcome of the template merge. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Actions/NotifyRoleHandler.php b/lib/Service/Actions/NotifyRoleHandler.php index 14213ca5..8d250ebe 100644 --- a/lib/Service/Actions/NotifyRoleHandler.php +++ b/lib/Service/Actions/NotifyRoleHandler.php @@ -59,6 +59,7 @@ public function __construct( * * @return string The action type slug handled by this handler. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string { return 'notifyRole'; @@ -73,6 +74,7 @@ public function type(): string * * @return ActionResult The outcome of the role notification. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Actions/ScheduleReminderHandler.php b/lib/Service/Actions/ScheduleReminderHandler.php index c9c061f8..58df4b86 100644 --- a/lib/Service/Actions/ScheduleReminderHandler.php +++ b/lib/Service/Actions/ScheduleReminderHandler.php @@ -72,6 +72,7 @@ public function __construct( * * @return string The action type slug handled by this handler. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string { return 'scheduleReminder'; @@ -86,6 +87,7 @@ public function type(): string * * @return ActionResult The outcome of scheduling the reminder. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Actions/SendEmailHandler.php b/lib/Service/Actions/SendEmailHandler.php index 3eaef659..9fd944a7 100644 --- a/lib/Service/Actions/SendEmailHandler.php +++ b/lib/Service/Actions/SendEmailHandler.php @@ -61,6 +61,7 @@ public function __construct( * * @return string The action type slug handled by this handler. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function type(): string { return 'sendEmail'; @@ -75,6 +76,7 @@ public function type(): string * * @return ActionResult The outcome of sending the email. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/AdviceService.php b/lib/Service/AdviceService.php index 2ee4ccca..5ac6d070 100644 --- a/lib/Service/AdviceService.php +++ b/lib/Service/AdviceService.php @@ -89,6 +89,7 @@ public function __construct( * * @throws \RuntimeException When OpenRegister unavailable / invalid status */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function transitionStatus(string $adviceId, string $to, array $payload=[]): array { if (in_array($to, self::VALID_STATUSES, true) === false) { @@ -148,6 +149,7 @@ public function transitionStatus(string $adviceId, string $to, array $payload=[] * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function dispatchReminder(string $adviceId): void { $advice = $this->loadAdvice(adviceId: $adviceId); @@ -173,6 +175,7 @@ public function dispatchReminder(string $adviceId): void * * @return array> Pending advice records */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function applyWorkflowGuard(string $caseId): array { $all = $this->getAdviceForCase(caseId: $caseId); @@ -197,6 +200,7 @@ public function applyWorkflowGuard(string $caseId): array * * @return array> Advice records for the case */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getAdviceForCase(string $caseId): array { $objectService = $this->settingsService->getObjectService(); @@ -239,6 +243,7 @@ public function getAdviceForCase(string $caseId): array * * @return array> Open advice records */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getOpenAdvice(): array { $objectService = $this->settingsService->getObjectService(); @@ -286,6 +291,7 @@ public function getOpenAdvice(): array * * @return array Updated advice record */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function expireAdvice(string $adviceId): array { try { diff --git a/lib/Service/AiService.php b/lib/Service/AiService.php index 6113a3e6..577c0cd3 100644 --- a/lib/Service/AiService.php +++ b/lib/Service/AiService.php @@ -81,6 +81,7 @@ public function __construct( * * @return bool */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function isEnabled(): bool { return $this->appConfig->getValueString( @@ -97,6 +98,7 @@ public function isEnabled(): bool * * @return bool */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function isFeatureEnabled(string $feature): bool { if ($this->isEnabled() === false) { @@ -122,6 +124,7 @@ public function isFeatureEnabled(string $feature): bool * * @return array Classification result with suggestion and confidence */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function classifyDocument(string $caseId, string $documentId, string $userId): array { if ($this->isFeatureEnabled(feature: 'classification') === false) { @@ -182,6 +185,7 @@ public function classifyDocument(string $caseId, string $documentId, string $use * * @return array Extraction result with field suggestions and confidence */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function extractData(string $caseId, ?string $documentId, string $userId): array { if ($this->isFeatureEnabled(feature: 'extraction') === false) { @@ -242,6 +246,7 @@ public function extractData(string $caseId, ?string $documentId, string $userId) * * @return array Answer with source citations */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function askQuestion(string $caseId, string $question, string $userId): array { if ($this->isFeatureEnabled(feature: 'qa') === false) { @@ -302,6 +307,7 @@ public function askQuestion(string $caseId, string $question, string $userId): a * * @return array Summary text */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function summarize(string $caseId, string $type, ?string $documentId, string $userId): array { if ($this->isFeatureEnabled(feature: 'summary') === false) { @@ -360,6 +366,7 @@ public function summarize(string $caseId, string $type, ?string $documentId, str * * @return array Routing suggestion with recommended case worker */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function suggestRouting(string $caseId, string $userId): array { if ($this->isFeatureEnabled(feature: 'routing') === false) { @@ -417,6 +424,7 @@ public function suggestRouting(string $caseId, string $userId): array * * @return array Next-step suggestions */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function suggestNextStep(string $caseId, string $userId): array { if ($this->isFeatureEnabled(feature: 'decision_support') === false) { @@ -480,6 +488,7 @@ public function suggestNextStep(string $caseId, string $userId): array * * @SuppressWarnings(PHPMD.ExcessiveParameterList) — audit entries need full context */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recordUserAction( string $caseId, string $type, @@ -512,6 +521,7 @@ public function recordUserAction( * * @return array Health check result */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function testHealth(): array { $startTime = microtime(true); @@ -543,6 +553,7 @@ public function testHealth(): array * * @return array AI settings (without sensitive data like API keys) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getAiSettings(): array { return [ diff --git a/lib/Service/AppointmentBackend/AppointmentBackendInterface.php b/lib/Service/AppointmentBackend/AppointmentBackendInterface.php index bb3b1539..f95ae158 100644 --- a/lib/Service/AppointmentBackend/AppointmentBackendInterface.php +++ b/lib/Service/AppointmentBackend/AppointmentBackendInterface.php @@ -49,6 +49,7 @@ public function getTimeslots(string $productId, string $locationId, string $date * * @return array Booking result with externalId */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function bookAppointment(array $data): array; /** @@ -58,6 +59,7 @@ public function bookAppointment(array $data): array; * * @return bool True if cancellation succeeded */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancelAppointment(string $externalId): bool; /** @@ -68,5 +70,6 @@ public function cancelAppointment(string $externalId): bool; * * @return array Updated booking result */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rescheduleAppointment(string $externalId, string $newDateTime): array; }//end interface diff --git a/lib/Service/AppointmentBackend/JccBackend.php b/lib/Service/AppointmentBackend/JccBackend.php index 0be8d1bf..1fadba45 100644 --- a/lib/Service/AppointmentBackend/JccBackend.php +++ b/lib/Service/AppointmentBackend/JccBackend.php @@ -60,6 +60,7 @@ public function __construct( * * @return array> List of available timeslots. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTimeslots(string $productId, string $locationId, string $date): array { try { @@ -90,6 +91,7 @@ public function getTimeslots(string $productId, string $locationId, string $date * * @return array Booking result from JCC, or error payload. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function bookAppointment(array $data): array { try { @@ -116,6 +118,7 @@ public function bookAppointment(array $data): array * * @return bool True on success, false on API error. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancelAppointment(string $externalId): bool { try { @@ -139,6 +142,7 @@ public function cancelAppointment(string $externalId): bool * * @return array The new booking result. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rescheduleAppointment(string $externalId, string $newDateTime): array { $this->cancelAppointment(externalId: $externalId); diff --git a/lib/Service/AppointmentBackend/LocalBackend.php b/lib/Service/AppointmentBackend/LocalBackend.php index cc2ab30f..8b52e125 100644 --- a/lib/Service/AppointmentBackend/LocalBackend.php +++ b/lib/Service/AppointmentBackend/LocalBackend.php @@ -69,6 +69,7 @@ public function __construct( * * @return array> List of generated timeslots. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTimeslots(string $productId, string $locationId, string $date): array { $slots = []; @@ -93,6 +94,7 @@ public function getTimeslots(string $productId, string $locationId, string $date * * @return array Local booking result with generated externalId. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function bookAppointment(array $data): array { return ['externalId' => 'local-'.bin2hex(random_bytes(8))]; @@ -105,6 +107,7 @@ public function bookAppointment(array $data): array * * @return bool Always true. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancelAppointment(string $externalId): bool { $this->logger->info('Local backend: appointment cancelled', ['externalId' => $externalId]); @@ -119,6 +122,7 @@ public function cancelAppointment(string $externalId): bool * * @return array Updated booking result. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rescheduleAppointment(string $externalId, string $newDateTime): array { return ['externalId' => $externalId]; diff --git a/lib/Service/AppointmentBackend/QmaticBackend.php b/lib/Service/AppointmentBackend/QmaticBackend.php index 1caf1272..a3a6d001 100644 --- a/lib/Service/AppointmentBackend/QmaticBackend.php +++ b/lib/Service/AppointmentBackend/QmaticBackend.php @@ -56,6 +56,7 @@ public function __construct( * * @return array> List of available timeslots. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTimeslots(string $productId, string $locationId, string $date): array { try { @@ -89,6 +90,7 @@ public function getTimeslots(string $productId, string $locationId, string $date * * @return array Booking result from Qmatic, or error payload. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function bookAppointment(array $data): array { try { @@ -115,6 +117,7 @@ public function bookAppointment(array $data): array * * @return bool True on success, false on API error. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancelAppointment(string $externalId): bool { try { @@ -138,6 +141,7 @@ public function cancelAppointment(string $externalId): bool * * @return array The new booking result. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rescheduleAppointment(string $externalId, string $newDateTime): array { $this->cancelAppointment(externalId: $externalId); diff --git a/lib/Service/AppointmentService.php b/lib/Service/AppointmentService.php index 60051039..d5c0c669 100644 --- a/lib/Service/AppointmentService.php +++ b/lib/Service/AppointmentService.php @@ -82,6 +82,7 @@ public function getTimeslots(string $productId, string $locationId, string $date * * @return array The stored appointment record or an error payload. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function bookAppointment(string $caseId, array $data): array { $objectService = $this->getObjectService(); @@ -131,6 +132,7 @@ public function bookAppointment(string $caseId, array $data): array * * @return array The updated appointment record. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cancelAppointment(string $appointmentId): array { $objectService = $this->getObjectService(); @@ -162,6 +164,7 @@ public function cancelAppointment(string $appointmentId): array * * @return array The updated appointment record. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function markNoShow(string $appointmentId): array { $objectService = $this->getObjectService(); @@ -187,6 +190,7 @@ public function markNoShow(string $appointmentId): array * * @return array List of appointments for the case. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getAppointmentsForCase(string $caseId): array { $objectService = $this->getObjectService(); @@ -209,6 +213,7 @@ public function getAppointmentsForCase(string $caseId): array * * @return array|null The appointment data, or null if not found. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getAppointmentByToken(string $token): ?array { $objectService = $this->getObjectService(); diff --git a/lib/Service/BerichtenboxAdapter/BerichtenboxAdapterInterface.php b/lib/Service/BerichtenboxAdapter/BerichtenboxAdapterInterface.php index 976d831a..548e21f1 100644 --- a/lib/Service/BerichtenboxAdapter/BerichtenboxAdapterInterface.php +++ b/lib/Service/BerichtenboxAdapter/BerichtenboxAdapterInterface.php @@ -39,6 +39,7 @@ interface BerichtenboxAdapterInterface * * @return array Result with messageId, status */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function sendMessage( string $bsn, string $subject, diff --git a/lib/Service/BerichtenboxAdapter/MockAdapter.php b/lib/Service/BerichtenboxAdapter/MockAdapter.php index 896fdc3c..4b1a541f 100644 --- a/lib/Service/BerichtenboxAdapter/MockAdapter.php +++ b/lib/Service/BerichtenboxAdapter/MockAdapter.php @@ -54,6 +54,7 @@ public function __construct( * * @return array Mock send result with a generated messageId. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function sendMessage( string $bsn, string $subject, @@ -88,6 +89,7 @@ public function sendMessage( * * @return array Mock read status (always read 1h ago). */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getReadStatus(string $messageId): array { // Simulate: messages are "read" after they've existed for a while. diff --git a/lib/Service/BerichtenboxService.php b/lib/Service/BerichtenboxService.php index 2c518e0b..c7af5e5b 100644 --- a/lib/Service/BerichtenboxService.php +++ b/lib/Service/BerichtenboxService.php @@ -70,6 +70,7 @@ public function __construct( * * @return array The stored message record or an error payload. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function sendMessage( string $caseId, string $bsn, @@ -141,6 +142,7 @@ public function sendMessage( * * @return array List of stored Berichtenbox messages for the case. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getMessagesForCase(string $caseId): array { $objectService = $this->getObjectService(); @@ -163,6 +165,7 @@ public function getMessagesForCase(string $caseId): array * * @return array The message record, possibly updated with read status. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function pollReadStatus(string $messageId): array { $objectService = $this->getObjectService(); @@ -213,6 +216,7 @@ public function pollReadStatus(string $messageId): array * * @return bool True when the BSN is a 9-digit number passing the 11-proef. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validateBsn(string $bsn): bool { if (preg_match('/^\d{9}$/', $bsn) !== 1) { diff --git a/lib/Service/Bezwaar/AdvisoryCommitteeService.php b/lib/Service/Bezwaar/AdvisoryCommitteeService.php index 5eba0ef2..5e723039 100644 --- a/lib/Service/Bezwaar/AdvisoryCommitteeService.php +++ b/lib/Service/Bezwaar/AdvisoryCommitteeService.php @@ -125,6 +125,7 @@ public function __construct( * * @throws RuntimeException When OpenRegister unavailable or refs invalid */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function assignToCommittee( string $bezwaarId, string $commissieId, @@ -221,6 +222,7 @@ public function assignToCommittee( * @throws RuntimeException When the transition is forbidden * @throws GuardFailedException When the independence check fails */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function transitionAdviceStatus( string $requestId, string $newStatus, @@ -370,6 +372,7 @@ public function transitionAdviceStatus( * null when no default committee * is configured. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function autoAssignDefaultCommittee(string $bezwaarId): ?array { $defaultId = $this->settingsService->getConfigValue( @@ -408,6 +411,7 @@ public function autoAssignDefaultCommittee(string $bezwaarId): ?array * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recordCouncilDeviation( string $requestId, string $besluitId, diff --git a/lib/Service/Bezwaar/BeroepService.php b/lib/Service/Bezwaar/BeroepService.php index 27a82a00..2830657f 100644 --- a/lib/Service/Bezwaar/BeroepService.php +++ b/lib/Service/Bezwaar/BeroepService.php @@ -158,6 +158,7 @@ public function __construct( * are unconfigured, or the contested decision * cannot be loaded. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function register( string $caseId, string $sourceBezwaarId, @@ -244,6 +245,7 @@ public function register( * @throws RuntimeException When OpenRegister is unavailable or the * beroep cannot be loaded. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function addFileInspectionRequest( string $beroepId, string $requestedAt, @@ -320,6 +322,7 @@ public function addFileInspectionRequest( * is unavailable, or the beroep cannot be * loaded. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recordJudgment( string $beroepId, string $outcome, @@ -395,6 +398,7 @@ public function recordJudgment( * is unavailable, or the beroep cannot be * loaded. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function executeCascade(string $beroepId, string $action): array { if (in_array($action, self::VALID_CASCADES, true) === false) { diff --git a/lib/Service/Bezwaar/DecisionService.php b/lib/Service/Bezwaar/DecisionService.php index 87ab379d..7356ee61 100644 --- a/lib/Service/Bezwaar/DecisionService.php +++ b/lib/Service/Bezwaar/DecisionService.php @@ -164,6 +164,7 @@ public function __construct( * are unconfigured, or the payload is * invalid at draft time. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function draft(string $bezwaarId, array $payload): array { $objectService = $this->settingsService->getObjectService(); @@ -246,6 +247,7 @@ public function draft(string $bezwaarId, array $payload): array * @throws RuntimeException When validation fails or persistence * errors occur. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publish(string $decisionId): array { $objectService = $this->settingsService->getObjectService(); @@ -328,6 +330,7 @@ public function publish(string $decisionId): array * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function applyToBezwaar(string $bezwaarId, string $decisionId): void { $objectService = $this->settingsService->getObjectService(); diff --git a/lib/Service/Bezwaar/HearingService.php b/lib/Service/Bezwaar/HearingService.php index 04b85435..988ce5d1 100644 --- a/lib/Service/Bezwaar/HearingService.php +++ b/lib/Service/Bezwaar/HearingService.php @@ -122,6 +122,7 @@ public function __construct( * inspection-of-file floor is violated, or * schemas are unconfigured. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function schedule( string $caseId, string $scheduledDate, @@ -228,6 +229,7 @@ public function schedule( * * @throws RuntimeException When the reason is empty or persistence fails. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function waive( string $caseId, string $reason, @@ -306,6 +308,7 @@ public function waive( * * @throws RuntimeException When the session is not found, persistence fails, or a late correction lacks a reason. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recordAttendance( string $sessionId, array $entries @@ -411,6 +414,7 @@ public function recordAttendance( * @throws RuntimeException When verslag is missing, audio consent is * denied, or persistence fails. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function addMinutes( string $sessionId, array $payload @@ -544,6 +548,7 @@ public function addMinutes( * * @return array|null Created hearing session, or null when one already exists / infra unavailable. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function seedDefaultHearing(string $bezwaarId): ?array { $objectService = $this->settingsService->getObjectService(); diff --git a/lib/Service/CaseDefinitionExportService.php b/lib/Service/CaseDefinitionExportService.php index 0ef85f70..a3c3c9c8 100644 --- a/lib/Service/CaseDefinitionExportService.php +++ b/lib/Service/CaseDefinitionExportService.php @@ -85,6 +85,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function exportCaseDefinition( string $caseTypeId, array $components=[], diff --git a/lib/Service/CaseDefinitionImportService.php b/lib/Service/CaseDefinitionImportService.php index 1bac9a1e..2c1a9fe2 100644 --- a/lib/Service/CaseDefinitionImportService.php +++ b/lib/Service/CaseDefinitionImportService.php @@ -83,6 +83,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validatePackage(string $zipPath): array { $result = [ @@ -227,6 +228,7 @@ public function validatePackage(string $zipPath): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function importCaseDefinition( string $zipPath, string $strategy='skip', diff --git a/lib/Service/CaseEmailService.php b/lib/Service/CaseEmailService.php index 40d7b592..e3b33d08 100644 --- a/lib/Service/CaseEmailService.php +++ b/lib/Service/CaseEmailService.php @@ -73,6 +73,7 @@ public function __construct( * * @throws \RuntimeException If sending fails */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function sendEmail( string $caseId, string $to, @@ -142,6 +143,7 @@ public function sendEmail( * * @throws \RuntimeException If template not found or sending fails */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function sendFromTemplate( string $caseId, string $templateId, @@ -172,6 +174,7 @@ public function sendFromTemplate( * * @return string The resolved string */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolveVariables(string $template, array $data): string { return preg_replace_callback( @@ -197,6 +200,7 @@ static function (array $matches) use ($data): string { * * @return array List of unresolved variable names */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function findUnresolvedVariables(string $template, array $data): array { $unresolved = []; @@ -218,6 +222,7 @@ public function findUnresolvedVariables(string $template, array $data): array * * @return string|null The extracted case identifier or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function extractCaseNumber(string $subject): ?string { if (preg_match(self::CASE_NUMBER_PATTERN, $subject, $matches) === 1) { @@ -238,6 +243,7 @@ public function extractCaseNumber(string $subject): ?string * * @return array Processing result */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function processInbound( string $from, string $to, @@ -290,6 +296,7 @@ public function processInbound( * * @return array> List of templates */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTemplatesForCaseType(string $caseTypeId): array { $objectService = $this->settingsService->getObjectService(); diff --git a/lib/Service/CaseSharingService.php b/lib/Service/CaseSharingService.php index 4c487108..084402e9 100644 --- a/lib/Service/CaseSharingService.php +++ b/lib/Service/CaseSharingService.php @@ -85,6 +85,7 @@ public function __construct( * * @return string The generated token (32 hex characters) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function generateToken(): string { return bin2hex(random_bytes(16)); @@ -105,6 +106,7 @@ public function generateToken(): string * * @SuppressWarnings(PHPMD.ExcessiveParameterList) — all params needed for share creation */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createTokenShare( string $caseId, string $permissionLevel, @@ -172,6 +174,7 @@ public function createTokenShare( * * @return array The created share data */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createPartnerShare( string $caseId, string $partnerId, @@ -223,6 +226,7 @@ public function createPartnerShare( * * @return array The updated share data */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function revokeShare(string $shareId, string $revokedBy): array { $objectService = $this->getObjectService(); @@ -270,6 +274,7 @@ public function revokeShare(string $shareId, string $revokedBy): array * * @return array Validation result with 'valid' boolean and share data or error */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validateToken(string $token, ?string $password=null): array { $objectService = $this->getObjectService(); @@ -357,6 +362,7 @@ public function validateToken(string $token, ?string $password=null): array * * @return array The filtered case data safe for external viewing */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getFilteredCaseData(array $shareData, array $caseData): array { $fieldExclusions = json_decode(($shareData['fieldExclusions'] ?? '[]'), true); @@ -384,6 +390,7 @@ public function getFilteredCaseData(array $shareData, array $caseData): array * * @return string The masked BSN (e.g., "***99*653") */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function maskBsn(string $bsn): string { $length = strlen($bsn); @@ -490,6 +497,7 @@ private function getObjectService(): ?\OCA\OpenRegister\Service\ObjectService * * @return array Document descriptor */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function storeExternalDocument(string $caseId, string $shareId, array $uploadedFile): array { $this->logger->info( diff --git a/lib/Service/CaseTransferService.php b/lib/Service/CaseTransferService.php index 34ab57ac..08d99a9d 100644 --- a/lib/Service/CaseTransferService.php +++ b/lib/Service/CaseTransferService.php @@ -67,6 +67,7 @@ public function __construct( * * @return array The created transfer request data */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function initiateTransfer( string $caseId, string $sourceOrganization, @@ -116,6 +117,7 @@ public function initiateTransfer( * * @return array The updated transfer data */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function acceptTransfer(string $transferId): array { $objectService = $this->getObjectService(); @@ -165,6 +167,7 @@ public function acceptTransfer(string $transferId): array * * @return array The updated transfer data */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rejectTransfer(string $transferId, string $rejectionReason): array { $objectService = $this->getObjectService(); diff --git a/lib/Service/ChecklistService.php b/lib/Service/ChecklistService.php index dd30f922..a1544c62 100644 --- a/lib/Service/ChecklistService.php +++ b/lib/Service/ChecklistService.php @@ -90,6 +90,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function completeItem( array $checklist, string $itemId, @@ -148,6 +149,7 @@ public function completeItem( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getProgress(array $checklist): array { $items = $checklist['items'] ?? []; @@ -182,6 +184,7 @@ public function getProgress(array $checklist): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validateCompletion(array $checklist): array { $items = $checklist['items'] ?? []; @@ -208,6 +211,7 @@ public function validateCompletion(array $checklist): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getConformitySummary(array $checklist): array { $items = $checklist['items'] ?? []; diff --git a/lib/Service/ConsultationService.php b/lib/Service/ConsultationService.php index c2377236..24bfa7e0 100644 --- a/lib/Service/ConsultationService.php +++ b/lib/Service/ConsultationService.php @@ -81,6 +81,7 @@ public function __construct( * * @throws \RuntimeException If OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createConsultation(array $data): array { $objectService = $this->settingsService->getObjectService(); @@ -129,6 +130,7 @@ public function createConsultation(array $data): array * * @return array> List of consultations */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getConsultationsForCase(string $caseId): array { $objectService = $this->settingsService->getObjectService(); @@ -168,6 +170,7 @@ public function getConsultationsForCase(string $caseId): array * * @throws \RuntimeException If invalid status or OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function updateStatus(string $consultationId, string $newStatus): array { if (in_array($newStatus, self::VALID_STATUSES, true) === false) { @@ -210,6 +213,7 @@ public function updateStatus(string $consultationId, string $newStatus): array * * @throws \RuntimeException If invalid response or OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function submitResponse(string $consultationId, array $response): array { $advies = $response['advies'] ?? ''; @@ -258,6 +262,7 @@ public function submitResponse(string $consultationId, array $response): array * * @return array> List of overdue consultations */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getOverdueConsultations(): array { $objectService = $this->settingsService->getObjectService(); diff --git a/lib/Service/DsoIntakeService.php b/lib/Service/DsoIntakeService.php index 83bdf322..12eeb914 100644 --- a/lib/Service/DsoIntakeService.php +++ b/lib/Service/DsoIntakeService.php @@ -68,6 +68,7 @@ public function __construct( * * @throws \RuntimeException If OpenRegister is unavailable or configuration missing */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function processAanvraag(array $dsoMessage): array { $objectService = $this->settingsService->getObjectService(); diff --git a/lib/Service/GisProxyService.php b/lib/Service/GisProxyService.php index d168d5c5..4e325c70 100644 --- a/lib/Service/GisProxyService.php +++ b/lib/Service/GisProxyService.php @@ -83,6 +83,7 @@ public function __construct( * * @throws \RuntimeException If URL is not allowed or rate limit exceeded */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function proxyRequest(string $url, array $query, string $type): array { // Validate URL against allowlist. @@ -157,6 +158,7 @@ public function proxyRequest(string $url, array $query, string $type): array * * @return array Parsed capabilities with layers list */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getCapabilities(string $url, string $type): array { $service = 'WMS'; diff --git a/lib/Service/Inspection/ChecklistService.php b/lib/Service/Inspection/ChecklistService.php index 712d7c62..5f137031 100644 --- a/lib/Service/Inspection/ChecklistService.php +++ b/lib/Service/Inspection/ChecklistService.php @@ -125,6 +125,7 @@ public function __construct( * @throws RuntimeException When configuration is missing, the template * cannot be loaded, or persistence fails. */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function createRun(string $templateId, string $caseId, ?string $inspectionId=null): array { [$objectService, $register] = $this->bootstrap(); @@ -188,6 +189,7 @@ public function createRun(string $templateId, string $caseId, ?string $inspectio * * @SuppressWarnings(PHPMD.CyclomaticComplexity) — branches cover validation + follow-up dispatch */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function submitRun(string $runId, array $payload): array { [$objectService, $register] = $this->bootstrap(); @@ -270,6 +272,7 @@ public function submitRun(string $runId, array $payload): array * * @return string conform | niet_conform | deels_conform */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function aggregateResult(array $responses, array $snapshot): string { $itemsByOrder = $this->indexItemsBySnapshot(snapshot: $snapshot); @@ -315,6 +318,7 @@ public function aggregateResult(array $responses, array $snapshot): string * * @SuppressWarnings(PHPMD.CyclomaticComplexity) — branches cover all response types */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function validateResponse(array $item, array $payload): void { $type = (string) ($item['responseType'] ?? ''); @@ -394,6 +398,7 @@ public function validateResponse(array $item, array $payload): void * * @return array> Tasks created */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function dispatchFollowUps(array $run): array { $responses = $run['responses'] ?? []; @@ -537,6 +542,7 @@ private function createHandhavingsactie( * * @throws RuntimeException With message "Checklist run is append-only". */ + /** @spec openspec/specs/inspection-checklists/spec.md */ public function assertRunMutable(array $run): void { $status = (string) ($run['status'] ?? ''); diff --git a/lib/Service/InspectionService.php b/lib/Service/InspectionService.php index 0b918d55..f948f92a 100644 --- a/lib/Service/InspectionService.php +++ b/lib/Service/InspectionService.php @@ -84,6 +84,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getInspections( string $inspectorId, ?string $date, @@ -134,6 +135,7 @@ function (array $a, array $b): int { * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function captureLocation( array $inspection, float $latitude, @@ -193,6 +195,7 @@ public function captureLocation( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function addPhoto(array $inspection, array $photoMetadata): array { $photo = [ @@ -222,6 +225,7 @@ public function addPhoto(array $inspection, array $photoMetadata): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function completeInspection(array $inspection, string $conclusion=''): array { $checklist = $inspection['checklist'] ?? []; diff --git a/lib/Service/LegesCalculationService.php b/lib/Service/LegesCalculationService.php index 201686ed..dec75ebd 100644 --- a/lib/Service/LegesCalculationService.php +++ b/lib/Service/LegesCalculationService.php @@ -97,6 +97,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function calculate( array $caseData, array $verordening, @@ -150,6 +151,7 @@ public function calculate( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function recalculate( array $caseData, array $verordening, @@ -180,6 +182,7 @@ public function recalculate( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function calculateVerrekening(float $currentAmount, float $previousAmount): array { $netAmount = round($currentAmount - $previousAmount, self::PRECISION); @@ -203,6 +206,7 @@ public function calculateVerrekening(float $currentAmount, float $previousAmount * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function calculateTeruggaaf( float $imposedAmount, float $refundFraction=1.0, diff --git a/lib/Service/LegesExportService.php b/lib/Service/LegesExportService.php index 8ec66e46..863bf8b0 100644 --- a/lib/Service/LegesExportService.php +++ b/lib/Service/LegesExportService.php @@ -101,6 +101,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function export(array $berekeningen, string $format=self::FORMAT_CSV): array { if (in_array($format, self::SUPPORTED_FORMATS, true) === false) { diff --git a/lib/Service/LocationService.php b/lib/Service/LocationService.php index 23e2a8be..9a2b5293 100644 --- a/lib/Service/LocationService.php +++ b/lib/Service/LocationService.php @@ -106,6 +106,7 @@ public function __construct( * * @return array Error codes (empty = valid) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validate(array $payload): array { $errors = []; @@ -207,6 +208,7 @@ public function validate(array $payload): array * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function reverseGeocode(float $latitude, float $longitude): ?array { // Sanity check on the coordinate envelope before we burn an HTTP call. @@ -356,6 +358,7 @@ private function extractDocs(array $response): array * * @throws \RuntimeException When validation fails or OpenRegister is missing */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function attachToCase(string $caseId, array $location): ?array { if ($caseId === '') { @@ -420,6 +423,7 @@ public function attachToCase(string $caseId, array $location): ?array * * @return array> Location records (possibly empty) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function listForCase(string $caseId): array { $objectService = $this->settingsService->getObjectService(); diff --git a/lib/Service/MilestoneService.php b/lib/Service/MilestoneService.php index 4976240e..c367cd24 100644 --- a/lib/Service/MilestoneService.php +++ b/lib/Service/MilestoneService.php @@ -56,6 +56,7 @@ public function __construct( * * @throws \RuntimeException If OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getMilestones(string $caseTypeId): array { $objectService = $this->settingsService->getObjectService(); @@ -93,6 +94,7 @@ public function getMilestones(string $caseTypeId): array * * @return array Progress data with milestones, reached count, total, percentage */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getCaseProgress(string $caseId, string $caseTypeId): array { $definitions = $this->getMilestones(caseTypeId: $caseTypeId); @@ -165,6 +167,7 @@ public function getCaseProgress(string $caseId, string $caseTypeId): array * * @throws \RuntimeException If OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function markMilestone( string $caseId, string $milestoneDefinitionId, @@ -217,6 +220,7 @@ public function markMilestone( * * @throws \RuntimeException If OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function reverseMilestone( string $caseId, string $milestoneDefinitionId, @@ -268,6 +272,7 @@ public function reverseMilestone( * * @return array Duration analytics per milestone pair */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getDurationAnalytics(string $caseTypeId): array { // Placeholder: in production, this would aggregate milestone records diff --git a/lib/Service/NotificatieService.php b/lib/Service/NotificatieService.php index 40010ba0..440cc8dc 100644 --- a/lib/Service/NotificatieService.php +++ b/lib/Service/NotificatieService.php @@ -94,6 +94,7 @@ private function loadOpenRegisterServices(): void * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publish( string $kanaal, string $hoofdObject, diff --git a/lib/Service/Parafering/AuditTrailService.php b/lib/Service/Parafering/AuditTrailService.php index 83d209c8..7e972ed8 100644 --- a/lib/Service/Parafering/AuditTrailService.php +++ b/lib/Service/Parafering/AuditTrailService.php @@ -98,6 +98,7 @@ public function __construct( * * @return array|null The persisted audit entry, or null when audit write failed (swallowed) */ + /** @spec openspec/specs/parafering-audit-trail/spec.md */ public function record( string $voorstelId, ?string $step, @@ -183,6 +184,7 @@ public function record( * * @throws OCSForbiddenException When append-only is violated */ + /** @spec openspec/specs/parafering-audit-trail/spec.md */ public function assertAppendOnly(array $entry, bool $isUpdate): void { if ($isUpdate === true) { @@ -221,6 +223,7 @@ public function assertAppendOnly(array $entry, bool $isUpdate): void * * @throws RuntimeException When configuration is missing */ + /** @spec openspec/specs/parafering-audit-trail/spec.md */ public function export(string $voorstelId, string $voorstelOnderwerp, string $exportedBy): array { $objectService = $this->settingsService->getObjectService(); @@ -295,6 +298,7 @@ static function (array $a, array $b): int { * * @return array */ + /** @spec openspec/specs/parafering-audit-trail/spec.md */ public function buildContentSnapshot(array $voorstel): array { $snapshot = []; diff --git a/lib/Service/ParaferingNotificationService.php b/lib/Service/ParaferingNotificationService.php index f6dcbcfa..2c9734c4 100644 --- a/lib/Service/ParaferingNotificationService.php +++ b/lib/Service/ParaferingNotificationService.php @@ -56,6 +56,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function notifyStepActivated( string $actorUserId, string $onderwerp, @@ -100,6 +101,7 @@ public function notifyStepActivated( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function notifyVoorstelReturned( string $stellerUserId, string $onderwerp, @@ -145,6 +147,7 @@ public function notifyVoorstelReturned( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function notifyParaferingReminder( string $actorUserId, string $onderwerp, diff --git a/lib/Service/ParaferingService.php b/lib/Service/ParaferingService.php index 970d4310..a4778e71 100644 --- a/lib/Service/ParaferingService.php +++ b/lib/Service/ParaferingService.php @@ -118,6 +118,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createVoorstel(array $voorstelData): array { $voorstel = [ @@ -155,6 +156,7 @@ public function createVoorstel(array $voorstelData): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function startParafering(array $voorstel, array $route): array { if ($voorstel['status'] !== self::STATUS_CONCEPT @@ -205,6 +207,7 @@ public function startParafering(array $voorstel, array $route): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function executeAction( array $voorstel, string $action, @@ -290,6 +293,7 @@ public function getAuditTrail(array $voorstel): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getCurrentStep(array $voorstel): ?array { if ($voorstel['status'] !== self::STATUS_IN_PARAFERING) { @@ -314,6 +318,7 @@ public function getCurrentStep(array $voorstel): ?array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function overrideRoute( array $voorstel, array $newRoute, diff --git a/lib/Service/Pdok/PdokBagService.php b/lib/Service/Pdok/PdokBagService.php index f3d22350..203771f6 100644 --- a/lib/Service/Pdok/PdokBagService.php +++ b/lib/Service/Pdok/PdokBagService.php @@ -93,6 +93,7 @@ public function __construct( * * @return array Normalised Procest-internal shape. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getNummeraanduiding(string $id): array { return $this->fetch( @@ -109,6 +110,7 @@ public function getNummeraanduiding(string $id): array * * @return array Normalised Procest-internal shape. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getVerblijfsobject(string $id): array { return $this->fetch( @@ -125,6 +127,7 @@ public function getVerblijfsobject(string $id): array * * @return array Normalised Procest-internal shape. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getPand(string $id): array { return $this->fetch( diff --git a/lib/Service/Pdok/PdokLocatieserverService.php b/lib/Service/Pdok/PdokLocatieserverService.php index f836f5f4..2442d760 100644 --- a/lib/Service/Pdok/PdokLocatieserverService.php +++ b/lib/Service/Pdok/PdokLocatieserverService.php @@ -124,6 +124,7 @@ public function __construct( * * @return array Decoded JSON response or `[]` while degraded. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function suggest(string $query, array $fq=[], int $rows=10): array { if ($this->isDegraded() === true) { @@ -154,6 +155,7 @@ public function suggest(string $query, array $fq=[], int $rows=10): array * * @return array Decoded JSON response. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function free(string $query, array $fq=[]): array { $params = ['q' => $query]; @@ -179,6 +181,7 @@ public function free(string $query, array $fq=[]): array * * @return array Decoded JSON response. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function lookup(string $id): array { return $this->call( @@ -200,6 +203,7 @@ public function lookup(string $id): array * * @return array Decoded JSON response. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function reverse(float $lat, float $lng): array { return $this->call( @@ -218,6 +222,7 @@ public function reverse(float $lat, float $lng): array * * @return string `ok` when healthy, `degraded` during the cool-down. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function health(): string { if ($this->isDegraded() === true) { diff --git a/lib/Service/RoleResolverService.php b/lib/Service/RoleResolverService.php index 55148242..d77f9d7f 100644 --- a/lib/Service/RoleResolverService.php +++ b/lib/Service/RoleResolverService.php @@ -106,6 +106,7 @@ public function __construct( * * @return array|null */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function normaliseRule(array $entry): ?array { $rule = $entry['routingRule'] ?? null; @@ -147,6 +148,7 @@ public function normaliseRule(array $entry): ?array * * @throws RoutingStrategyMissingException When the rule's strategy is unknown */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case): array { $strategyName = (string) ($rule['strategy'] ?? ''); @@ -215,6 +217,7 @@ public function resolve(array $rule, array $case): array * * @return bool */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function canExecute(array $rule, array $case, string $userId): bool { if ($userId === '') { @@ -242,6 +245,7 @@ public function canExecute(array $rule, array $case, string $userId): bool * * @return void */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function invalidateCache(string $caseId): void { if ($caseId === '') { @@ -433,6 +437,7 @@ private function toArray(mixed $value): array * * @throws RuntimeException */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function fail(string $message): never { throw new RuntimeException($message); diff --git a/lib/Service/Routing/RoutingStrategyInterface.php b/lib/Service/Routing/RoutingStrategyInterface.php index d00b9f69..78599e15 100644 --- a/lib/Service/Routing/RoutingStrategyInterface.php +++ b/lib/Service/Routing/RoutingStrategyInterface.php @@ -39,6 +39,7 @@ interface RoutingStrategyInterface * * @return string The strategy key (e.g. "single-role") */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function name(): string; /** @@ -56,5 +57,6 @@ public function name(): string; * * @return array Ordered participant references */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case, array $roles): array; }//end interface diff --git a/lib/Service/Routing/Strategy/HierarchicalStrategy.php b/lib/Service/Routing/Strategy/HierarchicalStrategy.php index 2f6c9d94..cac5d6c9 100644 --- a/lib/Service/Routing/Strategy/HierarchicalStrategy.php +++ b/lib/Service/Routing/Strategy/HierarchicalStrategy.php @@ -40,6 +40,7 @@ class HierarchicalStrategy implements RoutingStrategyInterface * * @return string The strategy name. */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function name(): string { return 'hierarchical'; @@ -56,6 +57,7 @@ public function name(): string * * @return array */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case, array $roles): array { $rawTypes = $rule['roleTypes'] ?? []; diff --git a/lib/Service/Routing/Strategy/LeastLoadedStrategy.php b/lib/Service/Routing/Strategy/LeastLoadedStrategy.php index 9302a465..ac154a37 100644 --- a/lib/Service/Routing/Strategy/LeastLoadedStrategy.php +++ b/lib/Service/Routing/Strategy/LeastLoadedStrategy.php @@ -41,6 +41,7 @@ class LeastLoadedStrategy implements RoutingStrategyInterface * * @return string The strategy name. */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function name(): string { return 'least-loaded'; @@ -59,6 +60,7 @@ public function name(): string * * @return array */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case, array $roles): array { $target = (string) ($rule['roleType'] ?? ''); diff --git a/lib/Service/Routing/Strategy/OrSetStrategy.php b/lib/Service/Routing/Strategy/OrSetStrategy.php index 0f5e02e4..8dfd63f0 100644 --- a/lib/Service/Routing/Strategy/OrSetStrategy.php +++ b/lib/Service/Routing/Strategy/OrSetStrategy.php @@ -40,6 +40,7 @@ class OrSetStrategy implements RoutingStrategyInterface * * @return string The strategy name. */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function name(): string { return 'or-set'; @@ -55,6 +56,7 @@ public function name(): string * * @return array */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case, array $roles): array { $rawTypes = $rule['roleTypes'] ?? []; diff --git a/lib/Service/Routing/Strategy/RoundRobinStrategy.php b/lib/Service/Routing/Strategy/RoundRobinStrategy.php index bacd59c4..6a866676 100644 --- a/lib/Service/Routing/Strategy/RoundRobinStrategy.php +++ b/lib/Service/Routing/Strategy/RoundRobinStrategy.php @@ -52,6 +52,7 @@ public function __construct(private readonly IAppConfig $appConfig) * * @return string The strategy name. */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function name(): string { return 'round-robin'; @@ -69,6 +70,7 @@ public function name(): string * * @return array */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case, array $roles): array { $target = (string) ($rule['roleType'] ?? ''); diff --git a/lib/Service/Routing/Strategy/SingleRoleStrategy.php b/lib/Service/Routing/Strategy/SingleRoleStrategy.php index ac20d934..eebb9497 100644 --- a/lib/Service/Routing/Strategy/SingleRoleStrategy.php +++ b/lib/Service/Routing/Strategy/SingleRoleStrategy.php @@ -40,6 +40,7 @@ class SingleRoleStrategy implements RoutingStrategyInterface * * @return string The strategy name. */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function name(): string { return 'single-role'; @@ -58,6 +59,7 @@ public function name(): string * * @return array */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function resolve(array $rule, array $case, array $roles): array { $target = (string) ($rule['roleType'] ?? ''); diff --git a/lib/Service/Routing/StrategyRegistry.php b/lib/Service/Routing/StrategyRegistry.php index 12524569..8ee1fe5e 100644 --- a/lib/Service/Routing/StrategyRegistry.php +++ b/lib/Service/Routing/StrategyRegistry.php @@ -82,6 +82,7 @@ public function __construct( * * @return void */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function register(RoutingStrategyInterface $strategy): void { $this->strategies[$strategy->name()] = $strategy; @@ -92,6 +93,7 @@ public function register(RoutingStrategyInterface $strategy): void * * @return array */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function list(): array { return array_keys($this->strategies); @@ -104,6 +106,7 @@ public function list(): array * * @return bool */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function has(string $name): bool { return isset($this->strategies[$name]); @@ -118,6 +121,7 @@ public function has(string $name): bool * * @throws RoutingStrategyMissingException When the strategy is not registered */ + /** @spec openspec/specs/role-based-step-routing/spec.md */ public function get(string $name): RoutingStrategyInterface { if (isset($this->strategies[$name]) === false) { diff --git a/lib/Service/SeedDataService.php b/lib/Service/SeedDataService.php index ada634d9..869746b5 100644 --- a/lib/Service/SeedDataService.php +++ b/lib/Service/SeedDataService.php @@ -60,6 +60,7 @@ public function __construct( * * @return array Result summary with counts of created and skipped objects */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function seedBezwaarBeroepData(): array { $seedPath = __DIR__.'/../Settings/bezwaar_seed_data.json'; diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index 106e4f75..a2a3c561 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -260,6 +260,7 @@ public function isOpenRegisterAvailable(): bool * @psalm-suppress MixedReturnStatement * @psalm-suppress MixedInferredReturnType */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getObjectService(): ?object { if ($this->isOpenRegisterAvailable() === false) { @@ -286,6 +287,7 @@ public function getObjectService(): ?object * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) — $force is a simple re-import toggle */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function loadConfiguration(bool $force=false): array { if ($this->isOpenRegisterAvailable() === false) { @@ -374,6 +376,7 @@ public function loadConfiguration(bool $force=false): array * * @return array */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getSettings(): array { $config = []; @@ -391,6 +394,7 @@ public function getSettings(): array * * @return array */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function updateSettings(array $data): array { foreach (self::CONFIG_KEYS as $key) { diff --git a/lib/Service/StatusTransitionService.php b/lib/Service/StatusTransitionService.php index 72dc3d38..229afb27 100644 --- a/lib/Service/StatusTransitionService.php +++ b/lib/Service/StatusTransitionService.php @@ -90,6 +90,7 @@ public function __construct( * * @return array{transitions: array>, current: array} */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function getAvailableTransitions(string $caseId, ?string $userId=null): array { $userId = $this->resolveUserId(explicit: $userId); @@ -160,6 +161,7 @@ public function getAvailableTransitions(string $caseId, ?string $userId=null): a * @throws GuardFailedException When server-side re-evaluation fails any guard * @throws RuntimeException When case/transition/template are not found */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function execute(string $caseId, string $transitionId, ?string $comment, ?string $userId=null): array { $userId = $this->resolveUserId(explicit: $userId); @@ -253,6 +255,7 @@ public function execute(string $caseId, string $transitionId, ?string $comment, * * @throws RuntimeException When the caller is not in the admin group or the target is invalid */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function executeFreeForm(string $caseId, string $toStatusId, ?string $comment, ?string $userId=null): array { $userId = $this->resolveUserId(explicit: $userId); @@ -292,6 +295,7 @@ public function executeFreeForm(string $caseId, string $toStatusId, ?string $com * * @return array{history: array>, replayable: bool} */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function replay(string $caseId): array { $objectService = $this->settingsService->getObjectService(); @@ -344,6 +348,7 @@ static function (array $left, array $right): int { * * @return bool */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function isAdmin(string $userId): bool { if ($userId === '') { diff --git a/lib/Service/StepConfigValidator.php b/lib/Service/StepConfigValidator.php index c8523c73..163759ac 100644 --- a/lib/Service/StepConfigValidator.php +++ b/lib/Service/StepConfigValidator.php @@ -121,6 +121,7 @@ final class StepConfigValidator * * @return array */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public static function validate( array $step, array $caseTypeSchema=[], diff --git a/lib/Service/StufFieldMappingService.php b/lib/Service/StufFieldMappingService.php index 5e5231b3..2501e63d 100644 --- a/lib/Service/StufFieldMappingService.php +++ b/lib/Service/StufFieldMappingService.php @@ -120,6 +120,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mapZknToInternal(array $stufData): array { $mappings = array_merge( @@ -139,6 +140,7 @@ public function mapZknToInternal(array $stufData): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mapInternalToZkn(array $internalData): array { $mappings = array_merge( @@ -158,6 +160,7 @@ public function mapInternalToZkn(array $internalData): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mapBgToInternal(array $stufData): array { $mappings = array_merge( @@ -177,6 +180,7 @@ public function mapBgToInternal(array $stufData): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mapInternalToBg(array $internalData): array { $mappings = array_merge( @@ -196,6 +200,7 @@ public function mapInternalToBg(array $internalData): array * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function stufDateToIso(string $stufDate): ?string { if (strlen($stufDate) === 8) { @@ -225,6 +230,7 @@ public function stufDateToIso(string $stufDate): ?string * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function isoToStufDate(string $isoDate): string { $dt = new \DateTimeImmutable($isoDate); @@ -240,6 +246,7 @@ public function isoToStufDate(string $isoDate): string * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function isoToStufDateTime(string $isoDateTime): string { $dt = new \DateTimeImmutable($isoDateTime); @@ -255,6 +262,7 @@ public function isoToStufDateTime(string $isoDateTime): string * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function confidentialityToInternal(string $stufValue): string { return self::CONFIDENTIALITY_MAP[strtoupper($stufValue)] ?? $stufValue; @@ -269,6 +277,7 @@ public function confidentialityToInternal(string $stufValue): string * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function confidentialityToStuf(string $internalValue): string { $flipped = array_flip(self::CONFIDENTIALITY_MAP); @@ -285,6 +294,7 @@ public function confidentialityToStuf(string $internalValue): string * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function addCustomMappings(string $type, array $mappings): void { $this->customMappings[$type] = array_merge( @@ -302,6 +312,7 @@ public function addCustomMappings(string $type, array $mappings): void * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getDefaultMappings(string $type): array { return match ($type) { diff --git a/lib/Service/StufMessageBuilder.php b/lib/Service/StufMessageBuilder.php index 5d3f264b..615fe87f 100644 --- a/lib/Service/StufMessageBuilder.php +++ b/lib/Service/StufMessageBuilder.php @@ -92,6 +92,7 @@ public function __construct( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildSoapEnvelope(string $bodyXml): string { $dom = new \DOMDocument('1.0', 'UTF-8'); @@ -136,6 +137,7 @@ public function buildSoapEnvelope(string $bodyXml): string * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildStuurgegevens( array $zender, array $ontvanger, @@ -172,6 +174,7 @@ public function buildStuurgegevens( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildBv01( array $zender, array $ontvanger, @@ -212,6 +215,7 @@ public function buildBv01( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildFo01( string $foutcode, string $foutbeschrijving, @@ -254,6 +258,7 @@ public function buildFo01( * * @psalm-suppress PossiblyUnusedMethod */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildSoapFault(string $faultString): string { $dom = new \DOMDocument('1.0', 'UTF-8'); diff --git a/lib/Service/TemplateLibraryService.php b/lib/Service/TemplateLibraryService.php index 8cf9260f..9757dd07 100644 --- a/lib/Service/TemplateLibraryService.php +++ b/lib/Service/TemplateLibraryService.php @@ -60,6 +60,7 @@ public function __construct( * * @return array> List of template metadata */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function listTemplates(): array { $templates = []; @@ -109,6 +110,7 @@ public function listTemplates(): array * * @return array|null The full template data or null if not found */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function loadTemplate(string $templateId): ?array { $dir = self::TEMPLATES_DIR; @@ -159,6 +161,7 @@ public function loadTemplate(string $templateId): ?array * * @throws \RuntimeException If template not found or OpenRegister unavailable */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function activateTemplate(string $templateId): array { $template = $this->loadTemplate(templateId: $templateId); diff --git a/lib/Service/TenantService.php b/lib/Service/TenantService.php index fee28e59..d3b6561c 100644 --- a/lib/Service/TenantService.php +++ b/lib/Service/TenantService.php @@ -86,6 +86,7 @@ public function __construct( * * @return array|null The tenant data or null when none found. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTenantForUser(string $userId): ?array { $orgs = $this->findOrganisationsByUserId(userId: $userId); @@ -119,6 +120,7 @@ public function getTenantForUser(string $userId): ?array * * @return array|null The tenant data, or null when not resolvable. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTenantByGroupId(string $groupId): ?array { $mapper = $this->getOrganisationMapper(); @@ -155,6 +157,7 @@ public function getTenantByGroupId(string $groupId): ?array * * @return array The provisioning result (the Organisation `jsonSerialize`d). */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function provisionTenant(string $tenantId): array { $mapper = $this->getOrganisationMapper(); @@ -198,6 +201,7 @@ public function provisionTenant(string $tenantId): array * * @return array Usage data with user count + OR quota fields. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getResourceUsage(string $tenantId): array { $mapper = $this->getOrganisationMapper(); @@ -236,6 +240,7 @@ public function getResourceUsage(string $tenantId): array * * @return bool True when the user is mapped to the tenant. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function isUserInTenant(string $userId, string $tenantId): bool { $tenant = $this->getTenantForUser(userId: $userId); @@ -269,6 +274,7 @@ public function isPlatformAdmin(string $userId): bool * @return string|null Status string (`active`, `suspended`, etc.) or null * when OR cannot resolve the Organisation. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getTenantStatus(string $tenantId): ?string { $mapper = $this->getOrganisationMapper(); diff --git a/lib/Service/Transitions/ActionHandlerInterface.php b/lib/Service/Transitions/ActionHandlerInterface.php index a2da95b1..7f44c3a4 100644 --- a/lib/Service/Transitions/ActionHandlerInterface.php +++ b/lib/Service/Transitions/ActionHandlerInterface.php @@ -43,5 +43,6 @@ interface ActionHandlerInterface * * @return ActionResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult; }//end interface diff --git a/lib/Service/Transitions/ActionHandlerRegistry.php b/lib/Service/Transitions/ActionHandlerRegistry.php index d6d8c77c..de63d1cf 100644 --- a/lib/Service/Transitions/ActionHandlerRegistry.php +++ b/lib/Service/Transitions/ActionHandlerRegistry.php @@ -79,6 +79,7 @@ public function __construct( * * @return void */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function registerHandler(string $type, ActionHandlerInterface $handler): void { $this->handlers[$type] = $handler; diff --git a/lib/Service/Transitions/ActionResult.php b/lib/Service/Transitions/ActionResult.php index 1c9cd9cf..47349278 100644 --- a/lib/Service/Transitions/ActionResult.php +++ b/lib/Service/Transitions/ActionResult.php @@ -54,6 +54,7 @@ public function __construct( * * @return self */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public static function success(array $data=[]): self { return new self(ok: true, error: null, data: $data); @@ -67,6 +68,7 @@ public static function success(array $data=[]): self * * @return self */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public static function failure(string $error, array $data=[]): self { return new self(ok: false, error: $error, data: $data); diff --git a/lib/Service/Transitions/ChecklistGuard.php b/lib/Service/Transitions/ChecklistGuard.php index 4382cd70..f0fb512e 100644 --- a/lib/Service/Transitions/ChecklistGuard.php +++ b/lib/Service/Transitions/ChecklistGuard.php @@ -60,6 +60,7 @@ public function __construct( * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function evaluate(array $guardConfig, array $case, string $userId): GuardResult { $taskId = (string) ($guardConfig['taskId'] ?? ''); diff --git a/lib/Service/Transitions/CreateSubCaseHandler.php b/lib/Service/Transitions/CreateSubCaseHandler.php index a0dd3fd8..63dfe962 100644 --- a/lib/Service/Transitions/CreateSubCaseHandler.php +++ b/lib/Service/Transitions/CreateSubCaseHandler.php @@ -56,6 +56,7 @@ public function __construct( * * @return ActionResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Transitions/CreateTaskHandler.php b/lib/Service/Transitions/CreateTaskHandler.php index 8689d74f..27a6bb8d 100644 --- a/lib/Service/Transitions/CreateTaskHandler.php +++ b/lib/Service/Transitions/CreateTaskHandler.php @@ -56,6 +56,7 @@ public function __construct( * * @return ActionResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Transitions/GuardEvaluatorInterface.php b/lib/Service/Transitions/GuardEvaluatorInterface.php index a5b1e86c..b22ca068 100644 --- a/lib/Service/Transitions/GuardEvaluatorInterface.php +++ b/lib/Service/Transitions/GuardEvaluatorInterface.php @@ -42,5 +42,6 @@ interface GuardEvaluatorInterface * * @return GuardResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function evaluate(array $guardConfig, array $case, string $userId): GuardResult; }//end interface diff --git a/lib/Service/Transitions/GuardRegistry.php b/lib/Service/Transitions/GuardRegistry.php index ca42cccf..f6ddc03d 100644 --- a/lib/Service/Transitions/GuardRegistry.php +++ b/lib/Service/Transitions/GuardRegistry.php @@ -76,6 +76,7 @@ public function __construct( * * @return void */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function registerEvaluator(string $type, GuardEvaluatorInterface $evaluator): void { $this->evaluators[$type] = $evaluator; @@ -90,6 +91,7 @@ public function registerEvaluator(string $type, GuardEvaluatorInterface $evaluat * * @return array}> */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function evaluateAll(array $guards, array $case, string $userId): array { $results = []; @@ -133,6 +135,7 @@ public function evaluateAll(array $guards, array $case, string $userId): array * * @return bool */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function allPassed(array $results): bool { foreach ($results as $result) { diff --git a/lib/Service/Transitions/GuardResult.php b/lib/Service/Transitions/GuardResult.php index 1f300ed4..e6bbc3af 100644 --- a/lib/Service/Transitions/GuardResult.php +++ b/lib/Service/Transitions/GuardResult.php @@ -54,6 +54,7 @@ public function __construct( * * @return self */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public static function pass(array $details=[]): self { return new self(passed: true, failureMessage: null, details: $details); @@ -67,6 +68,7 @@ public static function pass(array $details=[]): self * * @return self */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public static function fail(string $message, array $details=[]): self { return new self(passed: false, failureMessage: $message, details: $details); diff --git a/lib/Service/Transitions/NotifyHandler.php b/lib/Service/Transitions/NotifyHandler.php index 390cb582..8965d1c4 100644 --- a/lib/Service/Transitions/NotifyHandler.php +++ b/lib/Service/Transitions/NotifyHandler.php @@ -56,6 +56,7 @@ public function __construct( * * @return ActionResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Transitions/RequiredDocumentGuard.php b/lib/Service/Transitions/RequiredDocumentGuard.php index b56127f9..149290f0 100644 --- a/lib/Service/Transitions/RequiredDocumentGuard.php +++ b/lib/Service/Transitions/RequiredDocumentGuard.php @@ -45,6 +45,7 @@ class RequiredDocumentGuard implements GuardEvaluatorInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function evaluate(array $guardConfig, array $case, string $userId): GuardResult { $required = (string) ($guardConfig['documentType'] ?? ''); diff --git a/lib/Service/Transitions/RequiredFieldGuard.php b/lib/Service/Transitions/RequiredFieldGuard.php index d4fe03c2..91afa1dd 100644 --- a/lib/Service/Transitions/RequiredFieldGuard.php +++ b/lib/Service/Transitions/RequiredFieldGuard.php @@ -43,6 +43,7 @@ class RequiredFieldGuard implements GuardEvaluatorInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function evaluate(array $guardConfig, array $case, string $userId): GuardResult { $field = (string) ($guardConfig['field'] ?? ''); diff --git a/lib/Service/Transitions/RoleGuard.php b/lib/Service/Transitions/RoleGuard.php index 551750c0..f10d4f85 100644 --- a/lib/Service/Transitions/RoleGuard.php +++ b/lib/Service/Transitions/RoleGuard.php @@ -68,6 +68,7 @@ public function __construct( * * @return GuardResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function evaluate(array $guardConfig, array $case, string $userId): GuardResult { $allowed = $guardConfig['allowedRoles'] ?? []; diff --git a/lib/Service/Transitions/SendEmailHandler.php b/lib/Service/Transitions/SendEmailHandler.php index ae19dc37..ef030ea8 100644 --- a/lib/Service/Transitions/SendEmailHandler.php +++ b/lib/Service/Transitions/SendEmailHandler.php @@ -57,6 +57,7 @@ public function __construct( * * @return ActionResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Transitions/SetFieldHandler.php b/lib/Service/Transitions/SetFieldHandler.php index dfc7d0ed..ee7d1583 100644 --- a/lib/Service/Transitions/SetFieldHandler.php +++ b/lib/Service/Transitions/SetFieldHandler.php @@ -60,6 +60,7 @@ public function __construct( * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Transitions/SideEffectDispatcher.php b/lib/Service/Transitions/SideEffectDispatcher.php index 03add89c..0e818f32 100644 --- a/lib/Service/Transitions/SideEffectDispatcher.php +++ b/lib/Service/Transitions/SideEffectDispatcher.php @@ -58,6 +58,7 @@ public function __construct( * * @return array */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function dispatch(array $actions, array $case, array $transitionContext): array { $results = []; diff --git a/lib/Service/Transitions/WebhookHandler.php b/lib/Service/Transitions/WebhookHandler.php index 1afa13fa..68cd8c59 100644 --- a/lib/Service/Transitions/WebhookHandler.php +++ b/lib/Service/Transitions/WebhookHandler.php @@ -57,6 +57,7 @@ public function __construct( * * @return ActionResult */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function handle(array $actionConfig, array $case, array $transitionContext): ActionResult { try { diff --git a/lib/Service/Vth/LhsRecommendationService.php b/lib/Service/Vth/LhsRecommendationService.php index b22d8f84..cf7fdb75 100644 --- a/lib/Service/Vth/LhsRecommendationService.php +++ b/lib/Service/Vth/LhsRecommendationService.php @@ -108,6 +108,7 @@ public function __construct( * @throws RuntimeException When OpenRegister is unavailable, no matching * matrix exists, or no cell matches the triple. */ + /** @spec openspec/specs/enforcement-lhs/spec.md */ public function recommend( string $caseId, string $ernst, @@ -171,6 +172,7 @@ public function recommend( * * @throws RuntimeException When validation fails (HTTP-mapped by controller). */ + /** @spec openspec/specs/enforcement-lhs/spec.md */ public function override( array $recommendation, string $intervention, diff --git a/lib/Service/WmsWfsService.php b/lib/Service/WmsWfsService.php index 5426d0e6..72e1bb16 100644 --- a/lib/Service/WmsWfsService.php +++ b/lib/Service/WmsWfsService.php @@ -95,6 +95,7 @@ public function __construct( * * @return array> Plain array of layer dicts */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getLayersForCaseType(array|object $caseType): array { $caseTypeArr = $caseType; @@ -161,6 +162,7 @@ public function getLayersForCaseType(array|object $caseType): array * * @return array{ok: bool, errors: array} */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validateLayer(array $layer): array { $errors = []; @@ -225,6 +227,7 @@ public function validateLayer(array $layer): array * * @throws \RuntimeException When the request violates a guard rail */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function proxyRequest(array $layer, array $params): array { $type = strtoupper((string) ($layer['type'] ?? 'WMS')); @@ -316,6 +319,7 @@ public function proxyRequest(array $layer, array $params): array * * @return string Upstream URL (proxy POST path is /api/wms-wfs/proxy) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildGetMapUrl(array $layer, string $bbox, int $width, int $height): string { if ($width > self::MAX_TILE_DIMENSION) { @@ -361,6 +365,7 @@ public function buildGetMapUrl(array $layer, string $bbox, int $width, int $heig * * @throws \RuntimeException When BBOX is missing */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildGetFeatureUrl(array $layer, string $bbox): string { if ($bbox === '') { @@ -393,6 +398,7 @@ public function buildGetFeatureUrl(array $layer, string $bbox): string * * @return array|null The layer dict, or null when not found */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getLayerById(string $layerId): ?array { if ($layerId === '') { diff --git a/lib/Service/WorkflowDefinitionService.php b/lib/Service/WorkflowDefinitionService.php index 81b7c965..191c1ecc 100644 --- a/lib/Service/WorkflowDefinitionService.php +++ b/lib/Service/WorkflowDefinitionService.php @@ -109,6 +109,7 @@ public function __construct( * * @return array|null The definition or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getActiveDefinitionFor(string $caseTypeId): ?array { if ($caseTypeId === '') { @@ -154,6 +155,7 @@ public function getDefinition(string $id): ?array * * @return array|null The definition or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getDefinitionForCase(string $caseId): ?array { $objectService = $this->settingsService->getObjectService(); @@ -204,6 +206,7 @@ public function getDefinitionForCase(string $caseId): ?array * * @return array> The versions */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function listVersions(string $caseTypeId): array { return $this->listVersionsInternal(caseTypeId: $caseTypeId); @@ -222,6 +225,7 @@ public function listVersions(string $caseTypeId): array * * @return array|null Updated definition or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publish(string $id): ?array { $current = $this->loadDefinition(id: $id); @@ -335,6 +339,7 @@ public function publish(string $id): ?array * * @return array|null Updated definition or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function deprecate(string $id): ?array { $current = $this->loadDefinition(id: $id); @@ -402,6 +407,7 @@ public function deprecate(string $id): ?array * * @return array|null New draft definition or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function cloneDefinition(string $id): ?array { $source = $this->loadDefinition(id: $id); @@ -470,6 +476,7 @@ public function cloneDefinition(string $id): ?array * * @return array|null The created draft or null on failure */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createDraft(array $payload): ?array { $caseTypeId = (string) ($payload['caseType'] ?? ''); diff --git a/lib/Service/WorkflowTemplateLoader.php b/lib/Service/WorkflowTemplateLoader.php index 48d3bbd0..3380aa2c 100644 --- a/lib/Service/WorkflowTemplateLoader.php +++ b/lib/Service/WorkflowTemplateLoader.php @@ -64,6 +64,7 @@ public function __construct( * * @return array|null The template with `transitions` and `steps` decoded, or null when none active */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function getActiveTemplate(string $caseTypeId): ?array { if ($caseTypeId === '') { @@ -129,6 +130,7 @@ public function getActiveTemplate(string $caseTypeId): ?array * * @return array|null */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function getTransitionById(string $caseTypeId, string $transitionId): ?array { $template = $this->getActiveTemplate(caseTypeId: $caseTypeId); @@ -159,6 +161,7 @@ public function getTransitionById(string $caseTypeId, string $transitionId): ?ar * * @return void */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function clearCache(): void { $this->cache = []; diff --git a/lib/Service/ZgwBrcRulesService.php b/lib/Service/ZgwBrcRulesService.php index a1d547bd..e195bf0e 100644 --- a/lib/Service/ZgwBrcRulesService.php +++ b/lib/Service/ZgwBrcRulesService.php @@ -89,6 +89,7 @@ class ZgwBrcRulesService extends ZgwRulesBase * * @SuppressWarnings(PHPMD.CyclomaticComplexity) — ZGW business rules validation */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesBesluitenCreate(array $body): array { // Brc-001: Validate besluittype URL. @@ -146,6 +147,7 @@ public function rulesBesluitenCreate(array $body): array * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/besluiten/ */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesBesluitenUpdate(array $body, ?array $existingObject=null): array { $result = $this->isValid(body: $body); @@ -185,6 +187,7 @@ public function rulesBesluitenUpdate(array $body, ?array $existingObject=null): * * @see rulesBesluitenUpdate() Same immutability rules apply. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesBesluitenPatch(array $body, ?array $existingObject=null): array { $result = $this->isValid(body: $body); @@ -214,6 +217,7 @@ public function rulesBesluitenPatch(array $body, ?array $existingObject=null): a * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/besluiten/ */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesBesluitinformatieobjectenCreate(array $body): array { // Brc-003: Validate informatieobject URL. diff --git a/lib/Service/ZgwBusinessRulesService.php b/lib/Service/ZgwBusinessRulesService.php index 49963a55..ac36838d 100644 --- a/lib/Service/ZgwBusinessRulesService.php +++ b/lib/Service/ZgwBusinessRulesService.php @@ -80,6 +80,7 @@ public function __construct( * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.BooleanArgumentFlag) — ZGW scope flag from middleware */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validate( string $zgwApi, string $resource, diff --git a/lib/Service/ZgwDocumentService.php b/lib/Service/ZgwDocumentService.php index f46a7695..d952fa43 100644 --- a/lib/Service/ZgwDocumentService.php +++ b/lib/Service/ZgwDocumentService.php @@ -69,6 +69,7 @@ public function __construct( * * @return int The file size in bytes */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function storeBase64(string $uuid, string $fileName, string $content): int { $decoded = base64_decode(string: $content, strict: true); @@ -92,6 +93,7 @@ public function storeBase64(string $uuid, string $fileName, string $content): in * * @return int The file size in bytes */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function storeRaw(string $uuid, string $fileName, string $content): int { $folder = $this->getDocumentFolder(uuid: $uuid); @@ -111,6 +113,7 @@ public function storeRaw(string $uuid, string $fileName, string $content): int * * @throws NotFoundException If the file does not exist. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getContent(string $uuid, string $fileName): string { $folder = $this->getDocumentFolder(uuid: $uuid); @@ -130,6 +133,7 @@ public function getContent(string $uuid, string $fileName): string * * @return bool True if the file exists */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function fileExists(string $uuid, string $fileName): bool { try { @@ -148,6 +152,7 @@ public function fileExists(string $uuid, string $fileName): bool * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function deleteFiles(string $uuid): void { try { @@ -174,6 +179,7 @@ public function deleteFiles(string $uuid): void * * @throws NotFoundException If the file does not exist. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getMimeType(string $uuid, string $fileName): string { $folder = $this->getDocumentFolder(uuid: $uuid); @@ -194,6 +200,7 @@ public function getMimeType(string $uuid, string $fileName): string * * @return int The chunk size in bytes */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function storeChunk(string $uuid, int $volgnummer, string $content): int { $folder = $this->getDocumentFolder(uuid: $uuid); @@ -212,6 +219,7 @@ public function storeChunk(string $uuid, int $volgnummer, string $content): int * * @return array List of volgnummers that have been uploaded */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getUploadedChunks(string $uuid, int $totalParts): array { $folder = $this->getDocumentFolder(uuid: $uuid); @@ -243,6 +251,7 @@ public function getUploadedChunks(string $uuid, int $totalParts): array * * @throws InvalidArgumentException If not all chunks are present. */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mergeChunks(string $uuid, string $fileName, int $totalParts): int { $folder = $this->getDocumentFolder(uuid: $uuid); diff --git a/lib/Service/ZgwDrcRulesService.php b/lib/Service/ZgwDrcRulesService.php index f760ad57..6a1a77d8 100644 --- a/lib/Service/ZgwDrcRulesService.php +++ b/lib/Service/ZgwDrcRulesService.php @@ -87,6 +87,7 @@ class ZgwDrcRulesService extends ZgwRulesBase * @SuppressWarnings(PHPMD.CyclomaticComplexity) — ZGW business rules validation * @SuppressWarnings(PHPMD.NPathComplexity) — ZGW business rules validation */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesEnkelvoudiginformatieobjectenCreate(array $body): array { // Drc-001: Validate informatieobjecttype is published (not concept). @@ -156,6 +157,7 @@ public function rulesEnkelvoudiginformatieobjectenCreate(array $body): array * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/documenten/ */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesEnkelvoudiginformatieobjectenUpdate( array $body, ?array $existingObject=null @@ -181,6 +183,7 @@ public function rulesEnkelvoudiginformatieobjectenUpdate( * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/documenten/ */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesEnkelvoudiginformatieobjectenPatch( array $body, ?array $existingObject=null @@ -206,6 +209,7 @@ public function rulesEnkelvoudiginformatieobjectenPatch( * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/documenten/ */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesEnkelvoudiginformatieobjectenDestroy( array $body, ?array $existingObject=null @@ -263,6 +267,7 @@ public function rulesEnkelvoudiginformatieobjectenDestroy( * @SuppressWarnings(PHPMD.CyclomaticComplexity) — ZGW business rules validation * @SuppressWarnings(PHPMD.NPathComplexity) — ZGW business rules validation */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesObjectinformatieobjectenCreate(array $body): array { // Drc-002: Validate informatieobject URL. diff --git a/lib/Service/ZgwMappingService.php b/lib/Service/ZgwMappingService.php index 2ff27e7a..aff02390 100644 --- a/lib/Service/ZgwMappingService.php +++ b/lib/Service/ZgwMappingService.php @@ -99,6 +99,7 @@ public function __construct( * * @return array|null The mapping configuration or null if not found */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getMapping(string $resourceKey): ?array { $json = $this->appConfig->getValueString( @@ -127,6 +128,7 @@ public function getMapping(string $resourceKey): ?array * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function saveMapping(string $resourceKey, array $config): void { $json = json_encode($config, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); @@ -151,6 +153,7 @@ public function saveMapping(string $resourceKey, array $config): void * * @return array */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function listMappings(): array { $mappings = []; @@ -169,6 +172,7 @@ public function listMappings(): array * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function deleteMapping(string $resourceKey): void { $configKey = self::CONFIG_PREFIX.$resourceKey; @@ -212,6 +216,7 @@ public function hasMapping(string $resourceKey): bool * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resetToDefault(string $resourceKey, array $defaults): void { if (isset($defaults[$resourceKey]) === true) { diff --git a/lib/Service/ZgwPaginationHelper.php b/lib/Service/ZgwPaginationHelper.php index 5778ae61..10e55afa 100644 --- a/lib/Service/ZgwPaginationHelper.php +++ b/lib/Service/ZgwPaginationHelper.php @@ -48,6 +48,7 @@ class ZgwPaginationHelper * * @return array ZGW-formatted paginated response */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function wrapResults( array $mappedObjects, int $totalCount, diff --git a/lib/Service/ZgwRulesBase.php b/lib/Service/ZgwRulesBase.php index 2294b1bf..bd39dfc1 100644 --- a/lib/Service/ZgwRulesBase.php +++ b/lib/Service/ZgwRulesBase.php @@ -97,6 +97,7 @@ public function __construct( * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function setContext(?object $objectService, ?array $mappingConfig): void { $this->objectService = $objectService; @@ -110,6 +111,7 @@ public function setContext(?object $objectService, ?array $mappingConfig): void * * @return array{valid: bool, status: int, detail: string, enrichedBody: array} */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function isValid(array $body): array { return [ @@ -130,6 +132,7 @@ protected function isValid(array $body): array * * @return array{valid: bool, status: int, detail: string, invalidParams: array, enrichedBody: array} */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function error( int $status, string $detail, @@ -159,6 +162,7 @@ protected function error( * * @return array{name: string, code: string, reason: string} */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function fieldError(string $fieldName, string $code, string $reason): array { return [ @@ -175,6 +179,7 @@ protected function fieldError(string $fieldName, string $code, string $reason): * * @return array The validation error result */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function fieldImmutableError(string $fieldName): array { $detail = "Het veld {$fieldName} mag niet gewijzigd worden."; @@ -198,6 +203,7 @@ protected function fieldImmutableError(string $fieldName): array * * @return string|null The extracted UUID, or null */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function extractUuid(string $url): ?string { if (preg_match( @@ -227,6 +233,7 @@ protected function extractUuid(string $url): ?string * * @return bool True if valid */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function isValidUrl(string $url): bool { if (filter_var($url, FILTER_VALIDATE_URL) === false) { @@ -256,6 +263,7 @@ protected function isValidUrl(string $url): bool * * @return array|null Validation error, or null if valid */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function validateTypeUrl(string $typeUrl, string $fieldName, string $schemaKey): ?array { $extractedUuid = $this->extractUuid(url: $typeUrl); @@ -331,6 +339,7 @@ protected function validateTypeUrl(string $typeUrl, string $fieldName, string $s * * @return array|null Validation error, or null if valid */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function validateInformatieobjectUrl(string $ioUrl): ?array { if ($this->isValidUrl(url: $ioUrl) === false) { @@ -398,6 +407,7 @@ protected function validateInformatieobjectUrl(string $ioUrl): ?array * * @return array|null Validation error, or null if valid */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function validateExternalUrl(string $url, string $fieldName): ?array { if ($this->isValidUrl(url: $url) === false) { @@ -447,6 +457,7 @@ protected function validateExternalUrl(string $url, string $fieldName): ?array * * @return array|null The JSON response data, or null on failure */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function fetchExternalUrl(string $url): ?array { try { @@ -474,6 +485,7 @@ protected function fetchExternalUrl(string $url): ?array * * @return string A unique identifier */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function generateIdentificatie(string $prefix): string { $timestamp = strtoupper(base_convert((string) time(), 10, 36)); @@ -492,6 +504,7 @@ protected function generateIdentificatie(string $prefix): string * * @return string|null The object UUID, or null if not found */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function findObjectByField( string $register, string $schema, @@ -538,6 +551,7 @@ protected function findObjectByField( * * @return array Array of matching object UUIDs */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function findAllObjectsByField( string $register, string $schema, @@ -584,6 +598,7 @@ protected function findAllObjectsByField( * * @return array|null The object data, or null on failure */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function findBySchemaKey(string $uuid, string $schemaKey): ?array { if ($this->objectService === null) { @@ -626,6 +641,7 @@ protected function findBySchemaKey(string $uuid, string $schemaKey): ?array * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ protected function checkFieldUniqueness( string $field1Value, string $field1Search, diff --git a/lib/Service/ZgwService.php b/lib/Service/ZgwService.php index f6b36040..d8cfec52 100644 --- a/lib/Service/ZgwService.php +++ b/lib/Service/ZgwService.php @@ -267,6 +267,7 @@ public function getLogger(): LoggerInterface * * @return array|null The mapping configuration or null if not found */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function loadMappingConfig(string $zgwApi, string $resource): ?array { $resourceKey = self::RESOURCE_MAP[$zgwApi][$resource] ?? null; @@ -285,6 +286,7 @@ public function loadMappingConfig(string $zgwApi, string $resource): ?array * * @return array Translated filter parameters */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function translateQueryParams(array $params, array $mappingConfig): array { $queryMapping = $mappingConfig['queryParameterMapping'] ?? []; @@ -336,6 +338,7 @@ public function translateQueryParams(array $params, array $mappingConfig): array * * @return Mapping The outbound mapping entity */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createOutboundMapping(array $mappingConfig): object { $mapping = new Mapping(); @@ -358,6 +361,7 @@ public function createOutboundMapping(array $mappingConfig): object * * @return Mapping The inbound mapping entity */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function createInboundMapping(array $mappingConfig): object { $mapping = new Mapping(); @@ -383,6 +387,7 @@ public function createInboundMapping(array $mappingConfig): object * * @return array The mapped Dutch-language object */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function applyOutboundMapping( array $objectData, object $mapping, @@ -427,6 +432,7 @@ public function applyOutboundMapping( * * @return array The mapped English-language data */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function applyInboundMapping( array $body, object $mapping, @@ -470,6 +476,7 @@ public function applyInboundMapping( * * @return array The parsed request body */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getRequestBody(IRequest $request): array { // Return cached result if already parsed for this request. @@ -527,6 +534,7 @@ public function getRequestBody(IRequest $request): array * * @return string The correct UUID from the URL path, or the fallback */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolvePathUuid(IRequest $request, string $uuid): string { $uri = $request->getRequestUri(); @@ -547,6 +555,7 @@ public function resolvePathUuid(IRequest $request, string $uuid): string * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function updateCachedBodyField(string $key, mixed $value): void { if ($this->cachedRequestBody !== null) { @@ -563,6 +572,7 @@ public function updateCachedBodyField(string $key, mixed $value): void * * @return string The base URL */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildBaseUrl(IRequest $request, string $zgwApi, string $resource): string { $serverHost = $request->getServerHost(); @@ -578,6 +588,7 @@ public function buildBaseUrl(IRequest $request, string $zgwApi, string $resource * * @return JSONResponse|null 401 response on failure, null on success */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function validateJwtAuth(IRequest $request): ?JSONResponse { $authHeader = $request->getHeader('Authorization'); @@ -627,6 +638,7 @@ public function validateJwtAuth(IRequest $request): ?JSONResponse * @SuppressWarnings(PHPMD.CyclomaticComplexity) — multiple JWT validation paths * @SuppressWarnings(PHPMD.NPathComplexity) — multiple JWT validation paths */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function consumerHasScope(IRequest $request, string $component, string $scope): bool { if ($this->consumerMapper === null) { @@ -697,6 +709,7 @@ public function consumerHasScope(IRequest $request, string $component, string $s * * @SuppressWarnings(PHPMD.CyclomaticComplexity) — multiple JWT validation paths */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function getConsumerAuthorisaties(IRequest $request, string $component): ?array { if ($this->consumerMapper === null) { @@ -762,6 +775,7 @@ public function getConsumerAuthorisaties(IRequest $request, string $component): * * @return void */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function publishNotification( string $zgwApi, string $resource, @@ -786,6 +800,7 @@ public function publishNotification( * * @return array The error response data with detail and optional invalidParams */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function buildValidationError(array $ruleResult): array { $data = ['detail' => $ruleResult['detail']]; @@ -805,6 +820,7 @@ public function buildValidationError(array $ruleResult): array * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function unavailableResponse(): JSONResponse { return new JSONResponse( @@ -821,6 +837,7 @@ public function unavailableResponse(): JSONResponse * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function mappingNotFoundResponse(string $zgwApi, string $resource): JSONResponse { return new JSONResponse( @@ -838,6 +855,7 @@ public function mappingNotFoundResponse(string $zgwApi, string $resource): JSONR * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleIndex(IRequest $request, string $zgwApi, string $resource): JSONResponse { if ($this->objectService === null) { @@ -939,6 +957,7 @@ public function handleIndex(IRequest $request, string $zgwApi, string $resource) * @SuppressWarnings(PHPMD.BooleanArgumentFlag) — ZGW scope flags from middleware * @SuppressWarnings(PHPMD.ExcessiveMethodLength) — orchestration method with validation + mapping */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleCreate( IRequest $request, string $zgwApi, @@ -1059,6 +1078,7 @@ public function handleCreate( * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleShow( IRequest $request, string $zgwApi, @@ -1128,6 +1148,7 @@ public function handleShow( * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.BooleanArgumentFlag) — ZGW scope flags from middleware */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleUpdate( IRequest $request, string $zgwApi, @@ -1346,6 +1367,7 @@ public function handleUpdate( * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) — ZGW scope flags from middleware */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleDestroy( IRequest $request, string $zgwApi, @@ -1428,6 +1450,7 @@ public function handleDestroy( * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleAudittrailIndex( IRequest $request, string $zgwApi, @@ -1491,6 +1514,7 @@ public function handleAudittrailIndex( * * @return JSONResponse */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function handleAudittrailShow( IRequest $request, string $zgwApi, @@ -1621,6 +1645,7 @@ private function mapAuditTrailToZgw( * @SuppressWarnings(PHPMD.CyclomaticComplexity) — sub-resource lookup with multiple guard clauses * @SuppressWarnings(PHPMD.NPathComplexity) — sub-resource lookup with multiple guard clauses */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolveZaakClosed(string $resource, array $existingData): ?bool { if ($resource === 'zaken') { @@ -1698,6 +1723,7 @@ public function resolveZaakClosed(string $resource, array $existingData): ?bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) — sub-resource lookup with multiple guard clauses * @SuppressWarnings(PHPMD.NPathComplexity) — sub-resource lookup with multiple guard clauses */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolveZaakClosedFromBody(string $resource, array $body): ?bool { if ($resource === 'zaken') { @@ -1773,6 +1799,7 @@ public function resolveZaakClosedFromBody(string $resource, array $body): ?bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) — sub-resource lookup with multiple guard clauses * @SuppressWarnings(PHPMD.NPathComplexity) — sub-resource lookup with multiple guard clauses */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolveParentZaaktypeDraft(string $resource, array $existingData): ?bool { $subResources = [ @@ -1851,6 +1878,7 @@ public function resolveParentZaaktypeDraft(string $resource, array $existingData * @SuppressWarnings(PHPMD.CyclomaticComplexity) — sub-resource lookup with multiple guard clauses * @SuppressWarnings(PHPMD.NPathComplexity) — sub-resource lookup with multiple guard clauses */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function resolveParentZaaktypeDraftFromBody(string $resource, array $body): ?bool { $subResources = [ diff --git a/lib/Service/ZgwZrcRulesService.php b/lib/Service/ZgwZrcRulesService.php index 0156892e..dddaee7f 100644 --- a/lib/Service/ZgwZrcRulesService.php +++ b/lib/Service/ZgwZrcRulesService.php @@ -82,6 +82,7 @@ class ZgwZrcRulesService extends ZgwRulesBase * * @SuppressWarnings(PHPMD.CyclomaticComplexity) — ZGW business rules validation */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZakenCreate(array $body): array { // Zrc-001: Validate zaaktype URL. @@ -174,6 +175,7 @@ public function rulesZakenCreate(array $body): array * * @return array The validation result */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZakenUpdate(array $body, ?array $existingObject=null): array { // Zrc-002: Preserve immutable identificatie on PUT if not provided. @@ -215,6 +217,7 @@ public function rulesZakenUpdate(array $body, ?array $existingObject=null): arra * * @return array The validation result */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZakenPatch(array $body, ?array $existingObject=null): array { // Zrc-009: Derive vertrouwelijkheidaanduiding from zaaktype if not set. @@ -299,6 +302,7 @@ public function rulesStatussenCreate(array $body): array * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/zaken/ */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesResultatenCreate(array $body): array { // Zrc-020: Validate resultaattype belongs to zaak's zaaktype. @@ -332,6 +336,7 @@ public function rulesResultatenCreate(array $body): array * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/zaken/ */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesRollenCreate(array $body): array { // Zrc-019: Validate roltype belongs to zaak's zaaktype. @@ -367,6 +372,7 @@ public function rulesRollenCreate(array $body): array * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/zaken/ */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZaakinformatieobjectenCreate(array $body): array { // Zrc-003: Validate informatieobject URL exists. @@ -407,6 +413,7 @@ public function rulesZaakinformatieobjectenCreate(array $body): array * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/zaken/ */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZaakinformatieobjectenUpdate(array $body, ?array $existingObject=null): array { $result = $this->checkZioImmutability(result: $this->isValid(body: $body), existingObject: $existingObject); @@ -430,6 +437,7 @@ public function rulesZaakinformatieobjectenUpdate(array $body, ?array $existingO * * @see rulesZaakinformatieobjectenUpdate() Same immutability rules apply. */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZaakinformatieobjectenPatch(array $body, ?array $existingObject=null): array { return $this->rulesZaakinformatieobjectenUpdate(body: $body, existingObject: $existingObject); @@ -447,6 +455,7 @@ public function rulesZaakinformatieobjectenPatch(array $body, ?array $existingOb * * @link https://vng-realisatie.github.io/gemma-zaken/standaard/zaken/ */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function rulesZaakeigenschappenCreate(array $body): array { // Zrc-018: Validate eigenschap belongs to zaak's zaaktype. @@ -1102,6 +1111,7 @@ private function validateProductenOfDiensten(array $body): ?array * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function detectEindstatus(string $statustypeUuid, string $zaaktypeUuid): bool { if ($this->objectService === null) { @@ -1175,6 +1185,7 @@ public function detectEindstatus(string $statustypeUuid, string $zaaktypeUuid): * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ + /** @spec openspec/specs/status-transition-engine/spec.md */ public function filterZakenForConsumer(array $zaken, array $authorizations): array { // No authorizations context → return all zaken unfiltered. diff --git a/lib/Service/ZgwZtcRulesService.php b/lib/Service/ZgwZtcRulesService.php index 1166202d..e7554e5d 100644 --- a/lib/Service/ZgwZtcRulesService.php +++ b/lib/Service/ZgwZtcRulesService.php @@ -139,6 +139,7 @@ class ZgwZtcRulesService extends ZgwRulesBase * @link https://vng-realisatie.github.io/gemma-zaken/standaard/catalogi/ * @link https://vng-realisatie.github.io/gemma-zaken/standaard/catalogi/ */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function checkConceptProtection( string $resource, string $action, @@ -192,6 +193,7 @@ public function checkConceptProtection( * * @return array The body with concept defaulted */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function defaultConcept(array $body, string $resource): array { if (in_array($resource, self::CONCEPT_RESOURCES, true) === true @@ -214,6 +216,7 @@ public function defaultConcept(array $body, string $resource): array * * @return array The body with concept preserved */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function preserveConcept(array $body, string $resource, ?array $existingObject): array { if ($existingObject === null @@ -252,6 +255,7 @@ public function preserveConcept(array $body, string $resource, ?array $existingO * @SuppressWarnings(PHPMD.CyclomaticComplexity) — ZGW business rules validation * @SuppressWarnings(PHPMD.NPathComplexity) — ZGW business rules validation */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesZaaktypenCreate(array $body): array { // Ztc-001: Validate selectielijstProcestype URL. @@ -327,6 +331,7 @@ public function rulesZaaktypenCreate(array $body): array * * @return array The validation result */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesBesluittypenCreate(array $body): array { // Resolve reference arrays by omschrijving/identificatie to UUIDs. @@ -372,6 +377,7 @@ public function rulesBesluittypenCreate(array $body): array * * @SuppressWarnings(PHPMD.CyclomaticComplexity) — ZGW resolution of omschrijving/UUID/URL */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesZaaktypeinformatieobjecttypenCreate(array $body): array { // Resolve informatieobjecttype: omschrijving → UUID, or bare UUID → verify. @@ -436,6 +442,7 @@ public function rulesZaaktypeinformatieobjecttypenCreate(array $body): array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ public function rulesResultaattypenCreate(array $body): array { $errors = []; diff --git a/src/App.vue b/src/App.vue index f1776c68..c4df8431 100644 --- a/src/App.vue +++ b/src/App.vue @@ -23,6 +23,7 @@ export default { CnAppRoot, }, + /** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ provide() { return { // Provide/inject channel for index pages that auto-mount sidebar @@ -88,6 +89,7 @@ export default { }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ permissions() { const base = window.OC?.currentUser?.permissions ?? [] // CnAppNav's permission filter is an array-includes check; Nextcloud @@ -103,6 +105,7 @@ export default { }, }, + /** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ async created() { // Pinia stores still need to come up so legacy custom components // keep working through the manifest transition. CnAppRoot itself @@ -119,6 +122,7 @@ export default { * @param {string} key Translation key. * @return {string} Translated string (or the key on miss). */ + /** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ translateForApp(key) { return ncT('procest', key) }, diff --git a/src/components/map/AddressSearch.vue b/src/components/map/AddressSearch.vue index 41dd94ac..896fe625 100644 --- a/src/components/map/AddressSearch.vue +++ b/src/components/map/AddressSearch.vue @@ -50,6 +50,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ async onInput(value) { this.query = value if (value.length < 3) { @@ -66,6 +67,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ async onEnter() { if (this.query.length < 3) return this.loading = true @@ -78,6 +80,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ async selectResult(result) { // Look up full details if we have an ID let fullResult = result @@ -105,11 +108,13 @@ export default { this.results = [] }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ clear() { this.query = '' this.results = [] }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ getTypeIcon(type) { switch (type) { case 'adres': return '\uD83C\uDFE0' diff --git a/src/components/map/CaseMap.vue b/src/components/map/CaseMap.vue index f96deba6..104d565d 100644 --- a/src/components/map/CaseMap.vue +++ b/src/components/map/CaseMap.vue @@ -116,12 +116,14 @@ export default { }, watch: { geometries: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ handler() { this.renderGeometries() }, deep: true, }, overlayLayers: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ handler() { this.updateOverlayLayers() }, @@ -131,6 +133,7 @@ export default { mounted() { this.initMap() }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ beforeDestroy() { if (this.map) { this.map.remove() @@ -138,6 +141,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ initMap() { this.map = L.map(this.$refs.mapElement, { center: this.center, @@ -203,6 +207,7 @@ export default { }) }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ renderGeometries() { if (!this.map) return @@ -277,6 +282,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ createClusterIcon(cluster) { const count = cluster.getChildCount() let className = 'case-map-cluster case-map-cluster--green' @@ -294,6 +300,7 @@ export default { }) }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ updateOverlayLayers() { if (!this.map) return @@ -339,12 +346,14 @@ export default { }, /** Force a map size recalculation. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ invalidateSize() { if (this.map) { this.map.invalidateSize() } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ onKeydown(event) { if (!this.map) return const panStep = 100 diff --git a/src/components/map/CasePopup.vue b/src/components/map/CasePopup.vue index 0d10cf17..fb460718 100644 --- a/src/components/map/CasePopup.vue +++ b/src/components/map/CasePopup.vue @@ -35,6 +35,7 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ statusClass() { const cat = this.caseData.statusCategory || '' return `case-popup__status--${cat}` diff --git a/src/components/map/LocationPicker.vue b/src/components/map/LocationPicker.vue index 922aed96..086603d3 100644 --- a/src/components/map/LocationPicker.vue +++ b/src/components/map/LocationPicker.vue @@ -97,12 +97,14 @@ export default { mounted() { this.initMap() }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ beforeDestroy() { if (this.map) { this.map.remove() } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ initMap() { let center = NL_CENTER let zoom = 7 @@ -142,6 +144,7 @@ export default { this.$nextTick(() => this.map.invalidateSize()) }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ setMode(mode) { this.mode = mode @@ -188,6 +191,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ placeMarker(latlng) { if (this.marker) { this.marker.setLatLng(latlng) @@ -200,6 +204,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ onAddressSelect({ coordinates }) { if (!coordinates) return const latlng = L.latLng(coordinates.lat, coordinates.lng) @@ -209,6 +214,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ useCurrentLocation() { if (!navigator.geolocation) return navigator.geolocation.getCurrentPosition( @@ -225,12 +231,14 @@ export default { ) }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ save() { if (this.selectedGeometry) { this.$emit('save', this.selectedGeometry) } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ formatArea(sqm) { if (sqm > 10000) { return `${(sqm / 10000).toFixed(2)} ha` diff --git a/src/components/map/MapComponent.vue b/src/components/map/MapComponent.vue index d53b04a8..eff2189c 100644 --- a/src/components/map/MapComponent.vue +++ b/src/components/map/MapComponent.vue @@ -122,6 +122,7 @@ export default { * The resolved formatter function. `markerFormatter` is a * string so manifest entries can configure it declaratively. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ formatterFn() { return resolveFormatter(this.markerFormatter) }, @@ -130,6 +131,7 @@ export default { * plain array of marker descriptors plus a reference back to * the original location for event payloads. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ resolvedMarkers() { const out = [] for (const location of this.locations) { @@ -140,6 +142,7 @@ export default { } return out }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ ariaLabel() { // Inline fallback strings — `t()` returns the English // source when no l10n bundle is loaded, which is the @@ -148,6 +151,7 @@ export default { ? this.t('procest', 'Map with case locations') : this.t('procest', 'Map with case locations (read-only)') }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ containerStyle() { return { height: this.height } }, @@ -156,6 +160,7 @@ export default { * disables every interaction primitive; interactive mode lets * Leaflet's defaults apply. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ leafletOptions() { if (this.interactive) { return { @@ -177,6 +182,7 @@ export default { } }, }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ beforeDestroy() { if (this.viewportDebounceTimer) { clearTimeout(this.viewportDebounceTimer) @@ -192,6 +198,7 @@ export default { * * @param {object} marker The formatted marker descriptor. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ onMarkerClick(marker) { const location = marker && marker.__sourceLocation ? marker.__sourceLocation @@ -206,6 +213,7 @@ export default { * * @param {object} payload `{ center, zoom, bbox }` from the lib. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ onViewportChangeRaw(payload) { if (this.viewportDebounceTimer) { clearTimeout(this.viewportDebounceTimer) @@ -221,6 +229,7 @@ export default { * * @param {object} payload `{ map }` from the lib. */ + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ onReady(payload) { this.$emit('ready', payload) }, diff --git a/src/components/map/MapLayerSwitcher.vue b/src/components/map/MapLayerSwitcher.vue index 289ca48d..e438772d 100644 --- a/src/components/map/MapLayerSwitcher.vue +++ b/src/components/map/MapLayerSwitcher.vue @@ -45,6 +45,7 @@ export default { enabledLayers: [], } }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ created() { // Enable default layers this.enabledLayers = this.layers @@ -52,6 +53,7 @@ export default { .map(l => l.title) }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ toggleLayer(layer) { const idx = this.enabledLayers.indexOf(layer.title) if (idx >= 0) { @@ -61,6 +63,7 @@ export default { } this.$emit('toggle', { layer, enabled: this.enabledLayers.includes(layer.title) }) }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ onOpacityChange(layer, event) { const opacity = parseInt(event.target.value, 10) / 100 this.$emit('opacity-change', { layer, opacity }) diff --git a/src/components/map/SpatialFilter.vue b/src/components/map/SpatialFilter.vue index 69d08280..e28ca803 100644 --- a/src/components/map/SpatialFilter.vue +++ b/src/components/map/SpatialFilter.vue @@ -54,12 +54,14 @@ export default { } }, watch: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ map(newMap) { if (newMap && !this.drawnItems) { this.initDrawLayer() } }, }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ mounted() { if (this.map) { this.initDrawLayer() @@ -69,6 +71,7 @@ export default { this.cleanup() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ initDrawLayer() { this.drawnItems = new L.FeatureGroup() this.map.addLayer(this.drawnItems) @@ -82,6 +85,7 @@ export default { }) }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ startRectangle() { this.activeMode = 'rectangle' this.cleanup() @@ -90,6 +94,7 @@ export default { handler.enable() }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ startPolygon() { this.activeMode = 'polygon' this.cleanup() @@ -98,6 +103,7 @@ export default { handler.enable() }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ clearSelection() { this.activeMode = null this.selectedArea = null @@ -107,6 +113,7 @@ export default { this.$emit('clear') }, + /** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ cleanup() { if (this.drawControl && this.map) { this.map.removeControl(this.drawControl) diff --git a/src/components/workflow/EdgeProperties.vue b/src/components/workflow/EdgeProperties.vue index a462fc55..abae9e77 100644 --- a/src/components/workflow/EdgeProperties.vue +++ b/src/components/workflow/EdgeProperties.vue @@ -84,6 +84,7 @@ export default { }, }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ guardsJson() { if (!this.edge || !this.edge.data) { return '[]' @@ -94,6 +95,7 @@ export default { return '[]' } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ edgeIssues() { if (!this.edge) { return [] @@ -102,9 +104,11 @@ export default { }, }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateField(key, value) { this.$emit('update', { edgeId: this.edge.id, patch: { [key]: value } }) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateGuards(raw) { let parsed = [] try { @@ -115,6 +119,7 @@ export default { } this.$emit('update', { edgeId: this.edge.id, patch: { guards: parsed } }) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateRoles(raw) { const roles = raw.split(',').map((r) => r.trim()).filter(Boolean) this.$emit('update', { edgeId: this.edge.id, patch: { allowedRoles: roles } }) diff --git a/src/components/workflow/NodePalette.vue b/src/components/workflow/NodePalette.vue index 73884ebd..4fea4f2a 100644 --- a/src/components/workflow/NodePalette.vue +++ b/src/components/workflow/NodePalette.vue @@ -41,6 +41,7 @@ export default { name: 'NodePalette', computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ templates() { return [ { type: 'status', label: t('procest', 'Status'), icon: '▭' }, @@ -51,6 +52,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ onDragStart(event, type) { event.dataTransfer.setData('application/vue-flow-type', type) event.dataTransfer.effectAllowed = 'move' diff --git a/src/components/workflow/NodeProperties.vue b/src/components/workflow/NodeProperties.vue index e5a416cb..e153007f 100644 --- a/src/components/workflow/NodeProperties.vue +++ b/src/components/workflow/NodeProperties.vue @@ -88,6 +88,7 @@ export default { }, }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ heading() { if (!this.node) { return t('procest', 'Node properties') @@ -100,6 +101,7 @@ export default { } return labelByType[this.node.type] || t('procest', 'Node') }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ nodeIssues() { if (!this.node) { return [] @@ -108,6 +110,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateField(key, value) { this.$emit('update', { nodeId: this.node.id, patch: { [key]: value } }) }, diff --git a/src/components/workflow/VisualWorkflowEditor.vue b/src/components/workflow/VisualWorkflowEditor.vue index fd0a5489..5af943d6 100644 --- a/src/components/workflow/VisualWorkflowEditor.vue +++ b/src/components/workflow/VisualWorkflowEditor.vue @@ -171,12 +171,14 @@ export default { } }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ readOnly() { return !!this.template && this.template.isDraft === false }, hasErrors() { return this.issues.some((i) => i.level === 'error') }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ nodes() { const layout = this.workingCopy.layout || {} const stepNodes = (this.workingCopy.steps || []).map((step, idx) => { @@ -196,6 +198,7 @@ export default { }) return stepNodes }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ edges() { return (this.workingCopy.transitions || []).map((tr, idx) => { const id = tr.id || `edge-${idx}` @@ -215,6 +218,7 @@ export default { }, watch: { workingCopy: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ handler() { this.issues = validateGraph(this.workingCopy) this.scheduleAutosave() @@ -225,12 +229,14 @@ export default { async mounted() { await this.load() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ beforeDestroy() { if (this.autosaveTimer) { clearTimeout(this.autosaveTimer) } }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ async load() { this.loading = true this.loadError = '' @@ -250,6 +256,7 @@ export default { this.loading = false } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ parseTemplate(template) { const parseField = (raw, fallback) => { if (Array.isArray(raw) || typeof raw === 'object') { @@ -270,6 +277,7 @@ export default { layout: parseField(template.layout, {}), } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ serializeWorkingCopy() { return { steps: this.workingCopy.steps, @@ -277,6 +285,7 @@ export default { layout: this.workingCopy.layout, } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onNodesUpdate(nodes) { // Capture position changes back into the layout block. const layout = { ...(this.workingCopy.layout || {}) } @@ -294,6 +303,7 @@ export default { this.isDirty = true } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onEdgesUpdate(edges) { // Keep transitions in working copy aligned with rendered edges // (e.g. when vue-flow signals deletes from the user). @@ -307,14 +317,17 @@ export default { this.isDirty = true } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onSelectNode(node) { this.selectedNode = node this.selectedEdge = null }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onSelectEdge(edge) { this.selectedEdge = edge this.selectedNode = null }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onDropNode({ type, position }) { const id = `step-${Date.now()}` const newStep = { @@ -332,6 +345,7 @@ export default { } this.isDirty = true }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onConnect({ source, target }) { if (!source || !target) return const id = `tr-${Date.now()}` @@ -346,6 +360,7 @@ export default { this.workingCopy = { ...this.workingCopy, transitions: next } this.isDirty = true }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onNodePropsUpdate({ nodeId, patch }) { const steps = (this.workingCopy.steps || []).map((step, idx) => { const id = step.id || step.status || `step-${idx}` @@ -359,6 +374,7 @@ export default { this.workingCopy = { ...this.workingCopy, steps } this.isDirty = true }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onEdgePropsUpdate({ edgeId, patch }) { const transitions = (this.workingCopy.transitions || []).map((tr, idx) => { const id = tr.id || `edge-${idx}` @@ -368,11 +384,13 @@ export default { this.workingCopy = { ...this.workingCopy, transitions } this.isDirty = true }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ scheduleAutosave() { if (!this.isDirty || !this.template || this.readOnly) return if (this.autosaveTimer) clearTimeout(this.autosaveTimer) this.autosaveTimer = setTimeout(() => this.onSaveDraft(true), 2000) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async onSaveDraft(silent = false) { if (!this.template || this.readOnly) return this.saving = true @@ -395,6 +413,7 @@ export default { this.saving = false } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async onPublish() { if (!this.template || this.hasErrors || this.readOnly) return if (this.isDirty) { @@ -413,6 +432,7 @@ export default { this.saving = false } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onExport() { const data = JSON.stringify({ title: this.template && this.template.title, @@ -427,9 +447,11 @@ export default { a.click() URL.revokeObjectURL(url) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onImport() { this.$refs.importInput.click() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async onImportFile(event) { const file = event.target.files && event.target.files[0] if (!file) return diff --git a/src/components/workflow/WorkflowCanvas.vue b/src/components/workflow/WorkflowCanvas.vue index 7949986a..8d85694f 100644 --- a/src/components/workflow/WorkflowCanvas.vue +++ b/src/components/workflow/WorkflowCanvas.vue @@ -117,12 +117,14 @@ export default { }, watch: { nodes: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ handler(next) { this.localNodes = [...next] }, deep: true, }, edges: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ handler(next) { this.localEdges = [...next] }, @@ -133,6 +135,7 @@ export default { hasIssue(id, level) { return this.issues.some((issue) => (issue.nodeId === id || issue.edgeId === id) && issue.level === level) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onDrop(event) { if (this.readOnly) { return @@ -148,24 +151,30 @@ export default { } this.$emit('drop-node', { type, position }) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onNodesChange() { this.$emit('update:nodes', this.localNodes) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onEdgesChange() { this.$emit('update:edges', this.localEdges) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onConnect(params) { if (this.readOnly) { return } this.$emit('connect', params) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onNodeClick({ node }) { this.$emit('select-node', node) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onEdgeClick({ edge }) { this.$emit('select-edge', edge) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onPaneClick() { this.$emit('select-node', null) }, diff --git a/src/services/adviceApi.js b/src/services/adviceApi.js index 52005e9a..f9b99bf4 100644 --- a/src/services/adviceApi.js +++ b/src/services/adviceApi.js @@ -52,6 +52,7 @@ function actionUrl(path) { * @param {string} caseId Case UUID * @return {Promise} List of advice records */ +/** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ export async function getAdviceForCase(caseId) { const response = await axios.get(orUrl(), { params: { _filters: JSON.stringify({ case: caseId }), _limit: 200 }, @@ -71,6 +72,7 @@ export async function getAdviceForCase(caseId) { * @param {object} body Transition payload (to, adviesDocument, ...) * @return {Promise} Updated record */ +/** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ export async function transitionStatus(id, body) { const response = await axios.post(actionUrl(`${id}/transition`), body) return response.data @@ -82,6 +84,7 @@ export async function transitionStatus(id, body) { * @param {string} id Advice UUID * @return {Promise} Server confirmation */ +/** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ export async function dispatchReminder(id) { const response = await axios.post(actionUrl(`${id}/remind`)) return response.data @@ -97,6 +100,7 @@ export async function dispatchReminder(id) { * @param {object} data Advice payload (case, adviseur, type, deadline, ...) * @return {Promise} Created record */ +/** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ export async function createAdviceWithNotification(data) { const payload = { ...data, diff --git a/src/services/aiApi.js b/src/services/aiApi.js index b68b60db..3b12c969 100644 --- a/src/services/aiApi.js +++ b/src/services/aiApi.js @@ -16,6 +16,7 @@ const baseUrl = generateUrl('/apps/procest/api/ai') * @param {string} documentId The document UUID * @return {Promise} Classification suggestion with confidence */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function classifyDocument(caseId, documentId) { const response = await axios.post(`${baseUrl}/classify`, { caseId, documentId }) return response.data @@ -28,6 +29,7 @@ export async function classifyDocument(caseId, documentId) { * @param {string|null} documentId Optional document UUID * @return {Promise} Extracted fields with confidence scores */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function extractData(caseId, documentId = null) { const response = await axios.post(`${baseUrl}/extract`, { caseId, documentId }) return response.data @@ -40,6 +42,7 @@ export async function extractData(caseId, documentId = null) { * @param {string} question The question to ask * @return {Promise} Answer with source citations */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function askQuestion(caseId, question) { const response = await axios.post(`${baseUrl}/ask`, { caseId, question }) return response.data @@ -53,6 +56,7 @@ export async function askQuestion(caseId, question) { * @param {string|null} documentId Optional document UUID * @return {Promise} Generated summary */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function summarize(caseId, type = 'case', documentId = null) { const response = await axios.post(`${baseUrl}/summarize`, { caseId, type, documentId }) return response.data @@ -64,6 +68,7 @@ export async function summarize(caseId, type = 'case', documentId = null) { * @param {string} caseId The case UUID * @return {Promise} Routing suggestion */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function suggestRouting(caseId) { const response = await axios.post(`${baseUrl}/suggest-routing`, { caseId }) return response.data @@ -75,6 +80,7 @@ export async function suggestRouting(caseId) { * @param {string} caseId The case UUID * @return {Promise} Next-step suggestion */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function suggestNext(caseId) { const response = await axios.post(`${baseUrl}/suggest-next`, { caseId }) return response.data @@ -86,6 +92,7 @@ export async function suggestNext(caseId) { * @param {object} filters Query filters (caseId, type, limit, offset) * @return {Promise} Audit log entries */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function getAuditLog(filters = {}) { const response = await axios.get(`${baseUrl}/audit`, { params: filters }) return response.data @@ -96,6 +103,7 @@ export async function getAuditLog(filters = {}) { * * @return {Promise} AI configuration */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function getAiSettings() { const response = await axios.get(`${baseUrl}/settings`) return response.data @@ -107,6 +115,7 @@ export async function getAiSettings() { * @param {object} settings Settings to update * @return {Promise} Updated settings */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function updateAiSettings(settings) { const response = await axios.post(`${baseUrl}/settings`, settings) return response.data @@ -117,6 +126,7 @@ export async function updateAiSettings(settings) { * * @return {Promise} Health check result */ +/** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ export async function testAiHealth() { const response = await axios.post(`${baseUrl}/health`) return response.data diff --git a/src/services/appointmentApi.js b/src/services/appointmentApi.js index 106f4c6f..b0942da0 100644 --- a/src/services/appointmentApi.js +++ b/src/services/appointmentApi.js @@ -3,26 +3,31 @@ import { generateUrl } from '@nextcloud/router' const baseUrl = generateUrl('/apps/procest/api/appointments') +/** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ export async function listAppointments(caseId) { const response = await axios.get(baseUrl, { params: { caseId } }) return response.data } +/** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ export async function bookAppointment(data) { const response = await axios.post(baseUrl, data) return response.data } +/** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ export async function cancelAppointment(appointmentId) { const response = await axios.delete(`${baseUrl}/${appointmentId}`) return response.data } +/** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ export async function markNoShow(appointmentId) { const response = await axios.post(`${baseUrl}/${appointmentId}/no-show`) return response.data } +/** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ export async function getTimeslots(productId, locationId, date) { const response = await axios.get(`${baseUrl}/timeslots`, { params: { productId, locationId, date } }) return response.data diff --git a/src/services/berichtenboxApi.js b/src/services/berichtenboxApi.js index 6fbc75cf..581b82c2 100644 --- a/src/services/berichtenboxApi.js +++ b/src/services/berichtenboxApi.js @@ -3,21 +3,25 @@ import { generateUrl } from '@nextcloud/router' const baseUrl = generateUrl('/apps/procest/api/berichtenbox') +/** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ export async function sendMessage(data) { const response = await axios.post(`${baseUrl}/send`, data) return response.data } +/** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ export async function listMessages(caseId) { const response = await axios.get(`${baseUrl}/messages`, { params: { caseId } }) return response.data } +/** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ export async function getTypeCodes() { const response = await axios.get(`${baseUrl}/types`) return response.data } +/** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ export async function pollReadStatus(messageId) { const response = await axios.post(`${baseUrl}/poll/${messageId}`) return response.data diff --git a/src/services/coordinateService.js b/src/services/coordinateService.js index c6263f1f..6acb28d5 100644 --- a/src/services/coordinateService.js +++ b/src/services/coordinateService.js @@ -23,6 +23,7 @@ proj4.defs( * @param {number} y The y coordinate (or latitude) * @return {boolean} True if coordinates appear to be RD */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export function isRDCoordinate(x, y) { return x >= 0 && x <= 300000 && y >= 300000 && y <= 625000 } @@ -34,6 +35,7 @@ export function isRDCoordinate(x, y) { * @param {number} y RD y coordinate * @return {{ lat: number, lng: number }} WGS84 coordinates */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export function rdToWgs84(x, y) { const [lng, lat] = proj4('EPSG:28992', 'EPSG:4326', [x, y]) return { lat, lng } @@ -46,6 +48,7 @@ export function rdToWgs84(x, y) { * @param {number} lng Longitude * @return {{ x: number, y: number }} RD coordinates */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export function wgs84ToRd(lat, lng) { const [x, y] = proj4('EPSG:4326', 'EPSG:28992', [lng, lat]) return { x, y } @@ -59,6 +62,7 @@ export function wgs84ToRd(lat, lng) { * @param {string} crs Optional CRS identifier (e.g., "EPSG:28992") * @return {object} GeoJSON geometry in WGS84 */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export function ensureWgs84(geojson, crs = null) { if (!geojson || !geojson.type) { return geojson diff --git a/src/services/gisProxyService.js b/src/services/gisProxyService.js index ebe4ec9c..6687a099 100644 --- a/src/services/gisProxyService.js +++ b/src/services/gisProxyService.js @@ -18,6 +18,7 @@ import { generateUrl } from '@nextcloud/router' * @param {string} params.responseType Expected response type: 'image', 'json', 'xml' * @return {Promise<*>} The proxied response data */ +/** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ export async function proxyRequest({ url, query = {}, type = 'wms', responseType = 'json' }) { const apiUrl = generateUrl('/apps/procest/api/gis/proxy') const axiosConfig = {} @@ -42,6 +43,7 @@ export async function proxyRequest({ url, query = {}, type = 'wms', responseType * @param {string} type Service type: 'wms' or 'wfs' * @return {Promise} Parsed capabilities with available layers */ +/** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ export async function getCapabilities(url, type = 'wms') { const apiUrl = generateUrl('/apps/procest/api/gis/capabilities') const response = await axios.get(apiUrl, { diff --git a/src/services/mapFormatters.js b/src/services/mapFormatters.js index a9048334..803aaa2f 100644 --- a/src/services/mapFormatters.js +++ b/src/services/mapFormatters.js @@ -27,6 +27,7 @@ * @param {string} status The case status. * @return {string} CSS variable reference. */ +/** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ export function statusColor(status) { switch (status) { case 'blocked': @@ -48,6 +49,7 @@ export function statusColor(status) { * @param {string} status The case status. * @return {string} Icon glyph name. */ +/** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ export function statusIcon(status) { switch (status) { case 'blocked': @@ -113,6 +115,7 @@ function extractCoords(geometry) { * @param {object} caseObj Case object from OpenRegister. * @return {object|null} Marker descriptor or `null` if no geometry. */ +/** @spec openspec/changes/retrofit-2026-05-25-map-component/tasks.md */ export function caseMarkerFormatter(caseObj) { if (!caseObj || typeof caseObj !== 'object') { return null diff --git a/src/services/parafeerActieApi.js b/src/services/parafeerActieApi.js index 6b7236f5..684351e3 100644 --- a/src/services/parafeerActieApi.js +++ b/src/services/parafeerActieApi.js @@ -25,6 +25,7 @@ const ENDPOINT = generateUrl('/apps/procest/api/parafeer-actie') * @param {string} [data.mandate] Mandate reference when acting as delegate. * @return {Promise} The created parafeeractie and updated voorstel. */ +/** @spec openspec/specs/parafering-actions/spec.md */ export async function recordAction(data) { const response = await axios.post(ENDPOINT, data) return response.data @@ -36,6 +37,7 @@ export async function recordAction(data) { * @param {string} voorstelId The voorstel UUID. * @return {Promise>} The parafeeractie array. */ +/** @spec openspec/specs/parafering-actions/spec.md */ export async function listActions(voorstelId) { const response = await axios.get(ENDPOINT, { params: { voorstel: voorstelId } }) return Array.isArray(response.data) ? response.data : [] diff --git a/src/services/parafeerRouteApi.js b/src/services/parafeerRouteApi.js index 00779052..646f0a4d 100644 --- a/src/services/parafeerRouteApi.js +++ b/src/services/parafeerRouteApi.js @@ -24,6 +24,7 @@ const baseUrl = () => generateUrl('/apps/procest/api/parafeer-route') * @param {string} voorstelId Voorstel UUID * @return {Promise} */ +/** @spec openspec/specs/parafering-actions/spec.md */ export async function startParafering(voorstelId) { const response = await axios.post(`${baseUrl()}/voorstel/${encodeURIComponent(voorstelId)}/start`) return response.data @@ -36,6 +37,7 @@ export async function startParafering(voorstelId) { * @param {object} data Parafeeractie payload * @return {Promise} */ +/** @spec openspec/specs/parafering-actions/spec.md */ export async function completeStep(voorstelId, data) { const response = await axios.post(`${baseUrl()}/voorstel/${encodeURIComponent(voorstelId)}/complete-step`, data) return response.data @@ -48,6 +50,7 @@ export async function completeStep(voorstelId, data) { * @param {object} data { step: number, reason: string } * @return {Promise} */ +/** @spec openspec/specs/parafering-actions/spec.md */ export async function skipStep(voorstelId, data) { const response = await axios.post(`${baseUrl()}/voorstel/${encodeURIComponent(voorstelId)}/skip-step`, data) return response.data @@ -60,6 +63,7 @@ export async function skipStep(voorstelId, data) { * @param {object} data { afterStep: number, stepData: object } * @return {Promise} */ +/** @spec openspec/specs/parafering-actions/spec.md */ export async function addStep(voorstelId, data) { const response = await axios.post(`${baseUrl()}/voorstel/${encodeURIComponent(voorstelId)}/add-step`, data) return response.data diff --git a/src/services/pdokService.js b/src/services/pdokService.js index 8c0765c7..baf79938 100644 --- a/src/services/pdokService.js +++ b/src/services/pdokService.js @@ -91,6 +91,7 @@ function handleNetworkError(error, fallback) { * @param {string} query Search query (min 3 characters). * @return {Promise} Suggestions array, empty array, or null. */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export async function suggest(query) { if (!query || query.length < 3) { return [] @@ -119,6 +120,7 @@ export async function suggest(query) { * @param {string} id The PDOK object id. * @return {Promise} The full result object, or null when degraded. */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export async function lookup(id) { if (!id) { return null @@ -139,6 +141,7 @@ export async function lookup(id) { * @param {number} rows Max results (default 10). * @return {Promise} Results array, empty array, or null. */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export async function free(query, rows = 10) { if (!query) { return [] @@ -159,6 +162,7 @@ export async function free(query, rows = 10) { * @param {number} lng Longitude (WGS84). * @return {Promise} Nearest address, or null when degraded. */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export async function reverse(lat, lng) { clearWarning() try { @@ -181,6 +185,7 @@ export async function reverse(lat, lng) { * @param {object|string} resultOrWkt A PDOK result object or a raw WKT string. * @return {{ lat: number, lng: number }|null} Coordinates or null. */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export function extractCoordinates(resultOrWkt) { if (!resultOrWkt) { return null @@ -224,6 +229,7 @@ function parseWkt(wkt) { * @param {object} result A result object. * @return {string} Formatted address. */ +/** @spec openspec/changes/retrofit-2026-05-25-pdok-integration/tasks.md */ export function formatAddress(result) { if (!result) { return '' diff --git a/src/services/taskApi.js b/src/services/taskApi.js index 3e494ba9..c1e54e07 100644 --- a/src/services/taskApi.js +++ b/src/services/taskApi.js @@ -44,6 +44,7 @@ function mapCalDavPriority(icalPriority) { * @param {object} task CalDAV task object from API * @return {object} Normalized work item */ +/** @spec openspec/changes/task-management/tasks.md */ export function normalizeCalDavTask(task) { const due = task.due || null const isCompleted = task.status === 'completed' @@ -98,6 +99,7 @@ export function normalizeCalDavTask(task) { * @param {string} objectId The object UUID * @return {Promise} Array of normalized task work items */ +/** @spec openspec/changes/task-management/tasks.md */ export async function fetchTasksForObject(registerId, schemaId, objectId) { const url = `/apps/openregister/api/objects/${registerId}/${schemaId}/${objectId}/tasks` @@ -128,6 +130,7 @@ export async function fetchTasksForObject(registerId, schemaId, objectId) { * @param {object[]} cases Array of case objects (must have id property) * @return {Promise} Array of normalized task work items */ +/** @spec openspec/changes/task-management/tasks.md */ export async function fetchTasksForCases(cases) { const objectStore = useObjectStore() const caseConfig = objectStore.objectTypeRegistry.case diff --git a/src/store/modules/advice.js b/src/store/modules/advice.js index 1227b882..329020db 100644 --- a/src/store/modules/advice.js +++ b/src/store/modules/advice.js @@ -24,6 +24,7 @@ export const useAdviceStore = defineStore('advice', { * @param {object} state Store state * @return {Array} Pending requests */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ pendingRequests(state) { return state.requests.filter((r) => r.status === 'aangevraagd') }, @@ -34,6 +35,7 @@ export const useAdviceStore = defineStore('advice', { * @param {object} state Store state * @return {Array} Overdue requests */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ overdueRequests(state) { const now = new Date() return state.requests.filter((r) => { @@ -50,6 +52,7 @@ export const useAdviceStore = defineStore('advice', { * @param {object} state Store state * @return {Array} Received requests */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ receivedRequests(state) { return state.requests.filter((r) => r.status === 'ontvangen') }, @@ -60,6 +63,7 @@ export const useAdviceStore = defineStore('advice', { * @param {object} state Store state * @return {boolean} True if all received */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ allAdviceReceived(state) { return state.requests.length > 0 && state.requests.every((r) => r.status === 'ontvangen' || r.status === 'verlopen') @@ -73,6 +77,7 @@ export const useAdviceStore = defineStore('advice', { * @param {string} caseId UUID of the case * @return {Promise} Advice requests */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async fetchRequests(caseId) { this.loading = true this.error = null @@ -99,6 +104,7 @@ export const useAdviceStore = defineStore('advice', { * @param {object} requestData The request data * @return {Promise} Created request */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async createRequest(requestData) { this.loading = true this.error = null @@ -140,6 +146,7 @@ export const useAdviceStore = defineStore('advice', { * @param {string} documentId Nextcloud file ID of the advice document * @return {Promise} Updated request */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async markReceived(requestId, documentId) { this.loading = true try { @@ -175,6 +182,7 @@ export const useAdviceStore = defineStore('advice', { * @param {string} requestId UUID of the request * @return {Promise} Updated request */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async markExpired(requestId) { this.loading = true try { @@ -216,6 +224,7 @@ export const useAdviceStore = defineStore('advice', { * @param {object} request The advice request * @return {number} Positive = days remaining, negative = days overdue */ + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ getDaysToDeadline(request) { if (!request.deadline) { return null diff --git a/src/store/modules/bezwaar.js b/src/store/modules/bezwaar.js index 41604869..3a5da6de 100644 --- a/src/store/modules/bezwaar.js +++ b/src/store/modules/bezwaar.js @@ -127,6 +127,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {string} caseId The case UUID * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async loadBezwaarData(caseId) { this.loading = true this.error = null @@ -176,6 +177,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The objection data * @return {Promise} The created objection */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async createObjection(data) { this.loading = true this.error = null @@ -200,6 +202,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The updated objection data * @return {Promise} The updated objection */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async updateObjection(data) { this.loading = true this.error = null @@ -224,6 +227,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {string} objectionId The objection UUID * @return {Promise} Whether the deletion succeeded */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async deleteObjection(objectionId) { this.loading = true this.error = null @@ -250,6 +254,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The hearing session data * @return {Promise} The created hearing */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async createHearingSession(data) { this.loading = true this.error = null @@ -274,6 +279,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The updated hearing data * @return {Promise} The updated hearing */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async updateHearingSession(data) { this.loading = true this.error = null @@ -303,6 +309,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The advisory report data * @return {Promise} The created report */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async createAdvisoryReport(data) { this.loading = true this.error = null @@ -327,6 +334,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The updated report data * @return {Promise} The updated report */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async updateAdvisoryReport(data) { this.loading = true this.error = null @@ -353,6 +361,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The appeal decision data * @return {Promise} The created decision */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async createAppealDecision(data) { this.loading = true this.error = null @@ -377,6 +386,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} data The updated decision data * @return {Promise} The updated decision */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async updateAppealDecision(data) { this.loading = true this.error = null @@ -403,6 +413,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {string} ontvangstDatum The date the bezwaarschrift was received (ISO string) * @return {object} Calculated deadlines */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ calculateDeadlines(ontvangstDatum) { const received = new Date(ontvangstDatum) @@ -429,6 +440,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {string} currentDeadline The current deadline (ISO string) * @return {string} The new extended deadline (ISO date string) */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ calculateExtendedDeadline(currentDeadline) { const current = new Date(currentDeadline) const extended = addWeeks(current, 6) @@ -442,6 +454,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {boolean} isSuspended Whether the deadline is currently suspended * @return {object} Deadline status with daysRemaining, isAtRisk, isOverdue */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getDeadlineStatus(deadline, isSuspended = false) { if (isSuspended) { return { @@ -489,6 +502,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {object} options Additional options (voorzieningRequested) * @return {Promise} The created beroep case */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async escalateToBeroep(bezwaarCase, options = {}) { this.loading = true this.error = null @@ -536,6 +550,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * @param {string} bezwaarReceivedDate The date the bezwaar was received * @return {object} Timeliness assessment */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ checkTimeliness(besluitDate, bezwaarReceivedDate) { const besluit = new Date(besluitDate) const received = new Date(bezwaarReceivedDate) @@ -559,6 +574,7 @@ export const useBezwaarStore = defineStore('bezwaar', { * * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ clearBezwaarData() { this.currentObjection = null this.hearingSessions = [] diff --git a/src/store/modules/enforcement.js b/src/store/modules/enforcement.js index 3685dfeb..b7469875 100644 --- a/src/store/modules/enforcement.js +++ b/src/store/modules/enforcement.js @@ -53,6 +53,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} state Store state * @return {object|null} Active action */ + /** @spec openspec/changes/vth-module/tasks.md */ activeAction(state) { return state.actions.find((a) => a.status === 'opgelegd') || null }, @@ -63,6 +64,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} state Store state * @return {number} Total verbeurd amount */ + /** @spec openspec/changes/vth-module/tasks.md */ totalVerbeurd(state) { return state.actions .filter((a) => a.status === 'verbeurd') @@ -75,6 +77,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} state Store state * @return {Array} Ernst levels */ + /** @spec openspec/changes/vth-module/tasks.md */ ernstLevels(state) { return Object.keys(state.lhsMatrix) }, @@ -85,6 +88,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} state Store state * @return {Array} Gedrag levels */ + /** @spec openspec/changes/vth-module/tasks.md */ gedragLevels(state) { const first = Object.values(state.lhsMatrix)[0] return first ? Object.keys(first) : [] @@ -98,6 +102,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {string} caseId UUID of the case * @return {Promise} Actions */ + /** @spec openspec/changes/vth-module/tasks.md */ async fetchActions(caseId) { this.loading = true this.error = null @@ -125,6 +130,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {string} gedrag Behavior type (goedwillend/onverschillig/calculerend/crimineel) * @return {string|null} Suggested intervention */ + /** @spec openspec/changes/vth-module/tasks.md */ lookupLhs(ernst, gedrag) { if (!this.lhsMatrix[ernst]) { return null @@ -137,6 +143,7 @@ export const useEnforcementStore = defineStore('enforcement', { * * @return {Promise} */ + /** @spec openspec/changes/vth-module/tasks.md */ async loadLhsMatrix() { if (this.matrixLoaded) { return @@ -171,6 +178,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} matrix The matrix to save * @return {Promise} Success */ + /** @spec openspec/changes/vth-module/tasks.md */ async saveLhsMatrix(matrix) { try { const response = await fetch('/apps/procest/api/settings', { @@ -199,6 +207,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} actionData The action data * @return {Promise} Created action */ + /** @spec openspec/changes/vth-module/tasks.md */ async createAction(actionData) { this.loading = true this.error = null @@ -231,6 +240,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {string} newStatus New status (verbeurd/geeffectueerd/ingetrokken) * @return {Promise} Updated action */ + /** @spec openspec/changes/vth-module/tasks.md */ async updateStatus(actionId, newStatus) { this.loading = true try { @@ -265,6 +275,7 @@ export const useEnforcementStore = defineStore('enforcement', { * @param {object} action The enforcement action * @return {Promise} Created task */ + /** @spec openspec/changes/vth-module/tasks.md */ async createBegunstigingTask(caseId, action) { try { const objectStore = useObjectStore() diff --git a/src/store/modules/gis.js b/src/store/modules/gis.js index 145bd71b..7d6634b0 100644 --- a/src/store/modules/gis.js +++ b/src/store/modules/gis.js @@ -27,9 +27,11 @@ export const useGisStore = defineStore('gis', { }), getters: { + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ overlayLayers(state) { return state.layers.filter(l => !l.isBaseLayer) }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ baseLayers(state) { return state.layers.filter(l => l.isBaseLayer) }, @@ -39,6 +41,7 @@ export const useGisStore = defineStore('gis', { /** * Fetch all MapLayer objects from OpenRegister. */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async fetchLayers() { this.loading = true try { @@ -58,6 +61,7 @@ export const useGisStore = defineStore('gis', { * @param {object} layerData The layer configuration * @return {object} The created layer */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async createLayer(layerData) { const objectStore = useObjectStore() const created = await objectStore.saveObject('mapLayer', layerData) @@ -72,6 +76,7 @@ export const useGisStore = defineStore('gis', { * @param {object} layerData The updated configuration * @return {object} The updated layer */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async updateLayer(id, layerData) { const objectStore = useObjectStore() const updated = await objectStore.saveObject('mapLayer', { id, ...layerData }) @@ -84,6 +89,7 @@ export const useGisStore = defineStore('gis', { * * @param {string} id The layer ID */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async deleteLayer(id) { const objectStore = useObjectStore() await objectStore.deleteObject('mapLayer', id) @@ -96,6 +102,7 @@ export const useGisStore = defineStore('gis', { * @param {object|null} geometry GeoJSON geometry or null to clear * @param {string} mode Filter mode identifier */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ setSpatialFilter(geometry, mode = null) { this.selectedArea = geometry this.filterMode = mode @@ -104,6 +111,7 @@ export const useGisStore = defineStore('gis', { /** * Clear the spatial filter. */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ clearSpatialFilter() { this.selectedArea = null this.selectedCases = [] @@ -117,6 +125,7 @@ export const useGisStore = defineStore('gis', { * @param {number} lng Longitude * @return {Promise} The formatted address */ + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async reverseGeocode(lat, lng) { const key = `${lat.toFixed(5)},${lng.toFixed(5)}` diff --git a/src/store/modules/inspection.js b/src/store/modules/inspection.js index 52fb866c..a9c95635 100644 --- a/src/store/modules/inspection.js +++ b/src/store/modules/inspection.js @@ -28,6 +28,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {object} state Store state * @return {Array} Active checklists */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ activeChecklists(state) { return state.checklists.filter((c) => c.status === 'active') }, @@ -38,6 +39,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {object} state Store state * @return {number} Number of completed reports */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ completedReportsCount(state) { return state.reports.length }, @@ -48,6 +50,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {object} state Store state * @return {Array} Reports with failed items */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ nonConformReports(state) { return state.reports.filter((r) => r.result === 'niet_conform' || r.result === 'deels_conform') }, @@ -60,6 +63,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {string} caseTypeId UUID of the case type * @return {Promise} Checklists */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async fetchChecklists(caseTypeId) { this.loading = true this.error = null @@ -86,6 +90,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {object} checklistData The checklist data * @return {Promise} Saved checklist */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async saveChecklist(checklistData) { this.loading = true this.error = null @@ -115,6 +120,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {object} checklist The checklist to version * @return {Promise} New version */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async createNewVersion(checklist) { const newVersion = { ...checklist, @@ -135,6 +141,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {string} checklistId UUID of the checklist * @return {Promise} Success */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async deleteChecklist(checklistId) { this.loading = true try { @@ -156,6 +163,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {string} caseId UUID of the case * @return {Promise} Reports */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async fetchReports(caseId) { this.loading = true this.error = null @@ -182,6 +190,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {object} reportData Report data with items array * @return {Promise} Created report */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async createReport(reportData) { this.loading = true this.error = null @@ -237,6 +246,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {File} file The photo file * @return {Promise} Nextcloud file ID */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async uploadPhoto(caseId, file) { try { const objectStore = useObjectStore() @@ -259,6 +269,7 @@ export const useInspectionStore = defineStore('inspection', { * @param {string} reportId UUID of the inspection report * @return {Promise} Created task */ + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async createFollowUpTask(caseId, failedCount, reportId) { try { const objectStore = useObjectStore() diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js index 0a583b90..ba31d764 100644 --- a/src/store/modules/settings.js +++ b/src/store/modules/settings.js @@ -19,6 +19,7 @@ export const useSettingsStore = defineStore('settings', { getIsAdmin: (state) => state.isAdmin, }, actions: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async fetchSettings() { this.loading = true this.error = null @@ -53,6 +54,7 @@ export const useSettingsStore = defineStore('settings', { } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async saveSettings(settingsData) { this.loading = true this.error = null diff --git a/src/store/modules/workflow.js b/src/store/modules/workflow.js index 9aa7e8ae..db19003f 100644 --- a/src/store/modules/workflow.js +++ b/src/store/modules/workflow.js @@ -117,6 +117,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} caseTypeId UUID of the case type * @return {Promise} Array of workflow templates */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async listVersions(caseTypeId) { this.loading = true this.error = null @@ -143,6 +144,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} templateId UUID of the workflow template * @return {Promise} The workflow template or null */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async getTemplate(templateId) { this.loading = true this.error = null @@ -165,6 +167,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} caseTypeId UUID of the case type * @return {Promise} The active workflow template or null */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async getActiveVersion(caseTypeId) { this.loading = true this.error = null @@ -193,6 +196,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} title Name of the workflow * @return {Promise} The created template or null */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async createTemplate(caseTypeId, title) { this.loading = true this.error = null @@ -224,6 +228,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} templateData The template data to save * @return {Promise} The saved template or null */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async saveTemplate(templateData) { this.loading = true this.error = null @@ -257,6 +262,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} templateId UUID of the template * @return {Promise} Success */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async deleteTemplate(templateId) { this.loading = true this.error = null @@ -281,6 +287,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} templateId UUID of the template to publish * @return {Promise} The published template or null */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async publishVersion(templateId) { this.error = null @@ -340,6 +347,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} sourceTemplateId UUID of the source template * @return {Promise} The new draft version or null */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async createDraftFromVersion(sourceTemplateId) { this.loading = true this.error = null @@ -392,6 +400,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {Array} caseDocuments Documents linked to this case * @return {Array} Available transitions with guard status */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ computeAvailableTransitions(caseData, userRoles, workflow, caseTasks, caseDocuments) { if (!workflow?.transitions) return [] @@ -462,6 +471,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} currentStatus Current status UUID * @return {Array} Array of {met: boolean, message: string} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ evaluateGuards(guards, caseData, caseTasks, caseDocuments, steps, currentStatus) { return guards.map((guard) => { switch (guard.type) { @@ -487,6 +497,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {Array} caseTasks Tasks linked to the case * @return {object} {met: boolean, message: string} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ evaluateChecklistGuard(guard, caseTasks) { // Find tasks with checklists and check completion const tasksWithChecklists = caseTasks.filter((task) => { @@ -523,6 +534,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} caseData The case object * @return {object} {met: boolean, message: string} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ evaluateRequiredFieldGuard(guard, caseData) { const fieldName = guard.fieldName const value = caseData[fieldName] @@ -542,6 +554,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {Array} caseDocuments Documents linked to the case * @return {object} {met: boolean, message: string} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ evaluateRequiredDocumentGuard(guard, caseDocuments) { const requiredType = guard.documentTypeName const hasDocument = caseDocuments.some( @@ -564,6 +577,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {Array} caseTasks Tasks linked to the case * @return {object} {met: boolean, messages: Array} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ evaluateRequiredSteps(steps, currentStatus, caseTasks) { const stepsInStatus = steps.filter( (s) => s.status === currentStatus && s.isRequired, @@ -597,6 +611,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} transition The transition that triggered the actions * @return {Promise} Array of {action, success, error} results */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchActions(actions, caseData, transition) { const results = [] for (const action of actions) { @@ -645,6 +660,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} transition The transition context * @return {Promise} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchEmailAction(action, caseData, transition) { // Delegate to n8n webhook for email sending const webhookUrl = action.webhookUrl || '/apps/procest/api/workflow/actions/email' @@ -678,6 +694,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} caseData The case object * @return {Promise} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchCreateTaskAction(action, caseData) { const objectStore = useObjectStore() await objectStore.saveObject('task', { @@ -697,6 +714,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} caseData The parent case object * @return {Promise} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchCreateSubCaseAction(action, caseData) { const objectStore = useObjectStore() await objectStore.saveObject('case', { @@ -715,6 +733,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} transition The transition context * @return {Promise} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchWebhookAction(action, caseData, transition) { const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), 10000) @@ -752,6 +771,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} caseData The case object * @return {Promise} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchSetFieldAction(action, caseData) { const objectStore = useObjectStore() await objectStore.saveObject('case', { @@ -767,6 +787,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} caseData The case object * @return {Promise} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async dispatchNotifyAction(action, caseData) { // Use Nextcloud OCS notification API await fetch('/ocs/v2.php/apps/admin_notifications/api/v1/notifications/admin', { @@ -795,6 +816,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} transition The transition context (optional) * @return {string} Interpolated string */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ interpolateTemplate(template, caseData, transition) { return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (match, path) => { const parts = path.split('.') @@ -821,6 +843,7 @@ export const useWorkflowStore = defineStore('workflow', { * * @return {Array} Array of error/warning objects {type, message} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ validateWorkflow() { const errors = [] const steps = this.parsedSteps @@ -880,6 +903,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {Array} docTypes Document types of the case type * @return {object} Portable workflow definition */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ exportWorkflow(template, statusTypes, roleTypes, docTypes) { const statusMap = {} statusTypes.forEach((s) => { statusMap[s.id] = s.name }) @@ -935,6 +959,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {Array} docTypes Document types of the target case type * @return {object} {success, template, missingTypes} */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ async importWorkflow(importData, caseTypeId, statusTypes, roleTypes, docTypes) { // Build reverse maps (name -> UUID) const statusNameMap = {} @@ -1007,6 +1032,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} stepData Step properties (optional overrides) * @return {object} The new step */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ addStep(statusId, stepData = {}) { const steps = [...this.parsedSteps] const stepsInStatus = steps.filter((s) => s.status === statusId) @@ -1033,6 +1059,7 @@ export const useWorkflowStore = defineStore('workflow', { * * @param {string} stepId UUID of the step to remove */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ removeStep(stepId) { let steps = [...this.parsedSteps] const removed = steps.find((s) => s.id === stepId) @@ -1057,6 +1084,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} stepId UUID of the step to update * @param {object} updates Properties to update */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateStep(stepId, updates) { const steps = [...this.parsedSteps] const index = steps.findIndex((s) => s.id === stepId) @@ -1076,6 +1104,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {object} data Optional transition properties * @return {object} The new transition */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ addTransition(fromStatus, toStatus, data = {}) { const transitions = [...this.parsedTransitions] const newTransition = { @@ -1099,6 +1128,7 @@ export const useWorkflowStore = defineStore('workflow', { * * @param {string} transitionId UUID of the transition to remove */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ removeTransition(transitionId) { const transitions = this.parsedTransitions.filter( (t) => t.id !== transitionId, @@ -1114,6 +1144,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {string} transitionId UUID of the transition to update * @param {object} updates Properties to update */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateTransition(transitionId, updates) { const transitions = [...this.parsedTransitions] const index = transitions.findIndex((t) => t.id === transitionId) @@ -1132,6 +1163,7 @@ export const useWorkflowStore = defineStore('workflow', { * @param {number} x X coordinate * @param {number} y Y coordinate */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ updateNodePosition(statusId, x, y) { const positions = { ...this.parsedNodePositions } positions[statusId] = { x, y } diff --git a/src/store/modules/zgwMapping.js b/src/store/modules/zgwMapping.js index f2c8a82e..87ae7a28 100644 --- a/src/store/modules/zgwMapping.js +++ b/src/store/modules/zgwMapping.js @@ -13,6 +13,7 @@ export const useZgwMappingStore = defineStore('zgwMapping', { getMappings: (state) => state.mappings, }, actions: { + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ async fetchMappings() { this.loading = true this.error = null @@ -43,6 +44,7 @@ export const useZgwMappingStore = defineStore('zgwMapping', { } }, + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ async saveMapping(resourceKey, config) { this.loading = true this.error = null @@ -74,6 +76,7 @@ export const useZgwMappingStore = defineStore('zgwMapping', { } }, + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ async resetMapping(resourceKey) { this.loading = true this.error = null diff --git a/src/store/store.js b/src/store/store.js index 4478c4e2..d04476ac 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -2,6 +2,7 @@ import { useObjectStore } from './modules/object.js' import { useSettingsStore } from './modules/settings.js' import { useBezwaarStore } from './modules/bezwaar.js' +/** @spec openspec/changes/openregister-integration/tasks.md */ export async function initializeStores() { const settingsStore = useSettingsStore() const objectStore = useObjectStore() diff --git a/src/utils/caseHelpers.js b/src/utils/caseHelpers.js index 577c682c..fe0261ac 100644 --- a/src/utils/caseHelpers.js +++ b/src/utils/caseHelpers.js @@ -12,6 +12,7 @@ import { parseDuration, formatDuration } from './durationHelpers.js' * @param {string} durationString ISO 8601 duration (e.g., "P56D") * @return {Date|null} The calculated deadline, or null if inputs are invalid */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function calculateDeadline(startDate, durationString) { if (!startDate || !durationString) return null const parsed = parseDuration(durationString) @@ -33,6 +34,7 @@ export function calculateDeadline(startDate, durationString) { * * @return {string} Generated identifier (e.g., "2026-4281") */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function generateIdentifier() { const year = new Date().getFullYear() const suffix = Date.now() % 10000 @@ -47,6 +49,7 @@ export function generateIdentifier() { * @param {boolean} isFinal Whether the case is at a final status * @return {boolean} */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function isCaseOverdue(caseObj, isFinal = false) { if (!caseObj.deadline) return false if (isFinal) return false @@ -64,6 +67,7 @@ export function isCaseOverdue(caseObj, isFinal = false) { * @param {boolean} isFinal Whether the case is at a final status * @return {boolean} */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function isCaseDueToday(caseObj, isFinal = false) { if (!caseObj.deadline) return false if (isFinal) return false @@ -81,6 +85,7 @@ export function isCaseDueToday(caseObj, isFinal = false) { * @param {boolean} isFinal Whether the case is at a final status * @return {boolean} */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function isCaseDueTomorrow(caseObj, isFinal = false) { if (!caseObj.deadline) return false if (isFinal) return false @@ -99,6 +104,7 @@ export function isCaseDueTomorrow(caseObj, isFinal = false) { * @param {boolean} isFinal Whether the case is at a final status * @return {string|null} Overdue text or null if not overdue */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function getCaseOverdueText(caseObj, isFinal = false) { if (!isCaseOverdue(caseObj, isFinal)) return null const deadline = new Date(caseObj.deadline) @@ -120,6 +126,7 @@ export function getCaseOverdueText(caseObj, isFinal = false) { * @param {boolean} isFinal Whether the case is at a final status * @return {{ text: string, style: string }} Countdown text and style class */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function formatDeadlineCountdown(caseObj, isFinal = false) { if (!caseObj.deadline) return { text: '—', style: '' } if (isFinal) { @@ -147,6 +154,7 @@ export function formatDeadlineCountdown(caseObj, isFinal = false) { * @param {string} startDate ISO date string * @return {number} Days elapsed (0 if today or invalid) */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function getDaysElapsed(startDate) { if (!startDate) return 0 const start = new Date(startDate) @@ -163,6 +171,7 @@ export function getDaysElapsed(startDate) { * @param {string} deadline ISO date string * @return {number} Days remaining (negative if overdue) */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function getDaysRemaining(deadline) { if (!deadline) return 0 const dl = new Date(deadline) @@ -179,6 +188,7 @@ export function getDaysRemaining(deadline) { * @param {string} dateString ISO date string * @return {string} Formatted date */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function formatDate(dateString) { if (!dateString) return '—' const date = new Date(dateString) @@ -191,6 +201,7 @@ export function formatDate(dateString) { * @param {string} dateString ISO date string * @return {string} Formatted date */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function formatDateShort(dateString) { if (!dateString) return '—' const date = new Date(dateString) diff --git a/src/utils/caseTypeValidation.js b/src/utils/caseTypeValidation.js index 089cf620..07a33876 100644 --- a/src/utils/caseTypeValidation.js +++ b/src/utils/caseTypeValidation.js @@ -12,6 +12,7 @@ export const REQUIRED_FIELDS = [ 'responsibleUnit', ] +/** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ export function getOriginOptions() { return [ { id: 'internal', label: t('procest', 'Internal') }, @@ -19,6 +20,7 @@ export function getOriginOptions() { ] } +/** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ export function getConfidentialityOptions() { return [ { id: 'public', label: t('procest', 'Public') }, @@ -38,6 +40,7 @@ export function getConfidentialityOptions() { * @param {object} data Case type data * @return {{ valid: boolean, errors: object }} */ +/** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ export function validateCaseType(data) { const errors = {} @@ -80,6 +83,7 @@ export function validateCaseType(data) { * @param {Array} statusTypes Array of status type objects linked to this case type * @return {{ valid: boolean, errors: string[] }} */ +/** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ export function validateForPublish(caseType, statusTypes) { const errors = [] diff --git a/src/utils/caseValidation.js b/src/utils/caseValidation.js index 0b01442e..35ddb7c3 100644 --- a/src/utils/caseValidation.js +++ b/src/utils/caseValidation.js @@ -9,6 +9,7 @@ * @param {object} caseType Case type object * @return {boolean} */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function isCaseTypeUsable(caseType) { if (!caseType) return false if (caseType.isDraft === true || caseType.isDraft === 'true') return false @@ -37,6 +38,7 @@ export function isCaseTypeUsable(caseType) { * @param {object} caseType Case type object * @return {string|null} Reason why the case type cannot be used, or null if usable */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function getCaseTypeUnusableReason(caseType) { if (!caseType) return t('procest', 'Case type not found') @@ -75,6 +77,7 @@ export function getCaseTypeUnusableReason(caseType) { * @param {object[]} caseTypes Available case types for validation context * @return {{ valid: boolean, errors: object }} Validation result */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function validateCaseCreate(form, caseTypes = []) { const errors = {} @@ -106,6 +109,7 @@ export function validateCaseCreate(form, caseTypes = []) { * @param {object} form The form data with title * @return {{ valid: boolean, errors: object }} Validation result */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function validateCaseUpdate(form) { const errors = {} @@ -127,6 +131,7 @@ export function validateCaseUpdate(form) { * @param {object[]} statusTypes Available status types for the case type * @return {{ valid: boolean, error: string|null }} Validation result */ +/** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ export function validateStatusChange(targetStatus, caseObj, statusTypes) { if (!targetStatus) { return { valid: false, error: t('procest', 'Target status is required') } diff --git a/src/utils/dashboardHelpers.js b/src/utils/dashboardHelpers.js index 2e4c48a2..3316c316 100644 --- a/src/utils/dashboardHelpers.js +++ b/src/utils/dashboardHelpers.js @@ -25,6 +25,7 @@ function todayString() { * @param {object[]} myTasks Tasks assigned to current user (available/active) * @return {object} KPI values */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function computeKpis(openCases, completedCases, myTasks) { const today = todayString() @@ -63,6 +64,7 @@ export function computeKpis(openCases, completedCases, myTasks) { * @param {object[]} statusTypes All status types * @return {Array<{ name: string, count: number }>} Sorted by status type order */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function aggregateByStatus(openCases, statusTypes) { const statusMap = new Map() const orderMap = new Map() @@ -95,6 +97,7 @@ export function aggregateByStatus(openCases, statusTypes) { * @param {object[]} caseTypes All case types (for name resolution) * @return {Array<{ id, identifier, title, caseTypeName, daysOverdue, handler }>} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getOverdueCases(openCases, caseTypes) { const typeMap = new Map() for (const ct of caseTypes) { @@ -121,6 +124,7 @@ export function getOverdueCases(openCases, caseTypes) { * @param {number} limit Max entries to return * @return {Array<{ date, type, description, user, caseIdentifier }>} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getRecentActivity(cases, limit = 10) { const entries = [] @@ -149,6 +153,7 @@ export function getRecentActivity(cases, limit = 10) { * @param {number} limit Max items to return * @return {Array<{ type, id, title, reference, deadline, daysText, isOverdue, priority }>} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getMyWorkItems(cases, tasks, limit = 5) { const items = [] @@ -233,6 +238,7 @@ function endOfWeek() { * @param {object[]} normalizedTasks Already-normalized CalDAV task work items * @return {{ overdue: object[], dueThisWeek: object[], upcoming: object[], noDeadline: object[], totalCount: number }} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getGroupedMyWorkItems(cases, normalizedTasks) { const items = [] const today = new Date() @@ -332,6 +338,7 @@ export const STALLED_THRESHOLD_DAYS = 7 * @param {number} warningDays Number of days before deadline to flag as at-risk * @return {{ overdue: object[], atRisk: object[] }} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getDeadlineAlerts(openCases, caseTypes, warningDays = DEADLINE_WARNING_DAYS) { const typeMap = new Map() for (const ct of caseTypes) { @@ -380,6 +387,7 @@ export function getDeadlineAlerts(openCases, caseTypes, warningDays = DEADLINE_W * @param {number} warningDays Number of days before due date to flag as due-soon * @return {{ overdue: object[], dueSoon: object[] }} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getTaskDueReminders(tasks, warningDays = DEADLINE_WARNING_DAYS) { const today = new Date() today.setHours(0, 0, 0, 0) @@ -426,6 +434,7 @@ export function getTaskDueReminders(tasks, warningDays = DEADLINE_WARNING_DAYS) * @param {number} stalledDays Number of days without activity to consider stalled * @return {Array<{ id, title, identifier, caseTypeName, daysSinceActivity, handler }>} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function getStalledCases(openCases, caseTypes, stalledDays = STALLED_THRESHOLD_DAYS) { const typeMap = new Map() for (const ct of caseTypes) { @@ -469,6 +478,7 @@ export function getStalledCases(openCases, caseTypes, stalledDays = STALLED_THRE * @param {string} dateString ISO date string * @return {string} */ +/** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ export function formatRelativeTime(dateString) { if (!dateString) return '—' const date = new Date(dateString) diff --git a/src/utils/decisionHelpers.js b/src/utils/decisionHelpers.js index 4b90581e..f3c5a250 100644 --- a/src/utils/decisionHelpers.js +++ b/src/utils/decisionHelpers.js @@ -8,6 +8,7 @@ * @param {object} decision Decision object with effectiveDate and expiryDate * @return {{ status: string, label: string, style: string, remaining: string|null }} */ +/** @spec openspec/changes/roles-decisions/tasks.md */ export function getDecisionValidity(decision) { const today = new Date() today.setHours(0, 0, 0, 0) @@ -82,6 +83,7 @@ export function getDecisionValidity(decision) { * @param {string} dateString ISO date string * @return {string} */ +/** @spec openspec/changes/roles-decisions/tasks.md */ export function formatDecisionDate(dateString) { if (!dateString) return '—' const date = new Date(dateString) @@ -94,6 +96,7 @@ export function formatDecisionDate(dateString) { * @param {object} form Decision form data * @return {{ valid: boolean, errors: object }} */ +/** @spec openspec/changes/roles-decisions/tasks.md */ export function validateDecision(form) { const errors = {} diff --git a/src/utils/doorlooptijdHelpers.js b/src/utils/doorlooptijdHelpers.js index 8d64d75a..379059e2 100644 --- a/src/utils/doorlooptijdHelpers.js +++ b/src/utils/doorlooptijdHelpers.js @@ -16,6 +16,7 @@ import { translate as t } from '@nextcloud/l10n' * @param {string} duration ISO 8601 duration (e.g., "P30D") * @return {number|null} Number of days, or null if unparseable */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function parseDurationToDays(duration) { if (!duration || typeof duration !== 'string') return null @@ -37,6 +38,7 @@ export function parseDurationToDays(duration) { * @param {object} caseObj Case object with startDate and endDate * @return {number|null} Processing days, or null if dates are missing */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function getProcessingDays(caseObj) { if (!caseObj.startDate || !caseObj.endDate) return null @@ -56,6 +58,7 @@ export function getProcessingDays(caseObj) { * @param {Map} caseTypeMap Map of caseType id to caseType object * @return {number|null} Target days from processingDeadline, or null */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function getSlaTargetDays(caseObj, caseTypeMap) { const ct = caseTypeMap.get(caseObj.caseType) if (!ct || !ct.processingDeadline) return null @@ -68,6 +71,7 @@ export function getSlaTargetDays(caseObj, caseTypeMap) { * @param {object[]} caseTypes Array of case type objects * @return {Map} */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function buildCaseTypeMap(caseTypes) { const map = new Map() for (const ct of caseTypes) { @@ -83,6 +87,7 @@ export function buildCaseTypeMap(caseTypes) { * @param {object[]} caseTypes All case types * @return {{ overallRate: number|null, withinSla: number, total: number, excluded: number, byType: Array<{ id, name, total, withinSla, rate, avgActual, targetDays }> }} */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function computeSlaCompliance(completedCases, caseTypes) { const caseTypeMap = buildCaseTypeMap(caseTypes) const byType = new Map() @@ -167,6 +172,7 @@ const DEFAULT_BINS = [ * @param {Array<{ label, min, max }>} [bins] Custom bin definitions * @return {{ bins: Array<{ label, count }>, slaTargetDays: number|null }} */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function computeProcessingTimeDistribution(completedCases, caseTypes, bins) { const useBins = bins || DEFAULT_BINS const caseTypeMap = buildCaseTypeMap(caseTypes) @@ -210,6 +216,7 @@ export function computeProcessingTimeDistribution(completedCases, caseTypes, bin * @param {number} [months] Number of months to look back (defaults to 12) * @return {Array<{ month: string, rate: number|null, withinSla: number, total: number }>} */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function computeMonthlyTrend(completedCases, caseTypes, months) { const lookback = months || 12 const caseTypeMap = buildCaseTypeMap(caseTypes) @@ -258,6 +265,7 @@ export function computeMonthlyTrend(completedCases, caseTypes, months) { * @param {number} [thresholdPct] Threshold as fraction (defaults to 0.25 = 25%) * @return {Array<{ id, title, identifier, caseTypeName, targetDays, elapsedDays, remainingDays, percentUsed, isOverdue }>} */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function getAtRiskCases(openCases, caseTypes, thresholdPct) { const threshold = thresholdPct ?? 0.25 const caseTypeMap = buildCaseTypeMap(caseTypes) @@ -311,6 +319,7 @@ export function getAtRiskCases(openCases, caseTypes, thresholdPct) { * @param {object[]} caseTypes All case types * @return {Array<{ id, name, targetDays, avgActualDays, complianceRate, total, withinSla, status: 'good'|'warning'|'critical'|'no-target' }>} */ +/** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ export function computePerformanceTable(completedCases, caseTypes) { const byType = new Map() diff --git a/src/utils/durationHelpers.js b/src/utils/durationHelpers.js index c998b45f..b7077850 100644 --- a/src/utils/durationHelpers.js +++ b/src/utils/durationHelpers.js @@ -13,6 +13,7 @@ const DURATION_REGEX = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?$/ * @param {string} value The string to validate * @return {boolean} */ +/** @spec openspec/changes/milestone-tracking/tasks.md */ export function isValidDuration(value) { if (!value || typeof value !== 'string') return false return DURATION_REGEX.test(value) && value !== 'P' @@ -24,6 +25,7 @@ export function isValidDuration(value) { * @param {string} iso ISO 8601 duration string (e.g., "P56D", "P2M", "P1Y6M") * @return {{ years: number, months: number, weeks: number, days: number } | null} */ +/** @spec openspec/changes/milestone-tracking/tasks.md */ export function parseDuration(iso) { if (!isValidDuration(iso)) return null const match = iso.match(DURATION_REGEX) @@ -41,6 +43,7 @@ export function parseDuration(iso) { * @param {string} iso ISO 8601 duration string * @return {string} Human-readable text (e.g., "56 days", "2 months", "1 year, 6 months") */ +/** @spec openspec/changes/milestone-tracking/tasks.md */ export function formatDuration(iso) { const parsed = parseDuration(iso) if (!parsed) return iso || '' @@ -80,6 +83,7 @@ export function formatDuration(iso) { * @param {string} value The value to validate * @return {string} Error message or empty string */ +/** @spec openspec/changes/milestone-tracking/tasks.md */ export function getDurationError(value) { if (!value) return '' if (!isValidDuration(value)) { diff --git a/src/utils/i18nResolver.js b/src/utils/i18nResolver.js index dadaa56d..95c9aa4e 100644 --- a/src/utils/i18nResolver.js +++ b/src/utils/i18nResolver.js @@ -21,6 +21,7 @@ const APP_DEFAULT_LOCALE = 'nl' * * @return {string} BCP 47 language code (e.g., 'nl', 'en', 'de') */ +/** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ export function getUserLocale() { if (typeof OC !== 'undefined' && typeof OC.getLanguage === 'function') { const lang = OC.getLanguage() @@ -46,6 +47,7 @@ export function getUserLocale() { * @param {string} [fallbackLocale] The fallback locale (defaults to app default 'nl') * @return {{ text: string, lang: string|null, isFallback: boolean }} */ +/** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ export function resolveTranslatable(value, locale, fallbackLocale) { // Null/undefined -> empty string if (value === null || value === undefined) { @@ -105,6 +107,7 @@ export function resolveTranslatable(value, locale, fallbackLocale) { * @param {string} [locale] The preferred locale * @return {{ text: string, lang: string|null, isFallback: boolean }} */ +/** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ export function resolveField(obj, field, locale) { if (!obj || typeof obj !== 'object') { return { text: '', lang: null, isFallback: false } @@ -120,6 +123,7 @@ export function resolveField(obj, field, locale) { * @param {string} [locale] The preferred locale * @return {string} The resolved text */ +/** @spec openspec/changes/retrofit-2026-05-25-procest-app-scaffold/tasks.md */ export function resolveText(obj, field, locale) { return resolveField(obj, field, locale).text } diff --git a/src/utils/openregisterCheck.js b/src/utils/openregisterCheck.js index b2fcfe43..ac416fdb 100644 --- a/src/utils/openregisterCheck.js +++ b/src/utils/openregisterCheck.js @@ -10,6 +10,7 @@ * * @return {Promise<{ available: boolean, configured: boolean, error: string|null }>} */ +/** @spec openspec/changes/openregister-integration/tasks.md */ export async function checkOpenRegisterStatus() { try { const response = await fetch('/apps/procest/api/settings', { @@ -42,6 +43,7 @@ export async function checkOpenRegisterStatus() { * @param {{ available: boolean, configured: boolean, error: string|null }} status * @return {string} */ +/** @spec openspec/changes/openregister-integration/tasks.md */ export function getStatusMessage(status) { if (status.error) { return t('procest', 'Could not check OpenRegister status: {error}', { error: status.error }) diff --git a/src/utils/parafeerEngine.js b/src/utils/parafeerEngine.js index c35049da..196b78f6 100644 --- a/src/utils/parafeerEngine.js +++ b/src/utils/parafeerEngine.js @@ -11,6 +11,7 @@ * @param {object} voorstel The voorstel object * @return {Array} Ordered array of step objects */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function getRouteSteps(voorstel) { if (!voorstel?.routeSnapshot) return [] try { @@ -28,6 +29,7 @@ export function getRouteSteps(voorstel) { * @param {object} voorstel The voorstel object * @return {object|null} Current step or null */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function getCurrentStep(voorstel) { const steps = getRouteSteps(voorstel) if (!steps.length || !voorstel.currentStep) return null @@ -41,6 +43,7 @@ export function getCurrentStep(voorstel) { * @param {string} userId The Nextcloud user UID * @return {boolean} */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function isActiveActor(voorstel, userId) { const step = getCurrentStep(voorstel) if (!step) return false @@ -53,6 +56,7 @@ export function isActiveActor(voorstel, userId) { * @param {object} voorstel The voorstel object * @return {number|null} Next step number, or null if this was the last step */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function getNextStep(voorstel) { const steps = getRouteSteps(voorstel) const current = voorstel.currentStep || 0 @@ -66,6 +70,7 @@ export function getNextStep(voorstel) { * @param {object} voorstel The voorstel object * @return {string} New status ('in_parafering', 'ter_accordering', or 'geaccordeerd') */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function getStatusAfterAdvance(voorstel) { const steps = getRouteSteps(voorstel) const nextStepNum = getNextStep(voorstel) @@ -89,6 +94,7 @@ export function getStatusAfterAdvance(voorstel) { * @param {object} route The parafeerroute object * @return {Array} Snapshot of steps */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function createRouteSnapshot(route) { if (!route?.steps) return [] const steps = typeof route.steps === 'string' ? JSON.parse(route.steps) : route.steps @@ -110,6 +116,7 @@ export function createRouteSnapshot(route) { * @param {object} newStep The new step to insert (without order) * @return {Array} Updated steps with renumbered orders */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function insertAdHocStep(steps, afterOrder, newStep) { const result = [] let orderCounter = 1 @@ -137,6 +144,7 @@ export function insertAdHocStep(steps, afterOrder, newStep) { * @param {number} stepOrder The step order to skip * @return {Array} Updated steps with the step marked as skipped */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function markStepSkipped(steps, stepOrder) { return steps.map(step => { if (step.order === stepOrder) { @@ -154,6 +162,7 @@ export function markStepSkipped(steps, stepOrder) { * @param {string} voorstelType The voorstel type (dt_advies, collegeadvies, raadsvoorstel) * @return {object|null} The matching default route, or null */ +/** @spec openspec/specs/parafering-actions/spec.md */ export function findDefaultRoute(routes, caseTypeId, voorstelType) { return routes.find(r => r.isDefault === true diff --git a/src/utils/taskHelpers.js b/src/utils/taskHelpers.js index a33f2eb8..5b54cdd3 100644 --- a/src/utils/taskHelpers.js +++ b/src/utils/taskHelpers.js @@ -18,6 +18,7 @@ const PRIORITY_WEIGHTS = { * * @return {object} Priority levels keyed by priority name */ +/** @spec openspec/changes/task-management/tasks.md */ export function getPriorityLevels() { return { urgent: { label: t('procest', 'Urgent'), weight: 1, cssVar: '--color-error' }, @@ -34,6 +35,7 @@ export function getPriorityLevels() { * @param {object} task Task object with dueDate and status * @return {boolean} */ +/** @spec openspec/changes/task-management/tasks.md */ export function isOverdue(task) { if (!task.dueDate) return false if (isTerminalStatus(task.status)) return false @@ -50,6 +52,7 @@ export function isOverdue(task) { * @param {object} task Task object with dueDate and status * @return {boolean} */ +/** @spec openspec/changes/task-management/tasks.md */ export function isDueToday(task) { if (!task.dueDate) return false if (isTerminalStatus(task.status)) return false @@ -66,6 +69,7 @@ export function isDueToday(task) { * @param {object} task Task object with dueDate * @return {string|null} Overdue text or null if not overdue */ +/** @spec openspec/changes/task-management/tasks.md */ export function getOverdueText(task) { if (!isOverdue(task)) return null const due = new Date(task.dueDate) @@ -86,6 +90,7 @@ export function getOverdueText(task) { * @param {string} dateString ISO 8601 date string * @return {string} Formatted date */ +/** @spec openspec/changes/task-management/tasks.md */ export function formatDueDate(dateString) { if (!dateString) return '—' const date = new Date(dateString) @@ -98,6 +103,7 @@ export function formatDueDate(dateString) { * @param {string} priority One of urgent, high, normal, low * @return {number} */ +/** @spec openspec/changes/task-management/tasks.md */ export function prioritySortWeight(priority) { return PRIORITY_WEIGHTS[priority] ?? 3 } @@ -126,6 +132,7 @@ function statusGroupWeight(status) { * @param {object[]} tasks Array of task objects * @return {object[]} Sorted copy of the array */ +/** @spec openspec/changes/task-management/tasks.md */ export function sortTasks(tasks) { return [...tasks].sort((a, b) => { const statusDiff = statusGroupWeight(a.status) - statusGroupWeight(b.status) diff --git a/src/utils/taskLifecycle.js b/src/utils/taskLifecycle.js index 5012dca2..9ea4aa6b 100644 --- a/src/utils/taskLifecycle.js +++ b/src/utils/taskLifecycle.js @@ -50,6 +50,7 @@ const TERMINAL_STATUSES = new Set(['completed', 'terminated', 'disabled']) * @param {string} currentStatus One of the TASK_STATUSES values * @return {string[]} Array of valid target statuses */ +/** @spec openspec/changes/task-management/tasks.md */ export function getAllowedTransitions(currentStatus) { return TRANSITION_MAP[currentStatus] || [] } @@ -61,6 +62,7 @@ export function getAllowedTransitions(currentStatus) { * @param {string} to Target status * @return {boolean} */ +/** @spec openspec/changes/task-management/tasks.md */ export function validateTransition(from, to) { const allowed = TRANSITION_MAP[from] return Array.isArray(allowed) && allowed.includes(to) @@ -72,6 +74,7 @@ export function validateTransition(from, to) { * @param {string} status One of the TASK_STATUSES values * @return {string} */ +/** @spec openspec/changes/task-management/tasks.md */ export function getStatusLabel(status) { return getStatusLabels()[status] || status } @@ -82,6 +85,7 @@ export function getStatusLabel(status) { * @param {string} targetStatus The status being transitioned to * @return {string} */ +/** @spec openspec/changes/task-management/tasks.md */ export function getTransitionLabel(targetStatus) { return getTransitionLabels()[targetStatus] || targetStatus } @@ -92,6 +96,7 @@ export function getTransitionLabel(targetStatus) { * @param {string} status One of the TASK_STATUSES values * @return {boolean} */ +/** @spec openspec/changes/task-management/tasks.md */ export function isTerminalStatus(status) { return TERMINAL_STATUSES.has(status) } diff --git a/src/utils/taskValidation.js b/src/utils/taskValidation.js index 81a683a3..549f30d4 100644 --- a/src/utils/taskValidation.js +++ b/src/utils/taskValidation.js @@ -10,6 +10,7 @@ import { validateTransition } from './taskLifecycle.js' * @param {object} form The form data * @return {{ valid: boolean, errors: object }} Validation result */ +/** @spec openspec/changes/task-management/tasks.md */ export function validateTaskCreate(form) { const errors = {} @@ -33,6 +34,7 @@ export function validateTaskCreate(form) { * @param {object} form The form data * @return {{ valid: boolean, errors: object }} Validation result */ +/** @spec openspec/changes/task-management/tasks.md */ export function validateTaskUpdate(form) { const errors = {} @@ -53,6 +55,7 @@ export function validateTaskUpdate(form) { * @param {string} to Target status * @return {{ valid: boolean, error: string|null }} Validation result */ +/** @spec openspec/changes/task-management/tasks.md */ export function validateTaskTransition(from, to) { if (!from || !to) { return { valid: false, error: t('procest', 'Invalid status transition') } diff --git a/src/views/DoorlooptijdDashboard.vue b/src/views/DoorlooptijdDashboard.vue index 054a185b..f9a1c239 100644 --- a/src/views/DoorlooptijdDashboard.vue +++ b/src/views/DoorlooptijdDashboard.vue @@ -329,9 +329,11 @@ export default { } }, computed: { + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ datePresets() { return [ { key: '3m', label: t('procest', 'Last 3 months') }, @@ -341,6 +343,7 @@ export default { { key: 'all', label: t('procest', 'All time') }, ] }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ dateRange() { const now = new Date() let from = null @@ -365,6 +368,7 @@ export default { } return { from, to: now } }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ statusTypeMap() { const map = new Map() for (const st of this.statusTypes) { @@ -372,18 +376,21 @@ export default { } return map }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ completedCases() { return this.allCases.filter(c => { const st = this.statusTypeMap.get(c.status) return st?.isFinal && c.endDate }) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ openCases() { return this.allCases.filter(c => { const st = this.statusTypeMap.get(c.status) return !st?.isFinal }) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ filteredCompletedCases() { let cases = this.completedCases @@ -400,21 +407,26 @@ export default { return cases }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ filteredOpenCases() { if (this.selectedCaseType) { return this.openCases.filter(c => c.caseType === this.selectedCaseType) } return this.openCases }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ caseTypesWithSla() { return this.caseTypes.filter(ct => ct.processingDeadline && parseDurationToDays(ct.processingDeadline)) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ showNoCasesState() { return !this.loading && this.allCases.length === 0 }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ showNoSlaState() { return !this.loading && this.allCases.length > 0 && this.caseTypesWithSla.length === 0 }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ showNoDataInRange() { return !this.loading && this.allCases.length > 0 @@ -422,12 +434,15 @@ export default { && this.filteredCompletedCases.length === 0 && this.atRiskCases.length === 0 }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ slaData() { return computeSlaCompliance(this.filteredCompletedCases, this.caseTypes) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ distributionData() { return computeProcessingTimeDistribution(this.filteredCompletedCases, this.caseTypes) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ trendMonths() { switch (this.selectedPreset) { case '3m': return 3 @@ -440,18 +455,22 @@ export default { default: return 12 } }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ trendData() { const casesForTrend = this.selectedCaseType ? this.completedCases.filter(c => c.caseType === this.selectedCaseType) : this.completedCases return computeMonthlyTrend(casesForTrend, this.caseTypes, this.trendMonths) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ atRiskCases() { return getAtRiskCases(this.filteredOpenCases, this.caseTypes, 0.25) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ performanceData() { return computePerformanceTable(this.filteredCompletedCases, this.caseTypes) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ sortedPerformanceData() { const data = [...this.performanceData] const col = this.sortColumn @@ -470,10 +489,12 @@ export default { return data }, // Chart configurations + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ donutSeries() { const types = this.slaData.byType.filter(t => t.total > 0) return types.map(t => t.withinSla) }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ donutOptions() { const types = this.slaData.byType.filter(t => t.total > 0) return { @@ -520,6 +541,7 @@ export default { }, } }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ histogramSeries() { const bins = this.distributionData.bins if (bins.every(b => b.count === 0)) return [] @@ -528,6 +550,7 @@ export default { data: bins.map(b => b.count), }] }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ histogramOptions() { const bins = this.distributionData.bins const annotations = [] @@ -592,12 +615,14 @@ export default { }, } }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ trendSeries() { return [{ name: t('procest', 'SLA Compliance %'), data: this.trendData.map(d => d.rate), }] }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ trendOptions() { return { chart: { @@ -658,6 +683,7 @@ export default { await this.loadData() }, methods: { + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ async loadData() { this.loading = true try { @@ -676,9 +702,11 @@ export default { this.loading = false } }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ applyPreset(key) { this.selectedPreset = key }, + /** @spec openspec/changes/doorlooptijd-dashboard/tasks.md */ sortTable(column) { if (this.sortColumn === column) { this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc' diff --git a/src/views/MyWork.vue b/src/views/MyWork.vue index 39fbb29d..ad5b5d8e 100644 --- a/src/views/MyWork.vue +++ b/src/views/MyWork.vue @@ -249,15 +249,19 @@ export default { } }, computed: { + /** @spec openspec/changes/my-work/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/my-work/tasks.md */ grouped() { return getGroupedMyWorkItems(this.cases, this.normalizedTasks) }, + /** @spec openspec/changes/my-work/tasks.md */ totalCount() { return this.grouped.totalCount }, + /** @spec openspec/changes/my-work/tasks.md */ caseCount() { const all = [ ...this.grouped.overdue, @@ -267,6 +271,7 @@ export default { ] return all.filter(i => i.type === 'case').length }, + /** @spec openspec/changes/my-work/tasks.md */ taskCount() { const all = [ ...this.grouped.overdue, @@ -276,6 +281,7 @@ export default { ] return all.filter(i => i.type === 'task').length }, + /** @spec openspec/changes/my-work/tasks.md */ tabs() { return [ { key: 'all', label: t('procest', 'All'), count: this.totalCount }, @@ -283,6 +289,7 @@ export default { { key: 'tasks', label: t('procest', 'Tasks'), count: this.taskCount }, ] }, + /** @spec openspec/changes/my-work/tasks.md */ filteredGroups() { if (this.activeTab === 'all') return this.grouped const filterType = this.activeTab === 'cases' ? 'case' : 'task' @@ -293,6 +300,7 @@ export default { noDeadline: this.grouped.noDeadline.filter(i => i.type === filterType), } }, + /** @spec openspec/changes/my-work/tasks.md */ completedItems() { const items = [] for (const c of this.completedCases) { @@ -313,6 +321,7 @@ export default { } return items }, + /** @spec openspec/changes/my-work/tasks.md */ filteredCompletedItems() { if (this.activeTab === 'all') return this.completedItems const filterType = this.activeTab === 'cases' ? 'case' : 'task' @@ -326,6 +335,7 @@ export default { getCaseTypeName(caseTypeId) { return this.caseTypeMap[caseTypeId] || '' }, + /** @spec openspec/changes/my-work/tasks.md */ async fetchData() { this.loading = true try { @@ -359,6 +369,7 @@ export default { this.loading = false } }, + /** @spec openspec/changes/my-work/tasks.md */ async onToggleCompleted() { if (!this.showCompleted) { this.completedCases = [] @@ -412,6 +423,7 @@ export default { console.warn('Failed to fetch completed items:', err) } }, + /** @spec openspec/changes/my-work/tasks.md */ onItemClick(item) { if (item.type === 'case') { this.$router.push({ name: 'CaseDetail', params: { id: item.id } }) diff --git a/src/views/Werkvoorraad.vue b/src/views/Werkvoorraad.vue index c0920f55..5752b38a 100644 --- a/src/views/Werkvoorraad.vue +++ b/src/views/Werkvoorraad.vue @@ -163,9 +163,11 @@ export default { } }, computed: { + /** @spec openspec/changes/my-work/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/my-work/tasks.md */ kpis() { const openCount = this.cases.length const overdueCount = this.cases.filter(c => isCaseOverdue(c, false)).length @@ -173,6 +175,7 @@ export default { const completedCount = this.completedCases.length return { openCount, overdueCount, unassignedCount, completedCount } }, + /** @spec openspec/changes/my-work/tasks.md */ tabs() { return [ { key: 'all', label: t('procest', 'All'), count: this.cases.length }, @@ -180,9 +183,11 @@ export default { { key: 'overdue', label: t('procest', 'Overdue'), count: this.cases.filter(c => isCaseOverdue(c, false)).length }, ] }, + /** @spec openspec/changes/my-work/tasks.md */ caseTypeOptions() { return this.caseTypes }, + /** @spec openspec/changes/my-work/tasks.md */ filteredCases() { let result = [...this.cases] @@ -224,6 +229,7 @@ export default { await this.loadData() }, methods: { + /** @spec openspec/changes/my-work/tasks.md */ async loadData() { this.loading = true @@ -264,11 +270,13 @@ export default { this.activeFilter = filter }, + /** @spec openspec/changes/my-work/tasks.md */ getCaseTypeName(caseTypeId) { const ct = this.caseTypes.find(t => t.id === caseTypeId) return ct?.title || '—' }, + /** @spec openspec/changes/my-work/tasks.md */ getStatusName(statusId) { const st = this.statusTypes.find(s => s.id === statusId) return st?.name || '—' @@ -278,6 +286,7 @@ export default { return isCaseOverdue(caseItem, false) }, + /** @spec openspec/changes/my-work/tasks.md */ formatDeadline(caseItem) { if (!caseItem.deadline) return '—' const days = getDaysRemaining(caseItem.deadline) @@ -289,6 +298,7 @@ export default { return `${dateStr} (${days} ${t('procest', 'days')})` }, + /** @spec openspec/changes/my-work/tasks.md */ openCase(caseItem) { this.$router.push({ name: 'CaseDetail', params: { id: caseItem.id } }) }, diff --git a/src/views/cases/CaseCreateDialog.vue b/src/views/cases/CaseCreateDialog.vue index 4e2892bf..b819f951 100644 --- a/src/views/cases/CaseCreateDialog.vue +++ b/src/views/cases/CaseCreateDialog.vue @@ -150,12 +150,14 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ objectStore() { return useObjectStore() }, isSubCaseMode() { return !!this.parentCase }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ intakeChannelOptions() { // REQ-INTAKE-11a: channel options (Balie, Telefoon, E-mail, Post, Website, Overig) // Default is "manual" per REQ-INTAKE-11b. @@ -169,16 +171,19 @@ export default { { value: 'overig', label: t('procest', 'Other') }, ] }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ dialogTitle() { return this.isSubCaseMode ? t('procest', 'Create Sub-case') : t('procest', 'New Case') }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ submitLabel() { return this.isSubCaseMode ? t('procest', 'Create sub-case') : t('procest', 'Create case') }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ availableCaseTypes() { const usable = this.caseTypes.filter(ct => isCaseTypeUsable(ct)) @@ -194,20 +199,24 @@ export default { return usable }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formattedDeadline() { if (!this.selectedCaseType?.processingDeadline) return '\u2014' return formatDuration(this.selectedCaseType.processingDeadline) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ initialStatusName() { if (this.statusTypes.length === 0) return '\u2014' const sorted = [...this.statusTypes].sort((a, b) => (a.order || 0) - (b.order || 0)) return sorted[0]?.name || '\u2014' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ initialStatusType() { if (this.statusTypes.length === 0) return null const sorted = [...this.statusTypes].sort((a, b) => (a.order || 0) - (b.order || 0)) return sorted[0] }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ calculatedDeadlineText() { if (!this.selectedCaseType?.processingDeadline) return '\u2014' const deadline = calculateDeadline(new Date(), this.selectedCaseType.processingDeadline) @@ -218,6 +227,7 @@ export default { await this.loadCaseTypes() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async loadCaseTypes() { this.loadingTypes = true const results = await this.objectStore.fetchCollection('caseType', { _limit: 100 }) @@ -225,6 +235,7 @@ export default { this.loadingTypes = false }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async onCaseTypeSelected(caseType) { this.form.caseType = caseType?.id || null this.errors.caseType = '' @@ -240,6 +251,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async submit() { const validation = validateCaseCreate(this.form, this.caseTypes) if (!validation.valid) { @@ -462,31 +474,38 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ usableCaseTypes() { return this.caseTypes.filter(ct => isCaseTypeUsable(ct)) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formattedDeadline() { if (!this.selectedCaseType?.processingDeadline) return '—' return formatDuration(this.selectedCaseType.processingDeadline) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ initialStatusName() { if (this.statusTypes.length === 0) return '—' const sorted = [...this.statusTypes].sort((a, b) => (a.order || 0) - (b.order || 0)) return sorted[0]?.name || '—' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ initialStatusType() { if (this.statusTypes.length === 0) return null const sorted = [...this.statusTypes].sort((a, b) => (a.order || 0) - (b.order || 0)) return sorted[0] }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ calculatedDeadlineText() { if (!this.selectedCaseType?.processingDeadline) return '—' const deadline = calculateDeadline(new Date(), this.selectedCaseType.processingDeadline) return deadline ? formatDate(deadline.toISOString()) : '—' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ locationRequired() { return this.selectedCaseType?.requiresLocation === true || this.selectedCaseType?.requiresLocation === 'true' @@ -496,6 +515,7 @@ export default { await this.loadCaseTypes() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async loadCaseTypes() { this.loadingTypes = true const results = await this.objectStore.fetchCollection('caseType', { _limit: 100 }) @@ -503,6 +523,7 @@ export default { this.loadingTypes = false }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async onCaseTypeSelected(caseType) { // Look up active workflow version for this case type this.activeWorkflowId = null @@ -531,12 +552,14 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ onLocationSave(geometry) { this.form.geometry = geometry this.showLocationPicker = false this.errors.geometry = '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async submit() { const validation = validateCaseCreate(this.form, this.caseTypes) if (!validation.valid) { diff --git a/src/views/cases/components/ActivityTimeline.vue b/src/views/cases/components/ActivityTimeline.vue index 9bf83fb6..290d3b55 100644 --- a/src/views/cases/components/ActivityTimeline.vue +++ b/src/views/cases/components/ActivityTimeline.vue @@ -71,11 +71,13 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ sortedActivity() { return [...this.activity].sort((a, b) => new Date(b.date) - new Date(a.date)) }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getIcon(type) { const icons = { created: '+', @@ -86,9 +88,11 @@ export default { } return icons[type] || '•' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatEntryDate(dateString) { return formatDate(dateString) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ addNote() { if (!this.noteText.trim()) return this.$emit('add-note', this.noteText.trim()) diff --git a/src/views/cases/components/AddParticipantDialog.vue b/src/views/cases/components/AddParticipantDialog.vue index 29b02164..ae4c3337 100644 --- a/src/views/cases/components/AddParticipantDialog.vue +++ b/src/views/cases/components/AddParticipantDialog.vue @@ -84,13 +84,16 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ canSubmit() { return !!this.selectedRoleType && !!this.selectedUser }, }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ mounted() { if (this.preSelectHandler) { const handlerType = this.roleTypes.find(rt => rt.genericRole === 'handler') @@ -100,6 +103,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async submit() { if (!this.canSubmit) return diff --git a/src/views/cases/components/AdvicePanel.vue b/src/views/cases/components/AdvicePanel.vue index a125ba47..d0b22496 100644 --- a/src/views/cases/components/AdvicePanel.vue +++ b/src/views/cases/components/AdvicePanel.vue @@ -162,15 +162,19 @@ export default { }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ adviceStore() { return useAdviceStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ requests() { return this.adviceStore.requests }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ loading() { return this.adviceStore.loading }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ canSubmit() { return this.newRequest.adviseur && this.newRequest.deadline }, @@ -179,6 +183,7 @@ export default { watch: { caseId: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ handler(newId) { if (newId) { this.adviceStore.fetchRequests(newId) @@ -190,12 +195,14 @@ export default { methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ defaultDeadline() { const d = new Date() d.setDate(d.getDate() + 14) return d.toISOString().split('T')[0] }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ isOverdue(request) { if (request.status !== 'aangevraagd' || !request.deadline) { return false @@ -207,6 +214,7 @@ export default { return this.adviceStore.getDaysToDeadline(request) }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ formatDate(dateStr) { if (!dateStr) { return '' @@ -214,6 +222,7 @@ export default { return new Date(dateStr).toLocaleDateString() }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ statusLabel(status) { const labels = { aangevraagd: t('procest', 'Requested'), @@ -223,6 +232,7 @@ export default { return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ statusClass(status) { return { 'advice-panel__status-badge--aangevraagd': status === 'aangevraagd', @@ -231,6 +241,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async submitRequest() { this.submitting = true try { @@ -251,10 +262,12 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async markReceived(request) { await this.adviceStore.markReceived(request.id) }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ viewDocument(request) { if (request.adviesDocument) { window.open(`/apps/files/?fileid=${request.adviesDocument}`, '_blank') diff --git a/src/views/cases/components/AdviceRequestPanel.vue b/src/views/cases/components/AdviceRequestPanel.vue index 72ae701c..73335ca1 100644 --- a/src/views/cases/components/AdviceRequestPanel.vue +++ b/src/views/cases/components/AdviceRequestPanel.vue @@ -113,6 +113,7 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ isFormValid() { return this.form.department.trim() !== '' && this.form.subject.trim() !== '' @@ -120,6 +121,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ getStatusLabel(status) { const labels = { open: this.t('procest', 'Open'), @@ -129,6 +131,7 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ getResponseLabel(response) { const labels = { positief: this.t('procest', 'Positive'), @@ -138,18 +141,21 @@ export default { } return labels[response] || response }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ formatDate(dateStr) { if (!dateStr) return '---' const date = new Date(dateStr) if (isNaN(date.getTime())) return dateStr return date.toLocaleDateString('nl-NL') }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ isOverdue(req) { if (!req.deadline || req.status === 'afgesloten' || req.status === 'advies_uitgebracht') { return false } return new Date(req.deadline) < new Date() }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ submitRequest() { this.$emit('create', { caseId: this.caseId, diff --git a/src/views/cases/components/AdviesAanvraagDialog.vue b/src/views/cases/components/AdviesAanvraagDialog.vue index bd037851..f32e2efc 100644 --- a/src/views/cases/components/AdviesAanvraagDialog.vue +++ b/src/views/cases/components/AdviesAanvraagDialog.vue @@ -110,11 +110,13 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ canSubmit() { return this.form.adviseur.trim() !== '' && this.form.deadline !== '' }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async submit() { if (!this.canSubmit) { return diff --git a/src/views/cases/components/AdviesPanel.vue b/src/views/cases/components/AdviesPanel.vue index c6463087..8f20ad14 100644 --- a/src/views/cases/components/AdviesPanel.vue +++ b/src/views/cases/components/AdviesPanel.vue @@ -111,6 +111,7 @@ export default { watch: { caseId: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ handler(value) { if (value) { this.fetchAdvies() @@ -119,6 +120,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async fetchAdvies() { this.loading = true try { @@ -130,13 +132,16 @@ export default { this.loading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ openCreateDialog() { this.showDialog = true }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ onCreated() { this.showDialog = false this.fetchAdvies() }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async onRemind(item) { try { await dispatchReminder(item.id || item.uuid) @@ -144,6 +149,7 @@ export default { console.error('Procest: failed to send reminder', error) } }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ async onMarkReceived(item) { try { await transitionStatus(item.id || item.uuid, { @@ -155,19 +161,23 @@ export default { console.error('Procest: failed to mark received', error) } }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ onViewDocument(item) { if (item.adviesDocument) { window.open(`/index.php/f/${item.adviesDocument}`, '_blank') } }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ typeLabel(type) { return type === 'intern' ? this.t(this.appName, 'Intern') : this.t(this.appName, 'Extern') }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ typeBadgeType(type) { return type === 'intern' ? 'neutral' : 'info' }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ statusLabel(status) { const labels = { aangevraagd: this.t(this.appName, 'Aangevraagd'), @@ -176,6 +186,7 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ statusBadgeType(status) { const types = { aangevraagd: 'info', @@ -184,12 +195,14 @@ export default { } return types[status] || 'neutral' }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ isOverdue(item) { if (item.status !== 'aangevraagd' || !item.deadline) { return false } return new Date(item.deadline) < new Date() }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ daysOverdue(item) { if (!item.deadline) { return 0 @@ -197,6 +210,7 @@ export default { const diff = Date.now() - new Date(item.deadline).getTime() return Math.max(0, Math.floor(diff / (1000 * 60 * 60 * 24))) }, + /** @spec openspec/changes/retrofit-2026-05-24-advice-management/tasks.md */ formatDate(value) { if (!value) { return '' diff --git a/src/views/cases/components/AiAssistantPanel.vue b/src/views/cases/components/AiAssistantPanel.vue index 36bf9fc8..f4189561 100644 --- a/src/views/cases/components/AiAssistantPanel.vue +++ b/src/views/cases/components/AiAssistantPanel.vue @@ -90,6 +90,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async askQuestion() { if (!this.question) return const q = this.question @@ -112,6 +113,7 @@ export default { this.askLoading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async loadSuggestions() { this.suggestionsLoading = true try { @@ -123,6 +125,7 @@ export default { this.suggestionsLoading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async loadSummary() { this.summaryLoading = true try { @@ -134,9 +137,11 @@ export default { this.summaryLoading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ handleAccept(suggestion) { this.$emit('suggestion-accepted', suggestion) }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ handleReject(suggestion, reason) { this.$emit('suggestion-rejected', suggestion, reason) }, diff --git a/src/views/cases/components/AiClassifyDialog.vue b/src/views/cases/components/AiClassifyDialog.vue index b8b50efd..067247e4 100644 --- a/src/views/cases/components/AiClassifyDialog.vue +++ b/src/views/cases/components/AiClassifyDialog.vue @@ -73,12 +73,14 @@ export default { } }, watch: { + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ show(val) { if (val) this.classify() }, }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async classify() { this.loading = true this.error = null @@ -93,6 +95,7 @@ export default { this.loading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ apply() { this.$emit('applied', { documentType: this.modifiedType, @@ -101,6 +104,7 @@ export default { }) this.$emit('close') }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ reject() { this.$emit('close') }, diff --git a/src/views/cases/components/AiConfidenceBadge.vue b/src/views/cases/components/AiConfidenceBadge.vue index f903ac2d..0b5efe38 100644 --- a/src/views/cases/components/AiConfidenceBadge.vue +++ b/src/views/cases/components/AiConfidenceBadge.vue @@ -25,14 +25,17 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ level() { if (this.confidence > 0.85) return 'high' if (this.confidence >= 0.60) return 'medium' return 'low' }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ label() { return `${Math.round(this.confidence * 100)}%` }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ ariaLabel() { const levelLabel = this.level === 'high' ? t('procest', 'high') diff --git a/src/views/cases/components/AiExtractDialog.vue b/src/views/cases/components/AiExtractDialog.vue index 658abc29..e806140d 100644 --- a/src/views/cases/components/AiExtractDialog.vue +++ b/src/views/cases/components/AiExtractDialog.vue @@ -94,17 +94,20 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ allSelected() { return this.fields.length > 0 && this.selectedFields.length === this.fields.length }, }, watch: { + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ show(val) { if (val) this.extract() }, }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async extract() { this.loading = true this.error = null @@ -121,9 +124,11 @@ export default { this.loading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ toggleAll(checked) { this.selectedFields = checked ? this.fields.map((f) => f.name) : [] }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ toggleField(name) { const idx = this.selectedFields.indexOf(name) if (idx >= 0) { @@ -132,6 +137,7 @@ export default { this.selectedFields.push(name) } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ applySelected() { const applied = {} for (const name of this.selectedFields) { diff --git a/src/views/cases/components/AiSuggestionCard.vue b/src/views/cases/components/AiSuggestionCard.vue index f677d30e..fa8477ac 100644 --- a/src/views/cases/components/AiSuggestionCard.vue +++ b/src/views/cases/components/AiSuggestionCard.vue @@ -64,10 +64,12 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ formatValue(value) { if (typeof value === 'object') return JSON.stringify(value, null, 2) return String(value) }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ handleReject() { this.$emit('reject', this.suggestion, this.rejectReason) this.showRejectInput = false diff --git a/src/views/cases/components/AiSummaryPanel.vue b/src/views/cases/components/AiSummaryPanel.vue index ea6e718d..8a9613fb 100644 --- a/src/views/cases/components/AiSummaryPanel.vue +++ b/src/views/cases/components/AiSummaryPanel.vue @@ -43,6 +43,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async generate() { this.loading = true this.error = null @@ -55,6 +56,7 @@ export default { this.loading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ saveAsNote() { this.$emit('save-note', this.summary) }, diff --git a/src/views/cases/components/AppointmentBookingDialog.vue b/src/views/cases/components/AppointmentBookingDialog.vue index 112f5f41..ddbfdcbc 100644 --- a/src/views/cases/components/AppointmentBookingDialog.vue +++ b/src/views/cases/components/AppointmentBookingDialog.vue @@ -80,12 +80,14 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ canBook() { return this.form.productId && this.form.locationId && this.form.date && this.form.time }, }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async loadSlots() { if (!this.form.productId || !this.form.locationId || !this.form.date) return try { @@ -95,6 +97,7 @@ export default { this.timeslots = [] } }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async book() { try { await bookAppointment({ diff --git a/src/views/cases/components/AppointmentSection.vue b/src/views/cases/components/AppointmentSection.vue index b33463a1..f63739af 100644 --- a/src/views/cases/components/AppointmentSection.vue +++ b/src/views/cases/components/AppointmentSection.vue @@ -58,6 +58,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async loadAppointments() { try { const response = await listAppointments(this.caseId) @@ -66,10 +67,12 @@ export default { this.appointments = [] } }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ formatDateTime(dt) { if (!dt) return '-' return new Date(dt).toLocaleString('nl-NL', { dateStyle: 'long', timeStyle: 'short' }) }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ statusLabel(status) { const labels = { scheduled: t('procest', 'Scheduled'), @@ -80,14 +83,17 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async cancel(apt) { await cancelAppointment(apt.uuid || apt.id) await this.loadAppointments() }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async noShow(apt) { await markNoShow(apt.uuid || apt.id) await this.loadAppointments() }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async onBooked() { this.showBooking = false await this.loadAppointments() diff --git a/src/views/cases/components/BerichtenboxComposeDialog.vue b/src/views/cases/components/BerichtenboxComposeDialog.vue index 475021be..ce3e4bb4 100644 --- a/src/views/cases/components/BerichtenboxComposeDialog.vue +++ b/src/views/cases/components/BerichtenboxComposeDialog.vue @@ -96,6 +96,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ validate() { this.errors = {} if (!this.form.bsn) { @@ -109,6 +110,7 @@ export default { } return Object.keys(this.errors).length === 0 }, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ async send() { if (!this.validate()) return this.sending = true diff --git a/src/views/cases/components/BerichtenboxTab.vue b/src/views/cases/components/BerichtenboxTab.vue index 87d6099e..b1b8e1a8 100644 --- a/src/views/cases/components/BerichtenboxTab.vue +++ b/src/views/cases/components/BerichtenboxTab.vue @@ -55,6 +55,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ async loadMessages() { try { const response = await listMessages(this.caseId) @@ -63,10 +64,12 @@ export default { this.messages = [] } }, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ formatDate(dt) { if (!dt) return '-' return new Date(dt).toLocaleString('nl-NL', { dateStyle: 'short', timeStyle: 'short' }) }, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ statusLabel(status) { const labels = { draft: t('procest', 'Draft'), @@ -78,6 +81,7 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ async onSent() { this.showCompose = false await this.loadMessages() diff --git a/src/views/cases/components/CaseTransferDialog.vue b/src/views/cases/components/CaseTransferDialog.vue index 28ce4659..78d2aee1 100644 --- a/src/views/cases/components/CaseTransferDialog.vue +++ b/src/views/cases/components/CaseTransferDialog.vue @@ -92,6 +92,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async submitTransfer() { this.saving = true try { diff --git a/src/views/cases/components/ConsultationPanel.vue b/src/views/cases/components/ConsultationPanel.vue index 714ab3a8..10c6469a 100644 --- a/src/views/cases/components/ConsultationPanel.vue +++ b/src/views/cases/components/ConsultationPanel.vue @@ -156,11 +156,13 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ openCount() { return this.consultations.filter( c => c.status === 'open' || c.status === 'in_behandeling', ).length }, + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ isFormValid() { return this.newForm.adviesInstantie.trim() !== '' && this.newForm.onderwerp.trim() !== '' @@ -168,6 +170,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ getStatusLabel(status) { const labels = { open: this.t('procest', 'Open'), @@ -177,6 +180,7 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ getAdviceLabel(advies) { const labels = { positief: this.t('procest', 'Positive'), @@ -186,17 +190,20 @@ export default { } return labels[advies] || advies }, + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ formatDate(dateStr) { if (!dateStr) return '---' const d = new Date(dateStr) if (isNaN(d.getTime())) return dateStr return d.toLocaleDateString('nl-NL') }, + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ isOverdue(cons) { if (!cons.uiterlijkeReactiedatum) return false if (cons.status === 'afgesloten' || cons.status === 'advies_uitgebracht') return false return new Date(cons.uiterlijkeReactiedatum) < new Date() }, + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ conditions(cons) { if (!cons.voorwaarden) return [] try { @@ -208,6 +215,7 @@ export default { return [] } }, + /** @spec openspec/changes/retrofit-2026-05-24-consultation-management/tasks.md */ submitCreate() { this.$emit('create', { parentZaak: this.caseId, diff --git a/src/views/cases/components/CreateShareDialog.vue b/src/views/cases/components/CreateShareDialog.vue index ed7969c3..8eaadef9 100644 --- a/src/views/cases/components/CreateShareDialog.vue +++ b/src/views/cases/components/CreateShareDialog.vue @@ -151,6 +151,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async createShare() { this.saving = true try { @@ -177,6 +178,7 @@ export default { this.saving = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async copyLink() { if (this.generatedLink) { await navigator.clipboard.writeText(this.generatedLink) diff --git a/src/views/cases/components/CustomPropertiesPanel.vue b/src/views/cases/components/CustomPropertiesPanel.vue index ab8c0078..223a9885 100644 --- a/src/views/cases/components/CustomPropertiesPanel.vue +++ b/src/views/cases/components/CustomPropertiesPanel.vue @@ -98,23 +98,27 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ filledCount() { return this.propertyDefinitions.filter(pd => this.getPropertyValue(pd.id)).length }, }, watch: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ caseTypeId() { if (this.caseTypeId) { this.loadData() } }, }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async mounted() { if (this.caseTypeId) { await this.loadData() } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async loadData() { this.loading = true const objectStore = useObjectStore() @@ -135,11 +139,13 @@ export default { this.loading = false }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getPropertyValue(propDefId) { const prop = this.caseProperties.find(cp => cp.propertyDefinition === propDefId) return prop?.value || '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ startEdit(propDef) { if (this.isReadOnly) return this.editing = true @@ -147,18 +153,21 @@ export default { this.editValue = this.getPropertyValue(propDef.id) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ editAll() { if (this.propertyDefinitions.length > 0) { this.startEdit(this.propertyDefinitions[0]) } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ cancelEdit() { this.editing = false this.editingPropId = null this.editValue = '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async saveProperty(propDef) { const objectStore = useObjectStore() const existing = this.caseProperties.find(cp => cp.propertyDefinition === propDef.id) diff --git a/src/views/cases/components/DeadlinePanel.vue b/src/views/cases/components/DeadlinePanel.vue index e5305781..6ebbcc1c 100644 --- a/src/views/cases/components/DeadlinePanel.vue +++ b/src/views/cases/components/DeadlinePanel.vue @@ -94,24 +94,31 @@ export default { }, emits: ['extend'], computed: { + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ formattedStartDate() { return formatDate(this.startDate) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ formattedDeadline() { return formatDate(this.deadline) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ formattedProcessingDeadline() { return this.processingDeadline ? formatDuration(this.processingDeadline) : '—' }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ formattedExtensionPeriod() { return this.extensionPeriod ? formatDuration(this.extensionPeriod) : '—' }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ daysElapsed() { return getDaysElapsed(this.startDate) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ countdown() { return formatDeadlineCountdown({ deadline: this.deadline }, this.isFinal) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ canExtend() { return this.extensionAllowed && this.extensionCount === 0 && !this.isFinal }, diff --git a/src/views/cases/components/DecisionsSection.vue b/src/views/cases/components/DecisionsSection.vue index ef5363b6..5220b868 100644 --- a/src/views/cases/components/DecisionsSection.vue +++ b/src/views/cases/components/DecisionsSection.vue @@ -163,6 +163,7 @@ export default { } }, computed: { + /** @spec openspec/changes/roles-decisions/tasks.md */ sortedDecisions() { return [...this.decisions].sort((a, b) => { const dateA = a.decisionDate || a.created || '' @@ -170,6 +171,7 @@ export default { return dateB.localeCompare(dateA) // newest first }) }, + /** @spec openspec/changes/roles-decisions/tasks.md */ decisionTypeOptions() { return this.decisionTypes.map(dt => ({ value: dt.id, @@ -181,6 +183,7 @@ export default { await this.loadData() }, methods: { + /** @spec openspec/changes/roles-decisions/tasks.md */ async loadData() { this.loading = true const objectStore = useObjectStore() @@ -211,15 +214,18 @@ export default { return getDecisionValidity(decision) }, + /** @spec openspec/changes/roles-decisions/tasks.md */ formatDate(dateStr) { return formatDecisionDate(dateStr) }, + /** @spec openspec/changes/roles-decisions/tasks.md */ getDecisionTypeName(typeId) { const dt = this.decisionTypes.find(t => t.id === typeId) return dt?.name || '' }, + /** @spec openspec/changes/roles-decisions/tasks.md */ editDecision(decision) { if (this.isReadOnly) return this.editingDecision = decision @@ -234,6 +240,7 @@ export default { this.showCreateForm = true }, + /** @spec openspec/changes/roles-decisions/tasks.md */ closeForm() { this.showCreateForm = false this.editingDecision = null @@ -241,6 +248,7 @@ export default { this.formErrors = {} }, + /** @spec openspec/changes/roles-decisions/tasks.md */ async saveDecision() { const validation = validateDecision(this.form) if (!validation.valid) { @@ -276,6 +284,7 @@ export default { await this.loadData() }, + /** @spec openspec/changes/roles-decisions/tasks.md */ async deleteDecision() { if (!this.editingDecision) return if (!confirm(t('procest', 'Are you sure you want to delete this decision?'))) return diff --git a/src/views/cases/components/DocumentAssessmentPanel.vue b/src/views/cases/components/DocumentAssessmentPanel.vue index c07db7ef..58924aa3 100644 --- a/src/views/cases/components/DocumentAssessmentPanel.vue +++ b/src/views/cases/components/DocumentAssessmentPanel.vue @@ -121,6 +121,7 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ counts() { const result = { openbaar: 0, deels_openbaar: 0, niet_openbaar: 0, pending: 0 } for (const doc of this.documents) { @@ -141,6 +142,7 @@ export default { getGrounds(docId) { return this.assessments[docId]?.grounds || [] }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ setAssessment(docId, value) { this.$emit('update:assessment', { documentId: docId, @@ -148,6 +150,7 @@ export default { grounds: value === 'niet_openbaar' ? (this.assessments[docId]?.grounds || []) : [], }) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ setGrounds(docId, value) { this.$emit('update:assessment', { documentId: docId, diff --git a/src/views/cases/components/DocumentChecklist.vue b/src/views/cases/components/DocumentChecklist.vue index e179a605..68c9eb30 100644 --- a/src/views/cases/components/DocumentChecklist.vue +++ b/src/views/cases/components/DocumentChecklist.vue @@ -82,23 +82,27 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ presentCount() { return this.documentTypes.filter(dt => this.isDocPresent(dt.id)).length }, }, watch: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ caseTypeId() { if (this.caseTypeId) { this.loadData() } }, }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async mounted() { if (this.caseTypeId) { await this.loadData() } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async loadData() { this.loading = true const objectStore = useObjectStore() @@ -123,6 +127,7 @@ export default { return this.caseDocuments.some(cd => cd.documentType === docTypeId) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getUploadDate(docTypeId) { const doc = this.caseDocuments.find(cd => cd.documentType === docTypeId) if (!doc?.registrationDate) return null @@ -132,6 +137,7 @@ export default { }) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getStatusName(statusTypeId) { const st = this.statusTypes.find(s => s.id === statusTypeId) return st?.name || statusTypeId diff --git a/src/views/cases/components/EmailComposer.vue b/src/views/cases/components/EmailComposer.vue index 254975bf..a6713d3d 100644 --- a/src/views/cases/components/EmailComposer.vue +++ b/src/views/cases/components/EmailComposer.vue @@ -144,11 +144,13 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ isValid() { return this.form.to.trim() !== '' && this.form.subject.trim() !== '' && this.form.body.trim() !== '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ unresolvedVars() { const pattern = /\{\{(\w+)\}\}/g const vars = [] @@ -163,27 +165,33 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatVariable(varName) { return '{{' + varName + '}}' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ onTemplateSelected(template) { if (!template) return this.form.subject = template.subjectPattern || '' this.form.body = template.body || '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ insertVariable(varName) { this.form.body += '{{' + varName + '}}' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ previewEmail() { this.previewSubject = this.resolveVars(this.form.subject) this.previewBody = this.resolveVars(this.form.body) this.showPreview = true }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ resolveVars(text) { return text.replace(/\{\{(\w+)\}\}/g, (match, key) => { return this.caseData[key] || match }) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ sendEmail() { this.sending = true this.$emit('send', { diff --git a/src/views/cases/components/EmailThread.vue b/src/views/cases/components/EmailThread.vue index 1b261bca..7589e4dc 100644 --- a/src/views/cases/components/EmailThread.vue +++ b/src/views/cases/components/EmailThread.vue @@ -87,6 +87,7 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ sortedMessages() { return [...this.messages].sort((a, b) => { const dateA = new Date(a.sentAt || a.receivedAt || 0) @@ -96,6 +97,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatDateTime(dateStr) { if (!dateStr) return '---' const d = new Date(dateStr) @@ -108,6 +110,7 @@ export default { minute: '2-digit', }) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ truncateBody(body) { if (!body) return '' const id = 'temp' @@ -119,6 +122,7 @@ export default { isExpanded(id) { return this.expandedMessages[id] === true }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ toggleExpand(id) { this.$set(this.expandedMessages, id, !this.expandedMessages[id]) }, diff --git a/src/views/cases/components/EnforcementPanel.vue b/src/views/cases/components/EnforcementPanel.vue index a6748f28..6c92b8cb 100644 --- a/src/views/cases/components/EnforcementPanel.vue +++ b/src/views/cases/components/EnforcementPanel.vue @@ -114,18 +114,23 @@ export default { }, computed: { + /** @spec openspec/changes/vth-module/tasks.md */ enforcementStore() { return useEnforcementStore() }, + /** @spec openspec/changes/vth-module/tasks.md */ actions() { return this.enforcementStore.actions }, + /** @spec openspec/changes/vth-module/tasks.md */ activeAction() { return this.enforcementStore.activeAction }, + /** @spec openspec/changes/vth-module/tasks.md */ totalVerbeurd() { return this.enforcementStore.totalVerbeurd }, + /** @spec openspec/changes/vth-module/tasks.md */ loading() { return this.enforcementStore.loading }, @@ -134,6 +139,7 @@ export default { watch: { caseId: { immediate: true, + /** @spec openspec/changes/vth-module/tasks.md */ handler(newId) { if (newId) { this.enforcementStore.fetchActions(newId) @@ -145,6 +151,7 @@ export default { methods: { t, + /** @spec openspec/changes/vth-module/tasks.md */ statusLabel(status) { const labels = { opgelegd: t('procest', 'Imposed'), @@ -155,6 +162,7 @@ export default { return labels[status] || status }, + /** @spec openspec/changes/vth-module/tasks.md */ onActionCreated() { this.enforcementStore.fetchActions(this.caseId) }, diff --git a/src/views/cases/components/EnforcementWizard.vue b/src/views/cases/components/EnforcementWizard.vue index e01471f7..529ed11c 100644 --- a/src/views/cases/components/EnforcementWizard.vue +++ b/src/views/cases/components/EnforcementWizard.vue @@ -181,10 +181,12 @@ export default { }, computed: { + /** @spec openspec/changes/vth-module/tasks.md */ enforcementStore() { return useEnforcementStore() }, + /** @spec openspec/changes/vth-module/tasks.md */ ernstOptions() { return [ { value: 'gering', label: t('procest', 'Minor (gering)') }, @@ -193,6 +195,7 @@ export default { ] }, + /** @spec openspec/changes/vth-module/tasks.md */ gedragOptions() { return [ { value: 'goedwillend', label: t('procest', 'Cooperative (goedwillend)') }, @@ -202,6 +205,7 @@ export default { ] }, + /** @spec openspec/changes/vth-module/tasks.md */ suggestedIntervention() { if (!this.ernst || !this.gedrag) { return null @@ -217,6 +221,7 @@ export default { return this.interventie?.toLowerCase().includes('bestuursdwang') }, + /** @spec openspec/changes/vth-module/tasks.md */ canProceed() { if (this.step === 1) { return this.ernst && this.gedrag @@ -229,6 +234,7 @@ export default { }, watch: { + /** @spec openspec/changes/vth-module/tasks.md */ suggestedIntervention(val) { if (val && !this.interventie) { this.interventie = val @@ -243,6 +249,7 @@ export default { methods: { t, + /** @spec openspec/changes/vth-module/tasks.md */ async submit() { this.submitting = true try { @@ -265,6 +272,7 @@ export default { } }, + /** @spec openspec/changes/vth-module/tasks.md */ mapInterventionToType(intervention) { const lower = (intervention || '').toLowerCase() if (lower.includes('bestuursdwang')) { diff --git a/src/views/cases/components/InspectionChecklistPanel.vue b/src/views/cases/components/InspectionChecklistPanel.vue index 1a7b658f..9072be4e 100644 --- a/src/views/cases/components/InspectionChecklistPanel.vue +++ b/src/views/cases/components/InspectionChecklistPanel.vue @@ -131,36 +131,43 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ allItemsAnswered() { if (!this.activeChecklist) return false return this.activeChecklist.items.every( (item, idx) => this.itemResults[idx] && this.itemResults[idx].result, ) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ failedCount() { return Object.values(this.itemResults).filter(r => r.result === 'nee').length }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ overallResult() { return this.failedCount > 0 ? 'niet_conform' : 'conform' }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ startInspection() { this.activeChecklist = { ...this.selectedChecklist } this.itemResults = {} }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ setResult(index, value) { this.$set(this.itemResults, index, { ...(this.itemResults[index] || {}), result: value, }) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ setComment(index, value) { this.$set(this.itemResults, index, { ...(this.itemResults[index] || {}), comment: value, }) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async submitInspection() { this.saving = true try { diff --git a/src/views/cases/components/InspectionPanel.vue b/src/views/cases/components/InspectionPanel.vue index 960c002b..c2c9b81b 100644 --- a/src/views/cases/components/InspectionPanel.vue +++ b/src/views/cases/components/InspectionPanel.vue @@ -215,25 +215,32 @@ export default { }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ inspectionStore() { return useInspectionStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ reports() { return this.inspectionStore.reports }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ activeChecklists() { return this.inspectionStore.activeChecklists }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ loading() { return this.inspectionStore.loading }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ totalPhases() { return this.activeChecklists.length }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ completedPhases() { const completedChecklistIds = new Set(this.reports.map((r) => r.checklist)) return this.activeChecklists.filter((c) => completedChecklistIds.has(c.id)).length }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ progressPercent() { if (this.totalPhases === 0) { return 0 @@ -245,6 +252,7 @@ export default { watch: { caseId: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ handler(newId) { if (newId) { this.inspectionStore.fetchReports(newId) @@ -253,12 +261,14 @@ export default { }, caseTypeId: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ handler(newId) { if (newId) { this.inspectionStore.fetchChecklists(newId) } }, }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ selectedChecklist(checklist) { if (checklist) { this.formResults = (checklist.items || []).map((item) => ({ @@ -275,6 +285,7 @@ export default { methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ resultLabel(result) { const labels = { conform: t('procest', 'Conform'), @@ -284,6 +295,7 @@ export default { return labels[result] || result }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ formatDate(dateStr) { if (!dateStr) { return '' @@ -291,16 +303,19 @@ export default { return new Date(dateStr).toLocaleDateString() }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ toggleReport(reportId) { this.expandedReport = this.expandedReport === reportId ? null : reportId }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ closeForm() { this.showChecklistForm = false this.selectedChecklist = null this.formResults = [] }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async submitReport() { this.submitting = true try { diff --git a/src/views/cases/components/LocationTab.vue b/src/views/cases/components/LocationTab.vue index 5521f34e..77eb67c0 100644 --- a/src/views/cases/components/LocationTab.vue +++ b/src/views/cases/components/LocationTab.vue @@ -111,6 +111,7 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ parsedGeometry() { if (!this.geometry) return null if (typeof this.geometry === 'string') { @@ -125,6 +126,7 @@ export default { hasGeometry() { return this.parsedGeometry && this.parsedGeometry.type }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ mapCenter() { const geo = this.parsedGeometry if (!geo) return [52.1326, 5.2913] @@ -134,12 +136,14 @@ export default { // For polygons, use centroid return this.calculateCentroid(geo) }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ mapGeometry() { return { geometry: this.parsedGeometry, properties: {}, } }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ area() { const geo = this.parsedGeometry if (!geo || geo.type !== 'Polygon') return 0 @@ -149,12 +153,14 @@ export default { watch: { geometry: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ handler() { this.loadAddress() }, }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ async loadAddress() { const geo = this.parsedGeometry if (!geo) { @@ -166,11 +172,13 @@ export default { this.address = await gisStore.reverseGeocode(center[0], center[1]) }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ onLocationSave(newGeometry) { this.showPicker = false this.$emit('update-geometry', newGeometry) }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ calculateCentroid(geo) { if (geo.type === 'Point') { return [geo.coordinates[1], geo.coordinates[0]] @@ -188,6 +196,7 @@ export default { return [52.1326, 5.2913] }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ calculateArea(ring) { // Shoelace formula for approximate area in m2 if (!ring || ring.length < 3) return 0 @@ -201,6 +210,7 @@ export default { return Math.abs(area / 2) * 111320 * 111320 * Math.cos((ring[0][1] * Math.PI) / 180) }, + /** @spec openspec/changes/retrofit-2026-05-25-case-location/tasks.md */ formatArea(sqm) { if (sqm > 10000) { return `${(sqm / 10000).toFixed(2)} ha` diff --git a/src/views/cases/components/MilestoneProgress.vue b/src/views/cases/components/MilestoneProgress.vue index f60c8e36..27839d72 100644 --- a/src/views/cases/components/MilestoneProgress.vue +++ b/src/views/cases/components/MilestoneProgress.vue @@ -72,6 +72,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ stepClass(milestone, index) { return { 'milestone-progress__step--reached': milestone.reached, @@ -82,6 +83,7 @@ export default { && (index === 0 || !this.milestones[index - 1]?.reached), } }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ dotClass(milestone, index) { return { 'milestone-progress__dot--reached': milestone.reached, @@ -92,12 +94,14 @@ export default { && (index === 0 || !this.milestones[index - 1]?.reached), } }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ formatDate(dateStr) { if (!dateStr) return '' const date = new Date(dateStr) if (isNaN(date.getTime())) return dateStr return date.toLocaleDateString('nl-NL', { day: 'numeric', month: 'short' }) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ onDotClick(milestone, index) { if (milestone.reached) { this.$emit('reverse', { milestone, index }) diff --git a/src/views/cases/components/MilestoneProgressBar.vue b/src/views/cases/components/MilestoneProgressBar.vue index e7ad8585..aa456f5f 100644 --- a/src/views/cases/components/MilestoneProgressBar.vue +++ b/src/views/cases/components/MilestoneProgressBar.vue @@ -30,6 +30,7 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ fillClass() { if (this.percentage === 100) return 'milestone-bar__fill--complete' if (this.percentage >= 50) return 'milestone-bar__fill--progress' diff --git a/src/views/cases/components/ParticipantsSection.vue b/src/views/cases/components/ParticipantsSection.vue index 34434e1e..a710494e 100644 --- a/src/views/cases/components/ParticipantsSection.vue +++ b/src/views/cases/components/ParticipantsSection.vue @@ -126,9 +126,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ roleTypeMap() { const map = {} for (const rt of this.roleTypes) { @@ -136,6 +138,7 @@ export default { } return map }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ groupedRoles() { const groups = {} for (const role of this.roles) { @@ -153,6 +156,7 @@ export default { await this.fetchData() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async fetchData() { this.loading = true try { @@ -175,6 +179,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async fetchUsers() { try { const response = await fetch('/ocs/v2.php/cloud/users/details?format=json&limit=100', { @@ -196,6 +201,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async resolveDisplayNames() { for (const role of this.roles) { const uid = role.participant @@ -219,6 +225,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getInitials(name) { if (!name) return '?' const parts = name.split(/[\s.]+/) @@ -228,21 +235,25 @@ export default { return name.slice(0, 2).toUpperCase() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ isHandlerRole(role) { const rt = this.roleTypeMap[role.roleType] return rt?.genericRole === 'handler' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ onAssignHandler() { this.preSelectHandler = true this.showAddDialog = true }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ closeAddDialog() { this.showAddDialog = false this.preSelectHandler = false }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async onRoleCreated(newRole) { this.closeAddDialog() await this.fetchData() @@ -251,24 +262,28 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async removeRole(role) { if (!confirm(t('procest', 'Remove this participant?'))) return await this.objectStore.deleteObject('role', role.id) await this.fetchData() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ startReassign(role) { this.reassigningHandler = true this.reassignRole = role this.reassignUser = null }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ cancelReassign() { this.reassigningHandler = false this.reassignRole = null this.reassignUser = null }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async onReassignSelected(user) { if (!user || !this.reassignRole) return diff --git a/src/views/cases/components/QuickStatusDropdown.vue b/src/views/cases/components/QuickStatusDropdown.vue index 1b3d52e7..6c505ac1 100644 --- a/src/views/cases/components/QuickStatusDropdown.vue +++ b/src/views/cases/components/QuickStatusDropdown.vue @@ -39,9 +39,11 @@ export default { } }, computed: { + /** @spec openspec/specs/status-transition-engine/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ statusOptions() { return [...this.statusTypes].sort((a, b) => (a.order || 0) - (b.order || 0)) }, @@ -50,6 +52,7 @@ export default { this.selectedStatus = this.statusTypes.find(st => st.id === this.caseObj.status) || null }, methods: { + /** @spec openspec/specs/status-transition-engine/spec.md */ async onStatusChange(newStatus) { if (!newStatus || newStatus.id === this.caseObj.status) return diff --git a/src/views/cases/components/ResultSection.vue b/src/views/cases/components/ResultSection.vue index f55dc46d..a8aca3cd 100644 --- a/src/views/cases/components/ResultSection.vue +++ b/src/views/cases/components/ResultSection.vue @@ -43,13 +43,16 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ resultType() { if (!this.result?.resultType) return null return this.resultTypes.find(t => t.id === this.result.resultType) || null }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ resultTypeName() { return this.resultType?.name || '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ archivalInfo() { if (!this.resultType) return '' const rt = this.resultType @@ -74,6 +77,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatPeriod(isoDuration) { if (!isoDuration) return '' const match = isoDuration.match(/P(\d+)Y/) diff --git a/src/views/cases/components/ShareTab.vue b/src/views/cases/components/ShareTab.vue index b343f6dc..ded26e5a 100644 --- a/src/views/cases/components/ShareTab.vue +++ b/src/views/cases/components/ShareTab.vue @@ -70,6 +70,7 @@ export default { }, emits: ['revoke', 'create-token-share', 'create-partner-share'], methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ permissionLabel(level) { const labels = { bekijken: t('procest', 'View only'), @@ -78,6 +79,7 @@ export default { } return labels[level] || level }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatDate(dateString) { if (!dateString) return '' return new Date(dateString).toLocaleDateString('nl-NL', { diff --git a/src/views/cases/components/StatusTimeline.vue b/src/views/cases/components/StatusTimeline.vue index 5b18fc44..b5465db4 100644 --- a/src/views/cases/components/StatusTimeline.vue +++ b/src/views/cases/components/StatusTimeline.vue @@ -42,9 +42,11 @@ export default { }, }, computed: { + /** @spec openspec/specs/status-transition-engine/spec.md */ currentIndex() { return this.statusTypes.findIndex(st => st.id === this.currentStatusId) }, + /** @spec openspec/specs/status-transition-engine/spec.md */ historyMap() { const map = {} for (const entry of this.statusHistory) { @@ -54,6 +56,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/status-transition-engine/spec.md */ isPassed(statusType) { const idx = this.statusTypes.findIndex(st => st.id === statusType.id) return idx < this.currentIndex @@ -61,10 +64,12 @@ export default { isCurrent(statusType) { return statusType.id === this.currentStatusId }, + /** @spec openspec/specs/status-transition-engine/spec.md */ isFuture(statusType) { const idx = this.statusTypes.findIndex(st => st.id === statusType.id) return idx > this.currentIndex }, + /** @spec openspec/specs/status-transition-engine/spec.md */ stepClass(statusType) { return { 'status-timeline__step--passed': this.isPassed(statusType), @@ -72,6 +77,7 @@ export default { 'status-timeline__step--future': this.isFuture(statusType), } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ dotClass(statusType) { return { 'status-timeline__dot--passed': this.isPassed(statusType), @@ -79,11 +85,13 @@ export default { 'status-timeline__dot--future': this.isFuture(statusType), } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ lineClass(statusType) { return { 'status-timeline__line--passed': this.isPassed(statusType) || this.isCurrent(statusType), } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ getStatusDate(statusType) { const date = this.historyMap[statusType.id] if (!date) return null diff --git a/src/views/cases/components/SubCasesSection.vue b/src/views/cases/components/SubCasesSection.vue index 9b94ad33..2b81db40 100644 --- a/src/views/cases/components/SubCasesSection.vue +++ b/src/views/cases/components/SubCasesSection.vue @@ -85,21 +85,26 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ completedCount() { return this.subCases.filter(sc => sc.endDate).length }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ totalCount() { return this.subCases.length }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ rollUpText() { return t('procest', 'Sub-cases ({completed}/{total} completed)', { completed: this.completedCount, total: this.totalCount, }) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ canCreateSubCase() { // Hide when: parent case is closed, current case is itself a sub-case, // or case type has empty subCaseTypes @@ -113,6 +118,7 @@ export default { await this.fetchSubCases() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async fetchSubCases() { this.loading = true try { @@ -139,11 +145,13 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getStatusName(subCase) { if (!subCase.status) return '\u2014' return this.statusTypeCache[subCase.status]?.name || '\u2014' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ getStatusClass(subCase) { if (!subCase.status) return '' const st = this.statusTypeCache[subCase.status] @@ -151,11 +159,13 @@ export default { return 'status-badge--active' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatDeadline(deadline) { if (!deadline) return '\u2014' return formatDate(deadline) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ openSubCase(subCase) { this.$router.push({ name: 'CaseDetail', params: { id: subCase.id } }) }, diff --git a/src/views/cases/components/VoorstellenPanel.vue b/src/views/cases/components/VoorstellenPanel.vue index c4401593..b2d5d099 100644 --- a/src/views/cases/components/VoorstellenPanel.vue +++ b/src/views/cases/components/VoorstellenPanel.vue @@ -97,6 +97,7 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ objectStore() { return useObjectStore() }, @@ -105,6 +106,7 @@ export default { await this.loadVoorstellen() }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async loadVoorstellen() { this.loading = true try { @@ -123,12 +125,15 @@ export default { isActive(voorstel) { return !['besloten', 'gearchiveerd'].includes(voorstel.status) }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatType(type) { return TYPE_LABELS[type] || type || '-' }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatStatus(status) { return STATUS_LABELS[status] || status || '-' }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatStepProgress(voorstel) { let steps = [] if (voorstel.routeSnapshot) { @@ -143,6 +148,7 @@ export default { if (!steps.length) return `${voorstel.currentStep}` return `${voorstel.currentStep}/${steps.length}` }, + /** @spec openspec/specs/parafering-actions/spec.md */ async onCreated() { this.showCreate = false await this.loadVoorstellen() diff --git a/src/views/cases/components/WooIntakeForm.vue b/src/views/cases/components/WooIntakeForm.vue index dfca89f7..f0701d6d 100644 --- a/src/views/cases/components/WooIntakeForm.vue +++ b/src/views/cases/components/WooIntakeForm.vue @@ -154,6 +154,7 @@ export default { } }, computed: { + /** @spec openspec/changes/woo-case-type/tasks.md */ calculatedDeadline() { if (!this.form.ontvangstdatum) { return '---' @@ -172,6 +173,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/woo-case-type/tasks.md */ update(field, value) { this.$emit('update', { field, value }) }, diff --git a/src/views/cases/components/WorkflowTransitions.vue b/src/views/cases/components/WorkflowTransitions.vue index 9fa3e38a..151a240a 100644 --- a/src/views/cases/components/WorkflowTransitions.vue +++ b/src/views/cases/components/WorkflowTransitions.vue @@ -80,12 +80,15 @@ export default { } }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ workflowStore() { return useWorkflowStore() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ versionNotice() { if (!this.workflowTemplate || !this.activeVersion) return null if (this.caseData.workflowVersion @@ -98,6 +101,7 @@ export default { } return null }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ requiredStepsInfo() { if (!this.workflowTemplate) return [] const steps = typeof this.workflowTemplate.steps === 'string' @@ -118,11 +122,13 @@ export default { }, watch: { 'caseData.status': { + /** @spec openspec/specs/workflow-definition-model/spec.md */ handler() { this.computeTransitions() }, }, tasks: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ handler() { this.computeTransitions() }, @@ -133,6 +139,7 @@ export default { await this.loadWorkflow() }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ async loadWorkflow() { if (!this.caseData.caseType) return @@ -162,6 +169,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ computeTransitions() { if (!this.workflowTemplate) { this.availableTransitions = [] @@ -177,6 +185,7 @@ export default { ) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async executeTransition(transition) { if (!transition.available) return @@ -216,6 +225,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async createStepTasks(statusId) { if (!this.workflowTemplate) return @@ -249,6 +259,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async terminateOptionalTasks(fromStatusId) { if (!this.workflowTemplate) return diff --git a/src/views/cases/components/beroep/BeroepEscalationPanel.vue b/src/views/cases/components/beroep/BeroepEscalationPanel.vue index 6eebd145..e3e144dc 100644 --- a/src/views/cases/components/beroep/BeroepEscalationPanel.vue +++ b/src/views/cases/components/beroep/BeroepEscalationPanel.vue @@ -80,6 +80,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async escalate() { this.escalating = true const bezwaarStore = useBezwaarStore() @@ -94,6 +95,7 @@ export default { this.$emit('escalated', beroepCase) } }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ navigateToBeroep() { if (this.linkedBeroepCase) { this.$router.push({ diff --git a/src/views/cases/components/beroep/CourtProceedingsPanel.vue b/src/views/cases/components/beroep/CourtProceedingsPanel.vue index 3c2ca0bc..22869408 100644 --- a/src/views/cases/components/beroep/CourtProceedingsPanel.vue +++ b/src/views/cases/components/beroep/CourtProceedingsPanel.vue @@ -94,6 +94,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getRulingLabel(outcome) { const labels = { beroep_gegrond: t('procest', 'Appeal upheld'), @@ -103,6 +104,7 @@ export default { } return labels[outcome] || outcome }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ navigateToParent() { if (this.parentCase) { this.$router.push({ @@ -111,6 +113,7 @@ export default { }) } }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async saveRuling() { const objectStore = useObjectStore() await objectStore.saveObject('case', { diff --git a/src/views/cases/components/bezwaar/AdvisoryReportPanel.vue b/src/views/cases/components/bezwaar/AdvisoryReportPanel.vue index 1a5618fa..5b1c30a4 100644 --- a/src/views/cases/components/bezwaar/AdvisoryReportPanel.vue +++ b/src/views/cases/components/bezwaar/AdvisoryReportPanel.vue @@ -159,23 +159,28 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ hasReport() { const bezwaarStore = useBezwaarStore() return bezwaarStore.hasAdvisoryReport }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ report() { const bezwaarStore = useBezwaarStore() return bezwaarStore.currentAdvisoryReport }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ showCompositionWarning() { return this.committeeMembers.length < 3 }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ hasConflictOfInterest() { if (!this.primaryDecisionMaker) return false return this.committeeMembers.some((m) => m.id === this.primaryDecisionMaker) }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getAdviceTypeLabel(type) { const labels = { gegrond: t('procest', 'Upheld'), @@ -185,6 +190,7 @@ export default { } return labels[type] || type }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async save() { this.saving = true const bezwaarStore = useBezwaarStore() diff --git a/src/views/cases/components/bezwaar/BezwaarDecisionForm.vue b/src/views/cases/components/bezwaar/BezwaarDecisionForm.vue index 4d8cac4f..502c2cea 100644 --- a/src/views/cases/components/bezwaar/BezwaarDecisionForm.vue +++ b/src/views/cases/components/bezwaar/BezwaarDecisionForm.vue @@ -193,20 +193,24 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ hasDecision() { const bezwaarStore = useBezwaarStore() return bezwaarStore.hasAppealDecision }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ decision() { const bezwaarStore = useBezwaarStore() return bezwaarStore.currentAppealDecision }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ hasAdvisoryReport() { const bezwaarStore = useBezwaarStore() return bezwaarStore.hasAdvisoryReport }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getDispositionLabel(type) { const labels = { gegrond: t('procest', 'Upheld'), @@ -216,6 +220,7 @@ export default { } return labels[type] || type }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ validate() { this.errors = {} @@ -231,6 +236,7 @@ export default { return Object.keys(this.errors).length === 0 }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async save() { if (!this.validate()) return diff --git a/src/views/cases/components/bezwaar/BezwaarIntakeForm.vue b/src/views/cases/components/bezwaar/BezwaarIntakeForm.vue index e77d7a85..5bf38f2a 100644 --- a/src/views/cases/components/bezwaar/BezwaarIntakeForm.vue +++ b/src/views/cases/components/bezwaar/BezwaarIntakeForm.vue @@ -187,6 +187,7 @@ export default { this.loadExistingObjection() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async loadExistingObjection() { const bezwaarStore = useBezwaarStore() if (bezwaarStore.currentObjection) { @@ -206,6 +207,7 @@ export default { } } }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ checkTimeliness() { if (!this.form.receivedDate || !this.besluitDate) { this.timelinessResult = null @@ -220,6 +222,7 @@ export default { this.form.isTimely = this.timelinessResult.isTimely this.calculateDeadlines() }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ calculateDeadlines() { if (!this.form.receivedDate) { this.deadlines = null @@ -230,6 +233,7 @@ export default { this.deadlines = bezwaarStore.calculateDeadlines(this.form.receivedDate) this.$emit('deadlines-calculated', this.deadlines) }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ validate() { this.errors = {} @@ -245,6 +249,7 @@ export default { return Object.keys(this.errors).length === 0 }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async save() { if (!this.validate()) return diff --git a/src/views/cases/components/bezwaar/BezwaarTimeline.vue b/src/views/cases/components/bezwaar/BezwaarTimeline.vue index 97a4efa3..975d7bbc 100644 --- a/src/views/cases/components/bezwaar/BezwaarTimeline.vue +++ b/src/views/cases/components/bezwaar/BezwaarTimeline.vue @@ -35,6 +35,7 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ timelineItems() { const items = [] const bezwaarStore = useBezwaarStore() @@ -122,6 +123,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getAdviceTypeLabel(type) { const labels = { gegrond: t('procest', 'Upheld'), @@ -131,6 +133,7 @@ export default { } return labels[type] || type }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getDispositionLabel(type) { const labels = { gegrond: t('procest', 'Upheld'), diff --git a/src/views/cases/components/bezwaar/DeadlineIndicator.vue b/src/views/cases/components/bezwaar/DeadlineIndicator.vue index 66ca6297..a017bb1a 100644 --- a/src/views/cases/components/bezwaar/DeadlineIndicator.vue +++ b/src/views/cases/components/bezwaar/DeadlineIndicator.vue @@ -34,10 +34,12 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ status() { const bezwaarStore = useBezwaarStore() return bezwaarStore.getDeadlineStatus(this.deadline, this.isSuspended) }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ indicatorClass() { if (this.status.isSuspended) return 'deadline-indicator--suspended' if (this.status.isOverdue) return 'deadline-indicator--overdue' diff --git a/src/views/cases/components/bezwaar/HearingPanel.vue b/src/views/cases/components/bezwaar/HearingPanel.vue index 11bb038b..38d239de 100644 --- a/src/views/cases/components/bezwaar/HearingPanel.vue +++ b/src/views/cases/components/bezwaar/HearingPanel.vue @@ -201,14 +201,17 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ activeHearing() { const bezwaarStore = useBezwaarStore() return bezwaarStore.activeHearing }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ isHearingWaived() { const bezwaarStore = useBezwaarStore() return bezwaarStore.isHearingWaived }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ waiverReason() { const bezwaarStore = useBezwaarStore() const waived = bezwaarStore.hearingSessions.find((h) => h.hearingWaived === true) @@ -216,6 +219,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ getHearingStatusLabel(status) { const labels = { gepland: t('procest', 'Scheduled'), @@ -226,6 +230,7 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ formatDateTime(dateStr) { if (!dateStr) return '—' try { @@ -240,6 +245,7 @@ export default { return dateStr } }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async scheduleHearing() { const bezwaarStore = useBezwaarStore() await bezwaarStore.createHearingSession({ @@ -256,6 +262,7 @@ export default { this.showScheduleDialog = false this.$emit('hearing-scheduled') }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async sendInvitations() { if (!this.activeHearing) return const bezwaarStore = useBezwaarStore() @@ -264,6 +271,7 @@ export default { status: 'uitgenodigd', }) }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async recordMinutes() { if (!this.activeHearing || !this.minutesForm.summary) return const bezwaarStore = useBezwaarStore() @@ -276,6 +284,7 @@ export default { this.showMinutesDialog = false this.$emit('hearing-completed') }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async recordWaiver() { const bezwaarStore = useBezwaarStore() await bezwaarStore.createHearingSession({ @@ -291,6 +300,7 @@ export default { this.showWaiverDialog = false this.$emit('hearing-waived') }, + /** @spec openspec/changes/retrofit-2026-05-24-bezwaar-lifecycle/tasks.md */ async cancelHearing() { if (!this.activeHearing) return const bezwaarStore = useBezwaarStore() diff --git a/src/views/cases/widgets/CaseDocumentsWidget.vue b/src/views/cases/widgets/CaseDocumentsWidget.vue index e45ae8fe..1bb36cb6 100644 --- a/src/views/cases/widgets/CaseDocumentsWidget.vue +++ b/src/views/cases/widgets/CaseDocumentsWidget.vue @@ -37,6 +37,7 @@ export default { }, methods: { formatDate, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ getFileIcon(mimeType) { if (!mimeType) return '📄' if (mimeType.includes('pdf')) return '📕' diff --git a/src/views/cases/widgets/CasePropertiesWidget.vue b/src/views/cases/widgets/CasePropertiesWidget.vue index e49dd268..1e02573a 100644 --- a/src/views/cases/widgets/CasePropertiesWidget.vue +++ b/src/views/cases/widgets/CasePropertiesWidget.vue @@ -160,6 +160,7 @@ export default { watch: { caseData: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ handler(data) { if (data && data.title !== undefined) { this.form = { @@ -174,6 +175,7 @@ export default { }, methods: { formatDate, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async save() { const validation = validateCaseUpdate(this.form) if (!validation.valid) { diff --git a/src/views/cases/widgets/CaseTasksWidget.vue b/src/views/cases/widgets/CaseTasksWidget.vue index c084c585..7469d068 100644 --- a/src/views/cases/widgets/CaseTasksWidget.vue +++ b/src/views/cases/widgets/CaseTasksWidget.vue @@ -63,9 +63,11 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ sortedTasks() { return sortTasks(this.tasks) }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ completedCount() { return this.tasks.filter(t => t.status === 'completed').length }, @@ -75,6 +77,7 @@ export default { isDueToday, getOverdueText, formatDueDate, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ dueDateClass(task) { if (isOverdue(task)) return 'task-due--overdue' if (isDueToday(task)) return 'task-due--today' diff --git a/src/views/cases/widgets/CaseTimelineWidget.vue b/src/views/cases/widgets/CaseTimelineWidget.vue index a55cc5d0..f1a1d1c9 100644 --- a/src/views/cases/widgets/CaseTimelineWidget.vue +++ b/src/views/cases/widgets/CaseTimelineWidget.vue @@ -100,6 +100,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onStatusSelected(status) { if (!status || status.id === this.currentStatusId) { this.selectedStatus = null @@ -115,6 +116,7 @@ export default { this.selectedStatus = null } }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ confirmStatusChange() { let resultName = '' if (this.resultTypes.length > 0) { @@ -140,6 +142,7 @@ export default { this.resultText = '' this.selectedResultType = null }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ cancelStatusChange() { this.showResultPrompt = false this.pendingStatusChange = null diff --git a/src/views/complaints/components/ComplaintCreateDialog.vue b/src/views/complaints/components/ComplaintCreateDialog.vue index 657a0df0..51a91f1c 100644 --- a/src/views/complaints/components/ComplaintCreateDialog.vue +++ b/src/views/complaints/components/ComplaintCreateDialog.vue @@ -115,6 +115,7 @@ export default { } }, computed: { + /** @spec openspec/changes/complaint-management/tasks.md */ channelOptions() { return [ { id: 'balie', label: this.t('procest', 'Counter') }, @@ -125,6 +126,7 @@ export default { { id: 'socialmedia', label: this.t('procest', 'Social media') }, ] }, + /** @spec openspec/changes/complaint-management/tasks.md */ priorityOptions() { return [ { id: 'laag', label: this.t('procest', 'Low') }, @@ -135,6 +137,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/complaint-management/tasks.md */ validate() { this.errors = {} if (!this.form.onderwerp.trim()) { @@ -145,6 +148,7 @@ export default { } return Object.keys(this.errors).length === 0 }, + /** @spec openspec/changes/complaint-management/tasks.md */ async create() { if (!this.validate()) return this.saving = true diff --git a/src/views/dashboard/ActivityFeed.vue b/src/views/dashboard/ActivityFeed.vue index 9b2efddd..895c065f 100644 --- a/src/views/dashboard/ActivityFeed.vue +++ b/src/views/dashboard/ActivityFeed.vue @@ -74,9 +74,11 @@ export default { }, emits: ['view-all', 'retry'], methods: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ typeIcon(type) { return TYPE_ICONS[type] || '•' }, + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ formatTime(date) { return formatRelativeTime(date) }, diff --git a/src/views/dashboard/CaseMapWidget.vue b/src/views/dashboard/CaseMapWidget.vue index 37a499ae..2118aacf 100644 --- a/src/views/dashboard/CaseMapWidget.vue +++ b/src/views/dashboard/CaseMapWidget.vue @@ -26,6 +26,7 @@ export default { } }, computed: { + /** @spec openspec/specs/case-map-overview/spec.md */ geometries() { return this.cases .filter(c => c.geometry) @@ -47,6 +48,7 @@ export default { .filter(Boolean) }, }, + /** @spec openspec/specs/case-map-overview/spec.md */ async created() { const objectStore = useObjectStore() const currentUser = OC?.currentUser || '' @@ -60,6 +62,7 @@ export default { } }, methods: { + /** @spec openspec/specs/case-map-overview/spec.md */ getColor(caseObj) { if (caseObj.endDate) return '#4CAF50' if (caseObj.deadline) { @@ -68,6 +71,7 @@ export default { } return '#2196F3' }, + /** @spec openspec/specs/case-map-overview/spec.md */ onMarkerClick(properties) { if (properties?.id) { this.$router?.push({ name: 'CaseDetail', params: { id: properties.id } }) diff --git a/src/views/dashboard/CasesByType.vue b/src/views/dashboard/CasesByType.vue index eb8ce6b3..0c5ea0ac 100644 --- a/src/views/dashboard/CasesByType.vue +++ b/src/views/dashboard/CasesByType.vue @@ -87,18 +87,22 @@ export default { }, emits: ['retry', 'bar-click'], computed: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ maxCount() { return Math.max(1, ...this.typeData.map(t => t.count)) }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ barWidth(count) { const pct = (count / this.maxCount) * 100 return `max(20px, ${pct}%)` }, + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ barColor(index) { return BAR_COLORS[index % BAR_COLORS.length] }, + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ onBarClick(item) { // Default behaviour: navigate to /cases filtered by caseType. // Consumers can override by listening for `@bar-click` and diff --git a/src/views/dashboard/KpiCards.vue b/src/views/dashboard/KpiCards.vue index 6ef547a6..1e4d6b69 100644 --- a/src/views/dashboard/KpiCards.vue +++ b/src/views/dashboard/KpiCards.vue @@ -40,6 +40,7 @@ export default { }, emits: ['click-card'], computed: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ cards() { return [ { diff --git a/src/views/dashboard/OverduePanel.vue b/src/views/dashboard/OverduePanel.vue index 4c1c65a7..a7ec4fe9 100644 --- a/src/views/dashboard/OverduePanel.vue +++ b/src/views/dashboard/OverduePanel.vue @@ -75,6 +75,7 @@ export default { }, emits: ['click-case', 'view-all', 'retry'], methods: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ severityClass(daysOverdue) { if (daysOverdue > 7) return 'overdue-panel__row--severe' if (daysOverdue > 2) return 'overdue-panel__row--moderate' diff --git a/src/views/dashboard/StatusChart.vue b/src/views/dashboard/StatusChart.vue index 311461b3..7e411505 100644 --- a/src/views/dashboard/StatusChart.vue +++ b/src/views/dashboard/StatusChart.vue @@ -66,15 +66,18 @@ export default { }, emits: ['retry'], computed: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ maxCount() { return Math.max(1, ...this.statusData.map(s => s.count)) }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ barWidth(count) { const pct = (count / this.maxCount) * 100 return `max(20px, ${pct}%)` }, + /** @spec openspec/changes/retrofit-2026-05-24-dashboard/tasks.md */ barColor(index) { return BAR_COLORS[index % BAR_COLORS.length] }, diff --git a/src/views/public/PublicAppointmentPage.vue b/src/views/public/PublicAppointmentPage.vue index 22ba0739..0b108a30 100644 --- a/src/views/public/PublicAppointmentPage.vue +++ b/src/views/public/PublicAppointmentPage.vue @@ -47,6 +47,7 @@ export default { data() { return { loading: true, error: null, appointment: null, cancelled: false } }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async mounted() { try { const url = generateUrl(`/apps/procest/api/public/appointment/${this.token}`) @@ -60,10 +61,12 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ formatDateTime(dt) { if (!dt) return '-' return new Date(dt).toLocaleString('nl-NL', { dateStyle: 'long', timeStyle: 'short' }) }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ statusLabel(status) { const labels = { scheduled: t('procest', 'Scheduled'), @@ -73,6 +76,7 @@ export default { } return labels[status] || status }, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ async cancelAppointment() { try { const url = generateUrl(`/apps/procest/api/public/appointment/${this.token}/cancel`) diff --git a/src/views/public/PublicCaseView.vue b/src/views/public/PublicCaseView.vue index 1cfc1d3e..fc2d5267 100644 --- a/src/views/public/PublicCaseView.vue +++ b/src/views/public/PublicCaseView.vue @@ -124,6 +124,7 @@ export default { this.loadShareData() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async loadShareData() { this.loading = true this.error = '' @@ -149,6 +150,7 @@ export default { this.loading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async submitPassword() { this.passwordError = '' await this.loadShareData() @@ -156,6 +158,7 @@ export default { this.passwordError = t('procest', 'Incorrect password') } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async submitComment() { try { const response = await fetch(`/apps/procest/api/public/share/${this.token}/comment`, { @@ -174,6 +177,7 @@ export default { // Comment submission failed silently } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatDate(dateString) { if (!dateString) return '' return new Date(dateString).toLocaleDateString('nl-NL', { diff --git a/src/views/public/PublicStatusPage.vue b/src/views/public/PublicStatusPage.vue index ac9b2fa1..143739ea 100644 --- a/src/views/public/PublicStatusPage.vue +++ b/src/views/public/PublicStatusPage.vue @@ -72,6 +72,7 @@ export default { this.loadStatus() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ async loadStatus() { this.loading = true try { @@ -89,6 +90,7 @@ export default { this.loading = false } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-management/tasks.md */ formatDate(dateString) { if (!dateString) return '' return new Date(dateString).toLocaleDateString('nl-NL', { diff --git a/src/views/settings/AdminRoot.vue b/src/views/settings/AdminRoot.vue index 03e358b8..4d3a23f8 100644 --- a/src/views/settings/AdminRoot.vue +++ b/src/views/settings/AdminRoot.vue @@ -103,11 +103,13 @@ export default { messageType: 'success', } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async created() { await initializeStores() this.storesReady = true }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async reimport() { this.reimporting = true this.message = '' diff --git a/src/views/settings/CaseTypeAdmin.vue b/src/views/settings/CaseTypeAdmin.vue index a09fa125..68400f35 100644 --- a/src/views/settings/CaseTypeAdmin.vue +++ b/src/views/settings/CaseTypeAdmin.vue @@ -29,18 +29,22 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ openDetail(id) { this.currentId = id this.currentView = 'detail' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ openCreate() { this.currentId = null this.currentView = 'detail' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ showList() { this.currentView = 'list' this.currentId = null }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ onSaved(id) { this.currentId = id }, diff --git a/src/views/settings/CaseTypeDetail.vue b/src/views/settings/CaseTypeDetail.vue index ec899694..3ca9bd3c 100644 --- a/src/views/settings/CaseTypeDetail.vue +++ b/src/views/settings/CaseTypeDetail.vue @@ -178,12 +178,14 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ objectStore() { return useObjectStore() }, isCreate() { return !this.caseTypeId }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ tabs() { return [ { id: 'general', label: t('procest', 'General') }, @@ -195,6 +197,7 @@ export default { ] }, }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async mounted() { if (!this.isCreate) { await this.loadCaseType() @@ -203,6 +206,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async loadCaseType() { this.loadingDetail = true const data = await this.objectStore.fetchObject('caseType', this.caseTypeId) @@ -222,6 +226,7 @@ export default { this.loadingDetail = false }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ onFieldUpdate(field, value) { this.form[field] = value // Clear validation error for this field @@ -232,6 +237,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async save() { this.saveError = '' this.saveSuccess = false @@ -264,6 +270,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async publish() { this.publishErrors = [] this.saveError = '' @@ -285,6 +292,7 @@ export default { await this.save() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async unpublish() { const confirmed = confirm( t('procest', 'Unpublishing this case type will prevent new cases from being created. Existing cases will continue to function. Continue?'), diff --git a/src/views/settings/CaseTypeList.vue b/src/views/settings/CaseTypeList.vue index 5633e283..05579283 100644 --- a/src/views/settings/CaseTypeList.vue +++ b/src/views/settings/CaseTypeList.vue @@ -87,27 +87,34 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ settingsStore() { return useSettingsStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ loading() { return this.objectStore.loading.caseType || false }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ caseTypes() { return this.objectStore.collections.caseType || [] }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ defaultCaseTypeId() { return this.settingsStore.config?.default_case_type || '' }, }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async mounted() { this.schema = await this.objectStore.fetchSchema('caseType') await this.fetchCaseTypes() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async fetchCaseTypes() { await this.objectStore.fetchCollection('caseType', { _limit: 100 }) for (const ct of this.caseTypes) { @@ -115,6 +122,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async loadStatusTypeCount(caseTypeId) { const statusTypes = await this.objectStore.fetchCollection('statusType', { '_filters[caseType]': caseTypeId, @@ -128,10 +136,12 @@ export default { return this.defaultCaseTypeId === id }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ formatDeadline(duration) { return formatDuration(duration) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ formatValidity(ct) { if (!ct.validFrom) return '\u2014' const from = new Date(ct.validFrom).toLocaleDateString('nl-NL', { month: 'short', year: 'numeric' }) @@ -142,6 +152,7 @@ export default { return t('procest', '{from} \u2014 (no end)', { from }) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ validityClass(ct) { if (!ct.validUntil) return '' const now = new Date() @@ -150,10 +161,12 @@ export default { return '' }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ selectCaseType(row) { this.$emit('select', row.id) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async setDefault(ct) { this.error = '' if (ct.isDraft) { @@ -164,6 +177,7 @@ export default { await this.settingsStore.saveSettings(config) }, + /** @spec openspec/changes/retrofit-2026-05-24-case-types/tasks.md */ async confirmDelete(ct) { this.error = '' diff --git a/src/views/settings/MapLayerSettings.vue b/src/views/settings/MapLayerSettings.vue index 1ae84ce5..0a0aa84d 100644 --- a/src/views/settings/MapLayerSettings.vue +++ b/src/views/settings/MapLayerSettings.vue @@ -186,12 +186,14 @@ export default { form: this.emptyForm(), } }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async created() { const gisStore = useGisStore() await gisStore.fetchLayers() this.layers = gisStore.layers }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ emptyForm() { return { title: '', @@ -208,12 +210,14 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ editLayer(layer) { this.editingLayer = layer this.form = { ...layer } this.showAddDialog = true }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async saveLayer() { const gisStore = useGisStore() if (this.editingLayer) { @@ -225,6 +229,7 @@ export default { this.closeDialog() }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async removeLayer(layer) { if (!confirm(this.t('procest', 'Delete layer "{title}"?', { title: layer.title }))) { return @@ -234,12 +239,14 @@ export default { this.layers = gisStore.layers }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ addPreset(preset) { this.form = { ...this.emptyForm(), ...preset } this.showAddDialog = true this.showPresets = false }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ async testLayer(layer) { this.testResult = null try { @@ -257,12 +264,14 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ closeDialog() { this.showAddDialog = false this.editingLayer = null this.form = this.emptyForm() }, + /** @spec openspec/changes/retrofit-2026-05-24-wms-wfs-layers/tasks.md */ truncateUrl(url) { if (!url) return '' return url.length > 60 ? url.substring(0, 57) + '...' : url diff --git a/src/views/settings/Settings.vue b/src/views/settings/Settings.vue index 425449bd..f0dbf068 100644 --- a/src/views/settings/Settings.vue +++ b/src/views/settings/Settings.vue @@ -111,13 +111,16 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ settingsStore() { return useSettingsStore() }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ loading() { return this.settingsStore.isLoading }, }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async mounted() { const config = await this.settingsStore.fetchSettings() if (config) { @@ -125,6 +128,7 @@ export default { } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async save() { this.saved = false const result = await this.settingsStore.saveSettings(this.form) diff --git a/src/views/settings/WorkflowEditor.vue b/src/views/settings/WorkflowEditor.vue index 5486d3da..86ee1b44 100644 --- a/src/views/settings/WorkflowEditor.vue +++ b/src/views/settings/WorkflowEditor.vue @@ -150,21 +150,27 @@ export default { } }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ workflowStore() { return useWorkflowStore() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ steps() { return this.workflowStore.parsedSteps }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ transitions() { return this.workflowStore.parsedTransitions }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ nodePositions() { return this.workflowStore.parsedNodePositions }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ selectedTransitionData() { if (!this.selectedTransition) return null return this.transitions.find((t) => t.id === this.selectedTransition) || null @@ -179,18 +185,21 @@ export default { * * @return {boolean} Whether the current template is published. */ + /** @spec openspec/specs/workflow-definition-model/spec.md */ isPublished() { const tpl = this.workflowStore.currentTemplate || null if (!tpl) return false // isDraft true ⇒ editable; isDraft false ⇒ published/deprecated return tpl.isDraft === false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ canvasStyle() { return { transform: `translate(${this.panOffset.x}px, ${this.panOffset.y}px) scale(${this.zoom})`, transformOrigin: '0 0', } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ svgViewBox() { return '0 0 2000 1500' }, @@ -199,6 +208,7 @@ export default { await this.loadData() }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ async loadData() { // Load status types for this case type this.statusNodes = await this.objectStore.fetchCollection('statusType', { @@ -228,6 +238,7 @@ export default { this.ensureNodePositions() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ ensureNodePositions() { const positions = { ...this.nodePositions } let changed = false @@ -245,12 +256,14 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ getStepsForStatus(statusId) { return this.steps .filter((s) => s.status === statusId) .sort((a, b) => a.order - b.order) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ getNodeCenter(statusId) { const pos = this.nodePositions[statusId] if (!pos) return { x: 0, y: 0 } @@ -261,22 +274,26 @@ export default { }, // --- Selection --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ selectNode(statusId) { this.selectedNode = statusId this.selectedTransition = null this.selectedStep = null }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ selectTransition(transitionId) { this.selectedTransition = transitionId this.selectedNode = null this.selectedStep = null }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ editTransition(transitionId) { this.selectTransition(transitionId) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onStepClick(step) { this.selectedStep = { ...step } this.selectedNode = null @@ -284,6 +301,7 @@ export default { }, // --- Node drag --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ onNodeDragStart(statusId, event) { this.draggingNode = { statusId, @@ -292,6 +310,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onCanvasMouseMove(event) { if (this.draggingNode) { const rect = this.$refs.canvas.getBoundingClientRect() @@ -314,6 +333,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onCanvasMouseUp() { if (this.draggingNode) { this.draggingNode = null @@ -325,6 +345,7 @@ export default { this.panning = false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onCanvasMouseDown(event) { // Only pan if clicking empty canvas area if (event.target === this.$refs.canvas || event.target.classList.contains('workflow-editor__svg')) { @@ -339,6 +360,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onCanvasWheel(event) { event.preventDefault() const delta = event.deltaY > 0 ? -0.1 : 0.1 @@ -346,6 +368,7 @@ export default { }, // --- Connection drawing --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ onConnectionStart(statusId, event) { const center = this.getNodeCenter(statusId) this.drawingConnection = { @@ -357,6 +380,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onConnectionEnd(statusId) { if (this.drawingConnection && this.drawingConnection.fromStatus !== statusId) { this.workflowStore.addTransition( @@ -369,10 +393,12 @@ export default { }, // --- Palette drag & drop --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ onPaletteDragStart(type) { this.paletteDragType = type }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async onCanvasDrop(event) { if (this.paletteDragType === 'status') { const rect = this.$refs.canvas.getBoundingClientRect() @@ -397,12 +423,14 @@ export default { }, // --- Step management --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ onAddStep(statusId) { const step = this.workflowStore.addStep(statusId) this.selectedStep = { ...step } this.$emit('dirty') }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onStepUpdate(updatedStep) { this.workflowStore.updateStep(updatedStep.id, updatedStep) this.selectedStep = { ...updatedStep } @@ -410,11 +438,13 @@ export default { }, // --- Transition management --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ onTransitionUpdate(updatedTransition) { this.workflowStore.updateTransition(updatedTransition.id, updatedTransition) this.$emit('dirty') }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onTransitionDelete(transitionId) { this.workflowStore.removeTransition(transitionId) this.selectedTransition = null @@ -422,6 +452,7 @@ export default { }, // --- Public API --- + /** @spec openspec/specs/workflow-definition-model/spec.md */ validate() { this.validationErrors = this.workflowStore.validateWorkflow() return this.validationErrors.length === 0 diff --git a/src/views/settings/ZgwMappingSettings.vue b/src/views/settings/ZgwMappingSettings.vue index 80dc8583..c6c2b63d 100644 --- a/src/views/settings/ZgwMappingSettings.vue +++ b/src/views/settings/ZgwMappingSettings.vue @@ -148,12 +148,15 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ store() { return useZgwMappingStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ mappings() { return this.store.mappings }, + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ resourceKeys() { return [ 'zaak', 'zaaktype', 'status', 'statustype', @@ -166,6 +169,7 @@ export default { await this.store.fetchMappings() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ editMapping(key) { const mapping = this.mappings[key] || {} this.editingKey = key @@ -181,6 +185,7 @@ export default { this.jsonError = null }, + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ async saveMapping() { this.jsonError = null @@ -213,6 +218,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-zgw-api-mapping/tasks.md */ async resetMapping(key) { await this.store.resetMapping(key) this.saved = true diff --git a/src/views/settings/components/DurationPicker.vue b/src/views/settings/components/DurationPicker.vue index a4420ef8..f35969b2 100644 --- a/src/views/settings/components/DurationPicker.vue +++ b/src/views/settings/components/DurationPicker.vue @@ -47,6 +47,7 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ daysInput() { if (!this.value) return '' const parsed = parseDuration(this.value) @@ -58,6 +59,7 @@ export default { if (parsed.days) totalDays += parsed.days return String(totalDays) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ displayValue() { if (!this.value || !isValidDuration(this.value)) return '' const days = parseInt(this.daysInput, 10) @@ -69,6 +71,7 @@ export default { } return `${this.value} (${days} ${t('procest', 'days')})` }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ presets() { if (this.presetType === 'extension') { return [ @@ -86,6 +89,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ onDaysChange(val) { const days = parseInt(val, 10) if (!days || days < 0) { @@ -94,6 +98,7 @@ export default { } this.$emit('input', `P${days}D`) }, + /** @spec openspec/changes/retrofit-2026-05-24-milestone-tracking/tasks.md */ selectPreset(preset) { this.$emit('input', preset.value) }, diff --git a/src/views/settings/components/LhsMatrixAdmin.vue b/src/views/settings/components/LhsMatrixAdmin.vue index 17f5729f..98dd5820 100644 --- a/src/views/settings/components/LhsMatrixAdmin.vue +++ b/src/views/settings/components/LhsMatrixAdmin.vue @@ -82,10 +82,12 @@ export default { }, computed: { + /** @spec openspec/changes/vth-module/tasks.md */ enforcementStore() { return useEnforcementStore() }, + /** @spec openspec/changes/vth-module/tasks.md */ ernstLabels() { return [ { key: 'gering', label: t('procest', 'Minor (gering)') }, @@ -94,6 +96,7 @@ export default { ] }, + /** @spec openspec/changes/vth-module/tasks.md */ gedragLabels() { return [ { key: 'goedwillend', label: t('procest', 'Cooperative') }, @@ -103,6 +106,7 @@ export default { ] }, + /** @spec openspec/changes/vth-module/tasks.md */ interventionOptions() { return [ 'Waarschuwing', @@ -117,6 +121,7 @@ export default { }, }, + /** @spec openspec/changes/vth-module/tasks.md */ async mounted() { await this.enforcementStore.loadLhsMatrix() this.matrix = JSON.parse(JSON.stringify(this.enforcementStore.lhsMatrix)) @@ -126,6 +131,7 @@ export default { methods: { t, + /** @spec openspec/changes/vth-module/tasks.md */ updateCell(ernst, gedrag, value) { if (!this.matrix[ernst]) { this.matrix[ernst] = {} @@ -135,6 +141,7 @@ export default { this.saved = false }, + /** @spec openspec/changes/vth-module/tasks.md */ async saveMatrix() { this.saving = true try { @@ -146,6 +153,7 @@ export default { } }, + /** @spec openspec/changes/vth-module/tasks.md */ resetMatrix() { this.matrix = { gering: { diff --git a/src/views/settings/components/ParafeerStapEditor.vue b/src/views/settings/components/ParafeerStapEditor.vue index 718a6182..1e208425 100644 --- a/src/views/settings/components/ParafeerStapEditor.vue +++ b/src/views/settings/components/ParafeerStapEditor.vue @@ -101,14 +101,17 @@ export default { } }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ emitUpdate(steps) { const renumbered = steps.map((s, i) => ({ ...s, order: i + 1 })) this.$emit('update:steps', renumbered) }, + /** @spec openspec/specs/parafering-actions/spec.md */ updateStep(idx, key, value) { const next = this.steps.map((s, i) => i === idx ? { ...s, [key]: value } : s) this.emitUpdate(next) }, + /** @spec openspec/specs/parafering-actions/spec.md */ addStep() { this.emitUpdate([ ...this.steps, @@ -121,10 +124,12 @@ export default { }, ]) }, + /** @spec openspec/specs/parafering-actions/spec.md */ removeStep(idx) { const next = this.steps.filter((_, i) => i !== idx) this.emitUpdate(next) }, + /** @spec openspec/specs/parafering-actions/spec.md */ moveStep(idx, delta) { const target = idx + delta if (target < 0 || target >= this.steps.length) return diff --git a/src/views/settings/components/StepConfigPanel.vue b/src/views/settings/components/StepConfigPanel.vue index d189ea63..b507124a 100644 --- a/src/views/settings/components/StepConfigPanel.vue +++ b/src/views/settings/components/StepConfigPanel.vue @@ -345,6 +345,7 @@ export default { }, watch: { step: { + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ handler(newStep) { this.localStep = { ...newStep } this.localChecklist = this.parseChecklist(newStep.checklist) @@ -356,6 +357,7 @@ export default { }, }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ parseConfig(config) { let raw = config if (typeof raw === 'string') { @@ -389,6 +391,7 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ buildConfigPayload() { const out = {} if (this.localConfig.sla.value != null && this.localConfig.sla.unit !== '') { @@ -407,20 +410,24 @@ export default { return Object.keys(out).length > 0 ? out : undefined }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ addRequiredField() { this.localConfig.requiredFields.push('') this.emitUpdate() }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ removeRequiredField(index) { this.localConfig.requiredFields.splice(index, 1) this.emitUpdate() }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ onEscalationToggle() { this.emitUpdate() }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ parseChecklist(checklist) { if (!checklist) return [] if (typeof checklist === 'string') { @@ -429,6 +436,7 @@ export default { return [...checklist] }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ parseActions(actions) { if (!actions) return [] if (typeof actions === 'string') { @@ -437,6 +445,7 @@ export default { return [...actions] }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ emitUpdate() { const payload = { ...this.localStep, @@ -452,6 +461,7 @@ export default { this.$emit('update', payload) }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ addChecklistItem() { this.localChecklist.push({ id: `check-${nextCheckId++}`, @@ -461,16 +471,19 @@ export default { this.emitUpdate() }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ removeChecklistItem(index) { this.localChecklist.splice(index, 1) this.emitUpdate() }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ onCheckDragStart(index, event) { this.dragCheckIndex = index event.dataTransfer.effectAllowed = 'move' }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ onCheckDrop(targetIndex, event) { if (this.dragCheckIndex !== null && this.dragCheckIndex !== targetIndex) { const item = this.localChecklist.splice(this.dragCheckIndex, 1)[0] @@ -480,11 +493,13 @@ export default { this.dragCheckIndex = null }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ addAction() { this.localActions.push({ type: 'createTask', title: '' }) this.emitUpdate() }, + /** @spec openspec/changes/retrofit-2026-05-25-process-step-configuration/tasks.md */ removeAction(index) { this.localActions.splice(index, 1) this.emitUpdate() diff --git a/src/views/settings/components/TransitionConfigPanel.vue b/src/views/settings/components/TransitionConfigPanel.vue index 09484c4c..fee47a6e 100644 --- a/src/views/settings/components/TransitionConfigPanel.vue +++ b/src/views/settings/components/TransitionConfigPanel.vue @@ -287,6 +287,7 @@ export default { }, watch: { transition: { + /** @spec openspec/specs/status-transition-engine/spec.md */ handler(newVal) { this.localTransition = { ...newVal } this.localAllowedRoles = [...(newVal.allowedRoles || [])] @@ -297,6 +298,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/status-transition-engine/spec.md */ parseGuards(guards) { if (!guards) return [] if (typeof guards === 'string') { @@ -305,6 +307,7 @@ export default { return [...guards] }, + /** @spec openspec/specs/status-transition-engine/spec.md */ parseActions(actions) { if (!actions) return [] if (typeof actions === 'string') { @@ -313,6 +316,7 @@ export default { return [...actions] }, + /** @spec openspec/specs/status-transition-engine/spec.md */ emitUpdate() { this.$emit('update', { ...this.localTransition, @@ -322,6 +326,7 @@ export default { }) }, + /** @spec openspec/specs/status-transition-engine/spec.md */ toggleRole(roleId) { const index = this.localAllowedRoles.indexOf(roleId) if (index >= 0) { @@ -332,26 +337,31 @@ export default { this.emitUpdate() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ addGuard() { this.localGuards.push({ type: 'checklist' }) this.emitUpdate() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ removeGuard(index) { this.localGuards.splice(index, 1) this.emitUpdate() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ addAction() { this.localActions.push({ type: 'notify', message: '' }) this.emitUpdate() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ removeAction(index) { this.localActions.splice(index, 1) this.emitUpdate() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ onDelete() { if (confirm(t('procest', 'Are you sure you want to delete this transition?'))) { this.$emit('delete', this.transition.id) diff --git a/src/views/settings/components/VthTemplateLibrary.vue b/src/views/settings/components/VthTemplateLibrary.vue index 1260d9af..e7dae5f9 100644 --- a/src/views/settings/components/VthTemplateLibrary.vue +++ b/src/views/settings/components/VthTemplateLibrary.vue @@ -110,6 +110,7 @@ export default { }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md */ templates() { return [ { @@ -173,10 +174,12 @@ export default { methods: { t, + /** @spec openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md */ selectTemplate(template) { this.selectedTemplate = template }, + /** @spec openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md */ getIcon(category) { // Return simple SVG path based on category const icons = { diff --git a/src/views/settings/components/WorkflowNode.vue b/src/views/settings/components/WorkflowNode.vue index f90f63d4..5c718146 100644 --- a/src/views/settings/components/WorkflowNode.vue +++ b/src/views/settings/components/WorkflowNode.vue @@ -93,6 +93,7 @@ export default { } }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ nodeStyle() { return { position: 'absolute', @@ -102,6 +103,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ onMouseDown(event) { this.$emit('drag-start', { offsetX: event.offsetX, @@ -109,16 +111,19 @@ export default { }) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onConnectionStartFromPort(event) { this.$emit('connection-start', event) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onStepDragStart(step, event) { this.draggedStepId = step.id event.dataTransfer.setData('text/plain', step.id) event.dataTransfer.effectAllowed = 'move' }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ onStepDrop(targetStep, event) { const draggedId = event.dataTransfer.getData('text/plain') if (draggedId && draggedId !== targetStep.id) { diff --git a/src/views/settings/components/WorkflowPalette.vue b/src/views/settings/components/WorkflowPalette.vue index 3934bdde..baa171a2 100644 --- a/src/views/settings/components/WorkflowPalette.vue +++ b/src/views/settings/components/WorkflowPalette.vue @@ -35,6 +35,7 @@ export default { name: 'WorkflowPalette', emits: ['drag-start'], methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ onDragStart(type, event) { event.dataTransfer.setData('text/plain', type) event.dataTransfer.effectAllowed = 'copy' diff --git a/src/views/settings/components/WorkflowTransitionArrow.vue b/src/views/settings/components/WorkflowTransitionArrow.vue index 2d95210d..0c065e6c 100644 --- a/src/views/settings/components/WorkflowTransitionArrow.vue +++ b/src/views/settings/components/WorkflowTransitionArrow.vue @@ -62,12 +62,15 @@ export default { }, emits: ['click', 'dblclick'], computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ midX() { return (this.fromPos.x + this.toPos.x) / 2 }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ midY() { return (this.fromPos.y + this.toPos.y) / 2 }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ arrowPath() { const dx = this.toPos.x - this.fromPos.x const dy = this.toPos.y - this.fromPos.y @@ -80,6 +83,7 @@ export default { return `M ${this.fromPos.x} ${this.fromPos.y} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${this.toPos.x} ${this.toPos.y}` }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ arrowheadPoints() { // Calculate arrowhead at the end point const dx = this.toPos.x - this.fromPos.x diff --git a/src/views/settings/tabs/AiSettingsTab.vue b/src/views/settings/tabs/AiSettingsTab.vue index 42821773..cfb084fb 100644 --- a/src/views/settings/tabs/AiSettingsTab.vue +++ b/src/views/settings/tabs/AiSettingsTab.vue @@ -139,6 +139,7 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ featureToggles() { return [ { key: 'ai_feature_classification', label: t('procest', 'Document classification') }, @@ -150,6 +151,7 @@ export default { ] }, }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async mounted() { try { const response = await getAiSettings() @@ -160,6 +162,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async updateSetting(key, value) { this.settings[key] = value try { @@ -168,6 +171,7 @@ export default { // Revert on failure would go here } }, + /** @spec openspec/changes/retrofit-2026-05-24-ai-assistance/tasks.md */ async testHealth() { this.healthLoading = true this.healthResult = null diff --git a/src/views/settings/tabs/AppointmentSettingsTab.vue b/src/views/settings/tabs/AppointmentSettingsTab.vue index 926cb60e..71a851c3 100644 --- a/src/views/settings/tabs/AppointmentSettingsTab.vue +++ b/src/views/settings/tabs/AppointmentSettingsTab.vue @@ -56,6 +56,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-25-appointment-booking/tasks.md */ saveBackend() { // Save via settings API }, diff --git a/src/views/settings/tabs/BerichtenboxSettingsTab.vue b/src/views/settings/tabs/BerichtenboxSettingsTab.vue index 9aba365e..e8b75d2d 100644 --- a/src/views/settings/tabs/BerichtenboxSettingsTab.vue +++ b/src/views/settings/tabs/BerichtenboxSettingsTab.vue @@ -54,6 +54,7 @@ export default { }, methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-berichtenbox-integration/tasks.md */ async testConnection() { this.testLoading = true this.testResult = null diff --git a/src/views/settings/tabs/ChecklistAdmin.vue b/src/views/settings/tabs/ChecklistAdmin.vue index b5d7d74c..7122d46f 100644 --- a/src/views/settings/tabs/ChecklistAdmin.vue +++ b/src/views/settings/tabs/ChecklistAdmin.vue @@ -191,12 +191,15 @@ export default { }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ inspectionStore() { return useInspectionStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ checklists() { return this.inspectionStore.checklists }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ loading() { return this.inspectionStore.loading }, @@ -205,6 +208,7 @@ export default { watch: { caseTypeId: { immediate: true, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ handler(newId) { if (newId) { this.inspectionStore.fetchChecklists(newId) @@ -216,6 +220,7 @@ export default { methods: { t, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ createChecklist() { this.editingChecklist = { name: '', @@ -226,14 +231,17 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ editChecklist(checklist) { this.editingChecklist = JSON.parse(JSON.stringify(checklist)) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ cancelEdit() { this.editingChecklist = null }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async saveChecklist() { this.saving = true try { @@ -250,16 +258,19 @@ export default { } }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async createVersion(checklist) { await this.inspectionStore.createNewVersion(checklist) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ async deleteChecklist(checklist) { if (confirm(t('procest', 'Are you sure you want to delete this checklist?'))) { await this.inspectionStore.deleteChecklist(checklist.id) } }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ addItem() { if (!this.editingChecklist.items) { this.editingChecklist.items = [] @@ -275,19 +286,23 @@ export default { }) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ removeItem(index) { this.editingChecklist.items.splice(index, 1) }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ onDragStart(index, event) { this.dragIndex = index event.dataTransfer.effectAllowed = 'move' }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ onDragOver(index, event) { event.dataTransfer.dropEffect = 'move' }, + /** @spec openspec/changes/retrofit-2026-05-24-inspection-checklists/tasks.md */ onDrop(index) { if (this.dragIndex === null || this.dragIndex === index) { return diff --git a/src/views/settings/tabs/DocumentTypesTab.vue b/src/views/settings/tabs/DocumentTypesTab.vue index 307f40af..4d723bfc 100644 --- a/src/views/settings/tabs/DocumentTypesTab.vue +++ b/src/views/settings/tabs/DocumentTypesTab.vue @@ -117,6 +117,7 @@ export default { if (!this.isCreate) await this.loadItems() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async loadItems() { this.loading = true const objectStore = useObjectStore() @@ -127,12 +128,14 @@ export default { this.items = results || [] this.loading = false }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ startAdd() { this.editingId = 'new' this.editForm = { name: '', category: '', description: '', isRequired: false } this.editError = '' this.items.push({ id: 'new', name: '' }) }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ startEdit(item) { this.editingId = item.id this.editForm = { @@ -143,11 +146,13 @@ export default { } this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ cancelEdit() { if (this.editingId === 'new') this.items = this.items.filter(i => i.id !== 'new') this.editingId = null this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async saveEdit() { if (!this.editForm.name.trim()) { this.editError = t('procest', 'Name is required') @@ -166,6 +171,7 @@ export default { this.editingId = null await this.loadItems() }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async deleteItem(item) { if (!confirm(t('procest', 'Delete document type "{name}"?', { name: item.name }))) return const objectStore = useObjectStore() diff --git a/src/views/settings/tabs/GeneralTab.vue b/src/views/settings/tabs/GeneralTab.vue index 7a5ab141..82af645d 100644 --- a/src/views/settings/tabs/GeneralTab.vue +++ b/src/views/settings/tabs/GeneralTab.vue @@ -217,26 +217,33 @@ export default { }, }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ originOptions() { return getOriginOptions() }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ confidentialityOptions() { return getConfidentialityOptions() }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ selectedOrigin() { if (!this.form.origin) return null return this.originOptions.find(o => o.id === this.form.origin) || null }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ selectedConfidentiality() { if (!this.form.confidentiality) return null return this.confidentialityOptions.find(o => o.id === this.form.confidentiality) || null }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ deadlinePreview() { return this.form.processingDeadline ? formatDuration(this.form.processingDeadline) : '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ serviceTargetPreview() { return this.form.serviceTarget ? formatDuration(this.form.serviceTarget) : '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ extensionPreview() { return this.form.extensionPeriod ? formatDuration(this.form.extensionPeriod) : '' }, diff --git a/src/views/settings/tabs/PropertiesTab.vue b/src/views/settings/tabs/PropertiesTab.vue index 2fdf7011..ec19ce64 100644 --- a/src/views/settings/tabs/PropertiesTab.vue +++ b/src/views/settings/tabs/PropertiesTab.vue @@ -221,14 +221,17 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ objectStore() { return useObjectStore() }, }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async mounted() { if (!this.isCreate && this.caseTypeId) { await Promise.all([this.fetchPropertyDefs(), this.fetchStatusTypes()]) } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async fetchPropertyDefs() { this.loading = true try { @@ -239,6 +242,7 @@ export default { } catch (e) { this.error = e.message } this.loading = false }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async fetchStatusTypes() { try { const result = await this.objectStore.fetchCollection('statusType', { @@ -247,6 +251,7 @@ export default { this.statusTypes = result || [] } catch (e) { /* ignore — status types are optional for property definitions */ } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async addProperty() { this.addError = '' if (!this.newForm.name?.trim()) { this.addError = t('procest', 'Name is required'); return } @@ -260,8 +265,11 @@ export default { this.addError = this.objectStore.getError('propertyDefinition') || t('procest', 'Failed to add property') } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ startEdit(pd) { this.editingId = pd.id; this.editForm = { ...pd }; this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ cancelEdit() { this.editingId = null; this.editForm = {}; this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async saveEdit() { this.editError = '' if (!this.editForm.name?.trim()) { this.editError = t('procest', 'Name is required'); return } @@ -276,6 +284,7 @@ export default { this.editError = this.objectStore.getError('propertyDefinition') || t('procest', 'Failed to save') } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async deleteProperty(pd) { if (!confirm(t('procest', 'Delete property "{name}"?', { name: pd.name }))) return const ok = await this.objectStore.deleteObject('propertyDefinition', pd.id) diff --git a/src/views/settings/tabs/ResultTypesTab.vue b/src/views/settings/tabs/ResultTypesTab.vue index eae0de9b..f65b3311 100644 --- a/src/views/settings/tabs/ResultTypesTab.vue +++ b/src/views/settings/tabs/ResultTypesTab.vue @@ -118,6 +118,7 @@ export default { if (!this.isCreate) await this.loadItems() }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async loadItems() { this.loading = true const objectStore = useObjectStore() @@ -128,17 +129,20 @@ export default { this.items = results || [] this.loading = false }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ startAdd() { this.editingId = 'new' this.editForm = { name: '', description: '', archivalAction: '', archivalPeriod: '' } this.editError = '' this.items.push({ id: 'new', name: '' }) }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ startEdit(item) { this.editingId = item.id this.editForm = { name: item.name, description: item.description || '', archivalAction: item.archivalAction || '', archivalPeriod: item.archivalPeriod || '' } this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ cancelEdit() { if (this.editingId === 'new') { this.items = this.items.filter(i => i.id !== 'new') @@ -146,6 +150,7 @@ export default { this.editingId = null this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async saveEdit() { if (!this.editForm.name.trim()) { this.editError = t('procest', 'Name is required') @@ -164,6 +169,7 @@ export default { this.editingId = null await this.loadItems() }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async deleteItem(item) { if (!confirm(t('procest', 'Delete result type "{name}"?', { name: item.name }))) return const objectStore = useObjectStore() diff --git a/src/views/settings/tabs/ResultsTab.vue b/src/views/settings/tabs/ResultsTab.vue index b977b23e..57265fb4 100644 --- a/src/views/settings/tabs/ResultsTab.vue +++ b/src/views/settings/tabs/ResultsTab.vue @@ -194,14 +194,17 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ objectStore() { return useObjectStore() }, }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async mounted() { if (!this.isCreate && this.caseTypeId) { await this.fetchResultTypes() } }, methods: { + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ formatPeriod(period) { if (!period) return '—' const match = period.match(/^P(\d+)([YDMW])$/) @@ -209,6 +212,7 @@ export default { const units = { Y: 'years', M: 'months', W: 'weeks', D: 'days' } return `${match[1]} ${units[match[2]] || match[2]}` }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async fetchResultTypes() { this.loading = true try { @@ -220,6 +224,7 @@ export default { } catch (e) { this.error = e.message } this.loading = false }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async addResultType() { this.addError = '' if (!this.newForm.name || !this.newForm.name.trim()) { @@ -237,8 +242,11 @@ export default { this.addError = this.objectStore.getError('resultType') || t('procest', 'Failed to add result type') } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ startEdit(rt) { this.editingId = rt.id; this.editForm = { ...rt }; this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ cancelEdit() { this.editingId = null; this.editForm = {}; this.editError = '' }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async saveEdit() { this.editError = '' if (!this.editForm.name || !this.editForm.name.trim()) { @@ -257,6 +265,7 @@ export default { this.editError = this.objectStore.getError('resultType') || t('procest', 'Failed to save') } }, + /** @spec openspec/changes/retrofit-2026-05-25-admin-settings/tasks.md */ async deleteResultType(rt) { if (!confirm(t('procest', 'Delete result type "{name}"?', { name: rt.name }))) return const ok = await this.objectStore.deleteObject('resultType', rt.id) diff --git a/src/views/settings/tabs/RoleTypesTab.vue b/src/views/settings/tabs/RoleTypesTab.vue index c47ef5d0..b1be3bab 100644 --- a/src/views/settings/tabs/RoleTypesTab.vue +++ b/src/views/settings/tabs/RoleTypesTab.vue @@ -101,6 +101,7 @@ export default { if (!this.isCreate) await this.loadItems() }, methods: { + /** @spec openspec/specs/role-based-step-routing/spec.md */ async loadItems() { this.loading = true const objectStore = useObjectStore() @@ -111,22 +112,26 @@ export default { this.items = results || [] this.loading = false }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ startAdd() { this.editingId = 'new' this.editForm = { name: '', genericRole: '' } this.editError = '' this.items.push({ id: 'new', name: '' }) }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ startEdit(item) { this.editingId = item.id this.editForm = { name: item.name, genericRole: item.genericRole || '' } this.editError = '' }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ cancelEdit() { if (this.editingId === 'new') this.items = this.items.filter(i => i.id !== 'new') this.editingId = null this.editError = '' }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ async saveEdit() { if (!this.editForm.name.trim()) { this.editError = t('procest', 'Name is required') @@ -143,6 +148,7 @@ export default { this.editingId = null await this.loadItems() }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ async deleteItem(item) { if (!confirm(t('procest', 'Delete role type "{name}"?', { name: item.name }))) return const objectStore = useObjectStore() diff --git a/src/views/settings/tabs/RolesTab.vue b/src/views/settings/tabs/RolesTab.vue index 1719b199..7ee3d18e 100644 --- a/src/views/settings/tabs/RolesTab.vue +++ b/src/views/settings/tabs/RolesTab.vue @@ -163,17 +163,21 @@ export default { } }, computed: { + /** @spec openspec/specs/role-based-step-routing/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ genericRoleOptions() { return GENERIC_ROLES }, }, async mounted() { if (!this.isCreate && this.caseTypeId) await this.fetchRoleTypes() }, methods: { + /** @spec openspec/specs/role-based-step-routing/spec.md */ genericRoleLabel(value) { const opt = GENERIC_ROLES.find(r => r.value === value) return opt ? opt.label : value || '—' }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ async fetchRoleTypes() { this.loading = true try { @@ -184,6 +188,7 @@ export default { } catch (e) { this.error = e.message } this.loading = false }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ async addRoleType() { this.addError = '' if (!this.newForm.name?.trim()) { this.addError = t('procest', 'Name is required'); return } @@ -197,8 +202,11 @@ export default { this.addError = this.objectStore.getError('roleType') || t('procest', 'Failed to add role type') } }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ startEdit(rt) { this.editingId = rt.id; this.editForm = { ...rt }; this.editError = '' }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ cancelEdit() { this.editingId = null; this.editForm = {}; this.editError = '' }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ async saveEdit() { this.editError = '' if (!this.editForm.name?.trim()) { this.editError = t('procest', 'Name is required'); return } @@ -213,6 +221,7 @@ export default { this.editError = this.objectStore.getError('roleType') || t('procest', 'Failed to save') } }, + /** @spec openspec/specs/role-based-step-routing/spec.md */ async deleteRoleType(rt) { if (!confirm(t('procest', 'Delete role type "{name}"?', { name: rt.name }))) return const ok = await this.objectStore.deleteObject('roleType', rt.id) diff --git a/src/views/settings/tabs/StatusesTab.vue b/src/views/settings/tabs/StatusesTab.vue index 438fc543..71ffe669 100644 --- a/src/views/settings/tabs/StatusesTab.vue +++ b/src/views/settings/tabs/StatusesTab.vue @@ -204,19 +204,23 @@ export default { } }, computed: { + /** @spec openspec/specs/status-transition-engine/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/status-transition-engine/spec.md */ sortedStatusTypes() { return [...this.statusTypes].sort((a, b) => (a.order || 0) - (b.order || 0)) }, }, + /** @spec openspec/specs/status-transition-engine/spec.md */ async mounted() { if (!this.isCreate && this.caseTypeId) { await this.fetchStatusTypes() } }, methods: { + /** @spec openspec/specs/status-transition-engine/spec.md */ getEmptyForm() { return { name: '', @@ -227,6 +231,7 @@ export default { } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ async fetchStatusTypes() { this.loading = true try { @@ -241,6 +246,7 @@ export default { this.loading = false }, + /** @spec openspec/specs/status-transition-engine/spec.md */ async addStatusType() { this.addError = '' @@ -283,18 +289,21 @@ export default { } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ startEdit(st) { this.editingId = st.id this.editForm = { ...st } this.editError = '' }, + /** @spec openspec/specs/status-transition-engine/spec.md */ cancelEdit() { this.editingId = null this.editForm = {} this.editError = '' }, + /** @spec openspec/specs/status-transition-engine/spec.md */ async saveEdit() { this.editError = '' @@ -340,6 +349,7 @@ export default { } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ async deleteStatusType(st) { this.error = '' @@ -365,20 +375,24 @@ export default { }, // Drag and drop + /** @spec openspec/specs/status-transition-engine/spec.md */ onDragStart(index, event) { this.dragIndex = index event.dataTransfer.effectAllowed = 'move' }, + /** @spec openspec/specs/status-transition-engine/spec.md */ onDragOver(index) { if (this.dragIndex === null || this.dragIndex === index) return this.dragOverIndex = index }, + /** @spec openspec/specs/status-transition-engine/spec.md */ onDragLeave() { this.dragOverIndex = null }, + /** @spec openspec/specs/status-transition-engine/spec.md */ async onDrop(targetIndex) { if (this.dragIndex === null || this.dragIndex === targetIndex) { this.dragOverIndex = null @@ -409,6 +423,7 @@ export default { } }, + /** @spec openspec/specs/status-transition-engine/spec.md */ onDragEnd() { this.dragIndex = null this.dragOverIndex = null diff --git a/src/views/settings/tabs/TemplatesTab.vue b/src/views/settings/tabs/TemplatesTab.vue index 41e0327f..bbc64c0c 100644 --- a/src/views/settings/tabs/TemplatesTab.vue +++ b/src/views/settings/tabs/TemplatesTab.vue @@ -84,6 +84,7 @@ export default { await this.loadTemplates() }, methods: { + /** @spec openspec/specs/template-library/spec.md */ async loadTemplates() { this.loading = true this.error = '' @@ -98,6 +99,7 @@ export default { this.loading = false } }, + /** @spec openspec/specs/template-library/spec.md */ async activate(templateId) { this.activating = templateId this.activationResult = null diff --git a/src/views/settings/tabs/WorkflowTab.vue b/src/views/settings/tabs/WorkflowTab.vue index 25c58041..7d4ea3fc 100644 --- a/src/views/settings/tabs/WorkflowTab.vue +++ b/src/views/settings/tabs/WorkflowTab.vue @@ -160,27 +160,34 @@ export default { } }, computed: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ workflowStore() { return useWorkflowStore() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ versions() { return this.workflowStore.versions }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ currentTemplate() { return this.workflowStore.currentTemplate }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ currentIsDraft() { return this.currentTemplate?.isDraft === true }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ currentIsPublished() { return this.currentTemplate && !this.currentTemplate.isDraft }, hasDraft() { return this.versions.some((v) => v.isDraft) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ versionNotice() { if (!this.currentTemplate) return null const active = this.workflowStore.activeVersion @@ -197,6 +204,7 @@ export default { await this.loadVersions() }, methods: { + /** @spec openspec/specs/workflow-definition-model/spec.md */ async loadVersions() { this.loading = true await this.workflowStore.listVersions(this.caseTypeId) @@ -212,6 +220,7 @@ export default { this.loading = false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async onVersionChange() { if (this.selectedVersionId) { await this.workflowStore.getTemplate(this.selectedVersionId) @@ -220,6 +229,7 @@ export default { } }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async createWorkflow() { this.loading = true const template = await this.workflowStore.createTemplate( @@ -233,6 +243,7 @@ export default { this.loading = false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async save() { this.saving = true await this.workflowStore.saveTemplate(this.currentTemplate) @@ -240,6 +251,7 @@ export default { this.saving = false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async publish() { this.publishErrors = [] this.publishing = true @@ -265,6 +277,7 @@ export default { this.publishing = false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async editPublished() { this.loading = true const draft = await this.workflowStore.createDraftFromVersion(this.selectedVersionId) @@ -275,6 +288,7 @@ export default { this.loading = false }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async exportWorkflow() { if (!this.currentTemplate) return @@ -308,10 +322,12 @@ export default { URL.revokeObjectURL(url) }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ triggerImport() { this.$refs.importInput.click() }, + /** @spec openspec/specs/workflow-definition-model/spec.md */ async handleImport(event) { const file = event.target.files[0] if (!file) return diff --git a/src/views/tasks/TaskCreateDialog.vue b/src/views/tasks/TaskCreateDialog.vue index 99bbe567..194eac12 100644 --- a/src/views/tasks/TaskCreateDialog.vue +++ b/src/views/tasks/TaskCreateDialog.vue @@ -124,9 +124,11 @@ export default { } }, computed: { + /** @spec openspec/changes/task-management/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/task-management/tasks.md */ caseOptions() { return this.cases.map(c => ({ value: c.id, @@ -134,11 +136,13 @@ export default { })) }, }, + /** @spec openspec/changes/task-management/tasks.md */ async mounted() { const results = await this.objectStore.fetchCollection('case', { _limit: 200 }) this.cases = results || [] }, methods: { + /** @spec openspec/changes/task-management/tasks.md */ async submit() { this.errors = {} diff --git a/src/views/voorstellen/VoorstelDetail.vue b/src/views/voorstellen/VoorstelDetail.vue index 382dab00..c6d4a97f 100644 --- a/src/views/voorstellen/VoorstelDetail.vue +++ b/src/views/voorstellen/VoorstelDetail.vue @@ -221,9 +221,11 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/parafering-actions/spec.md */ steps() { if (this.voorstel.routeSnapshot) { try { @@ -236,13 +238,16 @@ export default { } return [] }, + /** @spec openspec/specs/parafering-actions/spec.md */ currentStepInfo() { if (!this.voorstel.currentStep || !this.steps.length) return null return this.steps.find(s => s.order === this.voorstel.currentStep) || null }, + /** @spec openspec/specs/parafering-actions/spec.md */ currentUserId() { return getCurrentUser()?.uid || '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ isActiveActor() { if (!this.currentStepInfo) return false return this.currentStepInfo.actor === this.currentUserId @@ -253,9 +258,11 @@ export default { isTerminalStatus() { return ['besloten', 'gearchiveerd'].includes(this.voorstel.status) }, + /** @spec openspec/specs/parafering-actions/spec.md */ canRegisterBesluit() { return ['geaccordeerd', 'aangeboden'].includes(this.voorstel.status) }, + /** @spec openspec/specs/parafering-actions/spec.md */ canOverrideRoute() { if (this.voorstel.status !== 'in_parafering' && this.voorstel.status !== 'ter_accordering') { return false @@ -271,6 +278,7 @@ export default { || this.isSteller }, }, + /** @spec openspec/specs/parafering-actions/spec.md */ async created() { await Promise.all([ this.loadVoorstel(), @@ -278,6 +286,7 @@ export default { ]) }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async loadVoorstel() { this.loading = true try { @@ -288,6 +297,7 @@ export default { this.loading = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ async loadActies() { this.loadingActies = true try { @@ -305,12 +315,15 @@ export default { this.loadingActies = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatType(type) { return TYPE_LABELS[type] || type || '-' }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatStatus(status) { return STATUS_LABELS[status] || status || '-' }, + /** @spec openspec/specs/parafering-actions/spec.md */ async onActionCompleted() { this.actieDialogOpen = false await Promise.all([ @@ -322,12 +335,15 @@ export default { await this.$refs.actieTimeline.load() } }, + /** @spec openspec/specs/parafering-actions/spec.md */ openSkipDialog() { this.showSkipDialog = true }, + /** @spec openspec/specs/parafering-actions/spec.md */ openAddStepDialog() { this.showAddStepDialog = true }, + /** @spec openspec/specs/parafering-actions/spec.md */ async onOverrideCompleted() { this.showSkipDialog = false this.showAddStepDialog = false @@ -336,6 +352,7 @@ export default { this.loadActies(), ]) }, + /** @spec openspec/specs/parafering-actions/spec.md */ async resubmit() { try { const resumeStep = this.voorstel.returnedFromStep || 1 @@ -349,6 +366,7 @@ export default { console.error('Failed to resubmit voorstel:', error) } }, + /** @spec openspec/specs/parafering-actions/spec.md */ async onBesluitRegistered() { this.showBesluitDialog = false await this.loadVoorstel() diff --git a/src/views/voorstellen/components/AddStepDialog.vue b/src/views/voorstellen/components/AddStepDialog.vue index 438a4fb1..8c042dee 100644 --- a/src/views/voorstellen/components/AddStepDialog.vue +++ b/src/views/voorstellen/components/AddStepDialog.vue @@ -105,17 +105,20 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ insertionOptions() { return this.routeSnapshot.map(s => ({ label: this.t('procest', 'Na stap {n} — {actor}', { n: s.order, actor: s.actor }), value: s.order, })) }, + /** @spec openspec/specs/parafering-actions/spec.md */ canSubmit() { return !this.submitting && this.afterStep !== null && this.actor.trim().length > 0 }, }, watch: { + /** @spec openspec/specs/parafering-actions/spec.md */ open(value) { if (value) { this.afterStep = null @@ -128,6 +131,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async onSubmit() { if (!this.canSubmit) return this.submitting = true @@ -150,6 +154,7 @@ export default { this.submitting = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ onClose() { if (this.submitting) return this.$emit('close') diff --git a/src/views/voorstellen/components/AuditTrail.vue b/src/views/voorstellen/components/AuditTrail.vue index a187ae15..f64f49c8 100644 --- a/src/views/voorstellen/components/AuditTrail.vue +++ b/src/views/voorstellen/components/AuditTrail.vue @@ -82,9 +82,11 @@ export default { }, }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ formatAction(action) { return ACTION_LABELS[action] || action }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatTimestamp(actie) { const ts = actie._self?.created || actie.timestamp if (!ts) return '-' @@ -96,6 +98,7 @@ export default { minute: '2-digit', }) }, + /** @spec openspec/specs/parafering-actions/spec.md */ exportAuditTrail() { const data = this.acties.map(a => ({ step: a.step, diff --git a/src/views/voorstellen/components/BesluitRegistration.vue b/src/views/voorstellen/components/BesluitRegistration.vue index 75cb7725..1cb1cab5 100644 --- a/src/views/voorstellen/components/BesluitRegistration.vue +++ b/src/views/voorstellen/components/BesluitRegistration.vue @@ -100,10 +100,12 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ objectStore() { return useObjectStore() }, }, + /** @spec openspec/specs/parafering-actions/spec.md */ async created() { try { const results = await this.objectStore.fetchCollection('decisionType', { _limit: 50 }) @@ -113,6 +115,7 @@ export default { } }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async register() { this.errors = {} if (!this.form.title.trim()) { diff --git a/src/views/voorstellen/components/DelegateSelectorField.vue b/src/views/voorstellen/components/DelegateSelectorField.vue index cd26a966..d9de1882 100644 --- a/src/views/voorstellen/components/DelegateSelectorField.vue +++ b/src/views/voorstellen/components/DelegateSelectorField.vue @@ -50,6 +50,7 @@ export default { } }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ formatOption(entry) { const name = entry.principalDisplayName || entry.principalUid return this.t('procest', 'On behalf of {name} (mandate {ref})', { @@ -57,6 +58,7 @@ export default { ref: entry.mandateReference, }) }, + /** @spec openspec/specs/parafering-actions/spec.md */ onChange(event) { const value = event.target.value this.selected = value diff --git a/src/views/voorstellen/components/ParafeerActieDialog.vue b/src/views/voorstellen/components/ParafeerActieDialog.vue index 4be9ad6a..f2286cbe 100644 --- a/src/views/voorstellen/components/ParafeerActieDialog.vue +++ b/src/views/voorstellen/components/ParafeerActieDialog.vue @@ -154,18 +154,22 @@ export default { isAdviesStep() { return this.step?.type === 'advies' }, + /** @spec openspec/specs/parafering-actions/spec.md */ stepLabel() { return this.step?.label || this.formatStepType(this.step?.type) }, + /** @spec openspec/specs/parafering-actions/spec.md */ dialogTitle() { return this.t('procest', 'Take action') }, + /** @spec openspec/specs/parafering-actions/spec.md */ primaryActionLabel() { if (this.step?.type === 'advies') return this.t('procest', 'Advise') if (this.step?.type === 'parafering') return this.t('procest', 'Approve (paraferen)') if (this.step?.type === 'accordering') return this.t('procest', 'Accord') return '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ canSubmit() { if (this.isAdviesStep) { return this.advice.trim() !== '' @@ -174,6 +178,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ formatStepType(type) { const labels = { advies: this.t('procest', 'Advise'), @@ -182,11 +187,13 @@ export default { } return labels[type] || type || '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ onClose() { if (this.submitting) return this.$emit('update:open', false) this.resetForm() }, + /** @spec openspec/specs/parafering-actions/spec.md */ resetForm() { this.advice = '' this.comment = '' @@ -197,6 +204,7 @@ export default { this.errorMessage = '' this.validationError = '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ buildPayload(action) { const payload = { voorstel: this.voorstelId, @@ -208,6 +216,7 @@ export default { if (this.mandate) payload.mandate = this.mandate return payload }, + /** @spec openspec/specs/parafering-actions/spec.md */ async submitPrimary() { if (!this.canSubmit) return const stepType = this.step?.type @@ -221,6 +230,7 @@ export default { } await this.submit(this.buildPayload(action)) }, + /** @spec openspec/specs/parafering-actions/spec.md */ async submitReturn() { if (this.returnReason.trim() === '') { this.validationError = this.t('procest', 'Return reason is required') @@ -234,6 +244,7 @@ export default { } await this.submit(payload) }, + /** @spec openspec/specs/parafering-actions/spec.md */ async submit(payload) { this.submitting = true this.errorMessage = '' diff --git a/src/views/voorstellen/components/ParafeerActieTimeline.vue b/src/views/voorstellen/components/ParafeerActieTimeline.vue index 2944faa0..cbf430b3 100644 --- a/src/views/voorstellen/components/ParafeerActieTimeline.vue +++ b/src/views/voorstellen/components/ParafeerActieTimeline.vue @@ -72,6 +72,7 @@ export default { await this.load() }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async load() { this.loading = true this.error = '' @@ -86,6 +87,7 @@ export default { this.loading = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatStageLabel(actie) { const englishKey = ACTION_LABELS[actie.action] || actie.action || '' const localized = this.t('procest', englishKey) @@ -94,6 +96,7 @@ export default { action: localized, }) }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatActor(actie) { if (actie.actorType === 'delegate' && actie.onBehalfOf) { return this.t('procest', 'On behalf of {name} (mandate {ref})', { @@ -103,6 +106,7 @@ export default { } return actie.actor || '—' }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatTimestamp(actie) { const raw = actie.createdAt || actie.created || actie['@self']?.created if (!raw) return '' diff --git a/src/views/voorstellen/components/ParafeerActionBar.vue b/src/views/voorstellen/components/ParafeerActionBar.vue index c142bb42..ab9f56cc 100644 --- a/src/views/voorstellen/components/ParafeerActionBar.vue +++ b/src/views/voorstellen/components/ParafeerActionBar.vue @@ -110,22 +110,26 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ objectStore() { return useObjectStore() }, isAdvisoryStep() { return this.currentStepInfo?.type === 'advies' }, + /** @spec openspec/specs/parafering-actions/spec.md */ actionTitle() { const label = this.currentStepInfo?.label || t('procest', 'Uw actie') return `${label} — ${this.formatStepType(this.currentStepInfo?.type)}` }, }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ formatStepType(type) { const labels = { advies: 'Advies', parafering: 'Parafering', accordering: 'Accordering' } return labels[type] || type || '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ async doParaferen() { this.acting = true try { @@ -161,6 +165,7 @@ export default { this.acting = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ async doAdviseren() { this.acting = true try { @@ -190,6 +195,7 @@ export default { this.acting = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ async doTerugsturen() { if (!this.returnComment.trim()) { this.returnError = t('procest', 'Reden is verplicht bij terugsturen') diff --git a/src/views/voorstellen/components/ParafeerInbox.vue b/src/views/voorstellen/components/ParafeerInbox.vue index 41d01ddb..4c95d214 100644 --- a/src/views/voorstellen/components/ParafeerInbox.vue +++ b/src/views/voorstellen/components/ParafeerInbox.vue @@ -61,12 +61,15 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/specs/parafering-actions/spec.md */ currentUserId() { return getCurrentUser()?.uid || '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ pendingVoorstellen() { return this.voorstellen.filter(v => ['in_parafering', 'ter_accordering'].includes(v.status) @@ -78,6 +81,7 @@ export default { await this.loadVoorstellen() }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async loadVoorstellen() { this.loading = true try { @@ -92,9 +96,11 @@ export default { this.loading = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatType(type) { return TYPE_LABELS[type] || type || '-' }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatDate(voorstel) { const date = voorstel._self?.updated || voorstel.updatedAt if (!date) return '-' diff --git a/src/views/voorstellen/components/ProgressTimeline.vue b/src/views/voorstellen/components/ProgressTimeline.vue index d62f5bee..1721cfe4 100644 --- a/src/views/voorstellen/components/ProgressTimeline.vue +++ b/src/views/voorstellen/components/ProgressTimeline.vue @@ -68,14 +68,17 @@ export default { isCurrent(step) { return step.order === this.currentStep }, + /** @spec openspec/specs/parafering-actions/spec.md */ stepClass(step) { if (this.isCompleted(step)) return 'progress-timeline__step--completed' if (this.isCurrent(step)) return 'progress-timeline__step--current' return 'progress-timeline__step--pending' }, + /** @spec openspec/specs/parafering-actions/spec.md */ formatStepType(type) { return STEP_TYPE_LABELS[type] || type || '' }, + /** @spec openspec/specs/parafering-actions/spec.md */ getCompletionInfo(step) { const actie = this.acties.find(a => a.step === step.order) if (!actie) return '' diff --git a/src/views/voorstellen/components/SkipStepDialog.vue b/src/views/voorstellen/components/SkipStepDialog.vue index cb0f45df..ac2161cc 100644 --- a/src/views/voorstellen/components/SkipStepDialog.vue +++ b/src/views/voorstellen/components/SkipStepDialog.vue @@ -77,6 +77,7 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ canSubmit() { return !this.submitting && this.step @@ -85,6 +86,7 @@ export default { }, }, watch: { + /** @spec openspec/specs/parafering-actions/spec.md */ open(value) { if (value) { this.reason = '' @@ -93,6 +95,7 @@ export default { }, }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ async onSubmit() { if (!this.canSubmit) return this.submitting = true @@ -111,6 +114,7 @@ export default { this.submitting = false } }, + /** @spec openspec/specs/parafering-actions/spec.md */ onClose() { if (this.submitting) return this.$emit('close') diff --git a/src/views/voorstellen/components/VoorstelCreateDialog.vue b/src/views/voorstellen/components/VoorstelCreateDialog.vue index a78bfa7f..96fbe500 100644 --- a/src/views/voorstellen/components/VoorstelCreateDialog.vue +++ b/src/views/voorstellen/components/VoorstelCreateDialog.vue @@ -110,10 +110,12 @@ export default { } }, computed: { + /** @spec openspec/specs/parafering-actions/spec.md */ objectStore() { return useObjectStore() }, }, + /** @spec openspec/specs/parafering-actions/spec.md */ async created() { if (!this.caseId) { try { @@ -125,11 +127,13 @@ export default { } }, methods: { + /** @spec openspec/specs/parafering-actions/spec.md */ onCaseSelected(caseObj) { if (caseObj && !this.form.onderwerp) { this.form.onderwerp = caseObj.title || '' } }, + /** @spec openspec/specs/parafering-actions/spec.md */ async create() { this.errors = {} if (!this.form.onderwerp.trim()) { diff --git a/src/views/widgets/CasesOverviewWidget.vue b/src/views/widgets/CasesOverviewWidget.vue index 9e175970..55701660 100644 --- a/src/views/widgets/CasesOverviewWidget.vue +++ b/src/views/widgets/CasesOverviewWidget.vue @@ -44,9 +44,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ items() { return this.cases.map((caseObj) => ({ id: caseObj.id, @@ -68,6 +70,7 @@ export default { * @param {object} item The case item to show * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onShow(item) { window.location.href = `/index.php/apps/procest/#/cases/${item.id}` }, @@ -76,6 +79,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchData() { this.loading = true try { diff --git a/src/views/widgets/DeadlineAlertsWidget.vue b/src/views/widgets/DeadlineAlertsWidget.vue index 437f9f91..6554ab59 100644 --- a/src/views/widgets/DeadlineAlertsWidget.vue +++ b/src/views/widgets/DeadlineAlertsWidget.vue @@ -45,9 +45,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ items() { const overdueItems = this.alerts.overdue.map((item) => ({ id: item.id, @@ -76,6 +78,7 @@ export default { * @param {object} item The case item to show * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onShow(item) { window.location.href = `/index.php/apps/procest/#/cases/${item.id}` }, @@ -84,6 +87,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchData() { this.loading = true try { diff --git a/src/views/widgets/MyTasksWidget.vue b/src/views/widgets/MyTasksWidget.vue index 1d16907e..05d5050e 100644 --- a/src/views/widgets/MyTasksWidget.vue +++ b/src/views/widgets/MyTasksWidget.vue @@ -44,9 +44,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ items() { return this.tasks.map((task) => ({ id: task.id, @@ -68,6 +70,7 @@ export default { * @param {object} item The task item to show * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onShow(item) { window.location.href = `/index.php/apps/procest/#/tasks/${item.id}` }, @@ -76,6 +79,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchData() { this.loading = true try { diff --git a/src/views/widgets/OverdueCasesWidget.vue b/src/views/widgets/OverdueCasesWidget.vue index 72587f5e..543b0283 100644 --- a/src/views/widgets/OverdueCasesWidget.vue +++ b/src/views/widgets/OverdueCasesWidget.vue @@ -45,9 +45,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ items() { return this.overdueCases.map((caseObj) => ({ id: caseObj.id, @@ -69,6 +71,7 @@ export default { * @param {object} item The case item to show * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onShow(item) { window.location.href = `/index.php/apps/procest/#/cases/${item.id}` }, @@ -77,6 +80,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchData() { this.loading = true try { diff --git a/src/views/widgets/StalledCasesWidget.vue b/src/views/widgets/StalledCasesWidget.vue index fa6af33b..10497213 100644 --- a/src/views/widgets/StalledCasesWidget.vue +++ b/src/views/widgets/StalledCasesWidget.vue @@ -45,9 +45,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ items() { return this.stalledCases.slice(0, 5).map((item) => ({ id: item.id, @@ -67,6 +69,7 @@ export default { * @param {object} item The case item to show * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onShow(item) { window.location.href = `/index.php/apps/procest/#/cases/${item.id}` }, @@ -75,6 +78,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchData() { this.loading = true try { diff --git a/src/views/widgets/StartCaseWidget.vue b/src/views/widgets/StartCaseWidget.vue index 1c200cde..733a13f9 100644 --- a/src/views/widgets/StartCaseWidget.vue +++ b/src/views/widgets/StartCaseWidget.vue @@ -52,6 +52,7 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, @@ -65,6 +66,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchCaseTypes() { this.loading = true try { @@ -86,6 +88,7 @@ export default { * @param {object} caseType The case type to start * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async startCase(caseType) { if (this.creating) { return diff --git a/src/views/widgets/TaskRemindersWidget.vue b/src/views/widgets/TaskRemindersWidget.vue index 81c4d224..a10da5ae 100644 --- a/src/views/widgets/TaskRemindersWidget.vue +++ b/src/views/widgets/TaskRemindersWidget.vue @@ -45,9 +45,11 @@ export default { } }, computed: { + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ objectStore() { return useObjectStore() }, + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ items() { const overdueItems = this.reminders.overdue.map((item) => ({ id: item.id, @@ -76,6 +78,7 @@ export default { * @param {object} item The task item to show * @return {void} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ onShow(item) { window.location.href = `/index.php/apps/procest/#/tasks/${item.id}` }, @@ -84,6 +87,7 @@ export default { * * @return {Promise} */ + /** @spec openspec/changes/retrofit-2026-05-24-signalering-widgets/tasks.md */ async fetchData() { this.loading = true try {