TIF_E41221079/lib/features/home/widgets/tumbal.dart

686 lines
25 KiB
Dart

// lib/features/home/widgets/home_status_card.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smpn1_lengkong/core/constants/app_colors.dart';
import 'package:smpn1_lengkong/core/service/student_service.dart';
class HomeStatusCard extends StatefulWidget {
final String category;
final String studentId;
final String studentName;
final String className;
final int totalPoin;
final String averageScore;
const HomeStatusCard({
super.key,
required this.category,
required this.studentId,
required this.studentName,
required this.className,
required this.totalPoin,
required this.averageScore,
});
@override
State<HomeStatusCard> createState() => _HomeStatusCardState();
}
class _HomeStatusCardState extends State<HomeStatusCard> {
bool _isAlreadySent = false;
bool _isLoading = true;
@override
void initState() {
super.initState();
_checkExistingRequest();
}
Color _getStatusColor(String status) {
switch (status) {
case 'Diterima':
case 'Disetujui':
return AppColors.successGreen;
case 'Ditolak':
return AppColors.dangerRed;
case 'Menunggu Konfirmasi':
default:
return AppColors.warningOrange;
}
}
IconData _getStatusIcon(String status) {
switch (status) {
case 'Diterima':
case 'Disetujui':
return Icons.check_circle_outline_rounded;
case 'Ditolak':
return Icons.cancel_outlined;
case 'Menunggu Konfirmasi':
default:
return Icons.access_time_rounded;
}
}
Future<void> _checkExistingRequest() async {
try {
final querySnapshot =
await FirebaseFirestore.instance
.collection('Bimbingan')
.where('student_id', isEqualTo: widget.studentId)
.orderBy('createdAt', descending: true)
.limit(1)
.get();
if (querySnapshot.docs.isNotEmpty) {
final lastData = querySnapshot.docs.first.data();
final Timestamp? timestamp = lastData['createdAt'] as Timestamp?;
if (timestamp != null) {
final lastSentDate = timestamp.toDate();
final now = DateTime.now();
final difference = now.difference(lastSentDate).inDays;
if (difference < 30) {
if (mounted) {
setState(() {
_isAlreadySent = true;
_isLoading = false;
});
return;
}
}
}
}
} catch (e) {
debugPrint("Error saat cek riwayat bimbingan: $e");
}
if (mounted) {
setState(() => _isLoading = false);
}
}
void _showSuccessDialog(BuildContext context, String message) {
showDialog(
context: context,
builder:
(context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle_outline_rounded,
color: AppColors.successGreen,
size: 64.sp,
),
SizedBox(height: 16.h),
Text(
"Berhasil!",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.sp,
),
),
SizedBox(height: 8.h),
Text(message, textAlign: TextAlign.center),
],
),
actions: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
),
child: const Text("Tutup"),
),
],
),
);
}
void _showDetailBimbingan(BuildContext context, Map<String, dynamic> data) {
final date = (data['createdAt'] as Timestamp?)?.toDate();
final String status = data['status'] ?? 'Menunggu Konfirmasi';
final String? jadwalTanggal = data['jadwal_tanggal'];
final String? jadwalJam = data['jadwal_jam'];
double avgVal =
double.tryParse(data['Nilai Rata-rata']?.toString() ?? '0') ?? 0;
String formattedAvg =
avgVal % 1 == 0 ? avgVal.toInt().toString() : avgVal.toStringAsFixed(1);
showDialog(
context: context,
builder:
(context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.r),
),
title: Text(
"Detail Status Kunjungan",
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildDetailRow(
"Status Pengajuan",
status,
color: _getStatusColor(status),
isBold: true,
),
if (status == 'Diterima' && jadwalTanggal != null)
Container(
margin: EdgeInsets.symmetric(vertical: 10.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: AppColors.successGreen.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.r),
border: Border.all(
color: AppColors.successGreen.withOpacity(0.3),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Jadwal Pertemuan:",
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: AppColors.successGreen,
),
),
SizedBox(height: 4.h),
Row(
children: [
Icon(
Icons.calendar_today,
size: 14.sp,
color: AppColors.successGreen,
),
SizedBox(width: 8.w),
Text(
jadwalTanggal,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
),
),
],
),
SizedBox(height: 4.h),
Row(
children: [
Icon(
Icons.access_time,
size: 14.sp,
color: AppColors.successGreen,
),
SizedBox(width: 8.w),
Text(
"Jam $jadwalJam WIB",
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
),
_buildDetailRow(
"Tanggal Ajuan",
date != null
? "${date.day}/${date.month}/${date.year}"
: "-",
),
_buildDetailRow("Nama Siswa", data['student_name'] ?? "-"),
_buildDetailRow("Kelas", data['kelas'] ?? "-"),
_buildDetailRow(
"Kategori Saat Diajukan",
data['Kategori Siswa'] ?? "-",
),
_buildDetailRow(
"Poin Pelanggaran",
"${data['total_poin'] ?? 0} Poin",
),
_buildDetailRow("Rata-rata Nilai", formattedAvg),
if (data['catatan_guru'] != null)
_buildDetailRow(
"Catatan Sekolah",
data['catatan_guru'],
color: Colors.black87,
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text(
"Tutup",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
);
}
Widget _buildDetailRow(
String label,
String value, {
Color? color,
bool isBold = false,
}) {
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: TextStyle(fontSize: 11.sp, color: Colors.grey)),
Text(
value,
style: TextStyle(
fontSize: 14.sp,
fontWeight: isBold ? FontWeight.bold : FontWeight.w500,
color: color ?? AppColors.textPrimary,
),
),
],
),
);
}
void _showHistorySheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder:
(context) => Container(
height: MediaQuery.of(context).size.height * 0.7,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(24.r)),
),
child: Column(
children: [
SizedBox(height: 12.h),
Container(
width: 40.w,
height: 4.h,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
Padding(
padding: EdgeInsets.all(24.w),
child: Row(
children: [
const Icon(
Icons.history_edu_rounded,
color: AppColors.primaryBlue,
),
SizedBox(width: 12.w),
Text(
"Riwayat Kunjungan Kelas ${widget.className}",
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
],
),
),
const Divider(height: 1),
Expanded(
child: FutureBuilder<List<Map<String, dynamic>>>(
future: StudentService().getBimbinganHistory(
widget.studentId,
widget.className,
),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(
child: Text(
"Belum ada riwayat di kelas ini",
style: TextStyle(
color: Colors.grey,
fontSize: 14.sp,
),
),
);
}
return ListView.builder(
padding: EdgeInsets.symmetric(
horizontal: 20.w,
vertical: 10.h,
),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final item = snapshot.data![index];
final date =
(item['createdAt'] as Timestamp?)?.toDate();
final String status =
item['status'] ?? 'Menunggu Konfirmasi';
return Card(
margin: EdgeInsets.only(bottom: 12.h),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
side: BorderSide(color: Colors.grey.shade100),
),
child: InkWell(
onTap: () => _showDetailBimbingan(context, item),
borderRadius: BorderRadius.circular(12.r),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: _getStatusColor(
status,
).withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
_getStatusIcon(status),
color: _getStatusColor(status),
size: 20.sp,
),
),
SizedBox(width: 16.w),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
status,
style: TextStyle(
fontWeight: FontWeight.bold,
color: _getStatusColor(status),
),
),
Text(
date != null
? "${date.day}/${date.month}/${date.year}"
: "-",
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
),
),
],
),
],
),
Icon(
Icons.chevron_right_rounded,
color: Colors.grey[400],
),
],
),
),
),
);
},
);
},
),
),
],
),
),
);
}
void _showBimbinganDialog(BuildContext context) {
showDialog(
context: context,
builder:
(context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
title: const Text("Konfirmasi Ajuan"),
content: const Text(
"Apakah Anda yakin ingin mengajukan permohonan kunjungan? Ajuan hanya dapat dikirim satu kali dalam sebulan.",
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text(
"Batal",
style: TextStyle(color: Colors.black),
),
),
ElevatedButton(
onPressed: () async {
Navigator.pop(context);
setState(() => _isLoading = true);
try {
// 1. Simpan pengajuan bimbingan ke Firestore
await FirebaseFirestore.instance
.collection('Bimbingan')
.add({
'student_id': widget.studentId,
'student_name': widget.studentName,
'kelas': widget.className,
'total_poin': widget.totalPoin,
'Nilai Rata-rata': widget.averageScore,
'Kategori Siswa': widget.category,
'status': 'Menunggu Konfirmasi',
'createdAt': FieldValue.serverTimestamp(),
});
// 2. Kirim Push Notifikasi FCM & Create dokumen notifikasi
await StudentService().sendBimbinganNotification(
studentId: widget.studentId,
studentName: widget.studentName,
className: widget.className,
category: widget.category,
);
if (mounted) {
setState(() {
_isAlreadySent = true;
_isLoading = false;
});
// 3. Tampilkan Pop-up Berhasil
_showSuccessDialog(
context,
"Permohonan kunjungan Anda telah berhasil dikirim ke sekolah. Silakan cek riwayat secara berkala.",
);
}
} catch (e) {
if (mounted) setState(() => _isLoading = false);
debugPrint("Gagal mengajukan bimbingan: $e");
}
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.dangerRed,
foregroundColor: Colors.white,
),
child: const Text("Ajukan"),
),
],
),
);
}
@override
Widget build(BuildContext context) {
Color statusColor;
IconData statusIcon;
bool showBimbinganButton = false;
switch (widget.category) {
case 'Siswa Teladan':
statusColor = AppColors.successGreen;
statusIcon = Icons.verified_rounded;
break;
case 'Siswa Bermasalah':
statusColor = AppColors.dangerRed;
statusIcon = Icons.report_problem_rounded;
showBimbinganButton = true;
break;
case 'Siswa Potensial':
statusColor = AppColors.warningOrange;
statusIcon = Icons.insights_rounded;
break;
default:
statusColor = Colors.grey;
statusIcon = Icons.help_outline_rounded;
}
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(statusIcon, color: statusColor, size: 28.sp),
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Kategori Siswa",
style: TextStyle(
color: AppColors.textSecondary,
fontSize: 12.sp,
),
),
InkWell(
onTap: () => _showHistorySheet(context),
borderRadius: BorderRadius.circular(4.r),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 4.h,
),
decoration: BoxDecoration(
color: AppColors.primaryBlue.withOpacity(0.05),
borderRadius: BorderRadius.circular(4.r),
),
child: Row(
children: [
Icon(
Icons.history_edu_rounded,
size: 14.sp,
color: AppColors.primaryBlue,
),
SizedBox(width: 4.w),
Text(
"Riwayat Kunjungan",
style: TextStyle(
fontSize: 10.sp,
color: AppColors.primaryBlue,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
Text(
widget.category.isEmpty || widget.category == "-"
? "Belum Ditentukan"
: widget.category,
style: TextStyle(
color: statusColor,
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
if (showBimbinganButton) ...[
SizedBox(height: 16.h),
Divider(color: Colors.grey.shade100, height: 1),
SizedBox(height: 12.h),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed:
(_isAlreadySent || _isLoading)
? null
: () => _showBimbinganDialog(context),
label: Text(
_isLoading
? "Memproses..."
: _isAlreadySent
? "Permohonan Sudah Dikirim"
: "Ajukan Kunjungan ke Sekolah",
),
style: ElevatedButton.styleFrom(
backgroundColor:
_isAlreadySent ? Colors.grey : AppColors.dangerRed,
foregroundColor: Colors.white,
disabledBackgroundColor: Colors.grey.shade300,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.r),
),
padding: EdgeInsets.symmetric(vertical: 10.h),
),
),
),
],
],
),
);
}
}