funktechnik-schubert-website/components/status/EstimateActions.tsx
2026-07-05 02:17:19 +02:00

153 lines
4.4 KiB
TypeScript

"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
type EstimateAction = "approve" | "decline" | "question";
type EstimateActionsProps = {
token: string;
};
type ActionResponse = {
message?: string;
};
const actionLabels: Record<EstimateAction, string> = {
approve: "Freigeben",
decline: "Ablehnen",
question: "Rückfrage senden",
};
async function readActionMessage(response: Response) {
try {
const body = await response.json() as ActionResponse;
return body.message || "Die Aktion konnte nicht abgeschlossen werden.";
} catch {
return "Die Aktion konnte nicht abgeschlossen werden.";
}
}
export function EstimateActions({ token }: EstimateActionsProps) {
const router = useRouter();
const [activeForm, setActiveForm] = useState<Exclude<EstimateAction, "approve"> | null>(null);
const [message, setMessage] = useState("");
const [pendingAction, setPendingAction] = useState<EstimateAction | null>(null);
const [feedback, setFeedback] = useState<{ type: "success" | "error"; text: string } | null>(null);
async function submitAction(action: EstimateAction, actionMessage = "") {
setPendingAction(action);
setFeedback(null);
try {
const response = await fetch(`/api/status/${encodeURIComponent(token)}/estimate/${action}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ message: actionMessage.trim() || null }),
});
const responseMessage = await readActionMessage(response);
if (!response.ok) {
setFeedback({ type: "error", text: responseMessage });
return;
}
setMessage("");
setActiveForm(null);
setFeedback({ type: "success", text: responseMessage });
router.refresh();
} catch {
setFeedback({
type: "error",
text: "Die Aktion konnte momentan nicht gesendet werden. Bitte versuchen Sie es erneut.",
});
} finally {
setPendingAction(null);
}
}
const isPending = pendingAction !== null;
return (
<div className="estimate-actions-panel">
<div className="estimate-action-buttons">
<button
className="button"
disabled={isPending}
type="button"
onClick={() => void submitAction("approve")}
>
{pendingAction === "approve" ? "Wird freigegeben..." : actionLabels.approve}
</button>
<button
className="button light"
disabled={isPending}
type="button"
onClick={() => {
setActiveForm("question");
setFeedback(null);
}}
>
Rückfrage
</button>
<button
className="button danger"
disabled={isPending}
type="button"
onClick={() => {
setActiveForm("decline");
setFeedback(null);
}}
>
Ablehnen
</button>
</div>
{activeForm && (
<form
className="estimate-action-form"
onSubmit={(event) => {
event.preventDefault();
void submitAction(activeForm, message);
}}
>
<label htmlFor={`estimate-${activeForm}-message`}>
{activeForm === "decline" ? "Nachricht zur Ablehnung" : "Ihre Rückfrage"}
</label>
<textarea
id={`estimate-${activeForm}-message`}
name="message"
rows={4}
value={message}
placeholder={activeForm === "decline" ? "Optionaler Hinweis für uns" : "Ihre Frage zum Kostenvoranschlag"}
onChange={(event) => setMessage(event.currentTarget.value)}
/>
<div className="estimate-form-actions">
<button className="button" disabled={isPending} type="submit">
{pendingAction === activeForm ? "Wird gesendet..." : actionLabels[activeForm]}
</button>
<button
className="button light"
disabled={isPending}
type="button"
onClick={() => {
setActiveForm(null);
setMessage("");
}}
>
Abbrechen
</button>
</div>
</form>
)}
{feedback && (
<p className={`estimate-feedback ${feedback.type === "error" ? "error" : "success"}`} role="status">
{feedback.text}
</p>
)}
</div>
);
}