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:
+123
-173
@@ -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 — {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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user