feat(status): add estimate customer actions
This commit is contained in:
parent
efdc4d5be1
commit
6f6891134e
9 changed files with 699 additions and 3 deletions
153
components/status/EstimateActions.tsx
Normal file
153
components/status/EstimateActions.tsx
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
"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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue