Skip to content

Commit 47895c8

Browse files
authored
Merge pull request #2169 from tekdi/release-1.12.0
Release 1.12.0 to prod fix
2 parents ea9a39c + 23a7b28 commit 47895c8

File tree

2 files changed

+180
-47
lines changed

2 files changed

+180
-47
lines changed

mfes/scp-teacher-repo/src/components/assessment/QuestionMarksManualUpdate.tsx

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -440,30 +440,68 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
440440
const max =
441441
content_details?.sections?.[sectionIndex]?.questions?.[questionIndex]
442442
?.maxScore || 0;
443-
const onlyDigits = (textValue || '').replace(/[^0-9]/g, '');
444-
if (onlyDigits === '') {
443+
444+
// Allow only digits and single decimal point
445+
const filtered = (textValue || '').replace(/[^0-9.]/g, '');
446+
447+
// Handle empty input
448+
if (filtered === '' || filtered === '.') {
445449
set_marksTextBySection((prev) => {
446450
const arr = [...(prev[String(sectionIndex)] || [])];
447-
arr[questionIndex] = '';
451+
arr[questionIndex] = filtered;
452+
return { ...prev, [String(sectionIndex)]: arr };
453+
});
454+
if (filtered === '') {
455+
set_marksBySection((prev) => {
456+
const arr = [...(prev[String(sectionIndex)] || [])];
457+
arr[questionIndex] = 0;
458+
return { ...prev, [String(sectionIndex)]: arr };
459+
});
460+
}
461+
return;
462+
}
463+
464+
// Prevent multiple decimal points
465+
const decimalCount = (filtered.match(/\./g) || []).length;
466+
if (decimalCount > 1) return;
467+
468+
// If input ends with a dot, allow it during typing
469+
if (filtered.endsWith('.')) {
470+
const beforeDot = filtered.slice(0, -1);
471+
const numBeforeDot = Number(beforeDot);
472+
// Don't allow if the number before dot already exceeds max
473+
if (Number.isFinite(numBeforeDot) && numBeforeDot > max) return;
474+
475+
set_marksTextBySection((prev) => {
476+
const arr = [...(prev[String(sectionIndex)] || [])];
477+
arr[questionIndex] = filtered;
448478
return { ...prev, [String(sectionIndex)]: arr };
449479
});
480+
// Update numeric value to the integer part while user is typing decimal
450481
set_marksBySection((prev) => {
451482
const arr = [...(prev[String(sectionIndex)] || [])];
452-
arr[questionIndex] = 0;
483+
arr[questionIndex] = numBeforeDot;
453484
return { ...prev, [String(sectionIndex)]: arr };
454485
});
455486
return;
456487
}
457-
// Normalize leading zeros: "000" -> "0", "0012" -> "12"
458-
let normalized = onlyDigits.replace(/^0+/, '');
459-
if (normalized === '') normalized = '0';
460-
const parsed = Number(normalized);
488+
489+
const parsed = Number(filtered);
461490
if (!Number.isFinite(parsed)) return;
462-
// Reject values above max: keep previous valid value unchanged
491+
492+
// Validation: Only allow whole numbers or numbers ending with .5
493+
const isValid =
494+
/^(\d+\.5|\d+)$/.test(filtered) ||
495+
filtered === '0.5' ||
496+
filtered === '.5';
497+
if (!isValid) return; // Reject invalid decimals like 0.1, 0.2, 1.3, etc.
498+
499+
// Reject values above max
463500
if (parsed > max) return;
501+
464502
set_marksTextBySection((prev) => {
465503
const arr = [...(prev[String(sectionIndex)] || [])];
466-
arr[questionIndex] = normalized;
504+
arr[questionIndex] = filtered;
467505
return { ...prev, [String(sectionIndex)]: arr };
468506
});
469507
set_marksBySection((prev) => {
@@ -477,19 +515,53 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
477515
const max =
478516
content_details?.sections?.[sectionIndex]?.questions?.[questionIndex]
479517
?.maxScore || 0;
480-
const text =
481-
marksTextBySection[String(sectionIndex)]?.[questionIndex] ?? '';
482-
if (text === '') {
483-
// keep UI empty but ensure numeric is 0
518+
let text = marksTextBySection[String(sectionIndex)]?.[questionIndex] ?? '';
519+
520+
// Handle empty or just dot
521+
if (text === '' || text === '.') {
484522
set_marksBySection((prev) => {
485523
const arr = [...(prev[String(sectionIndex)] || [])];
486524
arr[questionIndex] = 0;
487525
return { ...prev, [String(sectionIndex)]: arr };
488526
});
527+
set_marksTextBySection((prev) => {
528+
const arr = [...(prev[String(sectionIndex)] || [])];
529+
arr[questionIndex] = '';
530+
return { ...prev, [String(sectionIndex)]: arr };
531+
});
489532
return;
490533
}
534+
535+
// Handle incomplete input like "2." - convert to whole number
536+
if (text.endsWith('.')) {
537+
text = text.slice(0, -1);
538+
}
539+
540+
// Normalize ".5" to "0.5"
541+
if (text === '.5') {
542+
text = '0.5';
543+
}
544+
491545
const num = Number(text);
492-
const safe = Number.isFinite(num) ? Math.max(0, Math.min(num, max)) : 0;
546+
547+
// Validate: must be finite, within range, and either whole number or .5 decimal
548+
const isValidFormat = /^(\d+\.5|\d+)$/.test(text);
549+
if (!Number.isFinite(num) || !isValidFormat) {
550+
// Revert to previous valid value (0)
551+
set_marksBySection((prev) => {
552+
const arr = [...(prev[String(sectionIndex)] || [])];
553+
arr[questionIndex] = 0;
554+
return { ...prev, [String(sectionIndex)]: arr };
555+
});
556+
set_marksTextBySection((prev) => {
557+
const arr = [...(prev[String(sectionIndex)] || [])];
558+
arr[questionIndex] = '0';
559+
return { ...prev, [String(sectionIndex)]: arr };
560+
});
561+
return;
562+
}
563+
564+
const safe = Math.max(0, Math.min(num, max));
493565
set_marksBySection((prev) => {
494566
const arr = [...(prev[String(sectionIndex)] || [])];
495567
arr[questionIndex] = safe;
@@ -923,8 +995,7 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
923995
<label style={{ fontWeight: 500 }}>Marks:</label>
924996
<input
925997
type="text"
926-
inputMode="numeric"
927-
pattern="[0-9]*"
998+
inputMode="decimal"
928999
min={0}
9291000
max={q.maxScore}
9301001
value={
@@ -936,11 +1007,7 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
9361007
marksBySection[String(si)]?.[qi] ?? 0
9371008
)}
9381009
onChange={(e) => {
939-
const onlyDigits = e.target.value.replace(
940-
/[^0-9]/g,
941-
''
942-
);
943-
handleMarksTextChange(si, qi, onlyDigits);
1010+
handleMarksTextChange(si, qi, e.target.value);
9441011
}}
9451012
onBlur={() => handleMarksBlur(si, qi)}
9461013
style={{ width: 80 }}

mfes/youthNet/src/components/assessment/QuestionMarksManualUpdate.tsx

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -449,30 +449,68 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
449449
const max =
450450
content_details?.sections?.[sectionIndex]?.questions?.[questionIndex]
451451
?.maxScore || 0;
452-
const onlyDigits = (textValue || '').replace(/[^0-9]/g, '');
453-
if (onlyDigits === '') {
452+
453+
// Allow only digits and single decimal point
454+
const filtered = (textValue || '').replace(/[^0-9.]/g, '');
455+
456+
// Handle empty input
457+
if (filtered === '' || filtered === '.') {
454458
set_marksTextBySection((prev) => {
455459
const arr = [...(prev[String(sectionIndex)] || [])];
456-
arr[questionIndex] = '';
460+
arr[questionIndex] = filtered;
457461
return { ...prev, [String(sectionIndex)]: arr };
458462
});
463+
if (filtered === '') {
464+
set_marksBySection((prev) => {
465+
const arr = [...(prev[String(sectionIndex)] || [])];
466+
arr[questionIndex] = 0;
467+
return { ...prev, [String(sectionIndex)]: arr };
468+
});
469+
}
470+
return;
471+
}
472+
473+
// Prevent multiple decimal points
474+
const decimalCount = (filtered.match(/\./g) || []).length;
475+
if (decimalCount > 1) return;
476+
477+
// If input ends with a dot, allow it during typing
478+
if (filtered.endsWith('.')) {
479+
const beforeDot = filtered.slice(0, -1);
480+
const numBeforeDot = Number(beforeDot);
481+
// Don't allow if the number before dot already exceeds max
482+
if (Number.isFinite(numBeforeDot) && numBeforeDot > max) return;
483+
484+
set_marksTextBySection((prev) => {
485+
const arr = [...(prev[String(sectionIndex)] || [])];
486+
arr[questionIndex] = filtered;
487+
return { ...prev, [String(sectionIndex)]: arr };
488+
});
489+
// Update numeric value to the integer part while user is typing decimal
459490
set_marksBySection((prev) => {
460491
const arr = [...(prev[String(sectionIndex)] || [])];
461-
arr[questionIndex] = 0;
492+
arr[questionIndex] = numBeforeDot;
462493
return { ...prev, [String(sectionIndex)]: arr };
463494
});
464495
return;
465496
}
466-
// Normalize leading zeros: "000" -> "0", "0012" -> "12"
467-
let normalized = onlyDigits.replace(/^0+/, '');
468-
if (normalized === '') normalized = '0';
469-
const parsed = Number(normalized);
497+
498+
const parsed = Number(filtered);
470499
if (!Number.isFinite(parsed)) return;
471-
// Reject values above max: keep previous valid value unchanged
500+
501+
// Validation: Only allow whole numbers or numbers ending with .5
502+
const isValid =
503+
/^(\d+\.5|\d+)$/.test(filtered) ||
504+
filtered === '0.5' ||
505+
filtered === '.5';
506+
if (!isValid) return; // Reject invalid decimals like 0.1, 0.2, 1.3, etc.
507+
508+
// Reject values above max
472509
if (parsed > max) return;
510+
473511
set_marksTextBySection((prev) => {
474512
const arr = [...(prev[String(sectionIndex)] || [])];
475-
arr[questionIndex] = normalized;
513+
arr[questionIndex] = filtered;
476514
return { ...prev, [String(sectionIndex)]: arr };
477515
});
478516
set_marksBySection((prev) => {
@@ -486,19 +524,53 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
486524
const max =
487525
content_details?.sections?.[sectionIndex]?.questions?.[questionIndex]
488526
?.maxScore || 0;
489-
const text =
490-
marksTextBySection[String(sectionIndex)]?.[questionIndex] ?? '';
491-
if (text === '') {
492-
// keep UI empty but ensure numeric is 0
527+
let text = marksTextBySection[String(sectionIndex)]?.[questionIndex] ?? '';
528+
529+
// Handle empty or just dot
530+
if (text === '' || text === '.') {
493531
set_marksBySection((prev) => {
494532
const arr = [...(prev[String(sectionIndex)] || [])];
495533
arr[questionIndex] = 0;
496534
return { ...prev, [String(sectionIndex)]: arr };
497535
});
536+
set_marksTextBySection((prev) => {
537+
const arr = [...(prev[String(sectionIndex)] || [])];
538+
arr[questionIndex] = '';
539+
return { ...prev, [String(sectionIndex)]: arr };
540+
});
498541
return;
499542
}
543+
544+
// Handle incomplete input like "2." - convert to whole number
545+
if (text.endsWith('.')) {
546+
text = text.slice(0, -1);
547+
}
548+
549+
// Normalize ".5" to "0.5"
550+
if (text === '.5') {
551+
text = '0.5';
552+
}
553+
500554
const num = Number(text);
501-
const safe = Number.isFinite(num) ? Math.max(0, Math.min(num, max)) : 0;
555+
556+
// Validate: must be finite, within range, and either whole number or .5 decimal
557+
const isValidFormat = /^(\d+\.5|\d+)$/.test(text);
558+
if (!Number.isFinite(num) || !isValidFormat) {
559+
// Revert to previous valid value (0)
560+
set_marksBySection((prev) => {
561+
const arr = [...(prev[String(sectionIndex)] || [])];
562+
arr[questionIndex] = 0;
563+
return { ...prev, [String(sectionIndex)]: arr };
564+
});
565+
set_marksTextBySection((prev) => {
566+
const arr = [...(prev[String(sectionIndex)] || [])];
567+
arr[questionIndex] = '0';
568+
return { ...prev, [String(sectionIndex)]: arr };
569+
});
570+
return;
571+
}
572+
573+
const safe = Math.max(0, Math.min(num, max));
502574
set_marksBySection((prev) => {
503575
const arr = [...(prev[String(sectionIndex)] || [])];
504576
arr[questionIndex] = safe;
@@ -641,12 +713,11 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
641713

642714
const result = await createContentTracking({
643715
userId: userId,
644-
contentId: do_id,
716+
contentId: do_id,
645717
courseId: parentId,
646718
unitId: unitId,
647-
totalScore: totalScore
719+
totalScore: totalScore,
648720
});
649-
650721
} catch (error) {
651722
console.error('API error while creating assessment tracking:', error);
652723
set_isSubmitting(false);
@@ -941,8 +1012,7 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
9411012
<label style={{ fontWeight: 500 }}>Marks:</label>
9421013
<input
9431014
type="text"
944-
inputMode="numeric"
945-
pattern="[0-9]*"
1015+
inputMode="decimal"
9461016
min={0}
9471017
max={q.maxScore}
9481018
value={
@@ -954,11 +1024,7 @@ const QuestionMarksManualUpdate: React.FC<QuestionMarksManualUpdateProps> = ({
9541024
marksBySection[String(si)]?.[qi] ?? 0
9551025
)}
9561026
onChange={(e) => {
957-
const onlyDigits = e.target.value.replace(
958-
/[^0-9]/g,
959-
''
960-
);
961-
handleMarksTextChange(si, qi, onlyDigits);
1027+
handleMarksTextChange(si, qi, e.target.value);
9621028
}}
9631029
onBlur={() => handleMarksBlur(si, qi)}
9641030
style={{ width: 80 }}

0 commit comments

Comments
 (0)