218 lines
6.8 KiB
Dart
218 lines
6.8 KiB
Dart
// lib/features/discipline/discipline_screen.dart
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:smpn1_lengkong/core/models/student_model.dart';
|
|
import 'package:smpn1_lengkong/core/constants/app_colors.dart';
|
|
import 'package:smpn1_lengkong/core/service/student_service.dart';
|
|
|
|
class DisciplineScreen extends StatefulWidget {
|
|
const DisciplineScreen({super.key});
|
|
|
|
@override
|
|
State<DisciplineScreen> createState() => _DisciplineScreenState();
|
|
}
|
|
|
|
class _DisciplineScreenState extends State<DisciplineScreen> {
|
|
StudentModel? _studentState;
|
|
String?
|
|
_currentClassFilter; // Variabel untuk mengunci kelas yang sedang dilihat
|
|
bool _isInitialized = false;
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
// Inisialisasi data hanya sekali saat halaman dibuka
|
|
if (!_isInitialized) {
|
|
final args = ModalRoute.of(context)!.settings.arguments as StudentModel;
|
|
_studentState = args;
|
|
// Mengunci filter berdasarkan className yang dikirim saat navigasi
|
|
_currentClassFilter = args.className;
|
|
_isInitialized = true;
|
|
}
|
|
}
|
|
|
|
/// Fungsi untuk menarik data terbaru dari Firestore
|
|
Future<void> _handleRefresh() async {
|
|
if (_studentState == null) return;
|
|
|
|
try {
|
|
// Mengambil data profil siswa terbaru dari StudentService
|
|
final updatedStudent = await StudentService().getCurrentStudentData();
|
|
|
|
setState(() {
|
|
// Menggunakan copyWith untuk memastikan className tetap pada kelas yang dipilih
|
|
_studentState = updatedStudent.copyWith(className: _currentClassFilter);
|
|
});
|
|
|
|
if (mounted) {}
|
|
} catch (e) {}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (_studentState == null) {
|
|
return const Scaffold(body: Center(child: CircularProgressIndicator()));
|
|
}
|
|
|
|
final student = _studentState!;
|
|
|
|
// 1. Filter Pelanggaran sesuai kelas yang dipilih
|
|
final filteredViolations =
|
|
student.violationHistory.where((v) {
|
|
return v.className == _currentClassFilter;
|
|
}).toList();
|
|
|
|
// 2. LOGIKA SORTING: Mengurutkan dari tanggal TERBARU ke TERLAMA
|
|
filteredViolations.sort((a, b) {
|
|
try {
|
|
// Mengubah string tanggal menjadi DateTime untuk dibandingkan
|
|
DateTime dateA = DateTime.parse(a.date);
|
|
DateTime dateB = DateTime.parse(b.date);
|
|
return dateB.compareTo(dateA); // Descending (Terbaru di atas)
|
|
} catch (e) {
|
|
// Jika format tanggal tidak valid, biarkan urutan apa adanya
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
// 3. Hitung total poin secara dinamis berdasarkan hasil filter
|
|
final int totalPoints = filteredViolations.fold(
|
|
0,
|
|
(sum, item) => sum + item.point,
|
|
);
|
|
|
|
return Scaffold(
|
|
backgroundColor: AppColors.backgroundWhite,
|
|
appBar: AppBar(
|
|
title: Text(
|
|
"Kedisiplinan - Kelas $_currentClassFilter",
|
|
style: const TextStyle(color: Colors.white),
|
|
),
|
|
backgroundColor: AppColors.primaryBlue,
|
|
foregroundColor: Colors.white,
|
|
elevation: 0,
|
|
),
|
|
body: RefreshIndicator(
|
|
onRefresh: _handleRefresh,
|
|
color: AppColors.primaryBlue,
|
|
child: Column(
|
|
children: [
|
|
// Header Card Poin Pelanggaran
|
|
Container(
|
|
width: double.infinity,
|
|
padding: EdgeInsets.all(24.w),
|
|
margin: EdgeInsets.all(20.w),
|
|
decoration: BoxDecoration(
|
|
gradient: const LinearGradient(
|
|
colors: [AppColors.warningOrange, Colors.orangeAccent],
|
|
),
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Text(
|
|
"Total Poin Pelanggaran (Kelas $_currentClassFilter)",
|
|
style: TextStyle(color: Colors.white, fontSize: 14.sp),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
"$totalPoints",
|
|
style: TextStyle(
|
|
fontSize: 48.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Daftar Riwayat Pelanggaran
|
|
Expanded(
|
|
child: Container(
|
|
width: double.infinity,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.vertical(
|
|
top: Radius.circular(30.r),
|
|
),
|
|
),
|
|
child:
|
|
filteredViolations.isEmpty
|
|
? _buildEmptyState()
|
|
: ListView.separated(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
padding: EdgeInsets.all(24.w),
|
|
itemCount: filteredViolations.length,
|
|
separatorBuilder:
|
|
(context, index) => Divider(height: 30.h),
|
|
itemBuilder: (context, index) {
|
|
final item = filteredViolations[index];
|
|
return _buildViolationItem(item);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildViolationItem(dynamic item) {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Icon(Icons.circle, size: 12.sp, color: AppColors.dangerRed),
|
|
SizedBox(width: 16.w),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
item.date,
|
|
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
|
|
),
|
|
Text(
|
|
item.description,
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.normal,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
Text(
|
|
"Kategori: ${item.category}",
|
|
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Text(
|
|
"+${item.point}",
|
|
style: const TextStyle(
|
|
color: AppColors.dangerRed,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildEmptyState() {
|
|
return ListView(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
children: [
|
|
SizedBox(height: 100.h),
|
|
const Center(
|
|
child: Text(
|
|
"Tidak ada catatan pelanggaran",
|
|
style: TextStyle(color: Colors.grey),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|