add lucide-react dependency and update form handling in MainForm

- Added lucide-react to package dependencies
- Refactored MainForm to improve form handling and user selection
- Removed SuccessPage component and related logic
- Updated localization files to include new keys for invoice details
- Created interfaces for form data and messages
This commit is contained in:
2026-05-10 19:39:32 +02:00
parent 746530ae4c
commit 5c035ba1c0
8 changed files with 413 additions and 581 deletions
+123 -173
View File
@@ -1,23 +1,20 @@
import { Box, Paper, Typography, Chip, Button } from "@mui/material";
import { useEffect, useState } from "react";
import { CircleCheck } from "lucide-react";
import { useTranslation } from "../../node_modules/react-i18next";
import { useTranslation } from "react-i18next";
import { Sheet, Typography, Chip, Button } from "@mui/joy";
export const SuccessPage = () => {
const [orderId, setOrderId] = useState<string | null>(null);
const [tickets, setNumberOfTickets] = useState<number>(0);
const [animate, setAnimate] = useState(false);
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);
const id = params.get("id");
const numberOfTickets = params.get("tickets");
setOrderId(id);
setNumberOfTickets(numberOfTickets ? parseInt(numberOfTickets, 10) : 0);
setOrderId(params.get("id"));
setTickets(parseInt(params.get("tickets") ?? "0", 10));
// Small delay so the CSS transition actually plays
setTimeout(() => setAnimate(true), 100);
}, []);
@@ -26,185 +23,138 @@ export const SuccessPage = () => {
window.location.href = "/";
return;
}
const timer = setTimeout(() => setSeconds(seconds - 1), 1000);
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 (
<Box className="min-h-screen bg-gray-800 flex items-center justify-center p-4">
<Paper
elevation={3}
className="w-full max-w-md p-8 rounded-lg"
<div className="min-h-screen w-full flex items-center justify-center bg-gradient-to-br from-slate-800 to-slate-900 p-4">
<Sheet
variant="plain"
sx={{
backgroundColor: "#fff",
textAlign: "center",
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",
}}
>
{/* Animated Success Icon */}
<Box
sx={{
display: "flex",
justifyContent: "center",
mb: 3,
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} className="text-green-500" strokeWidth={2.5} />
</Box>
{/* Success Message */}
<Typography
variant="h4"
component="h1"
gutterBottom
sx={{
fontWeight: "bold",
color: "#2e7d32",
mb: 2,
transition: "all 0.5s ease-in-out 0.2s",
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
}}
>
{t("form-submitted-successfully")}
</Typography>
<Typography
variant="body1"
sx={{
color: "#666",
mb: 3,
transition: "all 0.5s ease-in-out 0.3s",
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
}}
>
{t("ticket-payment", { count: tickets })}
</Typography>
{/* Tickets Display */}
{tickets > 0 && (
<Box
sx={{
mb: 2,
transition: "all 0.5s ease-in-out 0.35s",
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
}}
>
<Chip
label={`${tickets} ${tickets === 1 ? t("ticket") : t("tickets")}`}
color="secondary"
sx={{
fontWeight: "bold",
fontSize: "1rem",
py: 2.5,
px: 2,
}}
/>
</Box>
)}
{/* Order ID Display */}
{orderId && (
<Box
sx={{
mb: 3,
transition: "all 0.5s ease-in-out 0.4s",
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
}}
>
<Typography
variant="body2"
sx={{
color: "#888",
mb: 1,
fontSize: "0.875rem",
}}
>
{t("entry-id")}
</Typography>
<Chip
label={`#${orderId}`}
color="primary"
sx={{
fontWeight: "bold",
fontSize: "1.25rem",
py: 3,
px: 2,
}}
/>
</Box>
)}
{/* Return button */}
<Box
sx={{
mb: 3,
transition: "all 0.5s ease-in-out 0.4s",
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
}}
>
<Button
href="/"
variant="contained"
color="primary"
sx={{
fontWeight: "bold",
fontSize: "1.25rem",
py: 3,
px: 2,
}}
>
{seconds + " " + t("return-to-homepage")}
</Button>
</Box>
{/* Additional Info */}
<Box
sx={{
mt: 4,
pt: 3,
borderTop: "1px solid #e0e0e0",
transition: "all 0.5s ease-in-out 0.5s",
transform: animate ? "translateY(0)" : "translateY(20px)",
opacity: animate ? 1 : 0,
}}
>
<Typography
variant="body2"
sx={{
color: "#666",
lineHeight: 1.6,
}}
>
{t("thank-you")}
</Typography>
</Box>
{/* Decorative Elements */}
<Box
sx={{
{/* Green accent bar at the top */}
<div
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
height: "4px",
background: "linear-gradient(90deg, #4caf50 0%, #81c784 100%)",
transition: "all 0.8s ease-in-out 0.6s",
background: "linear-gradient(90deg, #22c55e, #86efac)",
transition: "transform 0.8s ease-in-out 0.6s",
transform: animate ? "scaleX(1)" : "scaleX(0)",
transformOrigin: "left",
}}
/>
</Paper>
</Box>
{/* 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>
);
};