diff --git a/lib/models/advance_request.dart b/lib/models/advance_request.dart index 4791151..32d240d 100644 --- a/lib/models/advance_request.dart +++ b/lib/models/advance_request.dart @@ -2,12 +2,12 @@ class AdvanceRequest { final String id; final double amount; final String reason; - final String status; // e.g., "pending", "approved", "rejected" + final String status; // "waiting", "approved", or "denied" AdvanceRequest({ required this.id, required this.amount, required this.reason, - this.status = "pending", + this.status = "waiting", // Default status is "waiting" }); } \ No newline at end of file diff --git a/lib/models/leave_request.dart b/lib/models/leave_request.dart index 6599a11..fc9d282 100644 --- a/lib/models/leave_request.dart +++ b/lib/models/leave_request.dart @@ -1,4 +1,5 @@ -import 'package:flutter/material.dart' show TimeOfDay; +// LeaveRequest model +import 'package:flutter/material.dart'; class LeaveRequest { final String id; @@ -10,7 +11,8 @@ class LeaveRequest { final TimeOfDay toTime; final String reason; final DateTime requestDate; - final String status; // e.g + final String status; // "waiting", "approved", or "denied" + LeaveRequest({ required this.id, required this.leaveType, @@ -21,6 +23,8 @@ class LeaveRequest { required this.toTime, required this.reason, required this.requestDate, - this.status = "pending", + this.status = "waiting", // Default status is "waiting" }); } + +// AdvanceRequest model diff --git a/lib/screens/holiday_screen.dart b/lib/screens/holiday_screen.dart index c1eb540..05bcfe2 100644 --- a/lib/screens/holiday_screen.dart +++ b/lib/screens/holiday_screen.dart @@ -15,20 +15,33 @@ class HolidayScreen extends StatefulWidget { class _HolidayScreenState extends State { int activeTab = 0; // 0 = السلف | 1 = الأجازات + // Use the singleton instance final RequestService _requestService = RequestService(); - late Future> _leaveRequestsFuture; - late Future> _advanceRequestsFuture; + late List _leaveRequests; + late List _advanceRequests; @override void initState() { super.initState(); - _refreshData(); + _initializeData(); } - void _refreshData() { - setState(() { - _leaveRequestsFuture = Future.value(_requestService.leaveRequests); - _advanceRequestsFuture = Future.value(_requestService.advanceRequests); + void _initializeData() async { + // Get initial data + _leaveRequests = await _requestService.getLeaveRequests(); + _advanceRequests = await _requestService.getAdvanceRequests(); + + // Listen for updates + _requestService.leaveRequestsStream.listen((requests) { + setState(() { + _leaveRequests = requests; + }); + }); + + _requestService.advanceRequestsStream.listen((requests) { + setState(() { + _advanceRequests = requests; + }); }); } @@ -36,7 +49,7 @@ class _HolidayScreenState extends State { switch (status) { case 'approved': return Colors.green; - case 'rejected': + case 'denied': return Colors.red; default: return Colors.orange; @@ -47,13 +60,24 @@ class _HolidayScreenState extends State { switch (status) { case 'approved': return 'موافق عليه'; - case 'rejected': + case 'denied': return 'مرفوض'; default: return 'قيد الانتظار'; } } + String _getStatusIconPath(String status) { + switch (status) { + case 'approved': + return "assets/images/yes.svg"; + case 'denied': + return "assets/images/no.svg"; + default: + return "assets/images/waiting.svg"; + } + } + @override Widget build(BuildContext context) { return Directionality( @@ -148,14 +172,15 @@ class _HolidayScreenState extends State { svgPath: "assets/images/money2.svg", iconWidth: 38, iconHeight: 30, - onTap: () { + onTap: () async { // Navigate to RequestAdvanceScreen - Navigator.push( + await Navigator.push( context, MaterialPageRoute( builder: (context) => const RequestAdvanceScreen(), ), - ).then((_) => _refreshData()); // Refresh data when returning + ); + // Data will be updated automatically through the stream }, ), const SizedBox(height: 18), @@ -166,13 +191,14 @@ class _HolidayScreenState extends State { svgPath: "assets/images/plus.svg", iconWidth: 30, iconHeight: 30, - onTap: () { - Navigator.push( + onTap: () async { + await Navigator.push( context, MaterialPageRoute( builder: (context) => const RequestLeaveScreen(), ), - ).then((_) => _refreshData()); // Refresh data when returning + ); + // Data will be updated automatically through the stream }, ), ], @@ -184,80 +210,60 @@ class _HolidayScreenState extends State { } Widget _buildLeaveRequestsTab() { - return FutureBuilder>( - future: _leaveRequestsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator(color: Colors.white)); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}', style: TextStyle(color: Colors.white))); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center( - child: Text( - 'لا توجد طلبات أجازة', - style: TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ); - } - - final leaveRequests = snapshot.data!; - return RefreshIndicator( - onRefresh: () async { - _refreshData(); - }, - color: Colors.white, - child: ListView.builder( - padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), - itemCount: leaveRequests.length, - itemBuilder: (context, index) { - final request = leaveRequests[index]; - return _buildLeaveRequestCard(request); - }, + if (_leaveRequests.isEmpty) { + return const Center( + child: Text( + 'لا توجد طلبات أجازة', + style: TextStyle( + color: Colors.white, + fontSize: 18, ), - ); + ), + ); + } + + return RefreshIndicator( + onRefresh: () async { + _initializeData(); }, + color: Colors.white, + child: ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), + itemCount: _leaveRequests.length, + itemBuilder: (context, index) { + final request = _leaveRequests[index]; + return _buildLeaveRequestCard(request); + }, + ), ); } Widget _buildAdvanceRequestsTab() { - return FutureBuilder>( - future: _advanceRequestsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator(color: Colors.white)); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}', style: TextStyle(color: Colors.white))); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center( - child: Text( - 'لا توجد طلبات سلف', - style: TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ); - } - - final advanceRequests = snapshot.data!; - return RefreshIndicator( - onRefresh: () async { - _refreshData(); - }, - color: Colors.white, - child: ListView.builder( - padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), - itemCount: advanceRequests.length, - itemBuilder: (context, index) { - final request = advanceRequests[index]; - return _buildAdvanceRequestCard(request); - }, + if (_advanceRequests.isEmpty) { + return const Center( + child: Text( + 'لا توجد طلبات سلف', + style: TextStyle( + color: Colors.white, + fontSize: 18, ), - ); + ), + ); + } + + return RefreshIndicator( + onRefresh: () async { + _initializeData(); }, + color: Colors.white, + child: ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), + itemCount: _advanceRequests.length, + itemBuilder: (context, index) { + final request = _advanceRequests[index]; + return _buildAdvanceRequestCard(request); + }, + ), ); } @@ -283,19 +289,18 @@ class _HolidayScreenState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + // Status icon instead of text Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getStatusColor(request.status), - borderRadius: BorderRadius.circular(20), + shape: BoxShape.circle, ), - child: Text( - _getStatusText(request.status), - style: const TextStyle( - color: Colors.white, - fontSize: 14, - fontWeight: FontWeight.w600, - ), + child: SvgPicture.asset( + _getStatusIconPath(request.status), + width: 24, + height: 24, + color: Colors.white, ), ), Text( @@ -399,43 +404,24 @@ class _HolidayScreenState extends State { ), ), - // Action buttons for pending requests - if (request.status == 'pending') ...[ - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // Status text below the card + const SizedBox(height: 8), + Directionality( + textDirection: TextDirection.rtl, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - ElevatedButton( - onPressed: () { - _updateLeaveRequestStatus(request.id, 'approved'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.green, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), + Text( + _getStatusText(request.status), + style: TextStyle( + color: _getStatusColor(request.status), + fontSize: 14, + fontWeight: FontWeight.w600, ), - child: const Text('موافق'), - ), - ElevatedButton( - onPressed: () { - _updateLeaveRequestStatus(request.id, 'rejected'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - ), - child: const Text('رفض'), ), ], ), - ], + ), ], ), ); @@ -459,23 +445,22 @@ class _HolidayScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - // Header with status + // Header with status icon Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + // Status icon instead of text Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getStatusColor(request.status), - borderRadius: BorderRadius.circular(20), + shape: BoxShape.circle, ), - child: Text( - _getStatusText(request.status), - style: const TextStyle( - color: Colors.white, - fontSize: 14, - fontWeight: FontWeight.w600, - ), + child: SvgPicture.asset( + _getStatusIconPath(request.status), + width: 24, + height: 24, + color: Colors.white, ), ), Text( @@ -526,72 +511,28 @@ class _HolidayScreenState extends State { ), ), - // Request date - removed since advance requests don't have request date - // Action buttons for pending requests - if (request.status == 'pending') ...[ - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // Status text below the card + const SizedBox(height: 8), + Directionality( + textDirection: TextDirection.rtl, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - ElevatedButton( - onPressed: () { - _updateAdvanceRequestStatus(request.id, 'approved'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.green, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), + Text( + _getStatusText(request.status), + style: TextStyle( + color: _getStatusColor(request.status), + fontSize: 14, + fontWeight: FontWeight.w600, ), - child: const Text('موافق'), - ), - ElevatedButton( - onPressed: () { - _updateAdvanceRequestStatus(request.id, 'rejected'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - ), - child: const Text('رفض'), ), ], ), - ], + ), ], ), ); } - - void _updateLeaveRequestStatus(String id, String status) async { - await _requestService.updateLeaveRequestStatus(id, status); - _refreshData(); - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('تم ${status == 'approved' ? 'قبول' : 'رفض'} الطلب'), - backgroundColor: status == 'approved' ? Colors.green : Colors.red, - ), - ); - } - - void _updateAdvanceRequestStatus(String id, String status) async { - await _requestService.updateAdvanceRequestStatus(id, status); - _refreshData(); - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('تم ${status == 'approved' ? 'قبول' : 'رفض'} الطلب'), - backgroundColor: status == 'approved' ? Colors.green : Colors.red, - ), - ); - } } class _HolidayActionButton extends StatelessWidget { diff --git a/lib/screens/request_advance_scrren.dart b/lib/screens/request_advance_scrren.dart index 34d643f..8354675 100644 --- a/lib/screens/request_advance_scrren.dart +++ b/lib/screens/request_advance_scrren.dart @@ -20,6 +20,7 @@ class _RequestAdvanceScreenState extends State { // Text controller for reason final TextEditingController reasonController = TextEditingController(); + // Use the singleton instance final RequestService _requestService = RequestService(); // Method to save the advance request @@ -48,26 +49,37 @@ class _RequestAdvanceScreenState extends State { return; } - // Create a new advance request + // Create a new advance request with default status "waiting" final advanceRequest = AdvanceRequest( id: DateTime.now().millisecondsSinceEpoch.toString(), amount: amount, reason: reasonController.text, + status: "waiting", // Default status ); - // Save the advance request - await _requestService.addAdvanceRequest(advanceRequest); + try { + // Save the advance request + await _requestService.addAdvanceRequest(advanceRequest); - // Show a success message - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('تم إرسال طلب السلفة بنجاح'), - backgroundColor: Colors.green, - ), - ); + // Show a success message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('تم إرسال طلب السلفة بنجاح'), + backgroundColor: Colors.green, + ), + ); - // Navigate back to the previous screen - Navigator.pop(context); + // Navigate back to the previous screen + Navigator.pop(context); + } catch (e) { + // Show an error message if something went wrong + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('حدث خطأ: $e'), + backgroundColor: Colors.red, + ), + ); + } } @override diff --git a/lib/screens/request_leave_screen.dart b/lib/screens/request_leave_screen.dart index db5a856..25546b2 100644 --- a/lib/screens/request_leave_screen.dart +++ b/lib/screens/request_leave_screen.dart @@ -30,6 +30,7 @@ class _RequestLeaveScreenState extends State { // Text controller for reason final TextEditingController reasonController = TextEditingController(); + // Use the singleton instance final RequestService _requestService = RequestService(); /// PICK DATE @@ -91,7 +92,7 @@ class _RequestLeaveScreenState extends State { return; } - // Create a new leave request + // Create a new leave request with default status "waiting" final leaveRequest = LeaveRequest( id: DateTime.now().millisecondsSinceEpoch.toString(), leaveType: leaveType, @@ -102,21 +103,32 @@ class _RequestLeaveScreenState extends State { toTime: toTime!, reason: reasonController.text, requestDate: DateTime.now(), + status: "waiting", // Default status ); - // Save the leave request - await _requestService.addLeaveRequest(leaveRequest); + try { + // Save the leave request + await _requestService.addLeaveRequest(leaveRequest); - // Show a success message - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('تم إرسال طلب الأجازة بنجاح'), - backgroundColor: Colors.green, - ), - ); + // Show a success message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('تم إرسال طلب الأجازة بنجاح'), + backgroundColor: Colors.green, + ), + ); - // Navigate back to the previous screen - Navigator.pop(context); + // Navigate back to the previous screen + Navigator.pop(context); + } catch (e) { + // Show an error message if something went wrong + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('حدث خطأ: $e'), + backgroundColor: Colors.red, + ), + ); + } } @override diff --git a/lib/services/request_service.dart b/lib/services/request_service.dart index a04f31b..723fd4e 100644 --- a/lib/services/request_service.dart +++ b/lib/services/request_service.dart @@ -1,31 +1,57 @@ -import 'package:flutter/material.dart'; +import 'dart:async'; import '../models/leave_request.dart'; import '../models/advance_request.dart'; class RequestService { + // Singleton implementation static final RequestService _instance = RequestService._internal(); factory RequestService() => _instance; RequestService._internal(); final List _leaveRequests = []; final List _advanceRequests = []; + + // Stream controllers to notify listeners of changes + final _leaveRequestsController = StreamController>.broadcast(); + final _advanceRequestsController = StreamController>.broadcast(); + + // Stream getters + Stream> get leaveRequestsStream => _leaveRequestsController.stream; + Stream> get advanceRequestsStream => _advanceRequestsController.stream; - List get leaveRequests => List.unmodifiable(_leaveRequests); - List get advanceRequests => List.unmodifiable(_advanceRequests); + // Get all leave requests as a Future + Future> getLeaveRequests() async { + print("Getting ${_leaveRequests.length} leave requests"); + return List.from(_leaveRequests); + } + // Get all advance requests as a Future + Future> getAdvanceRequests() async { + print("Getting ${_advanceRequests.length} advance requests"); + return List.from(_advanceRequests); + } + + // Add a new leave request Future addLeaveRequest(LeaveRequest request) async { _leaveRequests.add(request); - // In a real app, you would save this to a database or API + print("Added leave request: ${request.id}, total: ${_leaveRequests.length}"); + // Notify listeners + _leaveRequestsController.add(List.from(_leaveRequests)); } + // Add a new advance request Future addAdvanceRequest(AdvanceRequest request) async { _advanceRequests.add(request); - // In a real app, you would save this to a database or API + print("Added advance request: ${request.id}, total: ${_advanceRequests.length}"); + // Notify listeners + _advanceRequestsController.add(List.from(_advanceRequests)); } + // Update leave request status Future updateLeaveRequestStatus(String id, String status) async { final index = _leaveRequests.indexWhere((request) => request.id == id); if (index != -1) { + // Create a new request with the updated status final updatedRequest = LeaveRequest( id: _leaveRequests[index].id, leaveType: _leaveRequests[index].leaveType, @@ -36,22 +62,40 @@ class RequestService { toTime: _leaveRequests[index].toTime, reason: _leaveRequests[index].reason, requestDate: _leaveRequests[index].requestDate, - status: status, + status: status, // Updated status ); + + // Replace the old request with the updated one _leaveRequests[index] = updatedRequest; + print("Updated leave request status: $id to $status"); + // Notify listeners + _leaveRequestsController.add(List.from(_leaveRequests)); } } + // Update advance request status Future updateAdvanceRequestStatus(String id, String status) async { final index = _advanceRequests.indexWhere((request) => request.id == id); if (index != -1) { + // Create a new request with the updated status final updatedRequest = AdvanceRequest( id: _advanceRequests[index].id, amount: _advanceRequests[index].amount, reason: _advanceRequests[index].reason, - status: status, + status: status, // Updated status ); + + // Replace the old request with the updated one _advanceRequests[index] = updatedRequest; + print("Updated advance request status: $id to $status"); + // Notify listeners + _advanceRequestsController.add(List.from(_advanceRequests)); } } + + // Dispose method to close streams + void dispose() { + _leaveRequestsController.close(); + _advanceRequestsController.close(); + } } \ No newline at end of file