362 lines
12 KiB
Dart
Raw Normal View History

2025-07-23 15:02:40 +04:00
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:my_attendance/screens/history_screen.dart';
import 'package:my_attendance/screens/leave_request_screen.dart';
import 'package:my_attendance/screens/correction_screen.dart';
import 'package:my_attendance/services/attendance_service.dart';
import 'package:my_attendance/services/settings_service.dart';
import 'package:my_attendance/widgets/settings_dialog.dart';
import 'package:my_attendance/widgets/info_dialog.dart';
import 'package:provider/provider.dart';
import 'package:my_attendance/models/attendance_entry.dart';
import 'package:my_attendance/widgets/stats_dialog.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Timer? _timer;
@override
void initState() {
super.initState();
// Periodically update the UI to check if logout is available
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (mounted) {
setState(() {});
}
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _showSettingsDialog() {
showDialog(context: context, builder: (context) => const SettingsDialog());
}
@override
Widget build(BuildContext context) {
final attendanceService = Provider.of<AttendanceService>(context);
final settingsService = Provider.of<SettingsService>(context);
final todayEntry = attendanceService.todayEntry;
final isLoggedIn = todayEntry != null &&
todayEntry.entryType == EntryType.work &&
todayEntry.logoutTime == null;
final hasLoggedOut = todayEntry != null &&
todayEntry.entryType == EntryType.work &&
todayEntry.logoutTime != null;
final isDayFinalized = todayEntry != null &&
(todayEntry.entryType == EntryType.leave ||
todayEntry.entryType == EntryType.wfh);
bool canLogOut = false;
if (isLoggedIn) {
final workDuration = settingsService.settings.dailyWorkDuration;
if (todayEntry.loginTime != null) {
final timeWorked = DateTime.now().difference(todayEntry.loginTime!);
canLogOut = timeWorked >= workDuration;
}
}
return Scaffold(
appBar: AppBar(
title: const Text('My Attendance'),
actions: [
IconButton(
icon: const Icon(Icons.history),
onPressed: () =>
Navigator.of(context).pushNamed(HistoryScreen.routeName),
),
IconButton(
icon: const Icon(Icons.bar_chart),
onPressed: () async {
await Provider.of<AttendanceService>(
context,
listen: false,
).calculateStats();
showDialog(context: context, builder: (_) => const StatsDialog());
},
),
IconButton(
icon: const Icon(Icons.edit_calendar_outlined),
onPressed: () =>
Navigator.of(context).pushNamed(CorrectionScreen.routeName),
),
PopupMenuButton<String>(
icon: const Icon(Icons.settings),
onSelected: (value) {
if (value == 'working_hour') {
_showSettingsDialog();
} else if (value == 'info') {
showDialog(
context: context,
builder: (context) => const InfoDialog(),
);
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'working_hour',
child: Text('Working Hour'),
),
const PopupMenuItem(
value: 'info',
child: Text('Info'),
),
],
),
],
),
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (attendanceService.isLoading && todayEntry == null)
const Center(child: CircularProgressIndicator())
else
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.surfaceContainerHighest,
Theme.of(context).colorScheme.surfaceContainer,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
),
child: isLoggedIn
? _buildLoggedInUI(
context,
todayEntry.loginTime!,
settingsService.settings.dailyWorkDuration,
)
: hasLoggedOut
? _buildLoggedOutUI(context, todayEntry)
: isDayFinalized
? _buildFinalizedUI(context, todayEntry)
: _buildNotLoggedInUI(context),
),
const SizedBox(height: 32),
ElevatedButton.icon(
onPressed: isLoggedIn || hasLoggedOut || isDayFinalized
? null
: attendanceService.logIn,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
icon: const Icon(Icons.login),
label: const Text('Log In'),
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed:
isLoggedIn && canLogOut ? attendanceService.logOut : null,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Theme.of(context).colorScheme.error,
foregroundColor: Theme.of(context).colorScheme.onError,
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
icon: const Icon(Icons.logout),
label: const Text('Log Out'),
),
const SizedBox(height: 24),
const Divider(),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: isDayFinalized || isLoggedIn || hasLoggedOut
? null
: () {
Navigator.of(
context,
).pushNamed(LeaveRequestScreen.routeName);
},
icon: const Icon(Icons.beach_access),
label: const Text('Request Leave'),
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: OutlinedButton.icon(
onPressed: isDayFinalized || isLoggedIn || hasLoggedOut
? null
: attendanceService.markAsWfh,
icon: const Icon(Icons.home_work),
label: const Text('Work From Home'),
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
],
),
),
),
),
);
}
Widget _buildLoggedInUI(
BuildContext context,
DateTime loginTime,
Duration workDuration,
) {
final now = DateTime.now();
final timeWorked = now.difference(loginTime);
final remaining = workDuration - timeWorked;
return Column(
children: [
Text(
'Logged in at: ${DateFormat.jm().format(loginTime)}',
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
Text('Time Worked', style: Theme.of(context).textTheme.bodyMedium),
Text(
_formatDuration(timeWorked),
style: Theme.of(
context,
).textTheme.displaySmall?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
if (remaining > Duration.zero) ...[
Text('Time Remaining', style: Theme.of(context).textTheme.bodyMedium),
Text(
_formatDuration(remaining),
style: Theme.of(context).textTheme.headlineMedium,
),
],
],
);
}
Widget _buildLoggedOutUI(BuildContext context, AttendanceEntry entry) {
if (entry.loginTime == null || entry.logoutTime == null) {
return const Text("Data Error"); // Should not happen for this UI state
}
final totalWork = entry.logoutTime!.difference(entry.loginTime!);
return Column(
children: [
Text(
'Checked out for today.',
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Text('Total Work', style: Theme.of(context).textTheme.bodyMedium),
Text(
_formatDuration(totalWork),
style: Theme.of(
context,
).textTheme.displaySmall?.copyWith(fontWeight: FontWeight.bold),
),
],
);
}
Widget _buildFinalizedUI(BuildContext context, AttendanceEntry entry) {
String message = 'Status for today:';
String status = '';
if (entry.entryType == EntryType.wfh) {
status = 'Work From Home';
} else if (entry.entryType == EntryType.leave) {
switch (entry.leaveType) {
case LeaveType.sick:
status = 'On Sick Leave';
break;
case LeaveType.annual:
status = 'On Annual Leave';
break;
case LeaveType.nationalHoliday:
status = 'National Holiday';
break;
case LeaveType.companyWfh:
status = 'Company WFH';
break;
default:
status = 'On Leave';
}
}
return Column(
children: [
Text(
message,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Text(
status,
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildNotLoggedInUI(BuildContext context) {
return Text(
'Not logged in for today.',
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
);
}
}
String _formatDuration(Duration d) {
d = Duration(seconds: d.inSeconds); // Truncate to seconds
final hours = d.inHours.toString().padLeft(2, '0');
final minutes = d.inMinutes.remainder(60).toString().padLeft(2, '0');
final seconds = d.inSeconds.remainder(60).toString().padLeft(2, '0');
return '$hours:$minutes:$seconds';
}