feat: update MainForm component for improved form handling and validation; add i18next support for localization

This commit is contained in:
2026-01-14 13:01:45 +01:00
parent 28c3c119f1
commit 411e3cba46
6 changed files with 520 additions and 374 deletions

View File

@@ -15,10 +15,11 @@
"@mui/material": "^7.3.6",
"@mui/styled-engine-sc": "^7.3.6",
"@tailwindcss/vite": "^4.1.11",
"i18next": "^25.7.4",
"js-cookie": "^3.0.5",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-i18next": "^16.2.0",
"react-i18next": "^16.5.3",
"react-router-dom": "^7.11.0",
"styled-components": "^6.1.19",
"tailwind-merge": "^3.3.1",
@@ -27,6 +28,7 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/js-cookie": "^3.0.6",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
@@ -2031,6 +2033,13 @@
"@types/react": "*"
}
},
"node_modules/@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -3263,7 +3272,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4"
},
@@ -4071,9 +4079,9 @@
}
},
"node_modules/react-i18next": {
"version": "16.5.2",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.2.tgz",
"integrity": "sha512-GG/SBVxx9dvrO1uCs8VYdKfOP8NEBUhNP+2VDQLCifRJ8DL1qPq296k2ACNGyZMDe7iyIlz/LMJTQOs8HXSRvw==",
"version": "16.5.3",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.3.tgz",
"integrity": "sha512-fo+/NNch37zqxOzlBYrWMx0uy/yInPkRfjSuy4lqKdaecR17nvCHnEUt3QyzA8XjQ2B/0iW/5BhaHR3ZmukpGw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",

View File

@@ -16,19 +16,21 @@
"@mui/icons-material": "^7.3.6",
"@mui/material": "^7.3.6",
"@mui/styled-engine-sc": "^7.3.6",
"@tailwindcss/vite": "^4.1.11",
"i18next": "^25.7.4",
"js-cookie": "^3.0.5",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-i18next": "^16.2.0",
"react-i18next": "^16.5.3",
"react-router-dom": "^7.11.0",
"styled-components": "^6.1.19",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"tailwindcss-animate": "^1.0.7",
"@tailwindcss/vite": "^4.1.11"
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/js-cookie": "^3.0.6",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",

View File

@@ -0,0 +1,434 @@
import {
Box,
Stack,
TextField,
FormControlLabel,
Checkbox,
Button,
Alert,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { useState } from "react";
const phonePattern = /^[+]?[- 0-9()]{7,}$/;
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
export const MainForm = () => {
const { t } = useTranslation();
const [invoice, setInvoice] = useState(false);
const [paymentMethod, setPaymentMethod] = useState<
"cash" | "paypal" | "transfer" | null
>(null);
const [formValues, setFormValues] = useState({
firstName: "",
lastName: "",
email: "",
phoneNumber: "",
tickets: "",
code: "",
companyName: "",
invoiceFirstName: "",
invoiceLastName: "",
street: "",
postalCode: "",
invoicePhoneNumber: "",
invoiceEmail: "",
});
const [errors, setErrors] = useState<Record<string, string>>({});
const [submitting, setSubmitting] = useState(false);
const [submitMessage, setSubmitMessage] = useState<string | null>(null);
const updateField =
(field: keyof typeof formValues) =>
(event: React.ChangeEvent<HTMLInputElement>) => {
setFormValues((prev) => ({ ...prev, [field]: event.target.value }));
};
const validate = () => {
const nextErrors: Record<string, string> = {};
if (!formValues.firstName.trim()) nextErrors.firstName = "Required";
if (!formValues.lastName.trim()) nextErrors.lastName = "Required";
if (!formValues.email.trim()) nextErrors.email = "Required";
else if (!emailPattern.test(formValues.email))
nextErrors.email = "Invalid email";
if (!formValues.phoneNumber.trim()) nextErrors.phoneNumber = "Required";
else if (!phonePattern.test(formValues.phoneNumber))
nextErrors.phoneNumber = "Invalid phone number";
const ticketsNumber = Number(formValues.tickets);
if (!formValues.tickets.trim()) nextErrors.tickets = "Required";
else if (!Number.isFinite(ticketsNumber) || ticketsNumber <= 0)
nextErrors.tickets = "Must be a positive number";
if (!paymentMethod) nextErrors.paymentMethod = "Select a payment method";
if (!formValues.code.trim()) nextErrors.code = "Required";
if (invoice) {
if (!formValues.companyName.trim()) nextErrors.companyName = "Required";
if (!formValues.invoiceFirstName.trim())
nextErrors.invoiceFirstName = "Required";
if (!formValues.invoiceLastName.trim())
nextErrors.invoiceLastName = "Required";
if (!formValues.street.trim()) nextErrors.street = "Required";
if (!formValues.postalCode.trim()) nextErrors.postalCode = "Required";
if (!formValues.invoicePhoneNumber.trim())
nextErrors.invoicePhoneNumber = "Required";
else if (!phonePattern.test(formValues.invoicePhoneNumber))
nextErrors.invoicePhoneNumber = "Invalid phone number";
if (!formValues.invoiceEmail.trim()) nextErrors.invoiceEmail = "Required";
else if (!emailPattern.test(formValues.invoiceEmail))
nextErrors.invoiceEmail = "Invalid email";
}
setErrors(nextErrors);
return Object.keys(nextErrors).length === 0;
};
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSubmitMessage(null);
const isValid = validate();
if (!isValid) return;
setSubmitting(true);
try {
const payload = {
invoice,
paymentMethod,
firstName: formValues.firstName.trim(),
lastName: formValues.lastName.trim(),
email: formValues.email.trim(),
phoneNumber: formValues.phoneNumber.trim(),
tickets: Number(formValues.tickets),
code: Number(formValues.code),
invoiceDetails: invoice
? {
companyName: formValues.companyName.trim(),
firstName: formValues.invoiceFirstName.trim(),
lastName: formValues.invoiceLastName.trim(),
street: formValues.street.trim(),
postalCode: formValues.postalCode.trim(),
phoneNumber: formValues.invoicePhoneNumber.trim(),
email: formValues.invoiceEmail.trim(),
}
: null,
};
const response = await fetch("http://localhost:8004/default/frontend", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
setSubmitMessage("Submitted successfully.");
} catch (error) {
setSubmitMessage("Submit failed. Please try again.");
} finally {
setSubmitting(false);
}
};
return (
<Box className="min-h-screen bg-neutral-900 flex justify-center items-start py-10 px-4">
<Box
component="form"
action=""
method="post"
onSubmit={handleSubmit}
className="w-full max-w-md bg-white shadow-sm rounded-md"
sx={{ display: "flex", flexDirection: "column", gap: 2, p: 3 }}
>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
gap: 2,
}}
>
<TextField
required
id="first-name"
label={t("first_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.firstName}
onChange={updateField("firstName")}
error={Boolean(errors.firstName)}
helperText={errors.firstName}
/>
<TextField
required
id="last-name"
label={t("last_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.lastName}
onChange={updateField("lastName")}
error={Boolean(errors.lastName)}
helperText={errors.lastName}
/>
</Box>
<TextField
required
id="email"
label={t("email")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="email"
value={formValues.email}
onChange={updateField("email")}
error={Boolean(errors.email)}
helperText={errors.email}
/>
<TextField
required
id="phone-number"
label={t("phone-number")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="tel"
value={formValues.phoneNumber}
onChange={updateField("phoneNumber")}
error={Boolean(errors.phoneNumber)}
helperText={errors.phoneNumber}
/>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "2fr 1fr" },
gap: 2,
alignItems: "center",
}}
>
<TextField
required
id="tickets"
label={t("tickets")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="number"
value={formValues.tickets}
onChange={updateField("tickets")}
error={Boolean(errors.tickets)}
helperText={errors.tickets}
/>
<FormControlLabel
control={
<Checkbox
checked={invoice}
onChange={(event) => setInvoice(event.target.checked)}
/>
}
label={t("invoice")}
/>
</Box>
{invoice && (
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<TextField
required
id="company-name"
label={t("company_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.companyName}
onChange={updateField("companyName")}
error={Boolean(errors.companyName)}
helperText={errors.companyName}
/>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
gap: 2,
}}
>
<TextField
required
id="first-name_invoice"
label={t("first_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.invoiceFirstName}
onChange={updateField("invoiceFirstName")}
error={Boolean(errors.invoiceFirstName)}
helperText={errors.invoiceFirstName}
/>
<TextField
required
id="last-name_invoice"
label={t("last_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.invoiceLastName}
onChange={updateField("invoiceLastName")}
error={Boolean(errors.invoiceLastName)}
helperText={errors.invoiceLastName}
/>
</Box>
<TextField
required
id="street"
label={t("street")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.street}
onChange={updateField("street")}
error={Boolean(errors.street)}
helperText={errors.street}
/>
<TextField
required
id="postal-code"
label={t("postal_code")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.postalCode}
onChange={updateField("postalCode")}
error={Boolean(errors.postalCode)}
helperText={errors.postalCode}
/>
<TextField
required
id="phone-number_invoice"
label={t("phone_number")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="tel"
value={formValues.invoicePhoneNumber}
onChange={updateField("invoicePhoneNumber")}
error={Boolean(errors.invoicePhoneNumber)}
helperText={errors.invoicePhoneNumber}
/>
<TextField
required
id="email_invoice"
label={t("email")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="email"
value={formValues.invoiceEmail}
onChange={updateField("invoiceEmail")}
error={Boolean(errors.invoiceEmail)}
helperText={errors.invoiceEmail}
/>
</Box>
)}
<Stack direction="row" spacing={2} flexWrap="wrap" pl={1}>
{/* Payment methods - only one must be selected */}
<FormControlLabel
control={
<Checkbox
checked={paymentMethod === "cash"}
onChange={() =>
setPaymentMethod((current) =>
current === "cash" ? null : "cash"
)
}
/>
}
label={t("cash")}
sx={{ color: errors.paymentMethod ? "error.main" : undefined }}
/>
<FormControlLabel
control={
<Checkbox
checked={paymentMethod === "paypal"}
onChange={() =>
setPaymentMethod((current) =>
current === "paypal" ? null : "paypal"
)
}
/>
}
label={t("paypal")}
sx={{ color: errors.paymentMethod ? "error.main" : undefined }}
/>
<FormControlLabel
control={
<Checkbox
checked={paymentMethod === "transfer"}
onChange={() =>
setPaymentMethod((current) =>
current === "transfer" ? null : "transfer"
)
}
/>
}
label={t("transfer")}
sx={{ color: errors.paymentMethod ? "error.main" : undefined }}
/>
</Stack>
<TextField
required
id="code"
label={t("code")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.code}
onChange={updateField("code")}
error={Boolean(errors.code)}
helperText={errors.code}
/>
<Button
type="submit"
variant="contained"
size="large"
sx={{ mt: 1 }}
fullWidth
disabled={submitting}
>
{t("submit")}
</Button>
{submitMessage && (
<Alert
severity={submitMessage.includes("failed") ? "error" : "success"}
>
{submitMessage}
</Alert>
)}
</Box>
</Box>
);
};

View File

@@ -1,6 +1,4 @@
import {
Box,
Stack,
TextField,
FormControlLabel,
Checkbox,
@@ -10,425 +8,123 @@ import {
import { useTranslation } from "react-i18next";
import { useState } from "react";
const phonePattern = /^[+]?[- 0-9()]{7,}$/;
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
interface Message {
type: "error" | "info" | "success" | "warning";
headline: string;
text: string;
}
export const MainForm = () => {
const { t } = useTranslation();
const [invoice, setInvoice] = useState(false);
const [paymentMethod, setPaymentMethod] = useState<
"cash" | "paypal" | "transfer" | null
>(null);
const [formValues, setFormValues] = useState({
firstName: "",
lastName: "",
email: "",
phoneNumber: "",
tickets: "",
code: "",
companyName: "",
invoiceFirstName: "",
invoiceLastName: "",
street: "",
postalCode: "",
invoicePhoneNumber: "",
invoiceEmail: "",
});
const [errors, setErrors] = useState<Record<string, string>>({});
const [submitting, setSubmitting] = useState(false);
const [submitMessage, setSubmitMessage] = useState<string | null>(null);
const [msg, setMsg] = useState<Message | null>(null);
const updateField =
(field: keyof typeof formValues) =>
(event: React.ChangeEvent<HTMLInputElement>) => {
setFormValues((prev) => ({ ...prev, [field]: event.target.value }));
};
const validate = () => {
const nextErrors: Record<string, string> = {};
if (!formValues.firstName.trim()) nextErrors.firstName = "Required";
if (!formValues.lastName.trim()) nextErrors.lastName = "Required";
if (!formValues.email.trim()) nextErrors.email = "Required";
else if (!emailPattern.test(formValues.email))
nextErrors.email = "Invalid email";
if (!formValues.phoneNumber.trim()) nextErrors.phoneNumber = "Required";
else if (!phonePattern.test(formValues.phoneNumber))
nextErrors.phoneNumber = "Invalid phone number";
const ticketsNumber = Number(formValues.tickets);
if (!formValues.tickets.trim()) nextErrors.tickets = "Required";
else if (!Number.isFinite(ticketsNumber) || ticketsNumber <= 0)
nextErrors.tickets = "Must be a positive number";
if (!paymentMethod) nextErrors.paymentMethod = "Select a payment method";
if (!formValues.code.trim()) nextErrors.code = "Required";
if (invoice) {
if (!formValues.companyName.trim()) nextErrors.companyName = "Required";
if (!formValues.invoiceFirstName.trim())
nextErrors.invoiceFirstName = "Required";
if (!formValues.invoiceLastName.trim())
nextErrors.invoiceLastName = "Required";
if (!formValues.street.trim()) nextErrors.street = "Required";
if (!formValues.postalCode.trim()) nextErrors.postalCode = "Required";
if (!formValues.invoicePhoneNumber.trim())
nextErrors.invoicePhoneNumber = "Required";
else if (!phonePattern.test(formValues.invoicePhoneNumber))
nextErrors.invoicePhoneNumber = "Invalid phone number";
if (!formValues.invoiceEmail.trim()) nextErrors.invoiceEmail = "Required";
else if (!emailPattern.test(formValues.invoiceEmail))
nextErrors.invoiceEmail = "Invalid email";
}
setErrors(nextErrors);
return Object.keys(nextErrors).length === 0;
};
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSubmitMessage(null);
const isValid = validate();
if (!isValid) return;
setSubmitting(true);
try {
const payload = {
invoice,
paymentMethod,
firstName: formValues.firstName.trim(),
lastName: formValues.lastName.trim(),
email: formValues.email.trim(),
phoneNumber: formValues.phoneNumber.trim(),
tickets: Number(formValues.tickets),
code: Number(formValues.code),
invoiceDetails: invoice
? {
companyName: formValues.companyName.trim(),
firstName: formValues.invoiceFirstName.trim(),
lastName: formValues.invoiceLastName.trim(),
street: formValues.street.trim(),
postalCode: formValues.postalCode.trim(),
phoneNumber: formValues.invoicePhoneNumber.trim(),
email: formValues.invoiceEmail.trim(),
}
: null,
};
const response = await fetch("http://localhost:8004/default/frontend", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
setSubmitMessage("Submitted successfully.");
} catch (error) {
setSubmitMessage("Submit failed. Please try again.");
} finally {
setSubmitting(false);
}
const handleSubmit = () => {
// Form submission logic goes here
};
return (
<Box className="min-h-screen bg-neutral-900 flex justify-center items-start py-10 px-4">
<Box
component="form"
action=""
method="post"
onSubmit={handleSubmit}
className="w-full max-w-md bg-white shadow-sm rounded-md"
sx={{ display: "flex", flexDirection: "column", gap: 2, p: 3 }}
>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
gap: 2,
}}
>
<>
<form action="" method="post">
<TextField
required
id="first-name"
label={t("first_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.firstName}
onChange={updateField("firstName")}
error={Boolean(errors.firstName)}
helperText={errors.firstName}
/>
<TextField
required
id="last-name"
label={t("last_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.lastName}
onChange={updateField("lastName")}
error={Boolean(errors.lastName)}
helperText={errors.lastName}
/>
</Box>
<TextField
required
id="email"
label={t("email")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="email"
value={formValues.email}
onChange={updateField("email")}
error={Boolean(errors.email)}
helperText={errors.email}
/>
<TextField required id="email" label={t("email")} variant="filled" />
<TextField
required
id="phone-number"
label={t("phone_number")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="tel"
value={formValues.phoneNumber}
onChange={updateField("phoneNumber")}
error={Boolean(errors.phoneNumber)}
helperText={errors.phoneNumber}
/>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "2fr 1fr" },
gap: 2,
alignItems: "center",
}}
>
<TextField
required
id="tickets"
label={t("tickets")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="number"
value={formValues.tickets}
onChange={updateField("tickets")}
error={Boolean(errors.tickets)}
helperText={errors.tickets}
/>
<FormControlLabel
control={
<Checkbox
checked={invoice}
onChange={(event) => setInvoice(event.target.checked)}
onChange={(e) => setInvoice(e.target.checked)}
/>
}
label={t("invoice")}
/>
</Box>
{invoice && (
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<>
<TextField
required
id="company-name"
label={t("company_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.companyName}
onChange={updateField("companyName")}
error={Boolean(errors.companyName)}
helperText={errors.companyName}
/>
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
gap: 2,
}}
>
<TextField
required
id="first-name_invoice"
label={t("first_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.invoiceFirstName}
onChange={updateField("invoiceFirstName")}
error={Boolean(errors.invoiceFirstName)}
helperText={errors.invoiceFirstName}
/>
<TextField
required
id="last-name_invoice"
label={t("last_name")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.invoiceLastName}
onChange={updateField("invoiceLastName")}
error={Boolean(errors.invoiceLastName)}
helperText={errors.invoiceLastName}
/>
</Box>
<TextField
required
id="street"
label={t("street")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.street}
onChange={updateField("street")}
error={Boolean(errors.street)}
helperText={errors.street}
/>
<TextField
required
id="postal-code"
label={t("postal_code")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.postalCode}
onChange={updateField("postalCode")}
error={Boolean(errors.postalCode)}
helperText={errors.postalCode}
/>
<TextField
required
id="phone-number_invoice"
label={t("phone_number")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="tel"
value={formValues.invoicePhoneNumber}
onChange={updateField("invoicePhoneNumber")}
error={Boolean(errors.invoicePhoneNumber)}
helperText={errors.invoicePhoneNumber}
/>
<TextField
required
id="email_invoice"
label={t("email")}
variant="filled"
size="small"
fullWidth
margin="dense"
type="email"
value={formValues.invoiceEmail}
onChange={updateField("invoiceEmail")}
error={Boolean(errors.invoiceEmail)}
helperText={errors.invoiceEmail}
/>
</Box>
</>
)}
<Stack direction="row" spacing={2} flexWrap="wrap" pl={1}>
{/* Payment methods - only one must be selected */}
<FormControlLabel
control={
<Checkbox
checked={paymentMethod === "cash"}
onChange={() =>
setPaymentMethod((current) =>
current === "cash" ? null : "cash"
)
}
/>
}
label={t("cash")}
sx={{ color: errors.paymentMethod ? "error.main" : undefined }}
/>
<FormControlLabel
control={
<Checkbox
checked={paymentMethod === "paypal"}
onChange={() =>
setPaymentMethod((current) =>
current === "paypal" ? null : "paypal"
)
}
/>
}
label={t("paypal")}
sx={{ color: errors.paymentMethod ? "error.main" : undefined }}
/>
<FormControlLabel
control={
<Checkbox
checked={paymentMethod === "transfer"}
onChange={() =>
setPaymentMethod((current) =>
current === "transfer" ? null : "transfer"
)
}
/>
}
label={t("transfer")}
sx={{ color: errors.paymentMethod ? "error.main" : undefined }}
/>
</Stack>
<TextField
required
id="code"
label={t("code")}
variant="filled"
size="small"
fullWidth
margin="dense"
value={formValues.code}
onChange={updateField("code")}
error={Boolean(errors.code)}
helperText={errors.code}
/>
<FormControlLabel control={<Checkbox />} label={t("cash")} />
<FormControlLabel control={<Checkbox />} label={t("paypal")} />
<FormControlLabel control={<Checkbox />} label={t("transfer")} />
<TextField required id="code" label={t("code")} variant="filled" />
<Button
type="submit"
onClick={() => {
handleSubmit();
}}
variant="contained"
size="large"
sx={{ mt: 1 }}
fullWidth
disabled={submitting}
>
{t("submit")}
</Button>
{submitMessage && (
<Alert
severity={submitMessage.includes("failed") ? "error" : "success"}
>
{submitMessage}
{msg && (
<Alert severity={msg.type}>
{msg.headline}: {msg.text}
</Alert>
)}
</Box>
</Box>
</form>
</>
);
};

View File

@@ -0,0 +1,3 @@
{
"phone-number": "Telefonnummer"
}

View File

@@ -0,0 +1,3 @@
{
"phone_number": "Telefonnummer"
}