Files
ca-lose/frontend/src/pages/SuccessPage.tsx
T
2026-05-24 13:50:41 +02:00

181 lines
5.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { CircleCheck } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Sheet, Typography, Chip, Button } from "@mui/joy";
export const SuccessPage = () => {
const { t } = useTranslation();
const [orderId, setOrderId] = useState<string | null>(null);
const [tickets, setTickets] = useState<number>(0);
const [animate, setAnimate] = useState(false);
const [seconds, setSeconds] = useState(30);
useEffect(() => {
const params = new URLSearchParams(window.location.search);
setOrderId(params.get("id"));
setTickets(parseInt(params.get("tickets") ?? "0", 10));
// Small delay so the CSS transition actually plays
setTimeout(() => setAnimate(true), 100);
document.body.classList.add("success-bg");
return () => {
document.body.classList.remove("success-bg");
};
}, []);
useEffect(() => {
if (seconds === 0) {
window.location.href = "/";
return;
}
const timer = setTimeout(() => setSeconds((s) => s - 1), 1000);
return () => clearTimeout(timer);
}, [seconds]);
// Returns a style object that slides the element up + fades it in.
// Each section gets a slightly later delay for a staggered entrance.
const fadeUp = (delay: string): React.CSSProperties => ({
transition: `opacity 0.5s ease-in-out ${delay}, transform 0.5s ease-in-out ${delay}`,
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
});
return (
<div className="flex-1 w-full flex items-center justify-center p-4">
<Sheet
variant="plain"
sx={{
position: "relative",
width: "100%",
maxWidth: 440,
borderRadius: "24px",
p: { xs: "2rem", sm: "2.5rem" },
boxShadow: "0 24px 64px -12px rgba(0,0,0,0.4)",
background: "#fff",
textAlign: "center",
overflow: "hidden",
}}
>
{/* Green accent bar at the top */}
<div
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
height: "4px",
background: "linear-gradient(90deg, #22c55e, #86efac)",
transition: "transform 0.8s ease-in-out 0.6s",
transform: animate ? "scaleX(1)" : "scaleX(0)",
transformOrigin: "left",
}}
/>
{/* Animated success icon */}
<div
style={{
display: "flex",
justifyContent: "center",
marginBottom: "1.5rem",
transition: "all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)",
transform: animate ? "scale(1)" : "scale(0)",
opacity: animate ? 1 : 0,
}}
>
<CircleCheck size={80} color="#22c55e" strokeWidth={2.5} />
</div>
{/* Headline */}
<div style={fadeUp("0.2s")}>
<Typography
level="h3"
sx={{ fontWeight: 700, color: "#15803d", mb: 1 }}
>
{t("form-submitted-successfully")}
</Typography>
</div>
{/* Subtitle */}
<div style={fadeUp("0.3s")}>
<Typography level="body-md" sx={{ color: "#6b7280", mb: 3 }}>
{t("ticket-payment", { count: tickets })}
</Typography>
</div>
{/* Tickets chip */}
{tickets > 0 && (
<div style={fadeUp("0.35s")} className="flex justify-center mb-3">
<Chip
size="lg"
variant="soft"
color="success"
sx={{ fontWeight: 700, fontSize: "1rem", px: 2, py: 1 }}
>
{tickets} {tickets === 1 ? t("ticket") : t("tickets")}
</Chip>
</div>
)}
{/* Order ID chip */}
{orderId && (
<div
style={fadeUp("0.4s")}
className="flex flex-col items-center gap-1 mb-4"
>
<Typography
level="body-xs"
sx={{
color: "#9ca3af",
textTransform: "uppercase",
letterSpacing: "0.08em",
}}
>
{t("entry-id")}
</Typography>
<Chip
size="lg"
variant="solid"
color="primary"
sx={{ fontWeight: 700, fontSize: "1.25rem", px: 3, py: 1.5 }}
>
#{orderId}
</Chip>
</div>
)}
{/* Return button with countdown */}
<div style={fadeUp("0.45s")} className="mb-4">
<Button
component="a"
href="/"
size="lg"
color="primary"
variant="solid"
fullWidth
sx={{
borderRadius: "12px",
fontWeight: 700,
background: "linear-gradient(135deg, #2563eb, #1d4ed8)",
"&:hover": {
background: "linear-gradient(135deg, #1d4ed8, #1e40af)",
},
}}
>
{seconds}s &mdash; {t("return-to-homepage")}
</Button>
</div>
{/* Thank-you note */}
<div style={fadeUp("0.5s")} className="pt-4 border-t border-slate-100">
<Typography
level="body-sm"
sx={{ color: "#9ca3af", lineHeight: 1.6 }}
>
{t("thank-you")}
</Typography>
</div>
</Sheet>
</div>
);
};