mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
fix(exam): stop Create/Update Exam form from silently failing to submit
Three bugs compounded into a button that looked unwired: - cuttingPoint carried a native HTML required attribute while sitting on the hidden Settings tab (Mantine keeps inactive tab panels mounted with display:none). The browser blocked form submission entirely, silently - no toast, no error, nothing. Replaced required with withAsterisk and moved validation into JS. - Manual required-field validation never checked type/form/adminMethod/ evalMethod/cuttingPoint at all, so even without the native block above a half-filled Settings tab would silently no-op. Validation now covers every required field on both tabs and switches to whichever tab has the problem. - Filling only the English Direction field (leaving Amharic blank) passed frontend validation but the backend hard-rejects it, producing a 400 with a confusing error. Frontend now requires both-or-neither, matching the same rule already applied to MCQ options. Also translates the previously hardcoded validation message in both locales. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,21 +77,23 @@ function ExamForm({
|
|||||||
editing?.cuttingPoint ?? 0,
|
editing?.cuttingPoint ?? 0,
|
||||||
);
|
);
|
||||||
const [status, setStatus] = useState<string | null>(editing?.status ?? null);
|
const [status, setStatus] = useState<string | null>(editing?.status ?? null);
|
||||||
|
const [activeTab, setActiveTab] = useState<string | null>("basic");
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (
|
if (!certificationId || !titleEn || !titleAm || !date || !venue) {
|
||||||
!certificationId ||
|
setActiveTab("basic");
|
||||||
!titleEn ||
|
notify.error(t("exam.form.fillRequiredBasic"));
|
||||||
!titleAm ||
|
return;
|
||||||
!date ||
|
}
|
||||||
!type ||
|
if ((directionEn || directionAm) && !(directionEn && directionAm)) {
|
||||||
!form ||
|
setActiveTab("basic");
|
||||||
!venue ||
|
notify.error(t("exam.form.directionBothLanguages"));
|
||||||
!adminMethod ||
|
return;
|
||||||
!evalMethod
|
}
|
||||||
) {
|
if (!type || !form || !adminMethod || !evalMethod || !cuttingPoint) {
|
||||||
notify.error("Please fill all required fields");
|
setActiveTab("settings");
|
||||||
|
notify.error(t("exam.form.fillRequiredSettings"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSubmit(
|
onSubmit(
|
||||||
@@ -121,7 +123,7 @@ function ExamForm({
|
|||||||
return (
|
return (
|
||||||
<Modal opened onClose={onCancel} title={editing ? t("exam.update") : t("exam.add")} size="xl">
|
<Modal opened onClose={onCancel} title={editing ? t("exam.update") : t("exam.add")} size="xl">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<Tabs defaultValue="basic" variant="outline" radius="md">
|
<Tabs value={activeTab} onChange={setActiveTab} variant="outline" radius="md">
|
||||||
<Tabs.List mb="md">
|
<Tabs.List mb="md">
|
||||||
<Tabs.Tab value="basic" leftSection={<IconInfoCircle size={15} />}>
|
<Tabs.Tab value="basic" leftSection={<IconInfoCircle size={15} />}>
|
||||||
{t("exam.form.basicInfo")}
|
{t("exam.form.basicInfo")}
|
||||||
@@ -296,7 +298,7 @@ function ExamForm({
|
|||||||
onChange={(v) => setCuttingPoint(Number(v))}
|
onChange={(v) => setCuttingPoint(Number(v))}
|
||||||
min={0}
|
min={0}
|
||||||
size="sm"
|
size="sm"
|
||||||
required
|
withAsterisk
|
||||||
/>
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
{editing && (
|
{editing && (
|
||||||
|
|||||||
@@ -254,6 +254,9 @@ export const am: Translations = {
|
|||||||
random: "በዘፈቀደ",
|
random: "በዘፈቀደ",
|
||||||
cuttingPoint: "የማለፊያ ነጥብ",
|
cuttingPoint: "የማለፊያ ነጥብ",
|
||||||
cuttingPointPlaceholder: "ለማለፍ ዝቅተኛ ነጥብ",
|
cuttingPointPlaceholder: "ለማለፍ ዝቅተኛ ነጥብ",
|
||||||
|
fillRequiredBasic: "በመሠረታዊ መረጃ ውስጥ ያሉ አስፈላጊ መስኮችን ይሙሉ።",
|
||||||
|
fillRequiredSettings: "በቅንብሮች ውስጥ ያሉ አስፈላጊ መስኮችን ይሙሉ — ዓይነት፣ ቅጽ፣ የአስተዳደር ዘዴ፣ የግምገማ ዘዴ እና የማለፊያ ነጥብ።",
|
||||||
|
directionBothLanguages: "መመሪያ በሁለቱም እንግሊዝኛ እና አማርኛ ጽሑፍ ያስፈልገዋል፣ ወይም ሁለቱንም ባዶ ይተዉ።",
|
||||||
status: "ሁኔታ",
|
status: "ሁኔታ",
|
||||||
statusPlaceholder: "የፈተና ሁኔታ",
|
statusPlaceholder: "የፈተና ሁኔታ",
|
||||||
pending: "በመጠባበቅ ላይ",
|
pending: "በመጠባበቅ ላይ",
|
||||||
|
|||||||
@@ -251,6 +251,9 @@ export const en = {
|
|||||||
random: 'Random',
|
random: 'Random',
|
||||||
cuttingPoint: 'Cutting Point (Pass Mark)',
|
cuttingPoint: 'Cutting Point (Pass Mark)',
|
||||||
cuttingPointPlaceholder: 'Minimum score to pass',
|
cuttingPointPlaceholder: 'Minimum score to pass',
|
||||||
|
fillRequiredBasic: 'Please fill all required fields in Basic Info.',
|
||||||
|
fillRequiredSettings: 'Please fill all required fields in Settings — type, form, administration method, evaluation method, and cutting point.',
|
||||||
|
directionBothLanguages: 'Direction needs text in both English and Amharic, or leave both empty.',
|
||||||
status: 'Status',
|
status: 'Status',
|
||||||
statusPlaceholder: 'Exam status',
|
statusPlaceholder: 'Exam status',
|
||||||
pending: 'Pending',
|
pending: 'Pending',
|
||||||
|
|||||||
Reference in New Issue
Block a user