feat: enhance MainForm layout and user experience; add new components and improve form structure

feat: update i18n configuration to prioritize browser language; enhance language detection
feat: add new translation keys for form submission feedback in English and German
This commit is contained in:
2026-01-20 19:07:03 +01:00
parent fd37e40253
commit c985a20207
5 changed files with 247 additions and 151 deletions

View File

@@ -1,6 +1,7 @@
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import "./index.css"; import "./index.css";
import "./utils/i18n";
import App from "./App.tsx"; import App from "./App.tsx";
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(

View File

@@ -7,6 +7,8 @@ import {
CircularProgress, CircularProgress,
Autocomplete, Autocomplete,
Chip, Chip,
Box,
Paper,
} from "@mui/material"; } from "@mui/material";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
@@ -115,168 +117,247 @@ export const MainForm = () => {
}; };
return ( return (
<> <Box className="min-h-screen bg-gray-800 flex items-center justify-center p-4">
<Chip label={`${t("next-id")}#${nextID ?? "N/A"}`} /> <Paper
<form elevation={3}
onSubmit={(e) => { className="w-full max-w-md p-6 rounded-lg"
e.preventDefault(); sx={{ backgroundColor: "#fff" }}
handleSubmit();
}}
> >
<Autocomplete <form
disablePortal onSubmit={(e) => {
options={users} e.preventDefault();
value={selectedUser} handleSubmit();
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label={t("user")} />}
onChange={(_event, value) => handleUserSelection(value)}
onKeyDown={(event) => {
if (event.key === "Enter") {
event.defaultMuiPrevented = true;
}
}} }}
/> className="flex flex-col gap-4"
<TextField >
required {/* User Selection */}
id="first-name" <Autocomplete
label={t("first-name")} disablePortal
variant="filled" options={users}
value={formData.firstName} value={selectedUser}
onChange={handleChange} fullWidth
name="firstName" renderInput={(params) => (
/> <TextField {...params} label={t("user")} variant="filled" />
<TextField )}
required onChange={(_event, value) => handleUserSelection(value)}
id="last-name" onKeyDown={(event) => {
label={t("last-name")} if (event.key === "Enter") {
variant="filled" event.defaultMuiPrevented = true;
value={formData.lastName} }
onChange={handleChange} }}
name="lastName" />
/>
<TextField {/* Next ID Chip */}
required <Chip
id="email" label={`#${nextID ?? "N/A"}`}
label={t("email")} color="primary"
variant="filled" sx={{
type="email" alignSelf: "flex-start",
value={formData.email} fontWeight: "bold",
onChange={handleChange} fontSize: "1rem",
name="email" py: 2,
/> px: 1,
<TextField }}
required />
id="phone-number"
label={t("phone-number")} {/* Name Fields - Two Columns */}
variant="filled" <Box className="grid grid-cols-2 gap-3">
type="tel"
value={formData.phoneNumber}
onChange={handleChange}
name="phoneNumber"
/>
<TextField
required
id="tickets"
type="number"
label={t("tickets")}
variant="filled"
value={formData.tickets}
onChange={handleChange}
name="tickets"
/>
<FormControlLabel
control={
<Checkbox
checked={invoice}
onChange={(e) => setInvoice(e.target.checked)}
/>
}
label={t("invoice")}
/>
{invoice && (
<>
<TextField <TextField
required required
id="company-name" id="first-name"
label={t("company-name")}
variant="filled"
value={formData.companyName}
onChange={handleChange}
name="companyName"
/>
<TextField
required
id="first-name_invoice"
label={t("first-name")} label={t("first-name")}
variant="filled" variant="filled"
value={formData.cmpFirstName} value={formData.firstName}
onChange={handleChange} onChange={handleChange}
name="cmpFirstName" name="firstName"
fullWidth
/> />
<TextField <TextField
required required
id="last-name_invoice" id="last-name"
label={t("last-name")} label={t("last-name")}
variant="filled" variant="filled"
value={formData.cpmLastName} value={formData.lastName}
onChange={handleChange} onChange={handleChange}
name="cpmLastName" name="lastName"
fullWidth
/> />
<TextField </Box>
required
id="street"
label={t("street")}
variant="filled"
value={formData.street}
onChange={handleChange}
name="street"
/>
<TextField
required
id="postal-code"
label={t("postal-code")}
variant="filled"
value={formData.postalCode}
onChange={handleChange}
name="postalCode"
/>
<TextField
required
id="phone-number_invoice"
label={t("phone-number")}
variant="filled"
type="tel"
value={formData.cpmPhoneNumber}
onChange={handleChange}
name="cpmPhoneNumber"
/>
<TextField
required
id="email_invoice"
label={t("email")}
variant="filled"
type="email"
value={formData.cpmEmail}
onChange={handleChange}
name="cpmEmail"
/>
</>
)}
{/* Payment methods - only one must be selected */}
<FormControlLabel control={<Checkbox />} label={t("cash")} />
<FormControlLabel control={<Checkbox />} label={t("paypal")} />
<FormControlLabel control={<Checkbox />} label={t("transfer")} />
{isLoading && <CircularProgress />}
<Button type="submit" variant="contained" disabled={isLoading}>
{t("submit")}
</Button>
{msg && ( {/* Email */}
<Alert severity={msg.type}> <TextField
{msg.headline}: {msg.text} required
</Alert> id="email"
)} label={t("email")}
</form> variant="filled"
</> type="email"
value={formData.email}
onChange={handleChange}
name="email"
fullWidth
/>
{/* Phone Number */}
<TextField
required
id="phone-number"
label={t("phone-number")}
variant="filled"
type="tel"
value={formData.phoneNumber}
onChange={handleChange}
name="phoneNumber"
fullWidth
/>
{/* Tickets and Invoice Checkbox */}
<Box className="grid grid-cols-2 gap-3 items-center">
<TextField
required
id="tickets"
type="number"
label={t("tickets")}
variant="filled"
value={formData.tickets}
onChange={handleChange}
name="tickets"
fullWidth
inputProps={{ min: 1 }}
/>
<FormControlLabel
control={
<Checkbox
checked={invoice}
onChange={(e) => setInvoice(e.target.checked)}
/>
}
label={t("invoice")}
className="justify-end"
/>
</Box>
{/* Invoice Fields */}
{invoice && (
<Box className="flex flex-col gap-3 pt-2 border-t border-gray-200">
<TextField
required
id="company-name"
label={t("company-name")}
variant="filled"
value={formData.companyName}
onChange={handleChange}
name="companyName"
fullWidth
/>
{/* Invoice Name Fields - Two Columns */}
<Box className="grid grid-cols-2 gap-3">
<TextField
required
id="first-name_invoice"
label={t("first-name")}
variant="filled"
value={formData.cmpFirstName}
onChange={handleChange}
name="cmpFirstName"
fullWidth
/>
<TextField
required
id="last-name_invoice"
label={t("last-name")}
variant="filled"
value={formData.cpmLastName}
onChange={handleChange}
name="cpmLastName"
fullWidth
/>
</Box>
<TextField
required
id="street"
label={t("street")}
variant="filled"
value={formData.street}
onChange={handleChange}
name="street"
fullWidth
/>
<TextField
required
id="postal-code"
label={t("postal-code")}
variant="filled"
value={formData.postalCode}
onChange={handleChange}
name="postalCode"
fullWidth
/>
<TextField
required
id="phone-number_invoice"
label={t("phone-number")}
variant="filled"
type="tel"
value={formData.cpmPhoneNumber}
onChange={handleChange}
name="cpmPhoneNumber"
fullWidth
/>
<TextField
required
id="email_invoice"
label={t("email")}
variant="filled"
type="email"
value={formData.cpmEmail}
onChange={handleChange}
name="cpmEmail"
fullWidth
/>
</Box>
)}
{/* Payment Methods */}
<Box className="flex justify-center gap-4 pt-2">
<FormControlLabel control={<Checkbox />} label={t("cash")} />
<FormControlLabel control={<Checkbox />} label={t("paypal")} />
<FormControlLabel control={<Checkbox />} label={t("transfer")} />
</Box>
{/* Submit Button */}
<Button
type="submit"
variant="contained"
disabled={isLoading}
fullWidth
size="large"
sx={{
mt: 2,
py: 1.5,
textTransform: "uppercase",
fontWeight: "bold",
}}
>
{isLoading ? (
<CircularProgress size={24} color="inherit" />
) : (
t("submit")
)}
</Button>
{/* Alert Message */}
{msg && (
<Alert severity={msg.type} sx={{ mt: 2 }}>
{msg.headline}: {msg.text}
</Alert>
)}
</form>
</Paper>
</Box>
); );
}; };

