chnages was made

This commit is contained in:
Daniah Ayad Al-sultani
2025-12-07 12:01:23 +03:00
parent d789bcdc88
commit dbf76b2d30
7 changed files with 672 additions and 11 deletions

View File

@@ -0,0 +1,3 @@
<svg width="11" height="12" viewBox="0 0 11 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.56543 0L4.11881 0.827619L10.0667 4.0374L10.5134 3.20978L4.56543 0ZM3.96178 1.40642L0.84422 7.18347L1.35813 7.4608L4.47568 1.68378L3.96178 1.40642ZM5.03776 1.98707C4.13962 3.69732 4.30305 5.01498 4.94941 5.62581C4.07265 5.41811 2.86134 6.02237 1.91932 7.76357L5.44464 9.66599C6.38303 7.92273 6.2242 6.57951 5.56997 5.9607C6.43498 6.16522 7.62469 5.57817 8.56133 3.88858L5.03776 1.98707ZM9.10617 4.18257L5.98861 9.95961L6.50255 10.237L9.62009 4.45994L9.10617 4.18257ZM0.446629 7.63241L0.000468303 8.45918L5.94841 11.669L6.39457 10.8422L0.446629 7.63241Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1,13 @@
class AdvanceRequest {
final String id;
final double amount;
final String reason;
final String status; // e.g., "pending", "approved", "rejected"
AdvanceRequest({
required this.id,
required this.amount,
required this.reason,
this.status = "pending",
});
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart' show TimeOfDay;
class LeaveRequest {
final String id;
final String leaveType;
final bool isTimedLeave;
final DateTime fromDate;
final DateTime toDate;
final TimeOfDay fromTime;
final TimeOfDay toTime;
final String reason;
final DateTime requestDate;
final String status; // e.g
LeaveRequest({
required this.id,
required this.leaveType,
required this.isTimedLeave,
required this.fromDate,
required this.toDate,
required this.fromTime,
required this.toTime,
required this.reason,
required this.requestDate,
this.status = "pending",
});
}

View File

@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../screens/request_leave_screen.dart';
import '../screens/request_advance_scrren.dart';
import '../models/leave_request.dart';
import '../models/advance_request.dart';
import '../services/request_service.dart';
class HolidayScreen extends StatefulWidget {
const HolidayScreen({super.key});
@@ -12,6 +15,44 @@ class HolidayScreen extends StatefulWidget {
class _HolidayScreenState extends State<HolidayScreen> {
int activeTab = 0; // 0 = السلف | 1 = الأجازات
final RequestService _requestService = RequestService();
late Future<List<LeaveRequest>> _leaveRequestsFuture;
late Future<List<AdvanceRequest>> _advanceRequestsFuture;
@override
void initState() {
super.initState();
_refreshData();
}
void _refreshData() {
setState(() {
_leaveRequestsFuture = Future.value(_requestService.leaveRequests);
_advanceRequestsFuture = Future.value(_requestService.advanceRequests);
});
}
Color _getStatusColor(String status) {
switch (status) {
case 'approved':
return Colors.green;
case 'rejected':
return Colors.red;
default:
return Colors.orange;
}
}
String _getStatusText(String status) {
switch (status) {
case 'approved':
return 'موافق عليه';
case 'rejected':
return 'مرفوض';
default:
return 'قيد الانتظار';
}
}
@override
Widget build(BuildContext context) {
@@ -19,6 +60,7 @@ class _HolidayScreenState extends State<HolidayScreen> {
textDirection: TextDirection.rtl,
child: Stack(
children: [
// Tabs
Positioned(
top: 40,
left: 0,
@@ -84,6 +126,17 @@ class _HolidayScreenState extends State<HolidayScreen> {
],
),
),
// Content area for requests
Positioned(
top: 100, // Position below the tabs
left: 0,
right: 0,
bottom: 120, // Leave space for action buttons
child: activeTab == 1 ? _buildLeaveRequestsTab() : _buildAdvanceRequestsTab(),
),
// Action buttons
Positioned(
bottom: 30,
right: 20,
@@ -96,14 +149,13 @@ class _HolidayScreenState extends State<HolidayScreen> {
iconWidth: 38,
iconHeight: 30,
onTap: () {
// Navigate to RequestAdvanceScreen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RequestAdvanceScreen(),
),
);
).then((_) => _refreshData()); // Refresh data when returning
},
),
const SizedBox(height: 18),
@@ -120,7 +172,7 @@ class _HolidayScreenState extends State<HolidayScreen> {
MaterialPageRoute(
builder: (context) => const RequestLeaveScreen(),
),
);
).then((_) => _refreshData()); // Refresh data when returning
},
),
],
@@ -130,6 +182,416 @@ class _HolidayScreenState extends State<HolidayScreen> {
),
);
}
Widget _buildLeaveRequestsTab() {
return FutureBuilder<List<LeaveRequest>>(
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);
},
),
);
},
);
}
Widget _buildAdvanceRequestsTab() {
return FutureBuilder<List<AdvanceRequest>>(
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);
},
),
);
},
);
}
Widget _buildLeaveRequestCard(LeaveRequest request) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// Header with type and status
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getStatusColor(request.status),
borderRadius: BorderRadius.circular(20),
),
child: Text(
_getStatusText(request.status),
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
Text(
request.isTimedLeave ? "أجازة زمنية" : request.leaveType,
style: const TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
// Date range
Directionality(
textDirection: TextDirection.rtl,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'من: ${request.fromDate.day}/${request.fromDate.month}/${request.fromDate.year}',
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
const SizedBox(width: 20),
Text(
'إلى: ${request.toDate.day}/${request.toDate.month}/${request.toDate.year}',
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
],
),
),
if (request.isTimedLeave) ...[
const SizedBox(height: 8),
Directionality(
textDirection: TextDirection.rtl,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'من: ${request.fromTime.hour}:${request.fromTime.minute.toString().padLeft(2, '0')}',
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
const SizedBox(width: 20),
Text(
'إلى: ${request.toTime.hour}:${request.toTime.minute.toString().padLeft(2, '0')}',
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
],
),
),
],
const SizedBox(height: 12),
// Reason
Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(8),
),
child: Text(
request.reason,
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
),
),
const SizedBox(height: 12),
// Request date
Directionality(
textDirection: TextDirection.rtl,
child: Text(
'تاريخ الطلب: ${request.requestDate.day}/${request.requestDate.month}/${request.requestDate.year}',
style: const TextStyle(
color: Colors.grey,
fontSize: 14,
),
),
),
// Action buttons for pending requests
if (request.status == 'pending') ...[
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
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),
),
),
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('رفض'),
),
],
),
],
],
),
);
}
Widget _buildAdvanceRequestCard(AdvanceRequest request) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// Header with status
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getStatusColor(request.status),
borderRadius: BorderRadius.circular(20),
),
child: Text(
_getStatusText(request.status),
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
Text(
'طلب سلفة',
style: const TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
// Amount
Directionality(
textDirection: TextDirection.rtl,
child: Text(
'المبلغ: ${request.amount.toStringAsFixed(2)} دع',
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 12),
// Reason
Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(8),
),
child: Text(
request.reason,
style: const TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
),
),
// 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,
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),
),
),
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 {
@@ -189,4 +651,4 @@ class _HolidayActionButton extends StatelessWidget {
),
);
}
}
}

