MIF_E31230910_MP-HRIS-MOBILE/lib/widgets/organisms/presensi_section.dart

224 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../core/theme.dart';
import '../../models/presensi_model.dart';
import '../atoms/bouncy_tap.dart';
import '../molecules/presensi_card.dart';
class PresensiSection extends StatelessWidget {
final PresensiModel? presensiToday;
final VoidCallback onPresensi;
final bool isLoading;
const PresensiSection({
Key? key,
this.presensiToday,
required this.onPresensi,
this.isLoading = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final now = DateTime.now();
final dateStr = "${now.day} ${_monthName(now.month)} ${now.year}";
final shift = presensiToday?.shift ?? "-";
final jamMasuk = presensiToday?.jamMasuk;
final jamPulang = presensiToday?.jamPulang;
final statusMasuk = _getStatusMasuk(now);
final statusPulang = _getStatusPulang(now);
final sudahMasuk = presensiToday?.sudahAbsenMasuk ?? false;
final sudahPulang = presensiToday?.sudahAbsenPulang ?? false;
String buttonText;
bool buttonDisabled = false;
if (!sudahMasuk) {
buttonText = "Absen Masuk";
} else if (!sudahPulang) {
buttonText = "Absen Pulang";
} else {
buttonText = "Selesai ✓";
buttonDisabled = true;
}
return Column(
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Presensi Hari ini", style: AppTheme.heading3),
const SizedBox(height: 4),
Text(dateStr, style: AppTheme.bodySmall, overflow: TextOverflow.ellipsis),
],
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppTheme.bgCard,
borderRadius: BorderRadius.circular(AppTheme.radiusFull),
),
child: Text(
shift,
style: AppTheme.labelMedium.copyWith(color: AppTheme.textPrimary),
),
),
],
),
const SizedBox(height: AppTheme.spacingMd),
if (presensiToday == null)
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24),
decoration: BoxDecoration(
color: AppTheme.bgCard,
borderRadius: BorderRadius.circular(AppTheme.radiusMd),
),
child: Column(
children: [
Icon(Icons.event_busy_outlined, size: 44, color: AppTheme.textTertiary.withOpacity(0.5)),
const SizedBox(height: 12),
Text(
"Tidak ada jadwal kerja hari ini",
style: AppTheme.bodyMedium.copyWith(color: AppTheme.textSecondary, fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
Text(
"Hubungi admin jika Anda merasa ini salah",
style: AppTheme.bodySmall.copyWith(color: AppTheme.textTertiary),
textAlign: TextAlign.center,
),
],
),
)
else ...[
Row(
children: [
Expanded(
child: PresensiCard(
type: PresensiType.datang,
time: jamMasuk,
status: statusMasuk,
statusText: presensiToday?.statusMasuk,
),
),
const SizedBox(width: AppTheme.spacingMd),
Expanded(
child: PresensiCard(
type: PresensiType.pulang,
time: jamPulang,
status: statusPulang,
statusText: presensiToday?.statusPulang,
),
),
],
),
const SizedBox(height: AppTheme.spacingLg),
BouncyTap(
onPressed: buttonDisabled || isLoading ? null : () {
HapticFeedback.lightImpact();
onPresensi();
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
height: 50,
decoration: BoxDecoration(
color: buttonDisabled
? AppTheme.statusGreen.withOpacity(0.5)
: (sudahMasuk ? AppTheme.primaryOrange : AppTheme.primaryDark),
borderRadius: BorderRadius.circular(AppTheme.radiusFull),
boxShadow: buttonDisabled ? [] : AppTheme.glowPrimary,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (isLoading)
const SizedBox(
width: 20, height: 20,
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
)
else ...[
Icon(sudahMasuk ? Icons.logout : Icons.fingerprint, color: Colors.white, size: 24),
const SizedBox(width: 8),
Text(
buttonText,
style: AppTheme.labelLarge.copyWith(color: Colors.white),
),
],
],
),
),
),
],
],
);
}
PresensiStatus _getStatusMasuk(DateTime now) {
if (presensiToday == null) return PresensiStatus.belumWaktunya;
if (presensiToday!.sudahAbsenMasuk) {
final status = presensiToday!.statusMasuk?.toLowerCase() ?? '';
if (status.contains('terlambat')) {
return PresensiStatus.adaMasalah;
}
return PresensiStatus.sudahAbsen;
}
return _isNearTime(now, presensiToday!.jadwalJamMasuk)
? PresensiStatus.waktunya
: PresensiStatus.belumWaktunya;
}
PresensiStatus _getStatusPulang(DateTime now) {
if (presensiToday == null) return PresensiStatus.belumWaktunya;
if (!presensiToday!.sudahAbsenMasuk) return PresensiStatus.belumWaktunya;
if (presensiToday!.sudahAbsenPulang) {
final status = presensiToday!.statusPulang?.toLowerCase() ?? '';
if (status.contains('awal') || status.contains('cepat')) {
return PresensiStatus.adaMasalah;
}
return PresensiStatus.sudahAbsen;
}
return _isNearTime(now, presensiToday!.jadwalJamPulang)
? PresensiStatus.waktunya
: PresensiStatus.belumWaktunya;
}
bool _isNearTime(DateTime now, String? timeStr) {
if (timeStr == null) return false;
try {
final parts = timeStr.split(':');
final hour = int.parse(parts[0]);
final minute = int.parse(parts[1]);
final target = DateTime(now.year, now.month, now.day, hour, minute);
final diff = now.difference(target).inMinutes;
return diff >= -30;
} catch (_) {
return false;
}
}
String _monthName(int month) {
const months = [
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
"Juli", "Agustus", "September", "Oktober", "November", "Desember"
];
return months[month - 1];
}
}