Files
hemhub/docs/features/007-task-deletion.md
2026-07-27 21:26:19 +02:00

443 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature 7 Radera uppgift
## Status
Implementerad och verifierad på feature-branchen. Merge återstår.
## 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 på feature-branchen:
- 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. Featuren ska ändå inte markeras
som mergad eller `Klar` i roadmapen förrän merge är genomförd.
## Relaterade commits
Fylls i efter commit och merge.
## 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.