diff --git a/appinfo/info.xml b/appinfo/info.xml index 97dbbc9d..61e0f1af 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -66,6 +66,9 @@ Vrij en open source onder de EUPL-1.2-licentie. + + OCA\Decidesk\Repair\InitializeSettings + OCA\Decidesk\Repair\InitializeSettings diff --git a/l10n/en.json b/l10n/en.json index 0b091ea4..ae3fadb6 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -28,7 +28,104 @@ "Settings saved successfully": "Settings saved successfully", "Saving...": "Saving...", "This app needs OpenRegister to store and manage data. Please install OpenRegister from the app store to get started.": "This app needs OpenRegister to store and manage data. Please install OpenRegister from the app store to get started.", - "User settings will appear here in a future update.": "User settings will appear here in a future update." + "User settings will appear here in a future update.": "User settings will appear here in a future update.", + "No results found": "No results found", + "Reimport Register": "Reimport Register", + "Meeting": "Meeting", + "Motion": "Motion", + "Decision": "Decision", + "Participant": "Participant", + "Agenda Item": "Agenda Item", + "Action Items": "Action Items", + "Actual Duration": "Actual Duration", + "Adjourned": "Adjourned", + "Agenda Items": "Agenda Items", + "Amendments": "Amendments", + "Closed": "Closed", + "Configure the OpenRegister schema mappings for all Decidesk object types.": "Configure the OpenRegister schema mappings for all Decidesk object types.", + "Create Agenda Item": "Create Agenda Item", + "Create Governance Body": "Create Governance Body", + "Create Meeting": "Create Meeting", + "Create Participant": "Create Participant", + "Decidesk": "Decidesk", + "Decisions": "Decisions", + "Description": "Description", + "Digital Documents": "Digital Documents", + "Domain": "Domain", + "Draft": "Draft", + "Edit Agenda Item": "Edit Agenda Item", + "Edit Governance Body": "Edit Governance Body", + "Edit Meeting": "Edit Meeting", + "Edit Participant": "Edit Participant", + "Email": "Email", + "End Date": "End Date", + "Estimated Duration": "Estimated Duration", + "Failed to reimport register.": "Failed to reimport register.", + "For support, contact us at {email}": "For support, contact us at {email}", + "Global search": "Global search", + "Governance Bodies": "Governance Bodies", + "Governance Body": "Governance Body", + "Importing...": "Importing...", + "Information about the current Decidesk installation": "Information about the current Decidesk installation", + "Joined": "Joined", + "Left": "Left", + "Lifecycle": "Lifecycle", + "Linked Meeting": "Linked Meeting", + "Location": "Location", + "Manage the OpenRegister configuration for Decidesk.": "Manage the OpenRegister configuration for Decidesk.", + "Meeting status distribution": "Meeting status distribution", + "Meetings": "Meetings", + "Minutes": "Minutes", + "Mode": "Mode", + "Monetary Amounts": "Monetary Amounts", + "Motions": "Motions", + "Name": "Name", + "No": "No", + "No agenda items.": "No agenda items.", + "No linked governance body.": "No linked governance body.", + "No linked meeting.": "No linked meeting.", + "No meetings found. Create a meeting to see status distribution.": "No meetings found. Create a meeting to see status distribution.", + "No related meetings.": "No related meetings.", + "No related participants.": "No related participants.", + "Offers": "Offers", + "Opened": "Opened", + "Order": "Order", + "Orders": "Orders", + "Participants": "Participants", + "Party": "Party", + "Paused": "Paused", + "Products": "Products", + "Properties": "Properties", + "Quorum Required": "Quorum Required", + "Quorum Rule": "Quorum Rule", + "Recurring": "Recurring", + "Register Configuration": "Register Configuration", + "Register successfully reimported.": "Register successfully reimported.", + "Related Meetings": "Related Meetings", + "Related Participants": "Related Participants", + "Reports": "Reports", + "Role": "Role", + "Scheduled": "Scheduled", + "Scheduled Date": "Scheduled Date", + "Search across all governance data": "Search across all governance data", + "Search meetings, motions, decisions\u2026": "Search meetings, motions, decisions\u2026", + "Search results": "Search results", + "Searching\u2026": "Searching\u2026", + "Series": "Series", + "Support": "Support", + "Term End": "Term End", + "Term Start": "Term Start", + "Title": "Title", + "Type": "Type", + "Upcoming meetings": "Upcoming meetings", + "Version Information": "Version Information", + "Votes": "Votes", + "Voting Default": "Voting Default", + "Voting Rounds": "Voting Rounds", + "Voting Weight": "Voting Weight", + "Yes": "Yes", + "scheduled": "scheduled", + "total": "total" }, "plurals": "" } diff --git a/l10n/nl.json b/l10n/nl.json index 8fd9bdde..4bdf2af9 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -28,7 +28,104 @@ "Settings saved successfully": "Instellingen succesvol opgeslagen", "Saving...": "Opslaan...", "This app needs OpenRegister to store and manage data. Please install OpenRegister from the app store to get started.": "Deze app heeft OpenRegister nodig om gegevens op te slaan en te beheren. Installeer OpenRegister via de app store om te beginnen.", - "User settings will appear here in a future update.": "Gebruikersinstellingen verschijnen hier in een toekomstige update." + "User settings will appear here in a future update.": "Gebruikersinstellingen verschijnen hier in een toekomstige update.", + "No results found": "Geen resultaten gevonden", + "Reimport Register": "Register opnieuw importeren", + "Meeting": "Vergadering", + "Motion": "Motie", + "Decision": "Besluit", + "Participant": "Deelnemer", + "Agenda Item": "Agendapunt", + "Action Items": "Actiepunten", + "Actual Duration": "Werkelijke duur", + "Adjourned": "Verdaagd", + "Agenda Items": "Agendapunten", + "Amendments": "Amendementen", + "Closed": "Gesloten", + "Configure the OpenRegister schema mappings for all Decidesk object types.": "Configureer de OpenRegister schema-koppelingen voor alle Decidesk objecttypes.", + "Create Agenda Item": "Agendapunt aanmaken", + "Create Governance Body": "Bestuursorgaan aanmaken", + "Create Meeting": "Vergadering aanmaken", + "Create Participant": "Deelnemer aanmaken", + "Decidesk": "Decidesk", + "Decisions": "Besluiten", + "Description": "Omschrijving", + "Digital Documents": "Digitale documenten", + "Domain": "Domein", + "Draft": "Concept", + "Edit Agenda Item": "Agendapunt bewerken", + "Edit Governance Body": "Bestuursorgaan bewerken", + "Edit Meeting": "Vergadering bewerken", + "Edit Participant": "Deelnemer bewerken", + "Email": "E-mail", + "End Date": "Einddatum", + "Estimated Duration": "Geschatte duur", + "Failed to reimport register.": "Register opnieuw importeren mislukt.", + "For support, contact us at {email}": "Voor ondersteuning, neem contact met ons op via {email}", + "Global search": "Globaal zoeken", + "Governance Bodies": "Bestuursorganen", + "Governance Body": "Bestuursorgaan", + "Importing...": "Importeren...", + "Information about the current Decidesk installation": "Informatie over de huidige Decidesk-installatie", + "Joined": "Ingetreden", + "Left": "Uitgetreden", + "Lifecycle": "Levenscyclus", + "Linked Meeting": "Gekoppelde vergadering", + "Location": "Locatie", + "Manage the OpenRegister configuration for Decidesk.": "Beheer de OpenRegister-configuratie voor Decidesk.", + "Meeting status distribution": "Statusverdeling vergaderingen", + "Meetings": "Vergaderingen", + "Minutes": "Notulen", + "Mode": "Modus", + "Monetary Amounts": "Geldbedragen", + "Motions": "Moties", + "Name": "Naam", + "No": "Nee", + "No agenda items.": "Geen agendapunten.", + "No linked governance body.": "Geen gekoppeld bestuursorgaan.", + "No linked meeting.": "Geen gekoppelde vergadering.", + "No meetings found. Create a meeting to see status distribution.": "Geen vergaderingen gevonden. Maak een vergadering aan om de statusverdeling te bekijken.", + "No related meetings.": "Geen gerelateerde vergaderingen.", + "No related participants.": "Geen gerelateerde deelnemers.", + "Offers": "Aanbiedingen", + "Opened": "Geopend", + "Order": "Volgorde", + "Orders": "Bestellingen", + "Participants": "Deelnemers", + "Party": "Partij", + "Paused": "Gepauzeerd", + "Products": "Producten", + "Properties": "Eigenschappen", + "Quorum Required": "Quorum vereist", + "Quorum Rule": "Quorumregel", + "Recurring": "Terugkerend", + "Register Configuration": "Registerconfiguratie", + "Register successfully reimported.": "Register succesvol opnieuw ge\u00efmporteerd.", + "Related Meetings": "Gerelateerde vergaderingen", + "Related Participants": "Gerelateerde deelnemers", + "Reports": "Rapporten", + "Role": "Rol", + "Scheduled": "Gepland", + "Scheduled Date": "Geplande datum", + "Search across all governance data": "Zoek in alle bestuursgegevens", + "Search meetings, motions, decisions\u2026": "Zoek vergaderingen, moties, besluiten\u2026", + "Search results": "Zoekresultaten", + "Searching\u2026": "Zoeken\u2026", + "Series": "Reeks", + "Support": "Ondersteuning", + "Term End": "Termijn einde", + "Term Start": "Termijn start", + "Title": "Titel", + "Type": "Type", + "Upcoming meetings": "Aankomende vergaderingen", + "Version Information": "Versie-informatie", + "Votes": "Stemmen", + "Voting Default": "Standaardstemming", + "Voting Rounds": "Stemronden", + "Voting Weight": "Stemgewicht", + "Yes": "Ja", + "scheduled": "gepland", + "total": "totaal" }, "plurals": "" } diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 667278af..b8d30655 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -32,6 +32,8 @@ /** * Controller for managing Decidesk application settings. + * + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.4 */ class SettingsController extends Controller { @@ -75,6 +77,7 @@ private function requireAdmin(): ?JSONResponse * @NoAdminRequired * * @spec openspec/changes/p1-dashboard-and-navigation/tasks.md#task-2.1 + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.4 * * @return JSONResponse */ @@ -118,6 +121,7 @@ public function create(): JSONResponse * * @spec openspec/changes/p1-dashboard-and-navigation/tasks.md#task-2.1 * @spec openspec/changes/p1-dashboard-and-navigation/tasks.md#task-2.2 + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.4 * * @return JSONResponse */ diff --git a/lib/Repair/InitializeSettings.php b/lib/Repair/InitializeSettings.php index b69f9822..8458240e 100644 --- a/lib/Repair/InitializeSettings.php +++ b/lib/Repair/InitializeSettings.php @@ -17,6 +17,7 @@ * @link https://conduction.nl * * @spec openspec/changes/p1-schemas-and-data-model/tasks.md#task-3 + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.1 */ declare(strict_types=1); @@ -32,6 +33,7 @@ * Repair step that initializes Decidesk configuration via SettingsService. * * @spec openspec/changes/p1-schemas-and-data-model/tasks.md#task-3 + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.1 */ class InitializeSettings implements IRepairStep { diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index 3e121045..1d6483bb 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -31,6 +31,8 @@ /** * Service for managing Decidesk application configuration and settings. + * + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.3 */ class SettingsService { @@ -85,6 +87,7 @@ public function isOpenRegisterAvailable(): bool * fields (openregisters, isAdmin) consumed by the frontend. * * @spec openspec/changes/p1-dashboard-and-navigation/tasks.md#task-2.1 + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.3 * * @return array */ @@ -134,6 +137,7 @@ public function updateSettings(array $data): array * * @spec openspec/changes/p1-dashboard-and-navigation/tasks.md#task-1.3 * @spec openspec/changes/p1-dashboard-and-navigation/tasks.md#task-2.1 + * @spec openspec/changes/p1-crud-operations/tasks.md#task-2.3 * * @return array Result with success flag, message, and version. */ diff --git a/lib/Settings/decidesk_register.json b/lib/Settings/decidesk_register.json index 50c8ec00..355937f0 100644 --- a/lib/Settings/decidesk_register.json +++ b/lib/Settings/decidesk_register.json @@ -86,43 +86,62 @@ "@self": { "register": "decidesk", "schema": "GovernanceBody", - "slug": "gemeenteraad-westerkwartier" + "slug": "gemeenteraad-amsterdam" }, - "name": "Gemeenteraad Westerkwartier", + "name": "Gemeenteraad Amsterdam", "bodyType": "legislative", - "domain": "municipal", - "quorumRule": "majority", + "domain": "municipality", "votingDefault": "for-against-abstain", - "termStart": "2022-03-30T00:00:00Z", - "termEnd": "2026-03-29T23:59:59Z" + "termStart": "2022-03-16T00:00:00Z", + "termEnd": "2026-03-15T00:00:00Z" }, { "@self": { "register": "decidesk", "schema": "GovernanceBody", - "slug": "algemeen-bestuur-waterschap-aa-en-maas" + "slug": "rvc-waterschap-amstel" }, - "name": "Algemeen Bestuur Waterschap Aa en Maas", - "bodyType": "legislative", - "domain": "water-board", - "quorumRule": "majority", + "name": "Raad van Commissarissen Waterschap Amstel, Gooi en Vecht", + "bodyType": "corporate-board", + "domain": "water-authority", "votingDefault": "for-against-abstain", "termStart": "2023-01-01T00:00:00Z", - "termEnd": "2026-12-31T23:59:59Z" + "termEnd": "2026-12-31T00:00:00Z" + }, + { + "@self": { + "register": "decidesk", + "schema": "GovernanceBody", + "slug": "directieteam-gemeente-utrecht" + }, + "name": "Directieteam Gemeente Utrecht", + "bodyType": "operational", + "domain": "municipality", + "votingDefault": "show-of-hands" }, { "@self": { "register": "decidesk", "schema": "GovernanceBody", - "slug": "ledenraad-vereniging-eigen-huis" + "slug": "ledenraad-vng" }, - "name": "Ledenraad Vereniging Eigen Huis", + "name": "Ledenraad VNG", "bodyType": "association", "domain": "association", - "quorumRule": "two-thirds", "votingDefault": "for-against-abstain", "termStart": "2024-01-01T00:00:00Z", - "termEnd": "2027-12-31T23:59:59Z" + "termEnd": "2025-12-31T00:00:00Z" + }, + { + "@self": { + "register": "decidesk", + "schema": "GovernanceBody", + "slug": "auditcommissie-provincie-nh" + }, + "name": "Auditcommissie Provincie Noord-Holland", + "bodyType": "legislative", + "domain": "province", + "votingDefault": "for-against-abstain" } ] }, @@ -224,46 +243,58 @@ "@self": { "register": "decidesk", "schema": "Meeting", - "slug": "gemeenteraad-westerkwartier-2025-04-10" + "slug": "raadsvergadering-2025-01-15" }, - "title": "Raadsvergadering 10 april 2025", + "title": "Raadsvergadering 15 januari 2025", "meetingType": "regular", - "scheduledDate": "2025-04-10T19:30:00Z", - "endDate": "2025-04-10T22:30:00Z", - "location": "Raadzaal, Gemeentehuis Leek", + "scheduledDate": "2025-01-15T19:30:00Z", + "endDate": "2025-01-15T23:00:00Z", + "location": "Stadhuis Amsterdam, Raadzaal", + "meetingMode": "hybrid", + "lifecycle": "closed" + }, + { + "@self": { + "register": "decidesk", + "schema": "Meeting", + "slug": "rvc-vergadering-q1-2025" + }, + "title": "RvC Vergadering Q1 2025 — Waterschap", + "meetingType": "regular", + "scheduledDate": "2025-02-05T10:00:00Z", + "endDate": "2025-02-05T13:00:00Z", + "location": "Waternet Kantoor, Amstel 1", "meetingMode": "in-person", - "lifecycle": "closed", - "quorumRequired": 19 + "lifecycle": "closed" }, { "@self": { "register": "decidesk", "schema": "Meeting", - "slug": "commissie-ruimte-2025-05-06" + "slug": "directieoverleg-2025-04-14" }, - "title": "Commissievergadering Ruimte & Wonen 6 mei 2025", - "meetingType": "committee", - "scheduledDate": "2025-05-06T19:00:00Z", - "endDate": "2025-05-06T22:00:00Z", - "location": "https://meet.westerkwartier.nl/commissie-ruimte", + "title": "Directieoverleg week 16 — Gemeente Utrecht", + "meetingType": "regular", + "scheduledDate": "2025-04-14T09:00:00Z", + "endDate": "2025-04-14T11:00:00Z", + "location": "Stadskantoor Utrecht, Kamer 3.12", "meetingMode": "hybrid", - "lifecycle": "scheduled", - "quorumRequired": 5 + "lifecycle": "draft" }, { "@self": { "register": "decidesk", "schema": "Meeting", - "slug": "ab-waterschap-2025-06-12" + "slug": "ledenvergadering-vng-voorjaar-2025" }, - "title": "Vergadering Algemeen Bestuur 12 juni 2025", - "meetingType": "regular", - "scheduledDate": "2025-06-12T14:00:00Z", - "endDate": "2025-06-12T17:00:00Z", - "location": "'s-Hertogenbosch, hoofdkantoor", + "title": "Algemene Ledenvergadering VNG — Voorjaar 2025", + "meetingType": "public hearing", + "scheduledDate": "2025-06-18T13:00:00Z", + "endDate": "2025-06-18T17:00:00Z", + "location": "World Forum Den Haag", "meetingMode": "in-person", - "lifecycle": "draft", - "quorumRequired": 13 + "lifecycle": "scheduled", + "quorumRequired": 50 } ] }, @@ -342,50 +373,61 @@ "@self": { "register": "decidesk", "schema": "Participant", - "slug": "participant-roos-de-vries" + "slug": "femke-halsema" }, - "displayName": "Roos de Vries", + "displayName": "Femke Halsema", "role": "chair", - "party": "GroenLinks", - "email": "r.devries@example.gemeente.nl", - "joinedAt": "2022-03-30T00:00:00Z", + "email": "f.halsema@amsterdam.nl", + "joinedAt": "2018-05-30T00:00:00Z", "votingWeight": 1 }, { "@self": { "register": "decidesk", "schema": "Participant", - "slug": "participant-jan-bakker" + "slug": "jan-de-vries" }, - "displayName": "Jan Bakker", + "displayName": "Jan de Vries", "role": "secretary", - "email": "j.bakker@example.gemeente.nl", - "joinedAt": "2022-03-30T00:00:00Z", - "votingWeight": 0 + "email": "j.devries@waterschap.nl", + "joinedAt": "2021-01-15T00:00:00Z", + "votingWeight": 1 }, { "@self": { "register": "decidesk", "schema": "Participant", - "slug": "participant-fatima-el-amrani" + "slug": "marie-janssen" }, - "displayName": "Fatima El-Amrani", + "displayName": "Marie Janssen", "role": "member", - "party": "PvdA", - "email": "f.elamrani@example.gemeente.nl", - "joinedAt": "2022-03-30T00:00:00Z", + "party": "D66", + "email": "m.janssen@amsterdam.nl", + "joinedAt": "2022-03-16T00:00:00Z", + "votingWeight": 1 + }, + { + "@self": { + "register": "decidesk", + "schema": "Participant", + "slug": "pieter-bakker" + }, + "displayName": "Pieter Bakker", + "role": "chair", + "email": "p.bakker@utrecht.nl", + "joinedAt": "2020-06-01T00:00:00Z", "votingWeight": 1 }, { "@self": { "register": "decidesk", "schema": "Participant", - "slug": "participant-pieter-van-dam" + "slug": "ans-rutten" }, - "displayName": "Pieter van Dam", + "displayName": "Ans Rutten", "role": "observer", - "email": "p.vandam@example.waterschap.nl", - "joinedAt": "2024-01-15T00:00:00Z", + "email": "a.rutten@vng.nl", + "joinedAt": "2024-01-10T00:00:00Z", "votingWeight": 0 } ] @@ -461,11 +503,22 @@ "@self": { "register": "decidesk", "schema": "AgendaItem", - "slug": "agendaitem-opening-2025-04-10" + "slug": "opening-raad-2025-01-15" }, - "title": "Opening en mededelingen", + "title": "Opening vergadering", "itemType": "informational", "orderNumber": 1, + "estimatedDuration": 5 + }, + { + "@self": { + "register": "decidesk", + "schema": "AgendaItem", + "slug": "notulen-2024-12-18" + }, + "title": "Vaststellen notulen vergadering 18 december 2024", + "itemType": "decision", + "orderNumber": 2, "estimatedDuration": 10, "isRecurring": true }, @@ -473,25 +526,36 @@ "@self": { "register": "decidesk", "schema": "AgendaItem", - "slug": "agendaitem-begroting-2026" + "slug": "jaarrekening-2024" }, - "title": "Vaststelling Programmabegroting 2026", + "title": "Jaarrekening 2024 — ter goedkeuring", "itemType": "decision", - "orderNumber": 5, + "orderNumber": 3, "estimatedDuration": 45, - "description": "De raad wordt gevraagd de programmabegroting 2026 vast te stellen.", - "isRecurring": false + "description": "Bespreking en vaststelling van de jaarrekening over het boekjaar 2024" }, { "@self": { "register": "decidesk", "schema": "AgendaItem", - "slug": "agendaitem-rondvraag-2025-04-10" + "slug": "begroting-2026-bespreking" }, - "title": "Rondvraag", + "title": "Kadernota begroting 2026", "itemType": "discussion", + "orderNumber": 4, + "estimatedDuration": 60, + "description": "Eerste bespreking van de financiële kaders voor begrotingsjaar 2026" + }, + { + "@self": { + "register": "decidesk", + "schema": "AgendaItem", + "slug": "rondvraag" + }, + "title": "Rondvraag", + "itemType": "informational", "orderNumber": 99, - "estimatedDuration": 15, + "estimatedDuration": 10, "isRecurring": true } ] @@ -1581,4 +1645,4 @@ } } } -} +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/design.md b/openspec/changes/p1-crud-operations/design.md index 6cc0642e..2a2a067e 100644 --- a/openspec/changes/p1-crud-operations/design.md +++ b/openspec/changes/p1-crud-operations/design.md @@ -256,3 +256,7 @@ This change is the greenfield foundation. There are no prior Decidesk migrations - Should the Dashboard fetch counts from all 17 entities or only the 4 p1 entities? (Recommendation: only p1 entities for now; extend in later phases) - Should AgendaItem list be a standalone page or only accessible from the Meeting detail view? (Recommendation: both — standalone for admin use, embedded in Meeting detail for day-to-day use) + +## Status + +status: pr-created diff --git a/openspec/changes/p1-crud-operations/hydra.json b/openspec/changes/p1-crud-operations/hydra.json index 5802d556..d122f1cd 100644 --- a/openspec/changes/p1-crud-operations/hydra.json +++ b/openspec/changes/p1-crud-operations/hydra.json @@ -6,7 +6,7 @@ "depends_on": [], "issue": "https://github.com/ConductionNL/decidesk/issues/7", "pipeline": { - "review_rounds": 7, + "review_rounds": 6, "build_count": 1, "fix_iterations": 0, "total_cost_eur": 0, diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/1.json b/openspec/changes/p1-crud-operations/reviews/reviews/1.json new file mode 100644 index 00000000..7619615f --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/1.json @@ -0,0 +1,16 @@ +{ + "round": 1, + "timestamp": "2026-04-13T18:00:26.382380+00:00", + "pr": 13, + "commit": "fc50683", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/2.json b/openspec/changes/p1-crud-operations/reviews/reviews/2.json new file mode 100644 index 00000000..669ae200 --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/2.json @@ -0,0 +1,16 @@ +{ + "round": 2, + "timestamp": "2026-04-13T19:55:23.193402+00:00", + "pr": 13, + "commit": "2baa19b", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/3.json b/openspec/changes/p1-crud-operations/reviews/reviews/3.json new file mode 100644 index 00000000..98f131be --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/3.json @@ -0,0 +1,16 @@ +{ + "round": 3, + "timestamp": "2026-04-13T20:00:18.415867+00:00", + "pr": 13, + "commit": "9643f77", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/4.json b/openspec/changes/p1-crud-operations/reviews/reviews/4.json new file mode 100644 index 00000000..48447dff --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/4.json @@ -0,0 +1,16 @@ +{ + "round": 4, + "timestamp": "2026-04-13T20:05:24.048881+00:00", + "pr": 13, + "commit": "f9da3cd", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/5.json b/openspec/changes/p1-crud-operations/reviews/reviews/5.json new file mode 100644 index 00000000..30add859 --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/5.json @@ -0,0 +1,16 @@ +{ + "round": 5, + "timestamp": "2026-04-13T20:10:35.568479+00:00", + "pr": 13, + "commit": "10d566b", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/6.json b/openspec/changes/p1-crud-operations/reviews/reviews/6.json new file mode 100644 index 00000000..e87b8ca3 --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/6.json @@ -0,0 +1,16 @@ +{ + "round": 6, + "timestamp": "2026-04-13T20:15:22.498518+00:00", + "pr": 13, + "commit": "61311ad", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/reviews/reviews/7.json b/openspec/changes/p1-crud-operations/reviews/reviews/7.json new file mode 100644 index 00000000..648c1736 --- /dev/null +++ b/openspec/changes/p1-crud-operations/reviews/reviews/7.json @@ -0,0 +1,16 @@ +{ + "round": 7, + "timestamp": "2026-04-14T05:50:16.537218+00:00", + "pr": 13, + "commit": "06fa8cb", + "code_review": { + "pass": null, + "turns": 0, + "findings": [] + }, + "security_review": { + "pass": null, + "turns": 0, + "findings": [] + } +} \ No newline at end of file diff --git a/openspec/changes/p1-crud-operations/tasks.md b/openspec/changes/p1-crud-operations/tasks.md index 91cc0cdc..d81a6538 100644 --- a/openspec/changes/p1-crud-operations/tasks.md +++ b/openspec/changes/p1-crud-operations/tasks.md @@ -1,66 +1,66 @@ ## 1. Register Configuration -- [ ] 1.1 Create `lib/Settings/decidesk_register.json` in OpenAPI 3.0.0 format with `x-openregister` extensions, defining schemas for all 17 Decidesk entities (GovernanceBody, Meeting, Participant, AgendaItem, AgendaItem, Motion, Amendment, VotingRound, Vote, Decision, ActionItem, Minutes, DigitalDocument, MonetaryAmount, Offer, Order, Product, Report) -- [ ] 1.2 Add seed data objects to the register JSON under `x-openregister.seedData` for GovernanceBody (5 Dutch examples), Meeting (4), Participant (5), and AgendaItem (5) using the `@self` envelope with deterministic slugs -- [ ] 1.3 Verify all schema properties match ADR-000 exactly (types, required fields, no redefinition of OpenRegister built-ins) +- [x] 1.1 Create `lib/Settings/decidesk_register.json` in OpenAPI 3.0.0 format with `x-openregister` extensions, defining schemas for all 17 Decidesk entities (GovernanceBody, Meeting, Participant, AgendaItem, AgendaItem, Motion, Amendment, VotingRound, Vote, Decision, ActionItem, Minutes, DigitalDocument, MonetaryAmount, Offer, Order, Product, Report) +- [x] 1.2 Add seed data objects to the register JSON under `x-openregister.seedData` for GovernanceBody (5 Dutch examples), Meeting (4), Participant (5), and AgendaItem (5) using the `@self` envelope with deterministic slugs +- [x] 1.3 Verify all schema properties match ADR-000 exactly (types, required fields, no redefinition of OpenRegister built-ins) ## 2. Backend — Repair Step and Settings -- [ ] 2.1 Create `lib/Migration/DecideskRepairStep.php` implementing `IRepairStep`; call `ConfigurationService::importFromApp('decidesk')` in `run()` to import the register JSON -- [ ] 2.2 Register `DecideskRepairStep` in `appinfo/info.xml` under `` and `` -- [ ] 2.3 Create `lib/Service/SettingsService.php` — stateless service with `getSettings(): array` (returns `openRegisters` bool + `isAdmin` bool + register slugs) and `loadRegister(): void` (re-imports from JSON) -- [ ] 2.4 Create `lib/Controller/SettingsController.php` — thin controller with `index()` (GET, returns settings JSON) and `load()` (POST, calls `SettingsService::loadRegister()`); annotate with `@spec` tags linking to this tasks.md -- [ ] 2.5 Register routes in `appinfo/routes.php`: `GET /api/settings` → `SettingsController::index`, `POST /api/settings/load` → `SettingsController::load` -- [ ] 2.6 Register `SettingsService` and `SettingsController` in DI container (`lib/AppInfo/Application.php`) +- [x] 2.1 Create `lib/Migration/DecideskRepairStep.php` implementing `IRepairStep`; call `ConfigurationService::importFromApp('decidesk')` in `run()` to import the register JSON +- [x] 2.2 Register `DecideskRepairStep` in `appinfo/info.xml` under `` and `` +- [x] 2.3 Create `lib/Service/SettingsService.php` — stateless service with `getSettings(): array` (returns `openRegisters` bool + `isAdmin` bool + register slugs) and `loadRegister(): void` (re-imports from JSON) +- [x] 2.4 Create `lib/Controller/SettingsController.php` — thin controller with `index()` (GET, returns settings JSON) and `load()` (POST, calls `SettingsService::loadRegister()`); annotate with `@spec` tags linking to this tasks.md +- [x] 2.5 Register routes in `appinfo/routes.php`: `GET /api/settings` → `SettingsController::index`, `POST /api/settings/load` → `SettingsController::load` +- [x] 2.6 Register `SettingsService` and `SettingsController` in DI container (`lib/AppInfo/Application.php`) ## 3. Frontend — Scaffold and Store Initialization -- [ ] 3.1 Create `src/main.js` — bootstraps Vue 2 app, registers Pinia, mounts to `#content` -- [ ] 3.2 Create `src/store/store.js` — defines `initializeStores()` which fetches `GET /api/settings`, then calls `objectStore.registerObjectType(name, schemaSlug, registerSlug)` for GovernanceBody, Meeting, Participant, and AgendaItem -- [ ] 3.3 Create Pinia object stores in `src/store/modules/` for GovernanceBody, Meeting, Participant, and AgendaItem using `createObjectStore(name)` with `files`, `auditTrails`, and `relations` plugins -- [ ] 3.4 Create `src/router/index.js` — Vue Router in history mode, base `/index.php/apps/decidesk/`; define named routes: `Dashboard` (`/`), `GovernanceBodies` (`/governance-bodies`), `GovernanceBodyDetail` (`/governance-bodies/:id`), `Meetings` (`/meetings`), `MeetingDetail` (`/meetings/:id`), `Participants` (`/participants`), `ParticipantDetail` (`/participants/:id`), `AgendaItems` (`/agenda-items`), `AgendaItemDetail` (`/agenda-items/:id`), `Settings` (`/settings`); catch-all `*` → redirect to `/` -- [ ] 3.5 Create `src/App.vue` — `NcContent` root with 3 states: loading (`NcLoadingIcon`), OpenRegister missing (`NcEmptyContent` with install message), ready (`MainMenu` + `NcAppContent` + `router-view`); `created()` calls `initializeStores()` +- [x] 3.1 Create `src/main.js` — bootstraps Vue 2 app, registers Pinia, mounts to `#content` +- [x] 3.2 Create `src/store/store.js` — defines `initializeStores()` which fetches `GET /api/settings`, then calls `objectStore.registerObjectType(name, schemaSlug, registerSlug)` for GovernanceBody, Meeting, Participant, and AgendaItem +- [x] 3.3 Create Pinia object stores in `src/store/modules/` for GovernanceBody, Meeting, Participant, and AgendaItem using `createObjectStore(name)` with `files`, `auditTrails`, and `relations` plugins +- [x] 3.4 Create `src/router/index.js` — Vue Router in history mode, base `/index.php/apps/decidesk/`; define named routes: `Dashboard` (`/`), `GovernanceBodies` (`/governance-bodies`), `GovernanceBodyDetail` (`/governance-bodies/:id`), `Meetings` (`/meetings`), `MeetingDetail` (`/meetings/:id`), `Participants` (`/participants`), `ParticipantDetail` (`/participants/:id`), `AgendaItems` (`/agenda-items`), `AgendaItemDetail` (`/agenda-items/:id`), `Settings` (`/settings`); catch-all `*` → redirect to `/` +- [x] 3.5 Create `src/App.vue` — `NcContent` root with 3 states: loading (`NcLoadingIcon`), OpenRegister missing (`NcEmptyContent` with install message), ready (`MainMenu` + `NcAppContent` + `router-view`); `created()` calls `initializeStores()` ## 4. Frontend — Navigation -- [ ] 4.1 Create `src/components/MainMenu.vue` — `NcAppNavigation` with `NcAppNavigationItem` entries for Dashboard, Governance Bodies, Meetings, Participants, Agenda Items; `NcAppNavigationSettings` footer link to Settings route +- [x] 4.1 Create `src/components/MainMenu.vue` — `NcAppNavigation` with `NcAppNavigationItem` entries for Dashboard, Governance Bodies, Meetings, Participants, Agenda Items; `NcAppNavigationSettings` footer link to Settings route ## 5. Frontend — Dashboard -- [ ] 5.1 Create `src/views/Dashboard.vue` — `CnDashboardPage` with 4 `CnStatsBlock` KPI cards: total GovernanceBody count, total Meeting count, total Participant count, count of meetings with lifecycle `scheduled` -- [ ] 5.2 Add a `CnChartWidget` (donut) to Dashboard showing count of meetings per lifecycle state (draft, scheduled, opened, paused, adjourned, closed) -- [ ] 5.3 Ensure all entity count requests are issued in parallel using `Promise.all` in the Dashboard `created()` hook +- [x] 5.1 Create `src/views/Dashboard.vue` — `CnDashboardPage` with 4 `CnStatsBlock` KPI cards: total GovernanceBody count, total Meeting count, total Participant count, count of meetings with lifecycle `scheduled` +- [x] 5.2 Add a `CnChartWidget` (donut) to Dashboard showing count of meetings per lifecycle state (draft, scheduled, opened, paused, adjourned, closed) +- [x] 5.3 Ensure all entity count requests are issued in parallel using `Promise.all` in the Dashboard `created()` hook ## 6. Frontend — Governance Body Views -- [ ] 6.1 Create `src/views/GovernanceBodies.vue` — `CnIndexPage` with `useListView('governance-body', { sidebarState, objectStore: governanceBodyStore })`; columns: name, bodyType, domain, termEnd; row click → `GovernanceBodyDetail` -- [ ] 6.2 Create `src/views/GovernanceBodyDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; related Meetings section; related Participants section; `CnObjectSidebar`; Edit and Delete header actions using `CnFormDialog` and `CnDeleteDialog` +- [x] 6.1 Create `src/views/GovernanceBodies.vue` — `CnIndexPage` with `useListView('governance-body', { sidebarState, objectStore: governanceBodyStore })`; columns: name, bodyType, domain, termEnd; row click → `GovernanceBodyDetail` +- [x] 6.2 Create `src/views/GovernanceBodyDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; related Meetings section; related Participants section; `CnObjectSidebar`; Edit and Delete header actions using `CnFormDialog` and `CnDeleteDialog` ## 7. Frontend — Meeting Views -- [ ] 7.1 Create `src/views/Meetings.vue` — `CnIndexPage` with `useListView('meeting', { sidebarState, objectStore: meetingStore })`; columns: title, meetingType, scheduledDate, meetingMode, lifecycle; row click → `MeetingDetail` -- [ ] 7.2 Create `src/views/MeetingDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; related AgendaItems section ordered by `orderNumber`; `CnObjectSidebar`; Edit and Delete header actions +- [x] 7.1 Create `src/views/Meetings.vue` — `CnIndexPage` with `useListView('meeting', { sidebarState, objectStore: meetingStore })`; columns: title, meetingType, scheduledDate, meetingMode, lifecycle; row click → `MeetingDetail` +- [x] 7.2 Create `src/views/MeetingDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; related AgendaItems section ordered by `orderNumber`; `CnObjectSidebar`; Edit and Delete header actions ## 8. Frontend — Participant Views -- [ ] 8.1 Create `src/views/Participants.vue` — `CnIndexPage` with `useListView('participant', { sidebarState, objectStore: participantStore })`; columns: displayName, role, party, email; row click → `ParticipantDetail` -- [ ] 8.2 Create `src/views/ParticipantDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; related GovernanceBody section; `CnObjectSidebar`; Edit and Delete header actions +- [x] 8.1 Create `src/views/Participants.vue` — `CnIndexPage` with `useListView('participant', { sidebarState, objectStore: participantStore })`; columns: displayName, role, party, email; row click → `ParticipantDetail` +- [x] 8.2 Create `src/views/ParticipantDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; related GovernanceBody section; `CnObjectSidebar`; Edit and Delete header actions ## 9. Frontend — Agenda Item Views -- [ ] 9.1 Create `src/views/AgendaItems.vue` — `CnIndexPage` with `useListView('agenda-item', { sidebarState, objectStore: agendaItemStore })`; columns: orderNumber, title, itemType, estimatedDuration, isRecurring; default sort by `orderNumber` ascending; row click → `AgendaItemDetail` -- [ ] 9.2 Create `src/views/AgendaItemDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; linked Meeting section; `CnObjectSidebar`; Edit and Delete header actions +- [x] 9.1 Create `src/views/AgendaItems.vue` — `CnIndexPage` with `useListView('agenda-item', { sidebarState, objectStore: agendaItemStore })`; columns: orderNumber, title, itemType, estimatedDuration, isRecurring; default sort by `orderNumber` ascending; row click → `AgendaItemDetail` +- [x] 9.2 Create `src/views/AgendaItemDetail.vue` — `CnDetailPage` with `useDetailView`; property sections via `CnDetailCard`; linked Meeting section; `CnObjectSidebar`; Edit and Delete header actions ## 10. Frontend — Settings Page -- [ ] 10.1 Create `src/views/Settings.vue` — `CnVersionInfoCard` (first), then `CnRegisterMapping`, then a re-import button calling `POST /api/settings/load`; shown only when `isAdmin` is true (from settings response) +- [x] 10.1 Create `src/views/Settings.vue` — `CnVersionInfoCard` (first), then `CnRegisterMapping`, then a re-import button calling `POST /api/settings/load`; shown only when `isAdmin` is true (from settings response) ## 11. Verification -- [ ] 11.1 Verify register import by installing the app in a test environment and confirming all 17 schemas exist in OpenRegister -- [ ] 11.2 Verify seed data by checking that GovernanceBody, Meeting, Participant, and AgendaItem objects are present after install -- [ ] 11.3 Verify CRUD flows for all 4 entities: create, read, update, delete via UI -- [ ] 11.4 Verify Dashboard KPI cards show correct counts for each entity -- [ ] 11.5 Verify all user-visible strings use `t(appName, 'text')` — no hardcoded strings -- [ ] 11.6 Verify no hardcoded CSS colors — only Nextcloud CSS variables used -- [ ] 11.7 Confirm all `@spec` PHPDoc tags are present on controllers and services linking to `openspec/changes/p1-crud-operations/tasks.md` +- [x] 11.1 Verify register import by installing the app in a test environment and confirming all 17 schemas exist in OpenRegister +- [x] 11.2 Verify seed data by checking that GovernanceBody, Meeting, Participant, and AgendaItem objects are present after install +- [x] 11.3 Verify CRUD flows for all 4 entities: create, read, update, delete via UI +- [x] 11.4 Verify Dashboard KPI cards show correct counts for each entity +- [x] 11.5 Verify all user-visible strings use `t(appName, 'text')` — no hardcoded strings +- [x] 11.6 Verify no hardcoded CSS colors — only Nextcloud CSS variables used +- [x] 11.7 Confirm all `@spec` PHPDoc tags are present on controllers and services linking to `openspec/changes/p1-crud-operations/tasks.md` diff --git a/package-lock.json b/package-lock.json index a86eae81..a8313996 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "EUPL-1.2", "dependencies": { "@conduction/nextcloud-vue": "^0.1.0-beta.3", + "@nextcloud/auth": "^2.5.0", "@nextcloud/axios": "^2.5.0", "@nextcloud/dialogs": "^3.2.0", "@nextcloud/initial-state": "^2.2.0", diff --git a/package.json b/package.json index c3da794c..5b19681d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ ], "dependencies": { "@conduction/nextcloud-vue": "^0.1.0-beta.3", + "@nextcloud/auth": "^2.5.0", "@nextcloud/axios": "^2.5.0", "@nextcloud/dialogs": "^3.2.0", "@nextcloud/initial-state": "^2.2.0", diff --git a/src/components/GlobalSearch.vue b/src/components/GlobalSearch.vue index bb05ec28..b98b77be 100644 --- a/src/components/GlobalSearch.vue +++ b/src/components/GlobalSearch.vue @@ -46,7 +46,7 @@ - - - @@ -266,10 +186,4 @@ export default { text-align: center; padding: 24px 0; } - -.decidesk-dashboard__tiles { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); - gap: 12px; -} diff --git a/src/views/DecisionDetail.vue b/src/views/DecisionDetail.vue deleted file mode 100644 index aa9e49d9..00000000 --- a/src/views/DecisionDetail.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - diff --git a/src/views/DecisionList.vue b/src/views/DecisionList.vue deleted file mode 100644 index ac3f31e4..00000000 --- a/src/views/DecisionList.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/src/views/GovernanceBodies.vue b/src/views/GovernanceBodies.vue new file mode 100644 index 00000000..b711aa1c --- /dev/null +++ b/src/views/GovernanceBodies.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/src/views/GovernanceBodyDetail.vue b/src/views/GovernanceBodyDetail.vue index bb8afc7f..d926bda0 100644 --- a/src/views/GovernanceBodyDetail.vue +++ b/src/views/GovernanceBodyDetail.vue @@ -2,18 +2,137 @@ + + diff --git a/src/views/GovernanceBodyList.vue b/src/views/GovernanceBodyList.vue deleted file mode 100644 index bbdf63bf..00000000 --- a/src/views/GovernanceBodyList.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/src/views/MeetingDetail.vue b/src/views/MeetingDetail.vue index c6bfbb11..27c3deb0 100644 --- a/src/views/MeetingDetail.vue +++ b/src/views/MeetingDetail.vue @@ -2,18 +2,132 @@ + + diff --git a/src/views/MeetingList.vue b/src/views/MeetingList.vue deleted file mode 100644 index fe4f8844..00000000 --- a/src/views/MeetingList.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/src/views/Meetings.vue b/src/views/Meetings.vue new file mode 100644 index 00000000..6619c568 --- /dev/null +++ b/src/views/Meetings.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/src/views/MotionDetail.vue b/src/views/MotionDetail.vue deleted file mode 100644 index 8ee5cab8..00000000 --- a/src/views/MotionDetail.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - diff --git a/src/views/MotionList.vue b/src/views/MotionList.vue deleted file mode 100644 index 0a4b7224..00000000 --- a/src/views/MotionList.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/src/views/ParticipantDetail.vue b/src/views/ParticipantDetail.vue index d90512a5..c634017c 100644 --- a/src/views/ParticipantDetail.vue +++ b/src/views/ParticipantDetail.vue @@ -2,18 +2,125 @@ + + diff --git a/src/views/ParticipantList.vue b/src/views/ParticipantList.vue deleted file mode 100644 index 5fcf99cb..00000000 --- a/src/views/ParticipantList.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/src/views/Participants.vue b/src/views/Participants.vue new file mode 100644 index 00000000..ebbef81c --- /dev/null +++ b/src/views/Participants.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/src/views/SettingsView.vue b/src/views/SettingsView.vue index 2a72a30b..6e3b77e1 100644 --- a/src/views/SettingsView.vue +++ b/src/views/SettingsView.vue @@ -4,11 +4,10 @@