Files
hemhub/docs/features/008-task-editing.md
2026-07-28 11:18:13 +02:00

18 KiB
Raw Permalink Blame History

Feature 8 Redigera uppgift

Status

Implementerad och automatiskt verifierad på feature-branchen. Manuell browserverifiering och merge till main återstår.

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 199.

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:

PUT /api/tasks/{taskId}/details

Endpointen ändrar endast uppgiftens redigerbara detaljfält.

Request

Requesten innehåller alltid hela den redigerbara uppsättningen:

{
  "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 199, 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 199;
  • 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 ska verifieras manuellt:

  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:

docs/features/008-task-editing.md

Vid implementation uppdateras README.md, docs/architecture.md, docs/roadmap.md och docs/development.md när relevant.

Roadmapen markerar Feature 8 som Klar först efter implementation, automatiska tester, produktionsbygge, manuell verifiering, merge till main och slutlig dokumentationsuppdatering.

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:

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

Fylls i efter implementation och merge.