feat: Buchungsdialog zeigt Preis erst unter der gewählten Zahlungsweise
Keine Vorauswahl mehr. Unter der gewählten Option stehen Preisaufstellung und Raten mit Fälligkeit; Weiter erst nach Auswahl. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
5c2878875b
commit
7a3e37f96f
1 file changed
+35
-17
@@ -60,7 +60,8 @@ export function BookingDialog({
|
||||
|
||||
const [step, setStep] = useState(0);
|
||||
const [participant, setParticipant] = useState<Participant>(EMPTY);
|
||||
const [chosenPlan, setPlanType] = useState<PlanType>("FULL");
|
||||
// Keine Vorauswahl: Preise erscheinen erst unter der gewaehlten Zahlungsweise.
|
||||
const [chosenPlan, setPlanType] = useState<PlanType | null>(null);
|
||||
const [voucherInput, setVoucherInput] = useState("");
|
||||
const [appliedVoucher, setAppliedVoucher] = useState<string | null>(null);
|
||||
const [voucherMessage, setVoucherMessage] = useState<string | null>(null);
|
||||
@@ -114,11 +115,11 @@ export function BookingDialog({
|
||||
};
|
||||
}, [open, quote]);
|
||||
|
||||
// Eine nicht (mehr) verfuegbare Ratenvariante faellt auf Einmalzahlung zurueck.
|
||||
// Eine nicht (mehr) verfuegbare Variante gilt als nicht gewaehlt.
|
||||
const chosen = quote?.plans.find((p) => p.type === chosenPlan);
|
||||
const planType: PlanType = chosen && !chosen.available ? "FULL" : chosenPlan;
|
||||
const planType: PlanType | null = chosen?.available ? chosenPlan : null;
|
||||
|
||||
const selectedPlan = quote?.plans.find((p) => p.type === planType) ?? null;
|
||||
const selectedPlan = chosen?.available ? chosen : null;
|
||||
|
||||
function updateParticipant(field: keyof Participant, value: string) {
|
||||
setParticipant((prev) => ({ ...prev, [field]: value }));
|
||||
@@ -174,6 +175,7 @@ export function BookingDialog({
|
||||
|
||||
async function submit() {
|
||||
setFormError(null);
|
||||
if (!planType) return;
|
||||
const missing: FieldErrors = {};
|
||||
if (!consents.adult) missing["consents.adult"] = "Bitte bestätige, dass du volljährig bist.";
|
||||
if (!consents.terms) missing["consents.terms"] = "Bitte akzeptiere die Buchungsbedingungen.";
|
||||
@@ -324,10 +326,11 @@ export function BookingDialog({
|
||||
<legend className="field-label">Wie möchtest du bezahlen?</legend>
|
||||
<div className="mt-2 space-y-3">
|
||||
{quote?.plans.map((plan) => (
|
||||
<label
|
||||
<div
|
||||
key={plan.type}
|
||||
className={`flex cursor-pointer gap-4 rounded-xl border p-4 ${planType === plan.type ? "border-ink bg-paper" : "border-line"} ${plan.available ? "" : "cursor-not-allowed opacity-60"}`}
|
||||
className={`rounded-xl border p-4 ${planType === plan.type ? "border-ink bg-paper" : "border-line"} ${plan.available ? "" : "opacity-60"}`}
|
||||
>
|
||||
<label className={`flex gap-4 ${plan.available ? "cursor-pointer" : "cursor-not-allowed"}`}>
|
||||
<input
|
||||
type="radio"
|
||||
name="planType"
|
||||
@@ -341,15 +344,11 @@ export function BookingDialog({
|
||||
/>
|
||||
<span className="flex-1">
|
||||
<span className="block font-semibold">{plan.label}</span>
|
||||
{plan.available ? (
|
||||
<span className="mt-1 block text-[0.9375rem] text-ink/80">
|
||||
{plan.installments.map((i) => money(i.amountCents)).join(" + ")}
|
||||
</span>
|
||||
) : (
|
||||
<span className="mt-1 block text-[0.9375rem] text-stone">{plan.reason}</span>
|
||||
)}
|
||||
{!plan.available && <span className="mt-1 block text-[0.9375rem] text-stone">{plan.reason}</span>}
|
||||
</span>
|
||||
</label>
|
||||
{plan.available && planType === plan.type && quote.price && <PlanDetails plan={plan} quote={quote} />}
|
||||
</div>
|
||||
)) ?? <p className="text-stone">Preise werden geladen …</p>}
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -384,8 +383,6 @@ export function BookingDialog({
|
||||
{voucherMessage}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{quote?.price && <PriceBreakdown quote={quote} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -462,7 +459,7 @@ export function BookingDialog({
|
||||
</a>{" "}
|
||||
gelesen.
|
||||
</Consent>
|
||||
{planType !== "FULL" && (
|
||||
{planType !== null && planType !== "FULL" && (
|
||||
<Consent
|
||||
id="installmentMandate"
|
||||
checked={consents.installmentMandate}
|
||||
@@ -494,7 +491,7 @@ export function BookingDialog({
|
||||
<span />
|
||||
)}
|
||||
{step < STEPS.length - 1 ? (
|
||||
<button type="button" className="btn btn-primary" onClick={next} disabled={step === 1 && !quote?.price}>
|
||||
<button type="button" className="btn btn-primary" onClick={next} disabled={step === 1 && (!quote?.price || !selectedPlan)}>
|
||||
Weiter
|
||||
</button>
|
||||
) : (
|
||||
@@ -509,6 +506,27 @@ export function BookingDialog({
|
||||
);
|
||||
}
|
||||
|
||||
/** Preis und Raten unter der gewaehlten Zahlungsweise. */
|
||||
function PlanDetails({ plan, quote }: { plan: QuoteData["plans"][number]; quote: QuoteData }) {
|
||||
return (
|
||||
<div className="mt-3 pl-9">
|
||||
<PriceBreakdown quote={quote} />
|
||||
{plan.installments.length > 1 && (
|
||||
<ul className="mt-3 space-y-1 text-[0.9375rem] text-ink/80">
|
||||
{plan.installments.map((i) => (
|
||||
<li key={i.seq} className="flex justify-between gap-4">
|
||||
<span>
|
||||
Rate {i.seq}, {i.dueDate ? `fällig ${isoDate(i.dueDate)}` : "heute"}
|
||||
</span>
|
||||
<span className="tabular-nums">{money(i.amountCents)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PriceBreakdown({ quote }: { quote: QuoteData }) {
|
||||
const p = quote.price!;
|
||||
return (
|
||||
|
||||
Reference in new issue
Block a user