View File

@@ -3,6 +3,8 @@ import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/app_background.dart';
import '../widgets/settings_bar.dart';
import '../widgets/onboarding_button.dart';
import '../models/advance_request.dart';
import '../services/request_service.dart';
class RequestAdvanceScreen extends StatefulWidget {
const RequestAdvanceScreen({super.key});
@@ -18,6 +20,56 @@ class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
// Text controller for reason
final TextEditingController reasonController = TextEditingController();
final RequestService _requestService = RequestService();
// Method to save the advance request
Future<void> _saveAdvanceRequest() async {
if (amountController.text.isEmpty || reasonController.text.isEmpty) {
// Show an error message if fields are empty
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('الرجاء إدخال جميع الحقول'),
backgroundColor: Colors.red,
),
);
return;
}
// Parse the amount to double
final amount = double.tryParse(amountController.text);
if (amount == null || amount <= 0) {
// Show an error message if amount is invalid
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('الرجاء إدخال مبلغ صحيح'),
backgroundColor: Colors.red,
),
);
return;
}
// Create a new advance request
final advanceRequest = AdvanceRequest(
id: DateTime.now().millisecondsSinceEpoch.toString(),
amount: amount,
reason: reasonController.text,
);
// Save the advance request
await _requestService.addAdvanceRequest(advanceRequest);
// Show a success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('تم إرسال طلب السلفة بنجاح'),
backgroundColor: Colors.green,
),
);
// Navigate back to the previous screen
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -155,7 +207,8 @@ class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
controller: reasonController,
maxLines: 5,
textAlign:
TextAlign.right, // Added this to align text to the right
TextAlign
.right, // Added this to align text to the right
decoration: const InputDecoration(
border: InputBorder.none,
hintText:
@@ -175,7 +228,7 @@ class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
text: "تأكيد الطلب",
backgroundColor: const Color(0xFFD1FEF0),
textColor: Colors.black,
onPressed: () {},
onPressed: _saveAdvanceRequest, // Call the save method
),
),
@@ -190,4 +243,4 @@ class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
),
);
}
}
}