View File

@@ -22,7 +22,7 @@ i18n
.init({ .init({
resources, resources,
fallbackLng: "en", // use en if detected lng is not available fallbackLng: "en", // use en if detected lng is not available
lng: Cookies.get("language") || "en", // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources lng: Cookies.get("language") || navigator.language.split("-")[0] || "en", // Check cookie first, then browser language
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage // you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option // if you're using a language detector, do not define the lng option

View File

@@ -11,5 +11,12 @@
"submit": "Kaufen", "submit": "Kaufen",
"failed-to-load-users": "Das Laden der Benutzer ist fehlgeschlagen.", "failed-to-load-users": "Das Laden der Benutzer ist fehlgeschlagen.",
"user": "Benutzer", "user": "Benutzer",
"next-id": "Nächste Eintragsnummer: " "next-id": "Nächste Eintragsnummer: ",
"form-submitted-successfully": "Formular erfolgreich übermittelt!",
"orm-submission-failed": "Formularübermittlung fehlgeschlagen.",
"success": "Erfolg",
"error": "Fehler",
"cash": "Bar",
"paypal": "PayPal",
"transfer": "Überweisung"
} }

View File

@@ -11,5 +11,12 @@
"submit": "Buy", "submit": "Buy",
"failed-to-load-users": "Failed to load users.", "failed-to-load-users": "Failed to load users.",
"user": "User", "user": "User",
"next-id": "Next Entry Number: " "next-id": "Next Entry Number: ",
"form-submitted-successfully": "Form submitted successfully!",
"orm-submission-failed": "Form submission failed.",
"success": "Success",
"error": "Error",
"cash": "Cash",
"paypal": "PayPal",
"transfer": "Bank Transfer"
} }