Kundgodkännande före arbete — förslag, besked och spärrat arbete
Verkstaden kan inte längre utföra föreslaget arbete utan spårbart besked från kunden. - Ny händelse atgardsforslag: föreslagen åtgärd (förifylld ur felorsaksanalysen) med uppskattad kostnad — kunddelbar och visad i Live Share, där kunden ser förslaget och sitt registrerade besked - Ny händelse kundbeslut: godkänt/avböjt/delvis med obligatorisk kanal (telefon, på plats, e-post, SMS, delningslänk) och motivering vid avböjt/delvis; loggposten bär vem i verkstaden som tog emot beskedet - "Dokumentera utförd åtgärd" är låst medan ett förslag saknar besked och förblir låst vid avböjt — vägen "Ingen åtgärd utförd" är öppen och hänvisar till beskedet - Kvalitetsgrinden: besked obligatoriskt när arbete utförts, plus hård flagga "Utfört arbete trots avböjt åtgärdsförslag" - Rapporten får avsnittet "Åtgärdsförslag och kundens besked"; demoärendet visar hela kedjan förslag → godkännande → åtgärd Avgränsning: kunden lämnar sitt besked via kontakt med verkstaden som registrerar det. Publikt godkännande direkt i delningslänken kräver en skrivande publik endpoint och hanteras separat. 69 vitest-tester, integrationstest, OpenAPI-validering och klicktest (blockerat arbete utan/vid avböjt besked) gröna. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
// bort även lokalt.
|
||||
|
||||
import type { Arende } from "./domain";
|
||||
import { handelseRubrik } from "./domain";
|
||||
import { KUNDBESLUT_LABEL, handelseRubrik } from "./domain";
|
||||
import { arAvslutat, arendeidentitet, brief, foton, tidsfordelningsRader, videor } from "./projektioner";
|
||||
import { metodikForArende } from "./store";
|
||||
import { tidDatum, tidKlockslag } from "./format";
|
||||
@@ -59,6 +59,10 @@ export function DelatArendeVy({
|
||||
const avslutat = arAvslutat(arende);
|
||||
const bilder = foton(arende);
|
||||
const klipp = videor(arende);
|
||||
// Åtgärdsförslaget är avsett för kunden — det visas alltid överst i
|
||||
// delningsvyn, tillsammans med det besked som registrerats.
|
||||
const forslag = arende.handelser.filter((p) => p.handelse.typ === "atgardsforslag");
|
||||
const beslut = [...arende.handelser].reverse().find((p) => p.handelse.typ === "kundbeslut");
|
||||
const matvarden = arende.handelser.filter((p) => p.handelse.typ === "matvarde");
|
||||
const kundposter = redanFiltrerad
|
||||
? arende.handelser
|
||||
@@ -93,6 +97,33 @@ export function DelatArendeVy({
|
||||
</Panel>
|
||||
)}
|
||||
|
||||
{forslag.length > 0 && (
|
||||
<Panel rubrik="Åtgärdsförslag">
|
||||
{forslag.map((p) => {
|
||||
const h = p.handelse;
|
||||
if (h.typ !== "atgardsforslag") return null;
|
||||
return (
|
||||
<div key={p.id} className="mb-2 last:mb-0">
|
||||
<p className="text-[14px]">{h.beskrivning}</p>
|
||||
{h.uppskattadKostnad && (
|
||||
<p className="text-[13px] text-[#4A5560]">Uppskattad kostnad: {h.uppskattadKostnad}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{beslut?.handelse.typ === "kundbeslut" ? (
|
||||
<p className="mt-2 border-t border-[#DDDDDD] pt-2 text-[13px] font-semibold">
|
||||
Ditt besked: {KUNDBESLUT_LABEL[beslut.handelse.beslut]}{" "}
|
||||
<span className="font-normal text-[#4A5560]">(registrerat via {beslut.handelse.kanal})</span>
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-2 border-t border-[#DDDDDD] pt-2 text-[12px] text-[#707070]">
|
||||
Inväntar ditt besked — kontakta verkstaden för att godkänna eller avböja.
|
||||
</p>
|
||||
)}
|
||||
</Panel>
|
||||
)}
|
||||
|
||||
<Panel rubrik="Aktuell status">
|
||||
{b.utfordaKontroller.map((k, i) => (
|
||||
<p key={`u${i}`} className="py-0.5 text-[14px]"><span className="text-[#1E6B34]"><IkonCheck /></span> {k.text}</p>
|
||||
|
||||
Reference in New Issue
Block a user