diff --git a/apps/backoffice/src/app/features/exam/pages/ExamPage/index.tsx b/apps/backoffice/src/app/features/exam/pages/ExamPage/index.tsx index 34039328c..1bdf2c247 100644 --- a/apps/backoffice/src/app/features/exam/pages/ExamPage/index.tsx +++ b/apps/backoffice/src/app/features/exam/pages/ExamPage/index.tsx @@ -79,21 +79,44 @@ function ExamForm({ const [status, setStatus] = useState(editing?.status ?? null); const [activeTab, setActiveTab] = useState("basic"); - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); + /** + * Split per tab so "Next" can check just the tab in front of the user. + * Submitting from Basic Info used to complain about Settings fields the + * user had not been shown yet — the error was correct and unactionable at + * the same time. Each returns the message key for what is missing, or null. + */ + const validateBasic = (): string | null => { if (!certificationId || !titleEn || !titleAm || !date || !venue) { - setActiveTab("basic"); - notify.error(t("exam.form.fillRequiredBasic")); - return; + return "exam.form.fillRequiredBasic"; } if ((directionEn || directionAm) && !(directionEn && directionAm)) { - setActiveTab("basic"); - notify.error(t("exam.form.directionBothLanguages")); + return "exam.form.directionBothLanguages"; + } + return null; + }; + + const validateSettings = (): string | null => + !type || !form || !adminMethod || !evalMethod || !cuttingPoint + ? "exam.form.fillRequiredSettings" + : null; + + const goNext = () => { + const error = validateBasic(); + if (error) { + notify.error(t(error)); return; } - if (!type || !form || !adminMethod || !evalMethod || !cuttingPoint) { - setActiveTab("settings"); - notify.error(t("exam.form.fillRequiredSettings")); + setActiveTab("settings"); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + // Still checks both: the tabs are clickable, so a user can reach Settings + // without going through Next. + const error = validateBasic() ?? validateSettings(); + if (error) { + setActiveTab(error === "exam.form.fillRequiredSettings" ? "settings" : "basic"); + notify.error(t(error)); return; } onSubmit( @@ -337,9 +360,26 @@ function ExamForm({ - + {activeTab === "basic" ? ( + /* Not type="submit": Basic Info is not the last step, so the + primary action advances rather than saves. */ + + ) : ( + <> + + + + )} diff --git a/apps/backoffice/src/app/i18n/locales/am.ts b/apps/backoffice/src/app/i18n/locales/am.ts index c8e51193d..9bdb4e114 100644 --- a/apps/backoffice/src/app/i18n/locales/am.ts +++ b/apps/backoffice/src/app/i18n/locales/am.ts @@ -271,6 +271,8 @@ export const am: Translations = { cuttingPointPercentageHint: "የመቶኛ ግምገማ — ከ100 አይበልጥም።", fillRequiredBasic: "በመሠረታዊ መረጃ ውስጥ ያሉ አስፈላጊ መስኮችን ይሙሉ።", fillRequiredSettings: "በቅንብሮች ውስጥ ያሉ አስፈላጊ መስኮችን ይሙሉ — ዓይነት፣ ቅጽ፣ የአስተዳደር ዘዴ፣ የግምገማ ዘዴ እና የማለፊያ ነጥብ።", + next: "ቀጣይ", + back: "ተመለስ", directionBothLanguages: "መመሪያ በሁለቱም እንግሊዝኛ እና አማርኛ ጽሑፍ ያስፈልገዋል፣ ወይም ሁለቱንም ባዶ ይተዉ።", status: "ሁኔታ", statusPlaceholder: "የፈተና ሁኔታ", diff --git a/apps/backoffice/src/app/i18n/locales/en.ts b/apps/backoffice/src/app/i18n/locales/en.ts index 7152067ce..ceb6e6c38 100644 --- a/apps/backoffice/src/app/i18n/locales/en.ts +++ b/apps/backoffice/src/app/i18n/locales/en.ts @@ -271,6 +271,8 @@ export const en = { 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.', + next: 'Next', + back: 'Back', status: 'Status', statusPlaceholder: 'Exam status', pending: 'Pending',