From e5a85483edcac57a813347ff512965ff57c68967 Mon Sep 17 00:00:00 2001 From: Vikrant Kumar Date: Mon, 5 Oct 2026 17:27:04 +0530 Subject: [PATCH] Let acad admin pick the financial year on the Fee Structure Certificate The certificate now covers whichever semester pair the admin selects instead of always defaulting to the student's current one, and the semester number renders with its ordinal suffix raised, matching the other certificates. --- .../Certificates/FeeStructureCertificate.jsx | 83 +++++++++++++++---- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/src/Modules/Certificates/FeeStructureCertificate.jsx b/src/Modules/Certificates/FeeStructureCertificate.jsx index 541876e8e..2d94d89c0 100644 --- a/src/Modules/Certificates/FeeStructureCertificate.jsx +++ b/src/Modules/Certificates/FeeStructureCertificate.jsx @@ -10,6 +10,7 @@ import { Group, Loader, Paper, + Select, Stack, Text, TextInput, @@ -63,6 +64,17 @@ const filenameFrom = (header, fallback) => { return match?.[1] || fallback; }; +function ordinalWithSuperscript(value) { + const match = /^(\d+)(st|nd|rd|th)$/.exec((value ?? "").trim()); + if (!match) return value; + return ( + <> + {match[1]} + {match[2]} + + ); +} + const indianCurrency = (value) => { const [whole, fraction] = Number(value).toFixed(2).split("."); const head = whole.slice(0, -3); @@ -80,6 +92,8 @@ export default function FeeStructureCertificate() { const [rollNumber, setRollNumber] = useState(""); const [selectedStudent, setSelectedStudent] = useState(null); const [structure, setStructure] = useState(null); + const [financialYears, setFinancialYears] = useState([]); + const [selectedSemester, setSelectedSemester] = useState(null); const [certificateMeta, setCertificateMeta] = useState(null); const [preview, setPreview] = useState(null); const [fetchingStudent, setFetchingStudent] = useState(false); @@ -89,25 +103,31 @@ export default function FeeStructureCertificate() { const clearStudent = () => { setSelectedStudent(null); setStructure(null); + setFinancialYears([]); + setSelectedSemester(null); setCertificateMeta(null); setPreview(null); }; - const fetchStudent = async () => { - const normalizedRollNumber = rollNumber.trim().toUpperCase(); - if (!normalizedRollNumber || fetchingStudent) return; - - clearStudent(); - setRollNumber(normalizedRollNumber); + const fetchStudent = async (roll, semester) => { + if (!roll || fetchingStudent) return; setFetchingStudent(true); try { const { data } = await axios.get(feeStructureCertificateStudentRoute, { ...authConfig(), - params: { roll_number: normalizedRollNumber }, + params: { roll_number: roll, semester: semester || undefined }, }); setSelectedStudent(data.student || null); setStructure(data.structure || null); + setFinancialYears( + (data.financial_years || []).map((item) => ({ + value: String(item.value), + label: item.label, + })), + ); + setSelectedSemester(data.selected_semester ?? null); setCertificateMeta(data.certificate || null); + setPreview(null); } catch (error) { notifications.show({ color: "red", @@ -119,6 +139,21 @@ export default function FeeStructureCertificate() { } }; + const searchStudent = () => { + const normalizedRollNumber = rollNumber.trim().toUpperCase(); + if (!normalizedRollNumber) return; + clearStudent(); + setRollNumber(normalizedRollNumber); + fetchStudent(normalizedRollNumber); + }; + + const changeFinancialYear = (value) => { + const semester = Number(value); + setSelectedSemester(semester); + setPreview(null); + fetchStudent(rollNumber, semester); + }; + const showPreview = () => { if (!selectedStudent || !structure?.rows?.length || !certificateMeta) return; @@ -135,7 +170,7 @@ export default function FeeStructureCertificate() { try { const response = await axios.post( feeStructureCertificatePdfRoute, - { student_id: selectedStudent.student_id }, + { student_id: selectedStudent.student_id, semester: selectedSemester }, { ...authConfig(), responseType: "blob" }, ); const filename = filenameFrom( @@ -159,7 +194,9 @@ export default function FeeStructureCertificate() { } }; - const ready = Boolean(selectedStudent?.is_ready && structure?.rows?.length); + const ready = Boolean( + selectedStudent?.is_ready && structure?.rows?.length && selectedSemester, + ); return ( <> @@ -185,7 +222,7 @@ export default function FeeStructureCertificate() { onKeyDown={(event) => { if (event.key === "Enter") { event.preventDefault(); - fetchStudent(); + searchStudent(); } }} rightSection={ @@ -195,7 +232,7 @@ export default function FeeStructureCertificate() {