View File

@@ -3,6 +3,8 @@ import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/app_background.dart';
import '../widgets/settings_bar.dart';
import '../widgets/onboarding_button.dart';
import '../models/leave_request.dart';
import '../services/request_service.dart';
class RequestLeaveScreen extends StatefulWidget {
const RequestLeaveScreen({super.key});
@@ -28,6 +30,8 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
// Text controller for reason
final TextEditingController reasonController = TextEditingController();
final RequestService _requestService = RequestService();
/// PICK DATE
Future<void> pickDate(bool isFrom) async {
DateTime initial = isFrom ? fromDate! : toDate!;
@@ -74,6 +78,47 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
}
}
// Method to save the leave request
Future<void> _saveLeaveRequest() async {
if (reasonController.text.isEmpty) {
// Show an error message if reason is empty
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('الرجاء إدخال السبب'),
backgroundColor: Colors.red,
),
);
return;
}
// Create a new leave request
final leaveRequest = LeaveRequest(
id: DateTime.now().millisecondsSinceEpoch.toString(),
leaveType: leaveType,
isTimedLeave: isTimedLeave,
fromDate: fromDate!,
toDate: toDate!,
fromTime: fromTime!,
toTime: toTime!,
reason: reasonController.text,
requestDate: DateTime.now(),
);
// Save the leave request
await _requestService.addLeaveRequest(leaveRequest);
// Show a success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('تم إرسال طلب الأجازة بنجاح'),
backgroundColor: Colors.green,
),
);
// Navigate back to the previous screen
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -104,7 +149,7 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
children: [
// Fixed alignment for "طلب أجازة"
const Align(
alignment: Alignment.topRight, // Changed to centerRight
alignment: Alignment.topRight,
child: Text(
"طلب أجازة ",
style: TextStyle(
@@ -459,7 +504,7 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
text: "تأكيد الطلب",
backgroundColor: const Color(0xFFD1FEF0),
textColor: Colors.black, // Changed to black
onPressed: () {},
onPressed: _saveLeaveRequest, // Call the save method
),
),
@@ -559,11 +604,13 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
case 4:
return "الخميس";
case 5:
return "السبت";
return "الجمعة";
case 6:
return "السبت";
case 7:
return "الأحد";
default:
return "";
}
}
}
}

View File

@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import '../models/leave_request.dart';
import '../models/advance_request.dart';
class RequestService {
static final RequestService _instance = RequestService._internal();
factory RequestService() => _instance;
RequestService._internal();
final List<LeaveRequest> _leaveRequests = [];
final List<AdvanceRequest> _advanceRequests = [];
List<LeaveRequest> get leaveRequests => List.unmodifiable(_leaveRequests);
List<AdvanceRequest> get advanceRequests => List.unmodifiable(_advanceRequests);
Future<void> addLeaveRequest(LeaveRequest request) async {
_leaveRequests.add(request);
// In a real app, you would save this to a database or API
}
Future<void> addAdvanceRequest(AdvanceRequest request) async {
_advanceRequests.add(request);
// In a real app, you would save this to a database or API
}
Future<void> updateLeaveRequestStatus(String id, String status) async {
final index = _leaveRequests.indexWhere((request) => request.id == id);
if (index != -1) {
final updatedRequest = LeaveRequest(
id: _leaveRequests[index].id,
leaveType: _leaveRequests[index].leaveType,
isTimedLeave: _leaveRequests[index].isTimedLeave,
fromDate: _leaveRequests[index].fromDate,
toDate: _leaveRequests[index].toDate,
fromTime: _leaveRequests[index].fromTime,
toTime: _leaveRequests[index].toTime,
reason: _leaveRequests[index].reason,
requestDate: _leaveRequests[index].requestDate,
status: status,
);
_leaveRequests[index] = updatedRequest;
}
}
Future<void> updateAdvanceRequestStatus(String id, String status) async {
final index = _advanceRequests.indexWhere((request) => request.id == id);
if (index != -1) {
final updatedRequest = AdvanceRequest(
id: _advanceRequests[index].id,
amount: _advanceRequests[index].amount,
reason: _advanceRequests[index].reason,
status: status,
);
_advanceRequests[index] = updatedRequest;
}
}
}