diff --git a/docs/features/000-project-foundation.md b/docs/features/000-project-foundation.md deleted file mode 100644 index 3b068a9..0000000 --- a/docs/features/000-project-foundation.md +++ /dev/null @@ -1,76 +0,0 @@ -# Feature 0 – Projektgrund - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -HemHub behövde en minimal projektgrund för inkrementell utveckling av en -webbapplikation med separat frontend och backend. - -## Mål - -Skapa körbara React- och Spring Boot-applikationer, koppla ihop dem lokalt och -etablera grundläggande tester och dokumentation. - -## Omfattning - -- monorepo med `backend/` och `frontend/`; -- Java 21, Spring Boot och Maven Wrapper; -- React, TypeScript, Vite och pnpm; -- health-endpoint och en tillfällig frontendstatus; -- Vite-proxy och grundtester; -- `README.md`, `AGENTS.md` och `.gitignore`. - -## Avgränsningar - -Feature 0 införde ingen databas, domänmodell, autentisering, deployment, -containerkonfiguration eller produktionskonfiguration. - -## Beslut - -Frontend och backend skapades som separata applikationer i samma repository. -Frontend använder relativa `/api`-adresser, och Vite proxar dem lokalt till -backend på port 8080. Ingen generell CORS-konfiguration infördes. - -## Implementerad lösning - -Backend skapades med Spring Boot 4.1.0, Java 21, Spring Web och Maven Wrapper. -Frontend skapades med React 19, TypeScript, Vite och pnpm. - -Den ursprungliga startsidan anropade health-endpointen och visade backendstatus -eller ett anslutningsfel. Senare features har ersatt denna startsida, men -health-endpointen och dess test finns kvar. - -## API-förändringar - -`GET /api/health` infördes och returnerar: - -```json -{"status":"UP"} -``` - -## Databasförändringar - -Inga. - -## Frontendförändringar - -En minimal startsida visade rubriken HemHub, att frontend hade startat och -resultatet från `/api/health`. Vite konfigurerades att proxya `/api` till -`http://localhost:8080`. - -## Tester och verifiering - -Ett MockMvc-test verifierar status 200 och `status: UP`. Det ursprungliga -frontendtestet verifierade rubriken HemHub med mockat API-anrop. - -## Kända begränsningar - -Projektgrunden innehöll ingen användar- eller uppgiftsfunktionalitet. Den -ursprungliga health-vyn är inte längre appens aktiva vy. - -## Relaterade commits - -- `9957383e88b08dc006d2eaeb2a513c7769bd5705` – `Initialize HemHub project foundation` diff --git a/docs/features/001-user-selection.md b/docs/features/001-user-selection.md deleted file mode 100644 index b233d87..0000000 --- a/docs/features/001-user-selection.md +++ /dev/null @@ -1,99 +0,0 @@ -# Feature 1 – Användarval - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -HemHub behövde centralt lagrade användare och ett enkelt sätt att välja vem som -använder applikationen, utan att införa autentisering. - -## Mål - -Göra det möjligt att lista och skapa användare, välja en aktiv användare, -återanvända valet i samma browser och lämna den aktiva vyn. - -## Omfattning - -- persistens, API och validering för användare; -- startflöden för tom och befintlig användarlista; -- lokalt lagrad aktiv användare; -- användarval, skapande och felhantering; -- automatiserade backend- och frontendtester. - -## Avgränsningar - -Ingen autentisering, lösenord, roll, behörighet, e-post, avatar, -hushållsrelation, redigering eller radering infördes. - -## Beslut - -Backend är slutlig auktoritet för namnvalidering. Namn normaliseras separat för -skiftlägesokänslig unikhet. Frontend lagrar endast UUID under -`hemhub.activeUserId` och verifierar det mot den hämtade användarlistan. - -## Implementerad lösning - -Vid appstart hämtar frontend alltid användarna. En tom lista leder direkt till -formuläret Skapa användare. Om användare finns men inget giltigt lokalt val -finns visas Vem är du?. - -Val eller lyckat skapande sparar användarens id och aktiverar användaren. Ett -ogiltigt lagrat id rensas utan tekniskt fel. Feature 1:s tillfälliga startsida -och kontrollen Byt användare ersattes i Feature 2 av uppgiftsbrädan och -kontrollen Logga ut; lagringsmekanismen är oförändrad. - -## API-förändringar - -- `GET /api/users` returnerar alla användare. -- `POST /api/users` skapar en användare och returnerar `201 Created`. - -API-responsen innehåller `id`, `name` och `createdAt`. `normalizedName` exponeras -inte. - -Tomt namn eller namn längre än 50 Unicode-kodpunkter ger `400` med -`INVALID_USER_NAME`. Ett dubblettnamn utan hänsyn till stora och små bokstäver -ger `409` med `USER_NAME_ALREADY_EXISTS`. - -## Databasförändringar - -Flyway-migreringen `V1__create_users.sql` skapade tabellen `app_user`: - -- UUID som primärnyckel; -- `name VARCHAR(50)`; -- unikt `normalized_name VARCHAR(150)`; -- `created_at TIMESTAMP WITH TIME ZONE`. - -Lokalt används filbaserad H2 och i tester H2 in-memory. Backend genererar UUID -och `createdAt` med en UTC-klocka. - -## Frontendförändringar - -Frontend fick laddnings-, fel-, användarvals- och användarskapandevyer. -Skapandeformuläret trimmar namnet, gör en enkel längdkontroll, blockerar -dubbelsubmit och behåller inmatningen vid fel. - -Nuvarande utloggning tar bort `hemhub.activeUserId`, rensar aktiv användare och -visar Vem är du? även om endast en användare finns. - -## Tester och verifiering - -Backendens integrationstester verifierar tom lista, skapande och listning, -trimning, ogiltiga namn, skiftlägesokänsliga dubbletter och alfabetisk -sortering. - -Frontendtesterna verifierar tom lista, användarval, automatisk aktivering efter -skapande, bevarad inmatning vid fel, hämtfel med återförsök, ogiltigt lagrat id -och utloggning. API-anropen mockas. - -## Kända begränsningar - -Aktiv användare är ett lokalt gränssnittsval, inte säker autentisering. Valet -synkroniseras inte mellan browsers eller enheter. Användare kan inte redigeras -eller raderas. - -## Relaterade commits - -- `1ec7a729085d456d3185a1c74d02f99f40de0e8d` – `feat: add user selection flow` -- `050f248857a01db2dc236a0ca35982fd70dab3d6` – merge till `main` diff --git a/docs/features/002-task-creation.md b/docs/features/002-task-creation.md deleted file mode 100644 index 02fcd72..0000000 --- a/docs/features/002-task-creation.md +++ /dev/null @@ -1,108 +0,0 @@ -# Feature 2 – Skapa uppgifter - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -Efter införandet av aktiv användare behövde HemHub en första gemensam -uppgiftsmodell och en enkel bräda för att skapa och visa uppgifter. - -## Mål - -Låta en aktiv användare se tre statuskolumner, skapa en uppgift med titel och -valfri beskrivning samt se den sparade uppgiften efter omladdning. - -## Omfattning - -- persistent uppgiftsmodell och Flyway-migrering; -- API för att skapa och lista uppgifter; -- bräda med Väntande, Pågående och Klart; -- modal för att skapa uppgifter; -- laddnings-, validerings- och felhantering; -- automatiserade backend- och frontendtester. - -## Avgränsningar - -Ingen ändring av status, drag-and-drop, tilldelning, användarrelation, poäng, -deadline, återkommande uppgift, redigering, radering, sökning, filtrering eller -paginering infördes. - -## Beslut - -Alla användare ser samma uppgifter; uppgiftsmodellen har ingen relation till en -användare. Backend väljer alltid status `WAITING` vid skapande. Listningen -sorteras i backend efter `createdAt ASC, id ASC`, och frontend bevarar den -ordningen. - -## Implementerad lösning - -JPA-entiteten `Task` innehåller UUID, titel, valfri beskrivning, status och -skapandetid. Backend genererar UUID och `createdAt` med en UTC-klocka. - -Frontend visar uppgiftsbrädan när ett giltigt aktivt användarval finns. Uppgifter -hämtas vid montering, grupperas efter status och visas med endast titel och -eventuell beskrivning. Tomma kolumner saknar tomlägestext. - -## API-förändringar - -- `GET /api/tasks` returnerar samtliga uppgifter, äldst först och med UUID som - sekundär sorteringsnyckel. -- `POST /api/tasks` skapar en uppgift och returnerar `201 Created`. - -Titel trimmas, är obligatorisk och får omfatta högst 100 Unicode-kodpunkter. -Beskrivning trimmas, får omfatta högst 500 Unicode-kodpunkter och lagras som -`null` om den är tom. Ogiltiga anrop ger `400` med felkoden `INVALID_TASK`. - -## Databasförändringar - -Flyway-migreringen `V2__create_tasks.sql` skapade tabellen `task`: - -- `id UUID PRIMARY KEY`; -- `title VARCHAR(100) NOT NULL`; -- `description VARCHAR(500)`; -- `status VARCHAR(20) NOT NULL`; -- `created_at TIMESTAMP WITH TIME ZONE NOT NULL`. - -Status lagras som text genom `@Enumerated(EnumType.STRING)`. Databasen har ingen -check constraint för enumvärden. - -## Frontendförändringar - -Feature 1:s tillfälliga aktiva vy ersattes med uppgiftsbrädan. Sidhuvudet visar -aktiv användares namn, Logga ut och Ny uppgift. - -Skapandemodalen innehåller titel och valfri beskrivning. Titelfältet får fokus -när modalen öppnas. När inget submit-anrop pågår kan den stängas med kryss, -Escape eller klick på bakgrunden. Normal stängning avmonterar komponenten och -nollställer därmed formuläret. - -Vid submit gör frontend samma grundläggande längdkontroller, skickar trimmade -värden och blockerar uppenbara dubbelsubmit. Vid fel stannar modalen öppen med -bevarad inmatning. Vid framgång läggs API-svaret sist i den befintliga listan, -vilket placerar den nya `WAITING`-uppgiften längst ned i Väntande utan att -sortera om backendens ordning. - -## Tester och verifiering - -Backendens integrationstester verifierar skapande, `WAITING`, trimning, tom -beskrivning som `null`, längdvalidering samt sorteringen `createdAt ASC, id ASC`. - -Frontendtesterna verifierar bräda och statusgruppering, tomma kolumner, -modalöppning och fokus, skapande, ordning efter skapande, bevarad formulärdata -vid API-fel samt utloggning. Parametriserade testfall verifierar också stängning -med kryss, Escape och bakgrundsklick samt att formuläret är rensat när modalen -öppnas igen. - -## Kända begränsningar - -Statusvärden utöver `WAITING` kan visas om de redan finns i databasen, men inget -nuvarande API eller gränssnitt kan flytta en uppgift mellan kolumnerna. Det -finns ingen koppling mellan uppgifter och skapande eller aktiv användare. -Modalen har ingen fokusfälla eller explicit fokusåterställning. - -## Relaterade commits - -- `3f152eecccdd88f840066543bf9321b81b4cead8` – `feat: add task creation board` -- `2f7b99fb21c57c2e9c5f019a2b5073458e41c939` – merge till `main` diff --git a/docs/features/003-task-points.md b/docs/features/003-task-points.md deleted file mode 100644 index 2ffa97f..0000000 --- a/docs/features/003-task-points.md +++ /dev/null @@ -1,443 +0,0 @@ -# Feature 3 – Uppgiftspoäng - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -HemHub ska på sikt kunna använda spelifiering för att uppmuntra -familjemedlemmar att utföra uppgifter. Exempel på framtida funktioner kan vara -mål, achievements och belöningar baserade på hur många poäng en användare -samlar under en viss period. - -Feature 3 inför den grundläggande poänginformationen på uppgiften. Funktionen -registrerar endast uppgiftens poängvärde. Intjäning av poäng och övrig -spelifiering införs i senare features. - -## Mål - -Feature 3 ska: - -- lägga till ett obligatoriskt poängvärde på varje uppgift; -- låta användaren ange poäng när en uppgift skapas; -- visa poängen på uppgiftskortet; -- validera poängen konsekvent i frontend och backend; -- dokumentera hur lokal utvecklingsdata hanteras. - -## Betydelsen av poäng - -Poängen uttrycker uppgiftens samlade värde utifrån hur: - -- tidskrävande uppgiften är; -- besvärlig uppgiften är; -- viktig uppgiften är. - -När poängintjäning införs i en senare feature ska samma värde motsvara hur många -poäng användaren får när uppgiften slutförs. - -Poängen är inte en exakt tidsuppskattning. En snabb men viktig uppgift kan -därför ha ett högre poängvärde än en längre men mindre betydelsefull uppgift. - -Feature 3 registrerar endast poängvärdet. Den ska inte registrera: - -- vem som har tjänat poängen; -- om poängen har delats ut; -- när poängen har tjänats in; -- någon historik över poäng. - -## Poängskala - -Poäng ska vara ett heltal mellan 1 och 99, inklusive gränsvärdena. - -Alla heltal i intervallet är tillåtna. Feature 3 inför inte någon fast skala med -fördefinierade steg. - -Giltiga exempel: - -- 1 -- 7 -- 25 -- 99 - -Ogiltiga exempel: - -- inget värde; -- `null`; -- 0; -- negativa tal; -- 100 eller högre; -- decimaltal; -- text som inte kan tolkas som ett heltal. - -En fast poängskala kan införas senare om erfarenhet från användningen visar att -det är lämpligt. - -## Avgränsning - -Feature 3 omfattar endast: - -- uppgiftens titel; -- uppgiftens valfria beskrivning; -- uppgiftens obligatoriska poängvärde; -- visning av poäng på uppgiftskortet. - -Feature 3 ska inte införa: - -- tilldelning av uppgifter; -- ändring av uppgiftsstatus; -- drag-and-drop; -- redigering av befintliga uppgifter; -- radering av uppgifter; -- deadlines; -- återkommande uppgifter; -- poänghistorik; -- användares poängsaldo; -- topplistor; -- statistik; -- mål; -- achievements; -- belöningar; -- automatisk utdelning av poäng när en uppgift slutförs. - -Dessa funktioner hanteras i senare features enligt roadmapen. - -## Användarflöde - -När användaren öppnar dialogen för att skapa en uppgift ska formuläret -innehålla: - -- titel; -- beskrivning; -- poäng. - -Poängfältet ska initialt innehålla värdet `1`. - -Användaren kan behålla standardvärdet eller ange ett annat heltal mellan 1 och -99. - -När uppgiften skapas ska frontend alltid skicka poängvärdet uttryckligen till -backend. Backend ska inte själv fylla i ett saknat värde. - -Efter att en uppgift har skapats framgångsrikt ska formuläret återställas. -Poängfältet ska då återgå till `1`. - -Om dialogen stängs och senare öppnas igen ska poängfältet också börja på `1`. - -## Skapandedialog - -Poäng ska anges med ett vanligt numeriskt inmatningsfält. - -Fältet ska ha: - -- etiketten `Poäng`; -- initialt värde `1`; -- minsta värde `1`; -- högsta värde `99`; -- heltalssteg. - -En kort hjälptext kan visas: - -> 1–99 poäng beroende på hur tidskrävande, besvärlig eller viktig uppgiften är. - -Fältet får tillfälligt vara tomt medan användaren redigerar värdet. Frontend ska -inte automatiskt återställa värdet till `1` medan användaren skriver. - -Validering ska främst ske när användaren försöker skicka formuläret. Avancerad -validering vid varje tangenttryckning ingår inte i denna feature. - -## Frontendvalidering - -Frontend ska blockera skapandeanropet om poängen inte är ett heltal mellan 1 och -99. - -Vid ett ogiltigt värde ska följande meddelande visas: - -> Poäng måste vara ett heltal mellan 1 och 99. - -Samma meddelande kan användas för: - -- tomt värde; -- värde under 1; -- värde över 99; -- decimaltal; -- annat ogiltigt innehåll. - -HTML-fältets attribut för minsta värde, högsta värde och heltalssteg får användas -som stöd, men formulärlogiken ska också kontrollera värdet explicit. - -Backend är alltid den slutliga garanten för valideringsreglerna. - -## Visning på uppgiftskortet - -Uppgiftens poäng ska visas på uppgiftskortet som en kompakt och dynamisk badge. - -Badgen ska: - -- renderas som en vanlig React- och HTML-komponent; -- använda text och CSS; -- läsa värdet från uppgiftens `points`; -- visa värdet i formatet `{points} p`. - -Exempel: - -- `1 p` -- `7 p` -- `99 p` - -Ingen genererad bild eller statisk grafik ska användas för själva poängvärdet. - -Placering och visuell utformning ska följa projektets befintliga skärmbilder och -nuvarande kortdesign. Poängindikatorn ska ligga i kortets metadataområde på -motsvarande plats som poängindikatorn i designreferensen. - -Mindre justeringar får göras för att passa den faktiska kortimplementationen. -Feature 3 ska däremot inte införa en ny övergripande design för uppgiftskortet. - -## API - -Fältnamnet ska vara `points` genomgående i API, backend och frontend. - -### Skapa uppgift - -Requesten för att skapa en uppgift ska innehålla: - -```json -{ - "title": "Töm diskmaskinen", - "description": "Ställ in allt i rätt skåp", - "points": 3 -} -``` - -`points` är obligatoriskt. - -Backend ska inte tolka ett saknat värde som `1`. - -### Uppgiftssvar - -API-svar som innehåller en uppgift ska också innehålla `points`. - -Exempel: - -```json -{ - "id": "00000000-0000-0000-0000-000000000000", - "title": "Töm diskmaskinen", - "description": "Ställ in allt i rätt skåp", - "status": "WAITING", - "points": 3, - "createdAt": "2026-07-26T12:00:00Z" -} -``` - -Det gäller både: - -- svaret efter att en uppgift skapats; -- listning av uppgifter. - -Det exakta API-formatet ska i övrigt följa den befintliga implementationen. - -## Backendregler - -En uppgift får aldrig existera med ett poängvärde utanför intervallet 1–99. - -Regeln ska skyddas genom hela backend, inte bara i HTTP-lagret. - -Beroende på repositoryts befintliga struktur ska valideringen tillämpas på -relevanta nivåer, exempelvis: - -- requestvalidering; -- applikations- eller domänlogik; -- entitetsmodell; -- databasens schema. - -Implementation ska följa projektets etablerade kodstruktur och inte introducera -ett nytt arkitekturmönster enbart för denna feature. - -## Felhantering - -Ett ogiltigt eller saknat `points` ska ge: - -```text -400 Bad Request -``` - -Backend ska använda projektets befintliga felformat och befintliga -felhantering. - -Feature 3 ska inte introducera en separat felmodell endast för poäng. - -Backend får ge mer precisa valideringsdetaljer för exempelvis: - -- saknat värde; -- `null`; -- värde under 1; -- värde över 99. - -Frontend behöver inte återge varje backenddetalj separat, utan kan visa det -gemensamma användarmeddelandet: - -> Poäng måste vara ett heltal mellan 1 och 99. - -Vid andra eller oväntade backendfel ska frontend fortsätta använda projektets -befintliga generella felhantering. - -## Databas - -Databasschemat ska innehålla ett obligatoriskt heltalsfält för uppgiftens poäng. - -Det logiska slutläget är: - -```text -points INTEGER NOT NULL -``` - -Databasen ska, om den befintliga schemahanteringen stödjer det, även skydda -intervallet 1–99 med en motsvarande constraint. - -Databasen ska inte ha ett permanent defaultvärde för nya uppgifter. Nya -uppgifter ska alltid få ett uttryckligt poängvärde från applikationen. - -Det förvalda värdet `1` är ett frontendbeteende och inte ett sätt för backend -eller databasen att tyst komplettera ofullständiga anrop. - -## Lokal utvecklingsdatabas - -Den lokala utvecklingsdatabasen ska vara en in-memory H2-databas. - -Databasen och dess innehåll ska återställas när backend startas om. - -Lokal utvecklingsdata betraktas därför som tillfällig. Användare och uppgifter -som skapats manuellt under utveckling behöver inte bevaras mellan starter. - -Detta innebär att Feature 3 inte behöver migrera verkliga befintliga -utvecklingsposter. En ny databas skapas direkt med det obligatoriska -poängfältet. - -Före Feature 3 var lokal H2 filbaserad. Feature 3 ändrar utvecklingsanslutningen -till in-memory och uppdaterar utvecklingsdokumentationen i samma ändring. - -## Schemahantering och framtida migrering - -Att lokal utvecklingsdata inte bevaras innebär inte att framtida -produktionsdata kan återställas vid varje release. - -När HemHub börjar använda en beständig PostgreSQL-databas med data som ska -bevaras måste schemaändringar hanteras med kontrollerade migreringar. - -Feature 3 behöver inte införa eller färdigställa hela den framtida -produktionsstrategin om den ännu inte finns i repositoryt. - -Projektet använder redan Flyway och versionshanterade migreringar. Feature 3 ska -därför lägga till en ny Flyway-migrering för poängfältet och inte ändra tidigare -migreringar. Hibernate ska fortsatt validera schemat i stället för att skapa -det. - -Bytet till in-memory H2 innebär att befintliga lokala utvecklingsposter inte -behöver bevaras eller fyllas på med poäng. Själva schemaändringen ska ändå -hanteras som en kontrollerad migrering så att migrationshistoriken förblir -sammanhängande inför framtida beständig data. - -Repositoryts faktiska arkitektur och dokumentation har företräde. - -## Backendtester - -Backendtesterna verifierar att: - -- en uppgift kan skapas med ett giltigt `points`; -- det skapade API-svaret innehåller samma `points`; -- listning av uppgifter innehåller `points`; -- gränsvärdet `1` accepteras; -- gränsvärdet `99` accepteras; -- saknat `points` ger `400 Bad Request`; -- `points: null` ger `400 Bad Request`; -- `points: 0` ger `400 Bad Request`; -- negativa värden ger `400 Bad Request`; -- `points: 100` ger `400 Bad Request`. - -Testerna följer den befintliga teststilen och utökar de tidigare -uppgifts-API-testerna. - -## Frontendtester - -Frontendtesterna verifierar att: - -- skapandedialogen öppnas med poängvärdet `1`; -- ett giltigt poängvärde skickas i create-anropet; -- tomt poängfält blockerar submit; -- ett värde under 1 blockerar submit; -- ett värde över 99 blockerar submit; -- ett ogiltigt värde visar felmeddelandet; -- formuläret återställs till poängvärdet `1` efter lyckad skapning; -- ett uppgiftskort visar uppgiftens dynamiska poängbadge; -- badgen visar värdet från uppgiftsdata, exempelvis `7 p`. - -Testerna är inte beroende av en viss pixelplacering eller detaljerad CSS. - -## Manuell verifiering - -Följande verifierades manuellt: - -1. Starta frontend och backend enligt projektets utvecklingsinstruktioner. -2. Skapa en uppgift utan att ändra poängfältet. -3. Verifiera att uppgiften får `1 p`. -4. Skapa en uppgift med ett mellanvärde, exempelvis `7`. -5. Verifiera att uppgiften får `7 p`. -6. Skapa en uppgift med `99`. -7. Verifiera att uppgiften får `99 p`. -8. Försök skapa en uppgift med tomt poängfält. -9. Verifiera att anropet blockeras och att rätt felmeddelande visas. -10. Försök använda värdena `0` och `100`. -11. Verifiera att båda avvisas. -12. Kontrollera att poängbadgen följer projektets designreferens och fungerar - med ett- och tvåsiffriga värden. -13. Starta om backend. -14. Verifiera att den lokala utvecklingsdatan inte finns kvar. - -## Acceptanskriterier - -Feature 3 är klar när: - -- varje ny uppgift har ett obligatoriskt `points`; -- `points` är ett heltal mellan 1 och 99; -- frontendens standardvärde är `1`; -- frontend alltid skickar `points` uttryckligen; -- backend avvisar saknat eller ogiltigt `points`; -- backend fyller inte automatiskt i ett saknat värde; -- uppgiftens poäng returneras av API:t; -- uppgiftens poäng visas dynamiskt på uppgiftskortet; -- frontend- och backendtester täcker centrala giltiga och ogiltiga fall; -- lokal H2 körs som in-memory och återställs vid omstart; -- relevant dokumentation är uppdaterad; -- Feature 3 inte inför funktionalitet som hör till senare features. - -## Implementationsprinciper - -När Feature 3 senare implementeras ska Codex först läsa: - -```text -AGENTS.md -README.md -docs/architecture.md -docs/development.md -docs/roadmap.md -docs/decisions/ -docs/features/ -``` - -Codex ska även läsa relevant backendkod, frontendkod och befintliga tester innan -ändringar görs. - -Repositoryts faktiska kod och dokumentation har företräde framför antaganden i -denna featurebeskrivning. - -Dokumentation, implementation och tester ska uppdateras tillsammans. - -Codex ska inte committa, pusha, skapa pull request eller merga utan uttrycklig -instruktion. - -## Relaterade commits - -- `059d4da9214969ed3e28592178160da6de614b4d` – `feat: add task points` -- `2e62261f49bb3142e28882483e41e0250ab11c5f` – merge till `main` diff --git a/docs/features/004-task-assignment.md b/docs/features/004-task-assignment.md deleted file mode 100644 index a9545bc..0000000 --- a/docs/features/004-task-assignment.md +++ /dev/null @@ -1,123 +0,0 @@ -# Feature 4 – Tilldelning av uppgifter - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -HemHub har centralt lagrade användare och gemensamma uppgifter. För att senare -kunna införa regler för pågående arbete behöver en uppgift kunna ha en ansvarig -användare, utan att tilldelning samtidigt ändrar uppgiftens status. - -## Mål - -- välja en valfri ansvarig när en uppgift skapas; -- visa ansvarig på uppgiftskortet; -- tilldela, byta eller ta bort ansvarig på en väntande uppgift; -- lagra tilldelningen centralt så att alla användare ser samma värde. - -## Omfattning - -En uppgift kan vara otilldelad eller tilldelad exakt en befintlig användare. -`Ingen` är standard vid skapande och den aktiva browseranvändaren förväljs -inte. Frontend återanvänder användarlistan som redan hämtas vid appstart. - -På ett otilldelat väntande kort öppnar `Ta uppgift` ett användarval. Ett -tilldelat väntande kort visar namnet och öppnar samma val. Ändringen skickas -direkt till backend och kortet uppdateras först med den bekräftade responsen. -Vid fel behålls den tidigare tilldelningen och ett lokalt felmeddelande visas. - -För `IN_PROGRESS` och `COMPLETED` visas ansvarig eller `Otilldelad` utan -redigerbar kontroll. - -## Produktregler - -- En uppgift har högst en ansvarig. -- Ansvarig är valfri och måste motsvara en befintlig användare. -- Endast uppgifter med status `WAITING` får få ändrad ansvarig. -- Tilldelning ändrar aldrig status, titel, beskrivning eller poäng. -- Vem som helst kan välja valfri ansvarig; aktiv användare är inte - autentisering eller behörighetskontroll. - -## API-förändringar - -`POST /api/tasks` accepterar det valfria fältet `assigneeId`. Saknat fält eller -`null` skapar en otilldelad uppgift. Ett UUID som inte motsvarar en användare -ger `404 Not Found`. - -`PUT /api/tasks/{taskId}/assignee` ändrar endast ansvarig: - -```json -{"assigneeId": "d56b54dd-31b0-4d71-8a10-82464be59a61"} -``` - -`{"assigneeId": null}` tar bort tilldelningen. Fältet måste finnas i requesten. -Responsen är den uppdaterade uppgiften. Task-responser innehåller: - -```json -{"assignee": {"id": "d56b54dd-31b0-4d71-8a10-82464be59a61", "name": "Anna"}} -``` - -Otilldelade uppgifter har `"assignee": null`. Ogiltigt UUID eller saknat fält -ger `400`, okänd uppgift eller användare ger `404` och ändring av en uppgift -som inte väntar ger `409`. Felen använder det befintliga formatet med `code` -och `message`. - -## Databasförändringar - -`V4__add_task_assignee.sql` lägger till `task.assignee_id UUID NULL` med en -främmande nyckel till `app_user.id`. Befintliga uppgifter blir otilldelade. -Migreringen använder varken `ON DELETE CASCADE` eller `ON DELETE SET NULL`. - -JPA-modellen använder en lazy `ManyToOne`. Repositoryts listning och -id-hämtning använder en entity graph för att hämta ansvarig tillsammans med -uppgiften och undvika N+1-frågor när responsen byggs. - -## Frontendförändringar - -Skapandedialogen innehåller ett tilldelningsval med `Ingen` och samtliga -användare. Valet bevaras tillsammans med övriga formulärvärden vid fel. - -Väntande kort har en separat tilldelningskontroll. Kontrollen är inaktiverad -medan just det kortets request pågår; övriga delar av brädan förblir -interaktiva. Serverns task-respons ersätter motsvarande uppgift i den befintliga -listan utan att ändra ordningen. - -## Tester och verifiering - -Backendens integrationstester täcker skapande med och utan ansvarig, -responsformat, okända id:n, tilldelning, byte, av-tilldelning, statuskonflikt -och att övriga uppgiftsfält inte ändras. - -Frontendtesterna täcker standardval och användarlista i skapandedialogen, -create-requestens `assigneeId`, kortens redigerbara och statiska lägen, -tilldelningsrequest, vänteläge, serverbekräftad uppdatering, av-tilldelning och -fel utan optimistisk ändring. - -Manuell verifiering genomfördes för skapande med och utan ansvarig, tilldelning, -byte, av-tilldelning, bevarad status, omladdning, statiska kontroller för andra -statusar, felrespons och projektets normala desktop- och mobilbredder. - -## Avgränsning mot Feature 5 - -Feature 4 inför inget API eller UI för statusändring och inte regeln att -`IN_PROGRESS` måste ha en ansvarig. Tilldelning leder inte automatiskt till -`IN_PROGRESS`, och av-tilldelning leder inte automatiskt till `WAITING`. - -## Ingår inte - -Flera ansvariga, statusändring, drag-and-drop, generell redigering, radering, -deadlines, återkommande uppgifter, poänghistorik, användaradministration, -autentisering, behörighetskontroll och automatisk tilldelning ingår inte. - -## Kända begränsningar - -Användare kan ännu inte raderas, så relationens framtida beteende vid -användarradering är inte beslutat. Frontend har ingen optimistisk uppdatering; -det tidigare värdet ligger kvar tills backend svarar. - -## Relaterade commits - -- `aaebe888f3bae43b3413e9fa3893fec90afac8b4` – `feat: add task assignment` -- `d78611f5f77374228f1f69b66734931a988f8355` – merge till `main` diff --git a/docs/features/005-task-status.md b/docs/features/005-task-status.md deleted file mode 100644 index 183a273..0000000 --- a/docs/features/005-task-status.md +++ /dev/null @@ -1,124 +0,0 @@ -# Feature 5 – Statusändring och statusregler - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -Efter Feature 4 kan uppgifter vara otilldelade eller ha en ansvarig, men inget -API eller gränssnitt kan ändra status. Feature 5 inför ett enkelt knappflöde -före drag-and-drop och säkerställer statusreglerna i backend. - -## Mål - -- ändra status genom ett särskilt backend-API; -- tillåta direkta övergångar mellan `WAITING`, `IN_PROGRESS` och `COMPLETED`; -- kräva ansvarig för `IN_PROGRESS`; -- automatiskt tilldela aktiv browseranvändare när en otilldelad uppgift - påbörjas; -- tillåta statusberoende ändringar av ansvarig; -- använda serverbekräftade uppdateringar och vänteläge per kort. - -## Status- och tilldelningsregler - -Alla statusar får ändras direkt till varandra. Ett anrop med aktuell status som -mål är giltigt och idempotent. En uppgift behöver inte passera -`IN_PROGRESS` för att bli `COMPLETED`. - -`WAITING` och `COMPLETED` får vara tilldelade eller otilldelade. -`IN_PROGRESS` måste alltid ha en ansvarig. Det befintliga tilldelnings-API:t -kan tilldela eller byta ansvarig i samtliga statusar och ta bort ansvarig i -`WAITING` och `COMPLETED`. Ett försök att ta bort ansvarig i `IN_PROGRESS` -avvisas med `409 TASK_REQUIRES_ASSIGNEE`. Tilldelning ändrar aldrig status. - -När en otilldelad uppgift ändras till `IN_PROGRESS` skickar frontend aktiv -användares id. Backend verifierar användaren, tilldelar den och ändrar status i -samma transaktion. Om uppgiften redan har en ansvarig behålls den, och skickat -`activeUserId` används inte för att byta ansvarig. - -## API-förändringar - -Status ändras med: - -```http -PUT /api/tasks/{taskId}/status -``` - -```json -{ - "status": "IN_PROGRESS", - "activeUserId": "d56b54dd-31b0-4d71-8a10-82464be59a61" -} -``` - -`status` är obligatoriskt. `activeUserId` krävs endast när en otilldelad -uppgift ska bli `IN_PROGRESS`. Responsen använder samma fullständiga -task-format som övriga task-operationer. - -Kända fel använder befintligt format med `code` och `message`: - -- okänd uppgift: `404 TASK_NOT_FOUND`; -- saknad, null eller okänd status: `400 INVALID_TASK_STATUS`; -- okänd användare: `404 USER_NOT_FOUND`; -- otilldelad `IN_PROGRESS`: `409 TASK_REQUIRES_ASSIGNEE`; -- ogiltigt UUID-format: `400` med befintlig requestfelkod. - -`USER_NOT_FOUND` och `INVALID_TASK_ASSIGNMENT` återanvänds från Feature 4 i -stället för att införa parallella felkoder för samma användar-id. - -## Databasförändringar - -Inga. Befintliga kolumner för status och ansvarig är tillräckliga. - -## Frontendförändringar - -Varje kort visar två statusknappar: - -- `WAITING`: `Påbörja`, `Markera klar`; -- `IN_PROGRESS`: `Till Väntande`, `Markera klar`; -- `COMPLETED`: `Till Väntande`, `Påbörja igen`. - -Tilldelningskontrollen är redigerbar i alla statusar. Alternativet `Ingen` -visas inte för `IN_PROGRESS`. - -Status och tilldelning delar ett vänteläge per uppgift. Under ett anrop ligger -kortet kvar i sin kolumn och båda kontrollerna på kortet är inaktiverade. -Övriga kort är fortsatt interaktiva. Vid framgång ersätts uppgiften med -serverresponsen; vid fel behålls tidigare data och felet visas lokalt. - -## Tester och verifiering - -Backendtesterna omfattar samtliga direkta övergångar, idempotens, automatisk -tilldelning, bevarad ansvarig, statusvalidering, okända id:n, statusberoende -tilldelning och oförändrade uppgiftsfält. - -Frontendtesterna omfattar knapparnas statusmappning, requestformat, -serverbekräftad flytt, automatisk tilldelning i responsen, lokalt vänteläge, -dubbelsubmitsskydd, fel utan optimistisk ändring och statusberoende -tilldelningsalternativ. - -Manuell verifiering genomfördes genom ett sammanhängande flöde genom alla tre -statusar, automatisk tilldelning, byte och borttagning av ansvarig, omladdning -och centrala API-fel. Browserflödet verifierades i desktop- och mobilbredd utan -upptäckta problem. - -## Avgränsningar - -Ingen drag-and-drop, radering, generell redigering, sortering, deadline, -återkommande uppgift, status- eller poänghistorik, slutföranderegistrering, -statistik, användaradministration, autentisering eller behörighetskontroll -införs. - -## Kända begränsningar - -Aktiv användare är ett lokalt browserval och inte autentisering. Backend kan -verifiera att id:t finns, men inte vem som faktiskt använder browsern. - -Statusknapparna är ett första gränssnitt före Feature 6. Det finns ingen -versionskontroll för konkurrerande uppdateringar utöver transaktioner och -aktuell serverlogik. - -## Relaterade commits - -- `65a6488c0b268f49b1361591f025bdea8d67f754` – `feat: add task status transitions` diff --git a/docs/features/006-task-drag-and-drop.md b/docs/features/006-task-drag-and-drop.md deleted file mode 100644 index fd2088b..0000000 --- a/docs/features/006-task-drag-and-drop.md +++ /dev/null @@ -1,551 +0,0 @@ -# Feature 6 – Drag-and-drop - -## Status - -Färdig och mergad till main. - -## Bakgrund - -Feature 5 införde backendstyrda statusövergångar mellan `WAITING`, -`IN_PROGRESS` och `COMPLETED`, ett särskilt status-API samt ett första -knappbaserat gränssnitt för statusändring. - -Feature 6 ska komplettera detta med drag-and-drop mellan brädans -statuskolumner. Featuren ska återanvända den befintliga statusmodellen, -status-API:t, tilldelningsreglerna och frontendens hantering av vänteläge och -lokala fel. Den ska inte skapa en parallell statusmekanism. - -## Mål - -Feature 6 ska: - -- låta användaren dra uppgiftskort mellan statuskolumner; -- använda status-API:t från Feature 5; -- ge omedelbar visuell återkoppling genom optimistisk flytt; -- återställa kortet om backend avvisar statusändringen; -- hantera automatisk tilldelning när en otilldelad uppgift dras till Pågående; -- behålla befintliga statusknappar som ett tillfälligt alternativ; -- fungera med mus och på en rimlig nivå med touch; -- hålla dragmekanik, statuslogik och kortlayout tillräckligt separerade för - framtida ändringar. - -## Omfattning - -Feature 6 är primärt en frontendfeature. - -Backendens befintliga endpoint används: - -```http -PUT /api/tasks/{taskId}/status -``` - -Requesten innehåller målstatus och kan innehålla aktiv användares id: - -```json -{ - "status": "IN_PROGRESS", - "activeUserId": "d56b54dd-31b0-4d71-8a10-82464be59a61" -} -``` - -Backend ska endast ändras om granskning av den faktiska implementationen visar -att en mindre korrigering behövs för att det befintliga kontraktet ska kunna -återanvändas korrekt. - -## Implementerad lösning - -Frontend använder `@dnd-kit/react` 0.5.0 som aktuell React-adapter och -`@dnd-kit/dom` 0.5.0 för en konfigurerad pointer-sensor. Legacy-paketen -`@dnd-kit/core` och `@dnd-kit/sortable` används inte. Sortable-stöd behövs inte -eftersom kort inte ordnas inom kolumner. - -`TaskDragAndDrop.tsx` avgränsar bibliotekskopplingen. Adaptern: - -- registrerar uppgiftskort som draggable och statuskolumner som droppable; -- översätter ett lyckat drop-event till task-id och målstatus; -- ignorerar avbrutna dragningar och ogiltiga mål; -- använder sex pixlars aktiveringsavstånd för mus och penna; -- använder 250 millisekunders fördröjning med åtta pixlars tolerans för touch; -- behåller dnd-kits standardplugins och tangentbordssensor; -- förlitar sig på sensorns standardskydd mot dragstart från interaktiva - element. - -Ingen särskild `touch-action`, collision detector eller drag-overlay har lagts -till. Den befintliga responsiva layouten är oförändrad. - -`TaskBoard` har fortsatt en gemensam requestväg och låsning per task-id för -statusändringar. Ett presentationsval avgör beteendet: - -- statusknappar använder `server-confirmed` och flyttar inte kortet före svar; -- drag-and-drop använder `optimistic`, sparar hela tidigare uppgiften och - flyttar kortet direkt; -- lyckade anrop ersätter uppgiften med hela serverresponsen; -- misslyckade optimistiska anrop återställer hela rollback-värdet och visar - felet lokalt. - -När en otilldelad uppgift dras till `IN_PROGRESS` visar det optimistiska värdet -den aktiva användarens id och namn. En befintlig ansvarig behålls. Backendens -befintliga status-API och Feature 5-regler återanvänds utan backendändringar. - -Frontendens 44 tester, TypeScript-kompilering, Vites produktionsbygge och -`git diff --check` passerar på feature-branchen. Manuell browserverifiering av -de centrala drag-, server- och rollbackflödena är genomförd. - -## Avgränsningar - -Feature 6 ska inte införa: - -- en ny statusmodell; -- ett nytt eller parallellt status-API; -- manuell sortering inom en kolumn; -- persistent kortordning; -- generell redigering av uppgifter; -- radering; -- deadlines; -- återkommande uppgifter; -- status- eller poänghistorik; -- undo-funktion; -- flera ansvariga; -- autentisering eller behörigheter; -- realtidsuppdatering mellan browsers; -- en ny global state-lösning; -- horisontell scrollning som ett särskilt mobilkoncept; -- ett specialbyggt tangentbordsflöde för drag-and-drop. - -Att flytta ett kort inom samma kolumn ska inte ändra någon ordning och ska inte -ge ett backend-anrop. - -## Befintliga statusregler - -Feature 6 ska behandla följande regler som redan beslutade: - -- statusarna är `WAITING`, `IN_PROGRESS` och `COMPLETED`; -- alla direkta statusövergångar är tillåtna; -- samma målstatus är giltig och idempotent; -- `IN_PROGRESS` kräver ansvarig; -- `WAITING` och `COMPLETED` får vara otilldelade; -- när en otilldelad uppgift sätts till `IN_PROGRESS` skickar frontend aktiv - användares id; -- backend tilldelar då användaren och ändrar status atomärt; -- om uppgiften redan har en ansvarig behålls denna; -- statusoperationen byter aldrig en befintlig ansvarig; -- backend returnerar hela den uppdaterade uppgiften; -- serverns fullständiga task-respons är slutlig sanning. - -## Uppdateringsstrategi - -Drag-and-drop använder en kontrollerad optimistisk uppdatering. - -När ett kort släpps i en annan statuskolumn ska frontend: - -1. spara hela den nuvarande task-versionen som rollback-värde; -2. skapa ett optimistiskt lokalt task-läge; -3. visa kortet omedelbart i målkolumnen; -4. markera kortet som upptaget; -5. skicka statusanropet; -6. vid framgång ersätta den optimistiska uppgiften med serverns fullständiga - respons; -7. vid fel återställa hela den tidigare task-versionen; -8. visa felet lokalt på det återställda kortet. - -Rollback ska återställa hela uppgiften, inte enbart statusfältet. Detta är -viktigt eftersom en optimistisk flytt till `IN_PROGRESS` även kan innehålla en -tillfällig antagen ansvarig. - -Ingen generell infrastruktur för optimistiska uppdateringar ska införas. -Lösningen ska hållas lokal till uppgiftslistan och drag-and-drop-flödet. -Feature 5:s statusknappar ska fortsatt vara serverbekräftade: kortet ligger kvar -i sin aktuella kolumn tills backend svarar. - -## Otilldelad uppgift till Pågående - -När en otilldelad uppgift dras till `IN_PROGRESS` ska frontend: - -- skicka målstatus `IN_PROGRESS`; -- skicka aktiv användares id; -- optimistiskt visa aktiv användare som ansvarig; -- låta backend tilldela användaren och ändra status i samma transaktion. - -Om uppgiften redan har en ansvarig ska frontend inte optimistiskt ersätta denna -med den aktiva användaren. Serverresponsen ersätter alltid det optimistiska -antagandet. - -Drag-and-drop ska inte visa ett nytt ansvarigval. - -## Drag-and-drop-bibliotek - -Feature 6 ska använda dnd-kit-ekosystemets aktuella stabila React-lösning. -Featuredokumentet låser inte exakta paket eller API:n. Codex ska vid -implementation verifiera den aktuella officiella dokumentationen, vilka paket -som är aktuella respektive legacy, kompatibilitet med repositoryts -React-version samt påverkan på Vitest/jsdom och React Testing Library. - -Den valda lösningen ska stödja: - -- draggable-kort; -- droppable-statuskolumner; -- pointer- och touchinteraktion; -- aktiveringsvillkor; -- avbruten dragning; -- identifiering av målkolumn. - -Biblioteket ska inte användas för: - -- sortering inom kolumner; -- persistent ordning; -- egen domänmodell; -- generell state-hantering; -- parallell statuslogik. - -Dragbibliotekets händelser ska översättas till en gemensam, -bibliotekoberoende ingång till statusflödet. - -## Statusknapparnas roll - -Feature 5:s statusknappar behålls tills vidare som ett fullt fungerande -alternativ. De betraktas inte som en permanent del av målbilden. Den -ursprungliga visuella designen innehåller inte statusknappar, och de ska därför -enkelt kunna tas bort efter utvärdering. - -Implementation ska följa dessa principer: - -- drag-and-drop får inte byggas ovanpå knappkomponenterna; -- draglogik får inte placeras i knapparna; -- statuslogik får inte dupliceras mellan knappar och drag-and-drop; -- båda ska dela underliggande requestlogik, låsning per task-id, felhantering - och ersättning med serverrespons; -- drag-and-drop använder optimistisk visuell flytt, medan statusknapparna - förblir serverbekräftade och låter kortet ligga kvar tills backend svarar; -- den visuella presentationsstrategin får därför skilja sig mellan - interaktionerna; -- vänteläge, felhantering och serverrespons ska höra till uppgiften och det - delade statusflödet, inte dupliceras per kontroll; -- kortlayouten får inte strukturellt förutsätta att knapparna alltid finns. - -Att ta bort statusknapparna senare ska inte kräva ändringar i status-API, -dragmekanik, rollback eller task-state. Statusarkitekturen får inte vara kopplad -till att knapparna finns kvar. - -## Vänteläge - -När statusanropet pågår ska det optimistiskt flyttade kortet tonas ned lätt. -Ingen text som `Flyttar…` och ingen spinner krävs i första versionen. - -Under vänteläget ska just detta kort inte kunna: - -- dras igen; -- initiera en ny statusändring; -- ändra ansvarig. - -Övriga kort ska förbli interaktiva. - -Flera olika kort får ha statusanrop pågående samtidigt. Låsning, nedtoning, -rollback och fel hanteras per task-id. - -## Fel och återställning - -Vid ett misslyckat statusanrop ska: - -- hela den tidigare task-versionen återställas; -- kortet återgå till ursprungskolumnen; -- tidigare ansvarig återställas; -- nedtoningen tas bort; -- felet visas lokalt på kortet; -- målkolumnen inte behålla någon tillfällig markering. - -Tidigare klientdata ska inte delvis blandas med det misslyckade optimistiska -tillståndet. Ett fel på ett kort ska inte blockera resten av brädan. - -## Serverrespons - -Vid lyckat statusanrop ska frontend alltid ersätta den optimistiska uppgiften -med hela serverresponsen. - -Det gäller även om serverresponsen skiljer sig från frontendens antagande vad -gäller exempelvis: - -- status; -- ansvarig; -- andra returnerade task-fält. - -Servern är slutlig sanning. - -## Dragyta - -Kortets icke-interaktiva yta ska fungera som dragyta. Knappar, select och andra -interaktiva element ska inte initiera dragning. - -Implementation får avgöra sensoruppsättning, aktiveringströskel samt avstånd, -fördröjning och tolerans. Vanliga klick och små fingerrörelser får inte -oavsiktligt starta dragning, och kortets interaktiva kontroller ska fungera -normalt. - -Dragmekaniken ska implementeras så att ett separat draghandtag senare kan -införas utan att statusoperation, API-anrop, rollback eller task-state behöver -ändras. Det bör räcka att flytta bibliotekets draglisteners och tillhörande -attribut från kortets rot till handtaget. - -## Målkolumner - -Kolumnerna behöver ingen stark eller permanent drop-markering. Den kolumn som -kortet befinner sig över kan vid behov få en mycket diskret hover-effekt, -exempelvis: - -- en svag bakgrundsförändring; -- en tunn kant; -- annan lågmäld visuell återkoppling. - -Feature 6 ska inte införa stora färgade drop-zoner eller en generell redesign -av brädan. Om manuell verifiering visar att ingen markering behövs kan även den -diskreta hover-effekten utelämnas. - -## Drop i samma kolumn - -Om ett kort släpps i kolumnen som motsvarar dess nuvarande status ska -operationen vara no-op. - -Det innebär: - -- inget API-anrop; -- ingen statusändring; -- ingen ändring av ordning; -- inget vänteläge; -- inget felmeddelande. - -## Avbruten dragning - -Om en dragning avbryts eller avslutas utanför en giltig målkolumn ska -operationen vara no-op. Kortet ska återgå till sin normala position utan -API-anrop eller felindikering. - -## Desktop och touch - -Desktop är det primära användningsfallet för Feature 6. - -Touch ska fungera på en rimlig grundnivå, men featuren ska inte införa en -särskild mobil Kanban-design. Brädans befintliga responsiva layout ska -behållas. Feature 6 ska inte införa horisontell scrollning. På smala skärmar -får kolumnerna fortsätta använda repositoryts nuvarande responsiva layout, -även om de staplas vertikalt. - -Implementation får avgöra sensoruppsättning, aktiveringsvillkor, eventuell -`touch-action`, collision detection och drag-overlay. Valen måste bevara normal -vertikal scrollning på mobil och får inte göra interaktiva kortkontroller -svåranvända. - -Draglogiken ska: - -- identifiera mål genom status, inte genom en fast skärmposition; -- inte förutsätta att kolumnerna ligger horisontellt; -- hållas separerad från layout-CSS; -- kunna fortsätta fungera om kolumnlayouten senare ändras. - -Om manuell verifiering visar att dragning mellan staplade kolumner fungerar -dåligt kan mobilbeteendet ändras senare utan att status- eller rollbacklogiken -görs om. Statusknapparna finns kvar som alternativ, särskilt där dragning är -opraktisk. - -## Tangentbord och tillgänglighet - -Tangentbordsstyrd drag-and-drop ingår inte som grundkrav i Feature 6. -Statusknapparna ska fortsatt ge en fungerande tangentbordsväg för -statusändring. Drag-and-drop får därför inte vara den enda möjliga vägen. - -Feature 6 ska ändå uppfylla grundläggande tillgänglighetskrav: - -- interaktiva kontroller ska fortsatt gå att nå med tangentbord; -- ett upptaget kort ska inte kunna aktiveras igen; -- fokus ska inte tappas oförklarligt efter lyckad operation eller rollback; -- begripliga etiketter ska bevaras; -- dragbibliotekets standard-ARIA får användas; -- ingen omfattande speciallösning för tangentbordsdragning ska byggas. - -Förbättrat tangentbordsstöd för själva dragningen kan införas i en senare -uppdatering. - -## Gemensamt statusflöde - -Frontend ska ha ett gemensamt, kontrolloberoende statusflöde för den -underliggande statusändringen. - -Det delade flödet ska ansvara för: - -- kontroll av pågående operation för task-id; -- requestformat; -- aktiv användares id vid behov; -- per-kort-vänteläge; -- ersättning med serverrespons; -- lokalt statusfel. - -Drag-and-drop-flödet ska därutöver beräkna den optimistiska task-versionen, -spara rollback-värdet och återställa hela den tidigare uppgiften vid fel. -Statusknapparna ska inte göra en optimistisk flytt. - -Drag-and-drop och statusknappar är separata sätt att ange målstatus till det -delade flödet, men får använda olika visuell presentationsstrategi. -Tilldelningskontrollen ska återanvända samma per-kort-låsning så att status och -tilldelning inte kan ändras samtidigt på samma uppgift. - -## Frontendtester - -Frontendtesterna verifierar beteende och state utan att förutsätta fysisk -layout eller exakta pointer-koordinater i jsdom. Dragadaptern mockas i -brädtesterna, medan den bibliotekoberoende mappningen från draghändelse till -task-id och målstatus testas separat. - -Testerna täcker bland annat: - -- korrekt statusanrop och optimistisk flytt till en annan kolumn; -- låsning och nedtoning per task-id medan anropet pågår; -- att andra kort kan ha samtidiga operationer; -- automatisk optimistisk tilldelning till aktiv användare; -- att en befintlig ansvarig behålls; -- att hela serverresponsen ersätter det optimistiska värdet; -- fullständig rollback och lokalt fel vid misslyckande; -- no-op för samma status, avbruten dragning och ogiltigt mål; -- att statusknapparnas befintliga serverbekräftade beteende är bevarat. - -Totalt passerar 44 frontendtester. TypeScript-kompileringen och Vites -produktionsbygge passerar också. - -## Backendtester - -Backend ändrades inte. Feature 5:s befintliga tester fortsätter därför att -utgöra verifiering av: - -- direkta statusövergångar; -- idempotens; -- automatisk tilldelning; -- bevarad befintlig ansvarig; -- statusvalidering; -- statusberoende tilldelningsregler; -- oförändrade övriga task-fält. - -## Manuell verifiering - -Manuell browserverifiering är genomförd. Följande verifierades: - -- drag-and-drop mellan statuskolumner fungerar; -- kortet flyttas optimistiskt och tonas ned under statusanropet; -- en otilldelad uppgift som flyttas till Pågående får aktiv användare som - ansvarig; -- en befintlig ansvarig behålls; -- serverns svar ersätter det optimistiska värdet; -- statusknapparna fungerar fortsatt; -- ett blockerat statusanrop visar - `Det gick inte att ändra status. Försök igen.`; -- kortet återställs till ursprungskolumnen vid fel; -- tidigare ansvarig återställs, nedtoningen försvinner och kortet blir - interaktivt igen; -- övriga kort förblir interaktiva under operationen. - -## Dokumentation - -Feature 6 ska dokumenteras i: - -```text -docs/features/006-task-drag-and-drop.md -``` - -Vid implementation ska även följande uppdateras när det är relevant: - -```text -README.md -docs/architecture.md -docs/roadmap.md -``` - -Roadmapen markerar Feature 6 som `Klar` efter verifiering och merge. - -Ett nytt ADR behövs endast om biblioteksvalet bedöms vara ett övergripande, -långlivat frontendbeslut som påverkar fler delar av applikationen än Feature -6. Om `dnd-kit` endast används lokalt för denna feature bör beslutet normalt -dokumenteras i feature- och arkitekturdokumentationen. - -## Acceptanskriterier - -Feature 6 är klar när: - -- kort kan dras mellan olika statuskolumner; -- dragningen använder Feature 5:s status-API; -- kortet flyttas optimistiskt till målkolumnen; -- kortet tonas ned under serveranropet; -- samma kort är låst för status, dragning och tilldelning under anropet; -- andra kort förblir interaktiva; -- otilldelad uppgift till Pågående använder aktiv användares id; -- befintlig ansvarig behålls; -- serverresponsen ersätter det optimistiska tillståndet; -- fel återställer hela tidigare task-versionen; -- kortet återgår till ursprungskolumnen vid fel; -- felet visas lokalt på kortet; -- drop i samma kolumn är no-op; -- avbruten dragning är no-op; -- ingen manuell eller persistent kortordning har införts; -- statusknapparna fungerar fortsatt men är arkitekturellt frikopplade; -- statusknapparna förblir serverbekräftade medan drag-and-drop är optimistisk; -- statusknapparna kan tas bort senare utan att drag- eller statuslogiken byggs - om; -- kortets icke-interaktiva yta fungerar som dragyta; -- interaktiva kortkontroller initierar inte dragning; -- ett senare draghandtag kan införas utan ändring av statusflödet; -- normal vertikal scrollning på mobil bevaras; -- ingen horisontell scrollning har införts; -- desktop fungerar väl; -- touch fungerar på rimlig grundnivå; -- tangentbordsdragning inte krävs; -- statusändring fortsatt är möjlig med tangentbord genom statusknapparna; -- frontendtesterna täcker centrala stateövergångar och felfall; -- manuell verifiering täcker dragkänsla, touch, layout och rollback; -- backend är oförändrad om inget konkret behov av justering hittas; -- relevant dokumentation är uppdaterad. - -## Implementationsprinciper - -Vid implementationen användes följande dokumentation som tekniskt underlag: - -```text -AGENTS.md -README.md -docs/architecture.md -docs/development.md -docs/roadmap.md -docs/decisions/ -docs/features/004-task-assignment.md -docs/features/005-task-status.md -``` - -Relevant frontendkod och tester granskades särskilt avseende: - -- aktuell React-version; -- frontendens installerade beroenden; -- aktuell officiell dnd-kit-dokumentation; -- vilka dnd-kit-paket som är aktuella respektive legacy; -- dnd-kit-lösningens kompatibilitet med React-versionen; -- påverkan på Vitest/jsdom och React Testing Library; -- aktuell task-typ; -- brädans kolumnstruktur; -- uppgiftskortets komponentstruktur; -- befintliga statusknappar; -- statusanropets requestformat; -- hur aktiv användare representeras; -- hur tasks ersätts i state; -- det gemensamma vänteläget per kort; -- status- och tilldelningsfel; -- tilldelningskontrollens inaktiveringslogik; -- aktuell teststil; -- vilka pointer- och draghändelser testmiljön stödjer. - -Repositoryts faktiska kod och dokumentation har företräde framför antaganden i -denna featurebeskrivning. - -Kod, tester och relevant dokumentation ska uppdateras tillsammans. - -Codex ska inte committa, pusha, skapa pull request eller merga utan uttrycklig -instruktion. - -## Relaterade commits - -- Feature-commit: - `c3c64482c062f144fd6cb6036e3c9db0afa5ec1e` -- Merge-commit till `main`: - `2696195e741c155a197ae9838d1938bdf2148cc2` diff --git a/docs/features/007-task-deletion.md b/docs/features/007-task-deletion.md deleted file mode 100644 index 45c8237..0000000 --- a/docs/features/007-task-deletion.md +++ /dev/null @@ -1,442 +0,0 @@ -# Feature 7 – Radera uppgift - -## Status - -Färdig och mergad till main. - -## Bakgrund - -HemHub stödjer skapande, visning, tilldelning, statusändring och drag-and-drop -av uppgifter. Det saknas möjlighet att ta bort uppgifter som inte längre är -relevanta eller som skapats av misstag. - -Feature 7 inför permanent radering av en enskild uppgift. Radering hålls -separat från generell redigering så att det destruktiva flödet, dess -bekräftelse och felhantering kan implementeras och verifieras isolerat. - -## Mål - -Feature 7 ska: - -- införa ett backend-API för permanent radering av en uppgift; -- låta användaren initiera radering från uppgiftskortet; -- kräva en tydlig bekräftelse före radering; -- ta bort kortet från brädan först efter serverbekräftelse; -- återanvända befintlig låsning och felhantering per task-id; -- fungera tillsammans med statusändring, tilldelning och drag-and-drop utan - parallella state- eller requestflöden. - -## Omfattning - -Feature 7 omfattar endast permanent radering av en befintlig uppgift. - -En uppgift får raderas oavsett om dess status är `WAITING`, `IN_PROGRESS` eller -`COMPLETED`. Uppgiftens ansvariga användare och aktiv browseranvändare påverkar -inte möjligheten att radera. Det lokala användarvalet är inte autentisering -eller behörighetskontroll. - -## Avgränsningar - -Feature 7 ska inte införa: - -- mjuk radering, papperskorg, återställning eller undo; -- arkivering, versions-, status- eller poänghistorik; -- generell redigering; -- batchradering eller markering av flera kort; -- radering av användare; -- behörigheter eller ägarskap; -- realtidsuppdatering mellan browsers; -- automatisk gallring; -- persistent kortordning; -- nya relationer till uppgifter; -- generell cascade-logik för framtida modeller. - -## Permanent radering - -Radering är permanent. När användaren har bekräftat raderingen tas uppgiften -bort ur databasen. Ingen `deleted`-flagga, `deletedAt`, dold arkiveringsmodell -eller annan form av mjuk radering införs. - -HemHub är en liten familjeapplikation utan revisionslogg, papperskorg eller -återställningsflöde. En mjukraderingsmodell skulle därför öka komplexiteten -utan ett tydligt nuvarande produktvärde. - -Om framtida features för återkommande uppgifter eller poänghistorik behöver -bevara information efter radering ska deras datamodeller och raderingsregler -beslutas i respektive feature. - -## Tillåtna statusar - -Samtliga uppgifter får raderas oavsett status. Det krävs inte att en -`IN_PROGRESS`-uppgift först flyttas till `WAITING`, och en `COMPLETED`-uppgift -behandlas inte annorlunda än övriga uppgifter. - -Bekräftelseflödet är samma för alla statusar. Ingen extra varning eller -ytterligare bekräftelse införs för pågående uppgifter. - -## Raderingskontroll - -Raderingskontrollen ska visas som en diskret sopkorgsikon direkt på det -befintliga uppgiftskortet, uppe till höger i ett eget åtgärdsområde. Feature 7 -inför inget kompakt eller expanderat kortläge. - -Kontrollen ska: - -- visas på befintliga uppgiftskort; -- ha en tillgänglig etikett som identifierar uppgiften, exempelvis - `Radera Töm diskmaskinen`; -- öppna bekräftelsedialogen; -- inte starta drag-and-drop; -- ha en rimlig klickyta för touch; -- ha neutral stil i normalläge och tydlig hover- och fokusmarkering; -- vara inaktiverad när samma uppgift har en pågående operation. - -Sopkorgen implementeras som inline-SVG enligt projektets befintliga -ikonmönster. Feature 7 lägger inte till något ikonbibliotek. - -Den destruktiva visuella betoningen ska primärt ligga i -bekräftelsedialogen. Kontrollen ska kunna flyttas till en framtida meny eller -detaljdialog utan att backend-API eller delete-flödet behöver göras om. - -## Bekräftelsedialog - -Radering bekräftas i en separat delete-modal. Den ska följa beteendemönstret i -`CreateTaskModal`, men Feature 7 inför ingen gemensam modalkomponent och gör -ingen bred modalrefaktorering. Dialogen ska visa: - -- rubriken `Radera uppgift?`; -- uppgiftens titel; -- tydlig information om att raderingen är permanent; -- knappen `Avbryt`; -- den destruktivt utformade knappen `Radera`. - -Exempel: - -> Är du säker på att du vill radera **Töm diskmaskinen**? Uppgiften raderas -> permanent och kan inte återställas. - -Delete-modalen har inget stängningskryss. Innan delete-anropet har startat ska -den kunna stängas med `Avbryt`, Escape eller klick på bakgrunden. Under -pågående delete-anrop blockeras samtliga stängningsvägar. - -Dialogen ska följa projektets befintliga modalstruktur och fokusprinciper. -`Avbryt` får initialt fokus när dialogen öppnas; den destruktiva knappen -`Radera` får inte initialt fokus. Båda knapparna ska vara -tangentbordsåtkomliga. - -## Backend-API - -Radering sker genom: - -```http -DELETE /api/tasks/{taskId} -``` - -### Lyckad radering - -När uppgiften finns och raderas svarar backend med `204 No Content` utan body. - -### Okänd uppgift - -Om uppgiften inte finns svarar backend med `404 Not Found` och projektets -befintliga felformat: - -```text -TASK_NOT_FOUND -``` - -Det gäller även om samma task-id tidigare har raderats. Ett andra delete-anrop -mot samma id ger därför `404 TASK_NOT_FOUND`. - -### Ogiltigt task-id - -Ett task-id som inte kan tolkas som UUID ger `400 Bad Request` med repositoryts -nuvarande requestfel och felformat. Den befintliga felkoden -`INVALID_TASK_ASSIGNMENT` ändras inte inom Feature 7. Feature 7 inför ingen -separat felmodell för UUID-fel. - -### Konflikter och transaktion - -Radering är tillåten för samtliga statusar och oavsett ansvarig. Feature 7 har -därför inget domänfall som ger `409 Conflict`. - -Raderingen ska ske inom backendens normala transaktionsgräns och endast ta bort -den identifierade uppgiften. Den får inte ändra eller radera ansvarig -användare, andra användare eller andra uppgifter. - -## Databas - -Feature 7 raderar raden permanent ur tabellen `task`. - -Nuvarande datamodell har inga dokumenterade beroendeentiteter som kräver en ny -migrering eller särskild cascade-policy. Den befintliga relationen från -`task.assignee_id` till `app_user.id` ska verifieras så att den inte hindrar -radering av uppgiften. Den ansvariga användaren ska finnas kvar. - -Ingen databasmigrering ska skapas om det faktiska schemat redan stödjer -radering. Framtida relationer till uppgifter får definiera sin delete-policy -när de införs. - -## Frontendens uppdateringsstrategi - -Frontend använder serverbekräftad radering. När användaren bekräftar ska -frontend: - -1. markera uppgiften som upptagen; -2. behålla kortet i dess nuvarande kolumn; -3. behålla bekräftelsedialogen öppen; -4. skicka delete-anropet; -5. vänta på serverns svar; -6. vid `204 No Content` ta bort uppgiften ur den lokala task-listan; -7. stänga dialogen; -8. frigöra låsningen för task-id. - -Kortet ska inte tas bort optimistiskt. Ingen rollback-modell behövs eftersom -kortet ligger kvar under anropet. - -## Vänteläge - -När delete-anropet pågår ska: - -- bekräftelsedialogen ligga kvar öppen; -- `Radera` och `Avbryt` vara inaktiverade; -- Escape och bakgrundsklick inte kunna stänga dialogen; -- kortet ligga kvar i sin kolumn och tonas ned lätt; -- alla interaktiva kontroller på samma kort vara inaktiverade. - -Ingen spinner eller text som `Raderar…` krävs. - -## Låsning och samspel med andra operationer - -Delete ska återanvända den befintliga låsningen per task-id. När uppgiften har -en pågående status-, tilldelnings- eller dragoperation ska radering inte kunna -initieras. - -När delete-anropet pågår ska samma uppgift inte kunna dras, ändra status, ändra -ansvarig, öppna en ny raderingsdialog eller skicka ytterligare delete-anrop. -Andra kort ska förbli interaktiva och kunna ha egna samtidiga operationer. - -Feature 7 inför inget globalt vänteläge, separat delete-lås eller parallell -requestmodell. - -## Felhantering - -### Vanliga delete-fel - -Vid nätverksfel, serverfel eller annat vanligt delete-fel ska: - -- kortet ligga kvar oförändrat; -- dialogen ligga kvar öppen; -- vänteläget avslutas; -- kontrollerna aktiveras igen; -- felmeddelandet - `Det gick inte att radera uppgiften. Försök igen.` visas i dialogen; -- användaren kunna försöka igen eller avbryta. - -Delete-felet ska inte blandas med status- eller tilldelningsfel på kortet. - -### `404 TASK_NOT_FOUND` - -Om backend svarar med `404 TASK_NOT_FOUND` betraktas kortet som inaktuellt. -Frontend ska då ta bort uppgiften ur den lokala task-listan, stänga dialogen -och frigöra låsningen utan att visa det generella delete-felet. - -Frontendens generella `ApiError`-typ utökas med ett valfritt `code`. Delete- -flödet ska använda `code === "TASK_NOT_FOUND"` och status `404` för detta fall -och får inte tolka meddelandetexten. - -Andra typer av `404` ska inte behandlas som en redan borttagen uppgift. - -## Frontendtester - -Frontendtesterna ska minst verifiera: - -- att sopkorgsknappen visas direkt på det befintliga uppgiftskortet; -- att ikonen är inline-SVG och inte kräver ett ikonbibliotek; -- tillgänglig etikett och rätt uppgift i bekräftelsedialogen; -- information om permanent radering; -- initialt fokus på `Avbryt`, aldrig på `Radera`; -- att modalen saknar stängningskryss; -- stängning med `Avbryt`, Escape och bakgrundsklick före anrop; -- `DELETE /api/tasks/{taskId}` först efter bekräftelse; -- att kort och dialog ligger kvar under anropet; -- att dialogen inte kan stängas medan anropet pågår; -- gemensam låsning för status, tilldelning, drag och radering; -- att andra kort förblir interaktiva; -- blockering av dubbla delete-anrop; -- att `204 No Content` tar bort rätt kort och stänger dialogen; -- att vanliga fel behåller kort och dialog samt kan återförsökas; -- att `404 TASK_NOT_FOUND` tar bort det inaktuella kortet; -- att ett annat `404`-fel inte feltolkas som `TASK_NOT_FOUND`; -- att raderingskontrollen inte bryter drag-and-drop; -- grundläggande tangentbordsfokus och knappaktivering. - -Testerna ska verifiera beteende och state, inte exakt ikonplacering, färg eller -pixelmått. - -## Backendtester - -Backendtesterna ska minst verifiera: - -- radering i `WAITING`, `IN_PROGRESS` och `COMPLETED`; -- `204 No Content` utan body; -- att den raderade uppgiften inte längre finns i `GET /api/tasks`; -- att andra uppgifter och den ansvariga användaren finns kvar oförändrade; -- `404 TASK_NOT_FOUND` för okänt id och ett andra delete-anrop; -- projektets befintliga `400`-fel för ogiltigt UUID-format. - -Testerna ska följa repositoryts befintliga integrationsteststil. - -## Manuell verifiering - -Följande ska verifieras manuellt: - -1. Sopkorgsknappens placering uppe till höger i ett eget åtgärdsområde, - neutrala normalläge, touchyta, hover, fokus och tillgängliga etikett. -2. Radering av uppgifter i samtliga tre statusar. -3. Initialt fokus på `Avbryt`, inget stängningskryss samt avbrytande med knapp, - Escape och bakgrundsklick före anrop. -4. Rätt titel och information om permanent radering. -5. Titel nära maximal längd. -6. Blockering av dubbla delete-anrop. -7. Vänteläge för kort och dialog under fördröjt svar. -8. Låsning av drag, status, tilldelning och ny radering för samma kort. -9. Fortsatt interaktion med andra kort. -10. Vanligt serverfel, visat felmeddelande och nytt försök. -11. `404 TASK_NOT_FOUND` och lokal borttagning av inaktuellt kort. -12. Desktop- och mobilbredd samt tangentbordsaktivering. -13. Omladdning efter lyckad radering så att uppgiften inte återkommer. - -## Dokumentation - -Feature 7 dokumenteras i: - -```text -docs/features/007-task-deletion.md -``` - -Vid implementation ska `README.md`, `docs/architecture.md` och -`docs/roadmap.md` uppdateras när det är relevant. - -Roadmapen ska markera Feature 7 som `Klar` först efter implementation, -automatiska tester, manuell verifiering och merge. - -Ett nytt ADR behövs inte för permanent radering. Beslutet gäller den nuvarande -task-livscykeln och etablerar inte en generell raderingspolicy för framtida -entiteter. - -## Acceptanskriterier - -Feature 7 är klar när: - -- en uppgift kan raderas permanent med `DELETE /api/tasks/{taskId}`; -- lyckad radering ger `204 No Content`; -- okänd eller redan raderad uppgift ger `404 TASK_NOT_FOUND`; -- ogiltigt UUID-format följer befintlig felhantering; -- alla tre statusar kan raderas oavsett ansvarig eller aktiv användare; -- radering kräver en egen bekräftelsedialog; -- dialogen visar rätt titel och anger att raderingen inte kan återställas; -- `Avbryt` får initialt fokus och `Radera` får inte initialt fokus; -- modalen saknar stängningskryss och blockerar alla stängningsvägar under - anropet; -- en diskret inline-SVG-sopkorg visas direkt på befintliga uppgiftskort; -- raderingskontrollen startar inte drag; -- kortet tas bort först efter serverbekräftelse; -- samma task-id låses för status, tilldelning, drag och ny radering; -- andra kort förblir interaktiva; -- vanliga fel behåller kort och dialog och kan återförsökas; -- `404 TASK_NOT_FOUND` tar bort det inaktuella lokala kortet; -- ingen mjukradering, återställningsmodell eller onödig migrering införs; -- backend- och frontendtester täcker centrala flöden; -- manuell verifiering genomförs; -- relevant dokumentation uppdateras. - -## Implementerad lösning - -Backendens task-controller och task-service har utökats med fysisk radering via -`DELETE /api/tasks/{taskId}`. Servicen hämtar först uppgiften för att -återanvända `TaskNotFoundException` och raderar därefter entiteten inom en -transaktion. Ingen entitet, exception handler eller Flyway-migrering behövde -ändras. - -Frontendens `TaskBoard` använder samma per-task-lås som status- och -tilldelningsoperationerna. Radering är serverbekräftad: kortet och dialogen -ligger kvar medan anropet pågår, och kortet tas bort först efter `204 No -Content`. Endast ett svar med både status `404` och felkoden -`TASK_NOT_FOUND` tar bort ett känt inaktuellt kort. Övriga fel behåller kortet -och dialogen så att användaren kan försöka igen. - -`TaskCard` har inget kompakt eller expanderat läge. En neutral -inline-SVG-knapp ligger direkt i kortets övre högra åtgärdsområde och stoppar -pointer-händelsen innan den når dragytan. Den separata delete-modalen följer -`CreateTaskModal`-mönstret utan en gemensam modalabstraktion. `Avbryt` får -initialt fokus, modalen saknar stängningskryss och samtliga stängningsvägar -blockeras under delete-anropet. - -Frontendens generella `ApiError` innehåller nu ett valfritt `code`. Den -befintliga backendhanteringen av felaktigt UUID är oförändrad och returnerar -fortsatt `400 INVALID_TASK_ASSIGNMENT`. - -## Tester och verifiering - -Automatiskt verifierat: - -- backendens fullständiga testsvit: 47 tester passerade; -- frontendens fullständiga testsvit: 49 tester passerade; -- frontendens produktionsbygge och TypeScript-kompilering passerade; -- `git diff --check` passerade. - -Backendtesterna ligger i den separata integrationstestklassen -`TaskDeletionApiTest`. Frontendens delete-flöden testas tillsammans med övriga -brädbeteenden i `App.test.tsx`. - -Manuell browserverifiering genomfördes mot lokalt körande frontend och backend -i Chrome. Följande verifierades: - -- permanent radering och kvarstående borttagning efter omladdning för - `WAITING`, `IN_PROGRESS` och `COMPLETED`; -- lång titel, radbrytning och korrekt uppgiftstitel i dialogen; -- initialt fokus på `Avbryt`, tabb-ordning till `Radera`, Escape och - bakgrundsklick före anrop samt avsaknad av stängningskryss; -- fördröjd delete-respons med kvarvarande och nedtonat kort, öppen låst modal - och inaktiverade stängningsvägar; -- gemensam låsning av drag, status, ansvarig och ny radering för samma kort, - samtidigt som andra kort förblev interaktiva; -- snabbt dubbelklick på `Radera` utan dubbla delete-anrop; -- vanligt serverfel där kort och modal låg kvar, felet visades och ett nytt - försök lyckades; -- `404 TASK_NOT_FOUND`, där det inaktuella kortet togs bort lokalt; -- neutral sopkorgsknapp med 40 × 40 pixlars klickyta, inline-SVG och - pointer-hantering som inte startade drag; -- desktopbredd 1440 × 1000 och mobilbredd 390 × 844 utan horisontell - scrollning. - -Inga problem upptäcktes i Feature 7-flödena. - -## Relaterade commits - -- Feature-commit: `f296d15` -- Merge-commit: `5df0146` - -## Implementationsprinciper - -Före implementation ska Codex läsa: - -```text -AGENTS.md -README.md -docs/architecture.md -docs/development.md -docs/roadmap.md -docs/decisions/ -docs/features/005-task-status.md -docs/features/006-task-drag-and-drop.md -``` - -Codex ska även läsa relevant backendkod, frontendkod och befintliga tester. -Repositoryts faktiska kod, tester och dokumentation har företräde framför -antaganden i detta dokument. - -Implementation, tester och relevant dokumentation ska uppdateras tillsammans. -Codex ska inte committa, pusha, skapa pull request eller merga utan uttrycklig -instruktion. diff --git a/docs/features/008-task-editing.md b/docs/features/008-task-editing.md deleted file mode 100644 index f082385..0000000 --- a/docs/features/008-task-editing.md +++ /dev/null @@ -1,489 +0,0 @@ -# Feature 8 – Redigera uppgift - -## Status - -Färdig och mergad till `main`. - -## Bakgrund - -HemHub stödjer skapande, visning, tilldelning, statusändring, drag-and-drop och -permanent radering av uppgifter. - -Det saknas fortfarande möjlighet att korrigera eller uppdatera en befintlig -uppgifts grundläggande innehåll. Feature 8 inför därför redigering av: - -- titel; -- beskrivning; -- poäng. - -Redigering hålls separat från de specialiserade flödena för ansvarig, status -och radering. - -## Mål - -Feature 8 ska: - -- låta användaren öppna en redigeringsdialog från uppgiftskortet; -- låta användaren ändra titel, beskrivning och poäng; -- återanvända samma valideringsregler som vid skapande; -- införa ett avgränsat backend-API för uppgiftens redigerbara detaljfält; -- använda serverbekräftad uppdatering; -- återanvända befintlig låsning och felhantering per task-id; -- fungera tillsammans med status, tilldelning, drag-and-drop och radering; -- behålla uppgiftens kolumn och ordning efter redigering. - -## Omfattning - -Feature 8 omfattar redigering av `title`, `description` och `points`. - -Följande egenskaper ska inte kunna ändras genom redigeringsflödet: - -- `id`; -- `status`; -- `assignee`; -- `createdAt`. - -Ansvarig ska fortsatt ändras genom tilldelningsflödet. Status ska fortsatt -ändras genom status-API:t och drag-and-drop-flödet. - -## Avgränsningar - -Feature 8 ska inte införa: - -- statusändring eller ändring av ansvarig i redigeringsformuläret; -- inline-redigering eller en generell detaljvy; -- deadline, återkommande uppgifter, kategorier, etiketter, kommentarer eller - bilagor; -- status-, poäng- eller versionshistorik eller revisionslogg; -- behörigheter, batchredigering eller autosave; -- realtidsuppdatering mellan browsers; -- en generell formulär- eller modalplattform; -- en ny global state-lösning; -- `updatedAt`; -- en ny åtgärdsmeny på uppgiftskortet. - -## Redigeringsflöde - -Redigering sker i en separat modal med: - -- titel; -- beskrivning; -- poäng; -- knappen `Avbryt`; -- knappen `Spara`; -- ett stängningskryss. - -Inline-redigering direkt på kortet ingår inte. En separat modal väljs eftersom -fälten redigeras som en sammanhållen operation, kortets layout ska förbli -stabil, inline-formulär skulle störa dragytan och ett serverbekräftat -spara-/avbrytflöde kan hanteras isolerat. - -## Initiering från uppgiftskortet - -Varje uppgiftskort får en separat synlig redigeringsknapp bredvid den befintliga -sopkorgsknappen. Den ska: - -- ligga i kortets befintliga åtgärdsområde uppe till höger; -- använda en neutral inline-SVG-ikon; -- ha ungefär samma klickyta som raderingsknappen; -- ha en tillgänglig etikett som identifierar uppgiften, exempelvis - `Redigera Töm diskmaskinen`; -- kunna aktiveras med tangentbord; -- inte initiera drag-and-drop; -- stoppa relevanta pointer-händelser innan de når dragytan; -- vara inaktiverad när samma uppgift har en pågående operation. - -Feature 8 inför ingen åtgärdsmeny. Om kortåtgärder senare flyttas till en meny -ska redigerings-API:t och det underliggande redigeringsflödet kunna behållas. - -## Separat redigeringsmodal - -Redigeringen implementeras som en separat komponent, exempelvis -`EditTaskModal`. Komponenten ska följa samma visuella och beteendemässiga -mönster som den befintliga skapandemodalen. - -Feature 8 kräver inte att skapande- och redigeringsmodalerna slås ihop till en -generell komponent med flera lägen. Mindre gemensamma valideringsfunktioner -eller formulärhjälpare får brytas ut om repositoryts faktiska kod tjänar på -det. - -## Formulärets initiala värden - -När redigeringsmodalen öppnas fylls den med uppgiftens aktuella titel, -beskrivning och poäng. En beskrivning som är `null` visas som tom sträng. - -Titelfältet får initialt fokus. Texten markeras inte automatiskt. Formuläret -baseras på task-versionen i frontend-state när dialogen öppnas. - -Om dialogen stängs utan att spara kastas lokala ändringar. När den öppnas igen -hämtas initialvärdena på nytt från den aktuella uppgiften i frontend-state. -Ingen särskild synkronisering eller versionshantering införs om task-data skulle -ändras medan modalen är öppen. - -## Tillåtna statusar och användare - -Titel, beskrivning och poäng får redigeras i `WAITING`, `IN_PROGRESS` och -`COMPLETED`, oavsett ansvarig eller aktiv browseranvändare. Aktiv användare är -ett lokalt browserval och inte autentisering eller behörighetskontroll. - -Att poäng kan ändras på en slutförd uppgift är accepterat i nuvarande modell -eftersom HemHub ännu saknar poänghistorik. En framtida historikfeature ska -besluta om intjänade poäng använder ett snapshot eller uppgiftens aktuella -poängvärde. - -## Stängningsbeteende - -Innan save-anropet har startat ska redigeringsmodalen kunna stängas med: - -- `Avbryt`; -- Escape; -- klick på modalens bakgrund; -- stängningskrysset. - -Osparade ändringar kastas utan extra bekräftelse. - -Under pågående save-anrop ska samtliga stängningsvägar blockeras: - -- `Avbryt` och `Spara` är inaktiverade; -- stängningskrysset är inaktiverat eller otillgängligt; -- Escape ignoreras; -- klick på bakgrunden ignoreras. - -Modalen ligger kvar öppen tills backend-anropet har slutförts. - -## Validering - -Redigering använder samma valideringsregler och användarmeddelanden som -skapande. Backend är alltid slutlig garant. - -### Titel - -Titeln trimmas, är obligatorisk och får innehålla högst 100 -Unicode-kodpunkter. Tom eller enbart blank titel är ogiltig. - -### Beskrivning - -Beskrivningen trimmas, är valfri och får innehålla högst 500 -Unicode-kodpunkter. Den skickas och lagras som `null` när den är tom efter -trimning. - -### Poäng - -Poäng är obligatoriskt, måste vara ett heltal mellan 1 och 99 och får inte -ersättas med ett backend-defaultvärde. - -Frontend blockerar submit vid tom eller för lång titel, för lång beskrivning, -tomt poängfält, text eller decimaltal samt poäng utanför 1–99. - -## Oförändrad submit - -`Spara` är tillgänglig även när användaren inte har ändrat något. Frontend -skickar ett normalt uppdateringsanrop och backend behandlar samma värden som en -giltig idempotent uppdatering. Ingen dirty-state införs. - -## Backend-API - -Redigering sker genom: - -```http -PUT /api/tasks/{taskId}/details -``` - -Endpointen ändrar endast uppgiftens redigerbara detaljfält. - -### Request - -Requesten innehåller alltid hela den redigerbara uppsättningen: - -```json -{ - "title": "Töm diskmaskinen", - "description": "Ställ in allt i rätt skåp", - "points": 3 -} -``` - -Samtliga tre fält ska finnas. `description` får vara `null`. Backend ska inte -implementera patchsemantik för saknade fält. - -### Lyckad uppdatering - -En lyckad uppdatering ger `200 OK` och hela den uppdaterade uppgiften i samma -task-format som övriga task-operationer. Serverns fullständiga respons är -slutlig sanning. - -### Fel - -- okänd uppgift ger `404 Not Found` och `TASK_NOT_FOUND`; -- ogiltigt task-id använder repositoryts befintliga hantering för ogiltiga - path-parametrar; -- ogiltig titel, beskrivning eller poäng ger `400 Bad Request` med den - befintliga task-valideringen och normalt felkoden `INVALID_TASK`. - -Repositoryts faktiska implementation har företräde. - -## Backendens uppdateringsregler - -Backend hämtar först den befintliga uppgiften och uppdaterar uttryckligen endast -`title`, `description` och `points`. - -Operationen får inte ändra `id`, `status`, `assignee` eller `createdAt`. Den ska -vara transaktionell, tillåten i samtliga statusar, idempotent för samma värden, -inte påverka andra uppgifter och använda samma trimning och normalisering som -skapandeflödet. Ingen `updatedAt` införs. - -## Databas - -Den befintliga task-tabellen innehåller redan titel, beskrivning och poäng. -Feature 8 ska därför inte kräva någon Flyway-migrering. - -Implementation ska verifiera kolumnlängder, `points NOT NULL`, -poängconstrainten 1–99, nullhantering för beskrivning och att övriga kolumner -inte påverkas. Ingen ny kolumn eller relation införs. - -## Frontendens uppdateringsstrategi - -Redigering är serverbekräftad. När användaren trycker `Spara` ska frontend: - -1. validera formuläret; -2. markera uppgiften som upptagen genom låsningen per task-id; -3. behålla kortets tidigare värden och modalen öppen; -4. inaktivera formuläret och samtliga stängningsvägar; -5. skicka `PUT /api/tasks/{taskId}/details`; -6. vid framgång ersätta uppgiften med serverns fullständiga respons på samma - plats i task-listan; -7. stänga modalen och frigöra låsningen. - -Frontend visar inte de redigerade värdena optimistiskt. Ingen rollback behövs -eftersom kortet behåller sina tidigare värden tills servern svarar. - -## Vänteläge och gemensam låsning - -Feature 8 återanvänder den befintliga låsningen per task-id. Under save ska: - -- formulärfält, knappar och stängningsvägar vara inaktiverade; -- kortet ligga kvar i samma kolumn och tonas ned; -- samma uppgift inte kunna dras, ändra status eller ansvarig, raderas, öppnas - för ny redigering eller skicka dubbla save-anrop; -- andra uppgifter förbli interaktiva. - -Ingen separat redigeringslåsning, global vänteläge eller parallell -requesthantering införs. En öppen modal låser inte tasken innan `Spara`. - -## Samspel med befintliga flöden - -Redigeringsknappen använder samma pointer-hantering som raderingsknappen och -startar inte drag-and-drop. - -Drag-and-drop förblir optimistiskt med rollback, medan redigering är -serverbekräftad. Status ändras fortsatt genom statusknappar eller drag-and-drop. -Ansvarig ändras fortsatt genom tilldelningsflödet. Raderingsknappen ligger -bredvid redigeringsikonen. Samtliga flöden delar låsningen per task-id. - -## Kortets ordning och kolumn - -En lyckad redigering ersätter uppgiften på befintlig plats i frontendens -task-lista utan omsortering. Status ändras inte, så kortet ligger normalt kvar i -samma kolumn. Serverns fullständiga task-respons ersätter ändå det lokala -värdet i sin helhet. - -## Felhantering - -### Frontendvalideringsfel - -Vid frontendvalideringsfel skickas inget API-anrop. Modalen och inmatningen -behålls, ett begripligt fel visas och användaren kan korrigera och försöka igen. - -### Vanliga API-fel - -Vid backendvalideringsfel, nätverksfel, serverfel eller oväntad respons ligger -kortet kvar oförändrat. Modalen och inmatningen behålls, vänteläget avslutas, -kontrollerna aktiveras och användaren kan försöka igen eller avbryta. - -Generellt meddelande: - -> Det gick inte att spara ändringarna. Försök igen. - -Frontend använder strukturerad felkod och HTTP-status där relevant och tolkar -inte meddelandetext. - -### `404 TASK_NOT_FOUND` - -Endast kombinationen HTTP `404` och `code === "TASK_NOT_FOUND"` behandlas som -ett inaktuellt lokalt kort. Frontend tar då bort uppgiften, stänger modalen och -frigör låsningen utan generellt redigeringsfel. Andra 404-fel behandlas som -vanliga fel. - -## Frontendtester - -Frontendtesterna ska verifiera beteende och state, inte exakt CSS eller intern -komponentstruktur. De ska minst täcka: - -- redigeringsknapp, inline-SVG, tillgänglig etikett, tangentbordsaktivering och - skydd mot dragstart; -- att en låst uppgift inte kan öppnas; -- rätt uppgift och initialvärden, inklusive `null` som tom beskrivning; -- initialt fokus i titelfältet; -- stängning med `Avbryt`, Escape, bakgrund och kryss; -- att osparade ändringar kastas och aktuell task-data används vid nästa - öppning; -- frontendvalidering av titel, beskrivning och poäng; -- rätt endpoint och fullständigt requestformat med trimmade värden och tom - beskrivning som `null`; -- oförändrad submit; -- serverbekräftat vänteläge, blockerade stängningsvägar, gemensam task-låsning - och blockerade dubbla save-anrop; -- att andra kort förblir interaktiva; -- fullständig serverrespons, bibehållen plats, ordning och kolumn; -- vanliga fel med bevarad modal/inmatning och fungerande återförsök; -- `404 TASK_NOT_FOUND` samt att andra 404-fel behandlas som vanliga fel. - -## Backendtester - -Backendtesterna bör ligga i en separat integrationstestklass, exempelvis -`TaskEditingApiTest`, om det passar repositoryts teststruktur. - -Testerna ska minst täcka: - -- samtidig ändring och trimning av titel, beskrivning och poäng; -- tom beskrivning som `null`; -- gränsvärdena 1/99 poäng, 100 kodpunkter i titel och 500 i beskrivning; -- idempotent uppdatering; -- redigering i samtliga tre statusar och fullständig `200 OK`-respons; -- saknad, null, tom eller för lång titel; -- för lång beskrivning; -- saknat, null, text, decimal eller poäng utanför 1–99; -- saknade fält i det fullständiga requestobjektet; -- okänt task-id och ogiltigt UUID; -- att id, status, ansvarig och `createdAt` bevaras; -- att andra uppgifter och ansvarig användare är oförändrade. - -## Implementerad lösning - -Backend exponerar `PUT /api/tasks/{taskId}/details`. Requestmodellen kräver -`title`, `description` och `points`; explicit `null` är endast tillåtet för -beskrivningen. Service-lagret återanvänder skapandeflödets trimning och -validering och uppdaterar en hämtad entitet genom en avgränsad -`changeDetails`-operation. ID, status, ansvarig och skapandetid bevaras. - -Frontend visar en neutral redigeringsknapp med inline-SVG bredvid -raderingsknappen. Den separata `EditTaskModal` fylls från aktuell task, -fokuserar titeln, validerar fälten och blockerar samtliga stängningsvägar under -save. Uppdateringen är serverbekräftad och återanvänder samma låsning per -task-id som status, tilldelning, drag-and-drop och radering. En fullständig -serverrespons ersätter tasken på dess befintliga plats. Endast ett strukturerat -`404 TASK_NOT_FOUND` tar bort ett inaktuellt lokalt kort. - -Ingen Flyway-migrering behövdes eftersom befintliga kolumner och constraints -täcker de redigerbara fälten. - -## Automatisk verifiering - -- Backendens riktade redigeringstester: 20 passerade. -- Fullständig backendtestsvit: 68 passerade. -- Frontendtester: 61 passerade. -- Frontendens TypeScript-kompilering och produktionsbygge passerade. -- `git diff --check` passerade. - -En verifierad begränsning i den lokala H2-databasen är att `VARCHAR` räknar -UTF-16-kodenheter för vissa tecken utanför BMP. Applikationen validerar enligt -Unicode-kodpunkter, men en titel med 100 sådana astrala tecken kan därför -avvisas av H2-kolumnen. Feature 8 ändrar inte databasschemat; PostgreSQL-målet -ska verifiera denna skillnad när produktionsdatabasen införs. - -## Manuell verifiering - -Följande verifierades manuellt före merge: - -1. Redigeringsikonen, klickytan, stilen, etiketten och skyddet mot dragstart. -2. Klick- och tangentbordsöppning av rätt uppgift. -3. Redigering i `WAITING`, `IN_PROGRESS` och `COMPLETED`. -4. Initialvärden, tom beskrivning och initialt fokus. -5. Gränser och fel för titel, beskrivning och poäng. -6. Stängning med `Avbryt`, Escape, bakgrund och kryss samt kastade osparade - ändringar. -7. Oförändrad submit. -8. Fördröjt svar med gamla kortvärden, låst modal och nedtonat kort. -9. Gemensam låsning och fortsatt interaktion med andra kort. -10. Vanligt serverfel, bevarad inmatning och lyckat återförsök. -11. `404 TASK_NOT_FOUND` och annat 404-fel. -12. Bibehållen kolumn, ordning, status och ansvarig. -13. Sparade värden efter omladdning. -14. Desktop, mobil, touch och tangentbordsordning. - -## Dokumentation - -Feature 8 dokumenteras i: - -```text -docs/features/008-task-editing.md -``` - -`README.md`, `docs/architecture.md` och `docs/roadmap.md` uppdaterades -tillsammans med implementationen. `docs/development.md` krävde ingen ändring. - -Ett nytt ADR behövs normalt inte. Separat modal, redigeringsikon, -`PUT /api/tasks/{taskId}/details` och serverbekräftad uppdatering är lokala -beslut för Feature 8. - -## Acceptanskriterier - -Feature 8 är klar när: - -- varje kort har en tangentbordsåtkomlig redigeringskontroll som inte startar - drag; -- titel, beskrivning och poäng kan redigeras i en separat modal; -- aktuella värden fylls i, titeln får fokus och `null` beskrivning visas tom; -- redigering fungerar i samtliga statusar utan behörighetsregler; -- skapande och redigering använder samma valideringsregler; -- backend använder `PUT /api/tasks/{taskId}/details` med hela fältuppsättningen; -- samma värden accepteras idempotent; -- endast titel, beskrivning och poäng ändras; -- `200 OK` returnerar hela task-responsen; -- frontend är serverbekräftad och behåller gamla kortvärden under anropet; -- modal och task är låsta under save genom befintlig per-task-låsning; -- andra uppgifter förblir interaktiva; -- serverresponsen ersätter tasken på befintlig plats och kolumn; -- vanliga fel behåller modal och inmatning och kan återförsökas; -- endast `404 TASK_NOT_FOUND` tar bort ett inaktuellt lokalt kort; -- stängningsvägar fungerar före och blockeras under anrop; -- ingen inline-redigering, generell modalplattform, Flyway-migrering eller - `updatedAt` införs; -- automatiska och manuella kontroller genomförs; -- relevant dokumentation uppdateras. - -## Implementationsprinciper - -Före implementation ska Codex läsa repositoryts faktiska: - -```text -AGENTS.md -README.md -docs/architecture.md -docs/development.md -docs/roadmap.md -docs/decisions/ -docs/features/002-task-creation.md -docs/features/003-task-points.md -docs/features/004-task-assignment.md -docs/features/005-task-status.md -docs/features/006-task-drag-and-drop.md -docs/features/007-task-deletion.md -``` - -Codex ska även läsa relevant backendkod, frontendkod och befintliga tester och -särskilt verifiera entitet, controller, service, repository, request/response, -validering, schema, felmodell, task-listans ordning, modal- och kortstruktur, -per-task-låsning samt befintliga status-, tilldelnings-, drag- och deleteflöden. - -Repositoryts faktiska kod, tester och dokumentation har företräde framför -antaganden i detta dokument. Implementation, tester och relevant dokumentation -ska uppdateras tillsammans. - -Codex ska inte committa, pusha, skapa pull request eller merga utan uttrycklig -instruktion. - -## Relaterade commits - -- Feature-commit: `443f686` -- Merge-commit: `28258f4` diff --git a/docs/completed-features-summary.md b/docs/features/completed-features-summary.md similarity index 100% rename from docs/completed-features-summary.md rename to docs/features/completed-features-summary.md