Harden published curriculum and student flows
This commit is contained in:
+67
-81
@@ -27,6 +27,7 @@ class _SmartErrorNotebookScreenState extends State<SmartErrorNotebookScreen> {
|
||||
final ErrorNotebookRepository _repository = ErrorNotebookRepository();
|
||||
|
||||
bool _isLoading = true;
|
||||
bool _loadFailed = false;
|
||||
ErrorNotebookSummary? _summary;
|
||||
List<ErrorNotebookItem> _items = [];
|
||||
|
||||
@@ -42,15 +43,27 @@ class _SmartErrorNotebookScreenState extends State<SmartErrorNotebookScreen> {
|
||||
|
||||
Future<void> _loadNotebookData() async {
|
||||
setState(() => _isLoading = true);
|
||||
final data = (widget.isGuardianMode && widget.studentId != null)
|
||||
? await _repository.getChildErrorNotebook(widget.studentId!)
|
||||
: await _repository.getErrorNotebook();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_summary = data['summary'] as ErrorNotebookSummary?;
|
||||
_items = (data['items'] as List<ErrorNotebookItem>?) ?? [];
|
||||
_isLoading = false;
|
||||
});
|
||||
try {
|
||||
final data = (widget.isGuardianMode && widget.studentId != null)
|
||||
? await _repository.getChildErrorNotebook(widget.studentId!)
|
||||
: await _repository.getErrorNotebook();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_summary = data['summary'] as ErrorNotebookSummary?;
|
||||
_items = (data['items'] as List<ErrorNotebookItem>?) ?? [];
|
||||
_loadFailed = false;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_summary = null;
|
||||
_items = [];
|
||||
_loadFailed = true;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +159,8 @@ class _SmartErrorNotebookScreenState extends State<SmartErrorNotebookScreen> {
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(color: Color(0xFF38BDF8)),
|
||||
)
|
||||
: _loadFailed
|
||||
? Center(child: TextButton(onPressed: _loadNotebookData, child: const Text('تعذر تحميل دفتر الأخطاء. اضغط لإعادة المحاولة.')))
|
||||
: RefreshIndicator(
|
||||
onRefresh: _loadNotebookData,
|
||||
color: const Color(0xFF38BDF8),
|
||||
@@ -647,7 +662,9 @@ class _SmartErrorNotebookScreenState extends State<SmartErrorNotebookScreen> {
|
||||
),
|
||||
)
|
||||
: ElevatedButton.icon(
|
||||
onPressed: () => _startRemediation(item),
|
||||
onPressed: item.isInRemediation && !item.retentionIsDue
|
||||
? null
|
||||
: () => _startRemediation(item),
|
||||
icon: Icon(
|
||||
item.isInRemediation
|
||||
? CupertinoIcons.arrow_2_circlepath_circle_fill
|
||||
@@ -656,7 +673,9 @@ class _SmartErrorNotebookScreenState extends State<SmartErrorNotebookScreen> {
|
||||
),
|
||||
label: Text(
|
||||
item.isInRemediation
|
||||
? 'بدء اختبار الاسترجاع والتثبيت (تكرار متباعد 7 أيام) 🎯'
|
||||
? (item.retentionIsDue
|
||||
? 'بدء اختبار الاسترجاع بعد 7 أيام 🎯'
|
||||
: 'اختبار الاسترجاع متاح بعد موعد المراجعة')
|
||||
: 'بدء المسار العلاجي لسد هذه الفجوة (3 أسئلة تفريدية) 🚀',
|
||||
style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w800),
|
||||
),
|
||||
@@ -723,11 +742,14 @@ class _RemediationModalSheet extends StatefulWidget {
|
||||
class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
bool _isLoadingQuiz = true;
|
||||
List<RemedialQuestion> _questions = [];
|
||||
String _attemptUuid = '';
|
||||
final List<int> _answers = [];
|
||||
int _currentQuestionIndex = 0;
|
||||
int? _selectedOptionIndex;
|
||||
bool _hasAnsweredCurrent = false;
|
||||
int _correctAnswersCount = 0;
|
||||
bool _isCompleted = false;
|
||||
bool _passed = false;
|
||||
bool _isSubmitting = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -736,44 +758,43 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
}
|
||||
|
||||
Future<void> _fetchQuiz() async {
|
||||
final qs = await widget.repository.getRemediationQuiz(
|
||||
final result = await widget.repository.getRemediationQuiz(
|
||||
errorUuid: widget.errorItem.uuid,
|
||||
topicName: widget.errorItem.topicName,
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_questions = qs;
|
||||
_questions = result['questions'] as List<RemedialQuestion>;
|
||||
_attemptUuid = result['attempt_uuid'] as String;
|
||||
_isLoadingQuiz = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _submitAnswer() {
|
||||
if (_selectedOptionIndex == null || _hasAnsweredCurrent) return;
|
||||
|
||||
final q = _questions[_currentQuestionIndex];
|
||||
final isCorrect = _selectedOptionIndex == q.correctIndex;
|
||||
|
||||
setState(() {
|
||||
_hasAnsweredCurrent = true;
|
||||
if (isCorrect) {
|
||||
_correctAnswersCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _nextQuestion() async {
|
||||
Future<void> _nextQuestion() async {
|
||||
if (_selectedOptionIndex == null || _isSubmitting) return;
|
||||
_answers.add(_questions[_currentQuestionIndex].options[_selectedOptionIndex!].id);
|
||||
if (_currentQuestionIndex < _questions.length - 1) {
|
||||
setState(() {
|
||||
_currentQuestionIndex++;
|
||||
_selectedOptionIndex = null;
|
||||
_hasAnsweredCurrent = false;
|
||||
});
|
||||
} else {
|
||||
// Completed drill
|
||||
setState(() => _isCompleted = true);
|
||||
if (_correctAnswersCount >= 2) {
|
||||
await widget.repository.resolveError(widget.errorItem.uuid);
|
||||
setState(() => _isSubmitting = true);
|
||||
final result = await widget.repository.resolveError(widget.errorItem.uuid, _attemptUuid, _answers);
|
||||
if (!mounted) return;
|
||||
if (result == null) {
|
||||
_answers.removeLast();
|
||||
setState(() => _isSubmitting = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('تعذر اعتماد المحاولة. حاول مرة أخرى.')));
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_correctAnswersCount = (result['correct_count'] as num?)?.toInt() ?? 0;
|
||||
_passed = result['passed'] == true;
|
||||
_isSubmitting = false;
|
||||
_isCompleted = true;
|
||||
});
|
||||
if (_passed) {
|
||||
widget.onRemediationCompleted();
|
||||
}
|
||||
}
|
||||
@@ -795,6 +816,8 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(color: Color(0xFF38BDF8)),
|
||||
)
|
||||
: _questions.isEmpty
|
||||
? const Center(child: Text('لا تتوفر أسئلة مراجعة معتمدة لهذا المفهوم حالياً.', style: TextStyle(color: Colors.white70)))
|
||||
: _isCompleted
|
||||
? _buildCompletionState()
|
||||
: _buildQuestionState(),
|
||||
@@ -856,30 +879,19 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
child: ListView.builder(
|
||||
itemCount: q.options.length,
|
||||
itemBuilder: (ctx, idx) {
|
||||
final optText = q.options[idx];
|
||||
final optText = q.options[idx].text;
|
||||
final isSel = _selectedOptionIndex == idx;
|
||||
final isCorrectOpt = idx == q.correctIndex;
|
||||
|
||||
Color borderCol = const Color(0xFF1E293B);
|
||||
Color bgCol = const Color(0xFF161F30);
|
||||
|
||||
if (_hasAnsweredCurrent) {
|
||||
if (isCorrectOpt) {
|
||||
borderCol = const Color(0xFF10B981);
|
||||
bgCol = const Color(0xFF064E3B);
|
||||
} else if (isSel) {
|
||||
borderCol = const Color(0xFFEF4444);
|
||||
bgCol = const Color(0xFF7F1D1D);
|
||||
}
|
||||
} else if (isSel) {
|
||||
if (isSel) {
|
||||
borderCol = const Color(0xFF38BDF8);
|
||||
bgCol = const Color(0xFF0369A1);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: _hasAnsweredCurrent
|
||||
? null
|
||||
: () => setState(() => _selectedOptionIndex = idx),
|
||||
onTap: () => setState(() => _selectedOptionIndex = idx),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
padding: const EdgeInsets.all(14),
|
||||
@@ -926,34 +938,11 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
),
|
||||
),
|
||||
|
||||
// Explanation if answered
|
||||
if (_hasAnsweredCurrent)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1E293B),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
q.explanation,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFFCBD5E1),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Action Button
|
||||
ElevatedButton(
|
||||
onPressed: !_hasAnsweredCurrent
|
||||
? (_selectedOptionIndex != null ? _submitAnswer : null)
|
||||
: _nextQuestion,
|
||||
onPressed: _selectedOptionIndex != null && !_isSubmitting ? _nextQuestion : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: _hasAnsweredCurrent
|
||||
? const Color(0xFF10B981)
|
||||
: const Color(0xFF38BDF8),
|
||||
backgroundColor: const Color(0xFF38BDF8),
|
||||
foregroundColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -961,11 +950,8 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
!_hasAnsweredCurrent
|
||||
? 'تأكيد الإجابة'
|
||||
: (_currentQuestionIndex < _questions.length - 1
|
||||
? 'السؤال التالي ➔'
|
||||
: 'إنهاء المسار العلاجي 🏆'),
|
||||
_isSubmitting ? 'جارٍ تصحيح الإجابات...' : (_currentQuestionIndex < _questions.length - 1
|
||||
? 'السؤال التالي ➔' : 'إنهاء وتصحيح المحاولة'),
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w800),
|
||||
),
|
||||
),
|
||||
@@ -974,7 +960,7 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
}
|
||||
|
||||
Widget _buildCompletionState() {
|
||||
final passed = _correctAnswersCount >= 2;
|
||||
final passed = _passed;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
@@ -1005,7 +991,7 @@ class _RemediationModalSheetState extends State<_RemediationModalSheet> {
|
||||
Text(
|
||||
passed
|
||||
? (widget.errorItem.isInRemediation
|
||||
? 'أجبت عن $_correctAnswersCount من ${_questions.length} أسئلة بشكل سليم. تم تثبيت المفهوم في الذاكرة طويلة المدى وترحيله إلى المتقنات 🏆.'
|
||||
? 'أجبت عن $_correctAnswersCount من ${_questions.length} أسئلة بشكل سليم في اختبار الاسترجاع بعد أسبوع.'
|
||||
: 'أجبت عن $_correctAnswersCount من ${_questions.length} أسئلة. دخل المفهوم مرحلة التثبيت والتكرار المتباعد (المراجعة بعد 7 أيام لتأكيد الإتقان التام).')
|
||||
: 'أجبت عن $_correctAnswersCount من ${_questions.length}. حاول مجدداً لضمان إتقان السؤال في الامتحان الوزاري.',
|
||||
style: const TextStyle(fontSize: 13, color: Color(0xFF94A3B8)),
|
||||
|
||||
Reference in New Issue
Block a user