Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8e8062990 | |||
| 470fe75d86 | |||
| d49e62a280 | |||
| a66b150d97 | |||
| f7a0a3753c |
@@ -158,6 +158,7 @@ keys/
|
|||||||
# Own files
|
# Own files
|
||||||
ToDo.txt
|
ToDo.txt
|
||||||
PayPal-QR-Code.png
|
PayPal-QR-Code.png
|
||||||
|
.docker/volumes
|
||||||
|
|
||||||
# only in development branch
|
# only in development branch
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
@@ -11,14 +11,16 @@ const pool = mysql
|
|||||||
})
|
})
|
||||||
.promise();
|
.promise();
|
||||||
|
|
||||||
export const getUser = async () => {
|
export const getInfo = async () => {
|
||||||
const [rows] = await pool.query("SELECT username FROM users");
|
const [rows] = await pool.query("SELECT username FROM users;");
|
||||||
|
const [rows2] = await pool.query("SELECT name FROM prize_draws;");
|
||||||
|
|
||||||
if (rows.length > 0) {
|
if (rows.length > 0 && rows2.length > 0) {
|
||||||
const users = rows.map((r) => r.username);
|
const users = rows.map((r) => r.username);
|
||||||
return { users };
|
const prize_draws = rows2.map((n) => n.name);
|
||||||
|
return { users, prize_draws };
|
||||||
} else {
|
} else {
|
||||||
return { users: [], message: "No data found" };
|
return { message: "No data found" };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,11 +45,13 @@ export const confirmUser = async (username) => {
|
|||||||
const [createTable] = await pool.query(
|
const [createTable] = await pool.query(
|
||||||
`CREATE TABLE IF NOT EXISTS ?? (
|
`CREATE TABLE IF NOT EXISTS ?? (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
Verlosung VARCHAR(100) NOT NULL,
|
||||||
Vorname VARCHAR(100) NOT NULL,
|
Vorname VARCHAR(100) NOT NULL,
|
||||||
Nachname Varchar(100) NOT NULL,
|
Nachname Varchar(100) NOT NULL,
|
||||||
EMail Varchar(100) NOT NULL,
|
EMail Varchar(100) NOT NULL,
|
||||||
Telefonnummer Varchar(100) NOT NULL,
|
Telefonnummer Varchar(100) NOT NULL,
|
||||||
Lose INT NOT NULL,
|
Lose INT NOT NULL,
|
||||||
|
Schokolade BOOLEAN NOT NULL,
|
||||||
Firmenname Varchar(100),
|
Firmenname Varchar(100),
|
||||||
Vorname_Geschaeftlich Varchar(100),
|
Vorname_Geschaeftlich Varchar(100),
|
||||||
Nachname_Geschaeftlich Varchar(100),
|
Nachname_Geschaeftlich Varchar(100),
|
||||||
@@ -80,7 +84,7 @@ export const confirmUser = async (username) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const newEntry = async (formData, username) => {
|
export const newEntry = async (formData, username, prizeDraw) => {
|
||||||
const confirmation = await confirmUser(username);
|
const confirmation = await confirmUser(username);
|
||||||
|
|
||||||
if (!confirmation || !confirmation.success) {
|
if (!confirmation || !confirmation.success) {
|
||||||
@@ -90,14 +94,16 @@ export const newEntry = async (formData, username) => {
|
|||||||
const tableName = confirmation.tableName;
|
const tableName = confirmation.tableName;
|
||||||
|
|
||||||
const [result] = await pool.query(
|
const [result] = await pool.query(
|
||||||
`INSERT INTO ?? (Vorname, Nachname, EMail, Telefonnummer, Lose, Firmenname, Vorname_Geschaeftlich, Nachname_Geschaeftlich, EMail_Geschaeftlich, Telefonnummer_Geschaeftlich, Strasse_Hausnr, Plz_Ort, Zahlungsmethode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
`INSERT INTO ?? (Verlosung, Vorname, Nachname, EMail, Telefonnummer, Lose, Schokolade, Firmenname, Vorname_Geschaeftlich, Nachname_Geschaeftlich, EMail_Geschaeftlich, Telefonnummer_Geschaeftlich, Strasse_Hausnr, Plz_Ort, Zahlungsmethode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
[
|
[
|
||||||
tableName,
|
tableName,
|
||||||
|
prizeDraw,
|
||||||
formData.firstName,
|
formData.firstName,
|
||||||
formData.lastName,
|
formData.lastName,
|
||||||
formData.email,
|
formData.email,
|
||||||
formData.phoneNumber,
|
formData.phoneNumber,
|
||||||
formData.tickets,
|
formData.tickets,
|
||||||
|
formData.chocolates,
|
||||||
formData.companyName,
|
formData.companyName,
|
||||||
formData.cmpFirstName,
|
formData.cmpFirstName,
|
||||||
formData.cpmLastName,
|
formData.cpmLastName,
|
||||||
|
|||||||
@@ -3,24 +3,33 @@ import dotenv from "dotenv";
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
import { getUser, newEntry, confirmUser } from "./frontend.data.js";
|
import { getInfo, newEntry, confirmUser } from "./frontend.data.js";
|
||||||
|
|
||||||
router.post("/new-entry", async (req, res) => {
|
router.post("/new-entry", async (req, res) => {
|
||||||
const username = req.query.username;
|
const username = req.query.username;
|
||||||
const result = await newEntry(req.body, username);
|
const draw = req.query.draw;
|
||||||
|
|
||||||
|
const result = await newEntry(req.body, username, draw);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return res.status(500).json({ message: "Form Data Invalid" });
|
return res.status(500).json({ message: "Form Data Invalid" });
|
||||||
}
|
}
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/users", async (req, res) => {
|
router.get("/info", async (req, res) => {
|
||||||
const users = await getUser();
|
const info = await getInfo();
|
||||||
res.json(users);
|
|
||||||
|
if (!info) {
|
||||||
|
return res.status(500).json({ message: "Server error" });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/confirm-user", async (req, res) => {
|
router.get("/confirm-user", async (req, res) => {
|
||||||
const username = req.query.username;
|
const username = req.query.username;
|
||||||
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
return res.status(400).json({ message: "Username is required" });
|
return res.status(400).json({ message: "Username is required" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
USE ca_lose;
|
||||||
|
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
username VARCHAR(100) NOT NULL UNIQUE,
|
username VARCHAR(100) NOT NULL UNIQUE,
|
||||||
@@ -6,9 +8,16 @@ CREATE TABLE users (
|
|||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE prize_draws (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
/* This scheme does not have to be implemented manually. It always will be generated by the backend */
|
/* This scheme does not have to be implemented manually. It always will be generated by the backend */
|
||||||
CREATE TABLE xx_DD_MM_YYYY (
|
CREATE TABLE xx_DD_MM_YYYY (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
Verlosung VARCHAR(100) NOT NULL,
|
||||||
Vorname VARCHAR(100) NOT NULL,
|
Vorname VARCHAR(100) NOT NULL,
|
||||||
Nachname Varchar(100) NOT NULL,
|
Nachname Varchar(100) NOT NULL,
|
||||||
EMail Varchar(100) NOT NULL,
|
EMail Varchar(100) NOT NULL,
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ services:
|
|||||||
MYSQL_DATABASE: ca_lose
|
MYSQL_DATABASE: ca_lose
|
||||||
TZ: Europe/Berlin
|
TZ: Europe/Berlin
|
||||||
volumes:
|
volumes:
|
||||||
- ca-lose_mysql:/var/lib/mysql
|
- ./.docker/volumes/ca-lose_mysql:/var/lib/mysql
|
||||||
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
|
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import { useTranslation } from "react-i18next";
|
|||||||
interface SelectUserModalProps {
|
interface SelectUserModalProps {
|
||||||
showSelectUser: boolean;
|
showSelectUser: boolean;
|
||||||
setShowSelectUser: (value: boolean) => void;
|
setShowSelectUser: (value: boolean) => void;
|
||||||
usernameData: { users: string[] };
|
info: { users: string[]; prize_draws: string[] };
|
||||||
usernameDataIsLoading: boolean;
|
infoIsLoading: boolean;
|
||||||
selectedUser: string | null;
|
selectedUser: string | null;
|
||||||
|
selectedDraw: string | null;
|
||||||
handleUserSelection: (value: string | null) => void;
|
handleUserSelection: (value: string | null) => void;
|
||||||
|
handleDrawSelection: (value: string | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectUserModal = (props: SelectUserModalProps) => {
|
export const SelectUserModal = (props: SelectUserModalProps) => {
|
||||||
@@ -25,8 +27,8 @@ export const SelectUserModal = (props: SelectUserModalProps) => {
|
|||||||
<Typography>{t("user")}</Typography>
|
<Typography>{t("user")}</Typography>
|
||||||
{/* User selection */}
|
{/* User selection */}
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
options={props.usernameData?.users ?? []}
|
options={props.info?.users ?? []}
|
||||||
loading={props.usernameDataIsLoading}
|
loading={props.infoIsLoading}
|
||||||
loadingText={t("loading")}
|
loadingText={t("loading")}
|
||||||
value={props.selectedUser}
|
value={props.selectedUser}
|
||||||
onChange={(_, value) => props.handleUserSelection(value)}
|
onChange={(_, value) => props.handleUserSelection(value)}
|
||||||
@@ -34,6 +36,17 @@ export const SelectUserModal = (props: SelectUserModalProps) => {
|
|||||||
variant="soft"
|
variant="soft"
|
||||||
sx={{ borderRadius: "10px" }}
|
sx={{ borderRadius: "10px" }}
|
||||||
/>
|
/>
|
||||||
|
{/* Prize selection */}
|
||||||
|
<Autocomplete
|
||||||
|
options={props.info?.prize_draws ?? []}
|
||||||
|
loading={props.infoIsLoading}
|
||||||
|
loadingText={t("loading")}
|
||||||
|
value={props.selectedDraw}
|
||||||
|
onChange={(_, value) => props.handleDrawSelection(value)}
|
||||||
|
placeholder={t("prize_draws")}
|
||||||
|
variant="soft"
|
||||||
|
sx={{ borderRadius: "10px" }}
|
||||||
|
/>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export const createFormSchema = (
|
|||||||
email: z.email(t("email-error")),
|
email: z.email(t("email-error")),
|
||||||
phoneNumber: z.string(t("phone-error")).refine(validator.isMobilePhone),
|
phoneNumber: z.string(t("phone-error")).refine(validator.isMobilePhone),
|
||||||
tickets: z.number(t("ticket-error")).min(1),
|
tickets: z.number(t("ticket-error")).min(1),
|
||||||
|
chocolates: z.boolean(t("chocolates-error")),
|
||||||
companyName: invoice
|
companyName: invoice
|
||||||
? z.string().min(1, t("name-error"))
|
? z.string().min(1, t("name-error"))
|
||||||
: z.string().optional(),
|
: z.string().optional(),
|
||||||
|
|||||||
@@ -21,51 +21,31 @@ import PersonIcon from "@mui/icons-material/Person";
|
|||||||
import QrCodeIcon from "@mui/icons-material/QrCode";
|
import QrCodeIcon from "@mui/icons-material/QrCode";
|
||||||
import TranslateIcon from "@mui/icons-material/Translate";
|
import TranslateIcon from "@mui/icons-material/Translate";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { confirmUser, fetchUsers } from "../utils/api/users";
|
import { confirmUser, fetchInfo } from "../utils/api/users";
|
||||||
import { QRcodeModal } from "../components/modals/QR-CodeModal";
|
import { QRcodeModal } from "../components/modals/QR-CodeModal";
|
||||||
import { SelectUserModal } from "../components/modals/SelectUserModal";
|
import { SelectUserModal } from "../components/modals/SelectUserModal";
|
||||||
import { useForm } from "@tanstack/react-form";
|
import { useForm } from "@tanstack/react-form";
|
||||||
import { changeTranslation } from "../utils/uxFncs";
|
import { changeTranslation } from "../utils/uxFncs";
|
||||||
import { createFormSchema } from "../config/interfaces.config";
|
import { createFormSchema } from "../config/interfaces.config";
|
||||||
import type { ZodObject, ZodRawShape } from "zod";
|
import { validateFieldWithZod } from "../utils/uxFncs";
|
||||||
import { TextField } from "../components/TextField";
|
import { TextField } from "../components/TextField";
|
||||||
|
import { PAYMENT_METHODS, PAYMENT_LABELS } from "../utils/uxFncs";
|
||||||
const PAYMENT_METHODS = ["bar", "paypal", "andere"] as const;
|
|
||||||
const PAYMENT_LABELS: Record<string, string> = {
|
|
||||||
bar: "Cash",
|
|
||||||
paypal: "PayPal",
|
|
||||||
andere: "Transfer",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a single field against the full Zod schema.
|
|
||||||
* Returns the first error message for that field, or undefined if valid.
|
|
||||||
*/
|
|
||||||
function validateFieldWithZod(
|
|
||||||
schema: ZodObject<ZodRawShape>,
|
|
||||||
fieldName: string,
|
|
||||||
allValues: Record<string, unknown>,
|
|
||||||
): string | undefined {
|
|
||||||
const result = schema.safeParse(allValues);
|
|
||||||
if (result.success) return undefined;
|
|
||||||
const issue = result.error.issues.find(
|
|
||||||
(i) => i.path.length === 1 && i.path[0] === fieldName,
|
|
||||||
);
|
|
||||||
return issue?.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MainForm = () => {
|
export const MainForm = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
// States
|
||||||
const [invoice, setInvoice] = useState(false);
|
const [invoice, setInvoice] = useState(false);
|
||||||
const [msg, setMsg] = useState<Message | null>(null);
|
const [msg, setMsg] = useState<Message | null>(null);
|
||||||
const [selectedUser, setSelectedUser] = useState<string | null>(null);
|
const [selectedUser, setSelectedUser] = useState<string | null>(null);
|
||||||
|
const [selectedDraw, setSelectedDraw] = useState<string | null>(null);
|
||||||
const [showSelectUser, setShowSelectUser] = useState(false);
|
const [showSelectUser, setShowSelectUser] = useState(false);
|
||||||
const [QRmodal, setQRmodal] = useState(false);
|
const [QRmodal, setQRmodal] = useState(false);
|
||||||
|
|
||||||
const formSchema = createFormSchema(t, invoice);
|
const formSchema = createFormSchema(t, invoice);
|
||||||
|
|
||||||
|
// Validates the fields and makes sure that the form is maching the zod schema
|
||||||
const makeFieldValidator = (fieldName: string) => ({
|
const makeFieldValidator = (fieldName: string) => ({
|
||||||
onSubmit: ({
|
onSubmit: ({
|
||||||
fieldApi,
|
fieldApi,
|
||||||
@@ -85,13 +65,15 @@ export const MainForm = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Creates a form with tanstack form
|
||||||
const { Field, Subscribe, handleSubmit, setFieldValue } = useForm({
|
const { Field, Subscribe, handleSubmit, setFieldValue } = useForm({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
firstName: "",
|
firstName: "",
|
||||||
lastName: "",
|
lastName: "",
|
||||||
email: "",
|
email: "",
|
||||||
phoneNumber: "",
|
phoneNumber: "",
|
||||||
tickets: 1,
|
tickets: 0,
|
||||||
|
chocolates: false,
|
||||||
companyName: "",
|
companyName: "",
|
||||||
cmpFirstName: "",
|
cmpFirstName: "",
|
||||||
cpmLastName: "",
|
cpmLastName: "",
|
||||||
@@ -108,6 +90,7 @@ export const MainForm = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This function returns the errors for one field
|
||||||
const getErrors = (field: {
|
const getErrors = (field: {
|
||||||
state: { meta: { errorMap: Record<string, unknown> } };
|
state: { meta: { errorMap: Record<string, unknown> } };
|
||||||
}) => {
|
}) => {
|
||||||
@@ -122,8 +105,14 @@ export const MainForm = () => {
|
|||||||
return [...blurErrors, ...submitErrors].filter(Boolean);
|
return [...blurErrors, ...submitErrors].filter(Boolean);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// useEffect that only executes on mount and is checking if there is a user selcted in the cookies
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedUser = Cookies.get("selectedUser");
|
const savedUser = Cookies.get("selectedUser");
|
||||||
|
const savedDraw = Cookies.get("selectedDraw");
|
||||||
|
if (savedDraw) {
|
||||||
|
setSelectedDraw(savedDraw);
|
||||||
|
}
|
||||||
|
|
||||||
if (savedUser) {
|
if (savedUser) {
|
||||||
setSelectedUser(savedUser);
|
setSelectedUser(savedUser);
|
||||||
} else {
|
} else {
|
||||||
@@ -135,17 +124,20 @@ export const MainForm = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { data: usernameData, isLoading: usernameDataIsLoading } = useQuery({
|
// Fetch users tanstack query
|
||||||
queryKey: ["users"],
|
const { data: info, isLoading: infoIsLoading } = useQuery({
|
||||||
queryFn: fetchUsers,
|
queryKey: ["info"],
|
||||||
|
queryFn: fetchInfo,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Query for validating selcted user
|
||||||
const { data: userData } = useQuery({
|
const { data: userData } = useQuery({
|
||||||
queryKey: ["user", selectedUser],
|
queryKey: ["user", selectedUser],
|
||||||
enabled: !!selectedUser,
|
enabled: !!selectedUser,
|
||||||
queryFn: () => confirmUser(selectedUser),
|
queryFn: () => confirmUser(selectedUser),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tanstack query (mutate) for sending the form
|
||||||
const { mutate: mutateForm, isPending: mutateFormIsPending } = useMutation({
|
const { mutate: mutateForm, isPending: mutateFormIsPending } = useMutation({
|
||||||
mutationFn: (values: FormData) => submitFormData(values, selectedUser),
|
mutationFn: (values: FormData) => submitFormData(values, selectedUser),
|
||||||
onSuccess: (_, values) => {
|
onSuccess: (_, values) => {
|
||||||
@@ -164,6 +156,7 @@ export const MainForm = () => {
|
|||||||
|
|
||||||
const nextID = userData?.nextID ?? "N/A";
|
const nextID = userData?.nextID ?? "N/A";
|
||||||
|
|
||||||
|
// function for selecting user
|
||||||
const handleUserSelection = (username: string | null) => {
|
const handleUserSelection = (username: string | null) => {
|
||||||
if (username == null || username == "") {
|
if (username == null || username == "") {
|
||||||
return;
|
return;
|
||||||
@@ -171,15 +164,26 @@ export const MainForm = () => {
|
|||||||
setSelectedUser(username);
|
setSelectedUser(username);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// function for selecting draw
|
||||||
|
const handleDrawSelection = (draw: string | null) => {
|
||||||
|
if (draw == null || draw == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelectedDraw(draw);
|
||||||
|
Cookies.set("selectedDraw", draw);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectUserModal
|
<SelectUserModal
|
||||||
showSelectUser={showSelectUser}
|
showSelectUser={showSelectUser}
|
||||||
setShowSelectUser={setShowSelectUser}
|
setShowSelectUser={setShowSelectUser}
|
||||||
usernameData={usernameData}
|
info={info}
|
||||||
usernameDataIsLoading={usernameDataIsLoading}
|
infoIsLoading={infoIsLoading}
|
||||||
selectedUser={selectedUser}
|
selectedUser={selectedUser}
|
||||||
|
selectedDraw={selectedDraw}
|
||||||
handleUserSelection={handleUserSelection}
|
handleUserSelection={handleUserSelection}
|
||||||
|
handleDrawSelection={handleDrawSelection}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<QRcodeModal setQRmodal={setQRmodal} QRmodal={QRmodal} />
|
<QRcodeModal setQRmodal={setQRmodal} QRmodal={QRmodal} />
|
||||||
@@ -313,7 +317,7 @@ export const MainForm = () => {
|
|||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
{/* Tickets + Invoice toggle */}
|
{/* Tickets + Invoice + Chocolate toggle */}
|
||||||
<div className="grid grid-cols-2 gap-3 items-end">
|
<div className="grid grid-cols-2 gap-3 items-end">
|
||||||
<FormControl required>
|
<FormControl required>
|
||||||
<FormLabel>{t("tickets")}</FormLabel>
|
<FormLabel>{t("tickets")}</FormLabel>
|
||||||
@@ -327,12 +331,14 @@ export const MainForm = () => {
|
|||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={field.state.value ?? ""}
|
value={
|
||||||
onBlur={field.handleBlur}
|
field.state.value === 0 ? "" : field.state.value
|
||||||
onChange={(e) =>
|
|
||||||
field.handleChange(Number(e.target.value))
|
|
||||||
}
|
}
|
||||||
slotProps={{ input: { min: 1 } }}
|
onBlur={field.handleBlur}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
field.handleChange(val === "" ? 0 : Number(val));
|
||||||
|
}}
|
||||||
variant="soft"
|
variant="soft"
|
||||||
sx={{ borderRadius: "10px" }}
|
sx={{ borderRadius: "10px" }}
|
||||||
/>
|
/>
|
||||||
@@ -346,7 +352,7 @@ export const MainForm = () => {
|
|||||||
}}
|
}}
|
||||||
</Field>
|
</Field>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<div className="flex items-center pb-2">
|
<div className="flex flex-col gap-2 pb-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={invoice}
|
checked={invoice}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -365,6 +371,16 @@ export const MainForm = () => {
|
|||||||
label={t("invoice")}
|
label={t("invoice")}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
|
<Field name="chocolates">
|
||||||
|
{(field) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={!!field.state.value}
|
||||||
|
onChange={(e) => field.handleChange(e.target.checked)}
|
||||||
|
label={t("chocolates")}
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { API_BASE } from "../../config/api.config";
|
import { API_BASE } from "../../config/api.config";
|
||||||
import type { FormData } from "../../config/interfaces.config";
|
import type { FormData } from "../../config/interfaces.config";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
export const submitFormData = async (
|
export const submitFormData = async (
|
||||||
data: FormData,
|
data: FormData,
|
||||||
@@ -7,10 +8,16 @@ export const submitFormData = async (
|
|||||||
) => {
|
) => {
|
||||||
console.warn("submitFormData is fetching!");
|
console.warn("submitFormData is fetching!");
|
||||||
|
|
||||||
|
const draw = Cookies.get("selectedDraw");
|
||||||
|
|
||||||
|
if (!draw) {
|
||||||
|
throw new Error("You need to set an prize draw!");
|
||||||
|
}
|
||||||
|
|
||||||
// await new Promise((resolve) => setTimeout(resolve, 3000)); // Wait 3 seconds
|
// await new Promise((resolve) => setTimeout(resolve, 3000)); // Wait 3 seconds
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${API_BASE}/default/new-entry?username=${username}`,
|
`${API_BASE}/default/new-entry?username=${username}&draw=${draw}`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { API_BASE } from "../../config/api.config";
|
import { API_BASE } from "../../config/api.config";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
export const fetchUsers = async () => {
|
export const fetchInfo = async () => {
|
||||||
console.warn("fetchUsers is fetching!");
|
console.warn("fetchInfo is fetching!");
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE}/default/users`);
|
const response = await fetch(`${API_BASE}/default/info`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
"user": "Benutzer",
|
"user": "Benutzer",
|
||||||
"next-id": "Nächste Eintragsnummer: ",
|
"next-id": "Nächste Eintragsnummer: ",
|
||||||
"form-submitted-successfully": "Formular erfolgreich übermittelt!",
|
"form-submitted-successfully": "Formular erfolgreich übermittelt!",
|
||||||
"form-submission-failed": "Formularübermittlung fehlgeschlagen.",
|
"form-submission-failed": "Formularübermittlung fehlgeschlagen. Haben Sie eine Verlosung ausgewählt?",
|
||||||
"success": "Erfolg",
|
"success": "Erfolg",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"cash": "Bar",
|
"cash": "Bar",
|
||||||
"paypal": "PayPal",
|
"paypal": "PayPal",
|
||||||
|
"prize_draws": "Verlosungen",
|
||||||
"transfer": "Andere (notieren)",
|
"transfer": "Andere (notieren)",
|
||||||
"ticket-payment_one": "Sie haben erfolgreich {{count}} Los gekauft.",
|
"ticket-payment_one": "Sie haben erfolgreich {{count}} Los gekauft.",
|
||||||
"ticket-payment_other": "Sie haben erfolgreich {{count}} Lose gekauft.",
|
"ticket-payment_other": "Sie haben erfolgreich {{count}} Lose gekauft.",
|
||||||
@@ -34,5 +35,7 @@
|
|||||||
"name-error": "Sie müssen einen Namen eingeben!",
|
"name-error": "Sie müssen einen Namen eingeben!",
|
||||||
"email-error": "Sie müssen eine gültige E-Mail Adresse eingeben!",
|
"email-error": "Sie müssen eine gültige E-Mail Adresse eingeben!",
|
||||||
"phone-error": "Sie müssen eine gültige Telefonnummer eingeben!",
|
"phone-error": "Sie müssen eine gültige Telefonnummer eingeben!",
|
||||||
"footer-headline": "Dieses System wurde vollständig konzipiert und entwickelt von Theis Gaedigk. - Portfolio: "
|
"footer-headline": "Dieses System wurde vollständig konzipiert und entwickelt von Theis Gaedigk. - Portfolio: ",
|
||||||
|
"chocolates": "Schokoladen",
|
||||||
|
"chocolates-error": "Dieses Feld kann nur wahr oder falsch sein."
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
"user": "User",
|
"user": "User",
|
||||||
"next-id": "Next Entry Number: ",
|
"next-id": "Next Entry Number: ",
|
||||||
"form-submitted-successfully": "Form submitted successfully!",
|
"form-submitted-successfully": "Form submitted successfully!",
|
||||||
"orm-submission-failed": "Form submission failed.",
|
"form-submission-failed": "Form submission failed. Have you set an prize draw?",
|
||||||
|
"prize_draws": "Prize draws",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"cash": "Cash",
|
"cash": "Cash",
|
||||||
@@ -35,5 +36,7 @@
|
|||||||
"name-error": "You have to enter a name!",
|
"name-error": "You have to enter a name!",
|
||||||
"email-error": "You have to enter a valid E-Mail adress!",
|
"email-error": "You have to enter a valid E-Mail adress!",
|
||||||
"phone-error": "You have to enter a vaild phone number!",
|
"phone-error": "You have to enter a vaild phone number!",
|
||||||
"footer-headline": "This system was fully designed and developed by Theis Gaedigk. - Portfolio: "
|
"footer-headline": "This system was fully designed and developed by Theis Gaedigk. - Portfolio: ",
|
||||||
|
"chocolates": "Chocolates",
|
||||||
|
"chocolates-error": "This field can only be true or false."
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import i18n from "./i18n";
|
import i18n from "./i18n";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
import { ZodObject } from "zod";
|
||||||
|
import type { ZodRawShape } from "zod";
|
||||||
|
|
||||||
export const changeTranslation = () => {
|
export const changeTranslation = () => {
|
||||||
const clientLng = i18n.language;
|
const clientLng = i18n.language;
|
||||||
@@ -14,3 +16,27 @@ export const changeTranslation = () => {
|
|||||||
alert("Cannot change language.");
|
alert("Cannot change language.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a single field against the full Zod schema.
|
||||||
|
* Returns the first error message for that field, or undefined if valid.
|
||||||
|
*/
|
||||||
|
export function validateFieldWithZod(
|
||||||
|
schema: ZodObject<ZodRawShape>,
|
||||||
|
fieldName: string,
|
||||||
|
allValues: Record<string, unknown>,
|
||||||
|
): string | undefined {
|
||||||
|
const result = schema.safeParse(allValues);
|
||||||
|
if (result.success) return undefined;
|
||||||
|
const issue = result.error.issues.find(
|
||||||
|
(i) => i.path.length === 1 && i.path[0] === fieldName,
|
||||||
|
);
|
||||||
|
return issue?.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PAYMENT_METHODS = ["bar", "paypal", "andere"] as const;
|
||||||
|
export const PAYMENT_LABELS: Record<string, string> = {
|
||||||
|
bar: "Cash",
|
||||||
|
paypal: "PayPal",
|
||||||
|
andere: "Transfer",
|
||||||
|
};
|
||||||
+1
-1
Submodule wg-easy-ca-lose updated: 9581e6eacb...8c70c24205
Reference in New Issue
Block a user