M3: console.log -> Logger + UI redesign (QuestionBank) + S7/A9/A10/A11/U11 bug fixes + #1/#2/#3/#4 enhancements + i18n for QuestionBank pages

This commit is contained in:
Developer
2026-05-19 16:57:45 +08:00
parent 5b5f14674d
commit 29bac74b58
20 changed files with 1081 additions and 501 deletions
@@ -29,6 +29,9 @@ export const AssessmentTemplateManager: React.FC = () => {
difficultyDistribution: 'Basic: 30%, Intermediate: 40%, Advanced: 30%',
style: 'Professional',
knowledgeGroupId: '',
passingScore: 6,
totalTimeLimit: 1800,
perQuestionTimeLimit: 300,
});
const [copiedId, setCopiedId] = useState<string | null>(null);
const [dimensions, setDimensions] = useState<AssessmentDimension[]>([]);
@@ -73,6 +76,9 @@ export const AssessmentTemplateManager: React.FC = () => {
: (template.difficultyDistribution || ''),
style: template.style || 'Professional',
knowledgeGroupId: template.knowledgeGroupId || '',
passingScore: template.passingScore ?? 6,
totalTimeLimit: template.totalTimeLimit ?? 1800,
perQuestionTimeLimit: template.perQuestionTimeLimit ?? 300,
});
setDimensions(template.dimensions || []);
} else {
@@ -85,6 +91,9 @@ export const AssessmentTemplateManager: React.FC = () => {
difficultyDistribution: '{"Basic": 3, "Intermediate": 4, "Advanced": 3}',
style: 'Professional',
knowledgeGroupId: '',
passingScore: 6,
totalTimeLimit: 1800,
perQuestionTimeLimit: 300,
});
setDimensions([]);
}
@@ -115,6 +124,9 @@ export const AssessmentTemplateManager: React.FC = () => {
style: formData.style,
knowledgeGroupId: formData.knowledgeGroupId || undefined,
dimensions: dimensions.length > 0 ? dimensions : undefined,
passingScore: formData.passingScore,
totalTimeLimit: formData.totalTimeLimit,
perQuestionTimeLimit: formData.perQuestionTimeLimit,
};
if (editingTemplate) {
@@ -400,17 +412,48 @@ export const AssessmentTemplateManager: React.FC = () => {
</select>
</div>
<div className="space-y-1.5 md:col-span-2">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-1 ml-1 flex items-center gap-2">
<Sliders size={12} className="text-indigo-500" />
{t('style')}
</label>
<input
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-[1.25rem] text-sm font-bold focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50 outline-none transition-all"
value={formData.style}
onChange={e => setFormData({ ...formData, style: e.target.value })}
/>
</div>
<div className="space-y-1.5 md:col-span-2">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-1 ml-1 flex items-center gap-2">
<Sliders size={12} className="text-indigo-500" />
{t('style')}
</label>
<input
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-[1.25rem] text-sm font-bold focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50 outline-none transition-all"
value={formData.style}
onChange={e => setFormData({ ...formData, style: e.target.value })}
/>
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-1 ml-1 flex items-center gap-2">
<Hash size={12} className="text-indigo-500" /> (0-10)
</label>
<input type="number" min="0" max="10" step="0.5"
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-[1.25rem] text-sm font-bold focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50 outline-none transition-all"
value={formData.passingScore}
onChange={e => setFormData({ ...formData, passingScore: parseFloat(e.target.value) || 0 })}
/>
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-1 ml-1 flex items-center gap-2">
<Hash size={12} className="text-indigo-500" /> ()
</label>
<input type="number" min="60" max="7200" step="60"
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-[1.25rem] text-sm font-bold focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50 outline-none transition-all"
value={formData.totalTimeLimit}
onChange={e => setFormData({ ...formData, totalTimeLimit: parseInt(e.target.value) || 1800 })}
/>
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-1 ml-1 flex items-center gap-2">
<Hash size={12} className="text-indigo-500" /> ()
</label>
<input type="number" min="30" max="1800" step="30"
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-[1.25rem] text-sm font-bold focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50 outline-none transition-all"
value={formData.perQuestionTimeLimit}
onChange={e => setFormData({ ...formData, perQuestionTimeLimit: parseInt(e.target.value) || 300 })}
/>
</div>
</div>
<div className="space-y-1.5 md:col-span-2">