Files
finger_print_app/lib/screens/holiday_screen.dart
2025-12-07 17:41:57 +03:00

639 lines
21 KiB
Dart

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});
@override
State<HolidayScreen> createState() => _HolidayScreenState();
}
class _HolidayScreenState extends State<HolidayScreen> {
int activeTab = 0; // 0 = السلف | 1 = الأجازات
// Use the singleton instance
final RequestService _requestService = RequestService();
late List<LeaveRequest> _leaveRequests;
late List<AdvanceRequest> _advanceRequests;
@override
void initState() {
super.initState();
_initializeData();
}
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;
});
});
}
Widget buildStatusCircle({
required bool active,
required String label,
required String? svgPath,
required Color activeColor,
required Color glowColor,
}) {
return Column(
children: [
Container(
width: 36, // Smaller circle
height: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: active ? activeColor : const Color(0xFFD6D6D6),
boxShadow:
active
? [
BoxShadow(
color: glowColor.withOpacity(0.6),
blurRadius: 15,
spreadRadius: 2,
),
]
: [],
),
child:
active && svgPath != null
? Center(
child: SvgPicture.asset(
svgPath,
width: 18, // Smaller icon
height: 18,
color: Colors.white,
),
)
: null,
),
const SizedBox(height: 4), // Space between circles
Text(label, style: const TextStyle(fontSize: 10)), // Smaller text
],
);
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Stack(
children: [
// Tabs
Positioned(
top: 5,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// الأجازات TAB
GestureDetector(
onTap: () => setState(() => activeTab = 1),
child: Column(
children: [
Text(
"الأجازات",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
color:
activeTab == 1
? const Color(0xFF8EFDC2)
: const Color(0x9EFFFFFF),
),
),
if (activeTab == 1)
Container(
width: 60,
height: 2,
margin: const EdgeInsets.only(top: 4),
color: const Color(0xFF8EFDC2),
),
],
),
),
const SizedBox(width: 70),
// السلف TAB
GestureDetector(
onTap: () => setState(() => activeTab = 0),
child: Column(
children: [
Text(
"السلف",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
color:
activeTab == 0
? const Color(0xFF8EFDC2)
: const Color(0x9EFFFFFF),
),
),
if (activeTab == 0)
Container(
width: 60,
height: 2,
margin: const EdgeInsets.only(top: 4),
color: const Color(0xFF8EFDC2),
),
],
),
),
],
),
),
// Content area for requests
Positioned(
top: 30, // Position below the tabs
left: 0,
right: 0,
bottom: 0, // Leave space for action buttons
child: activeTab == 1 ? _buildLeaveRequestsTab() : _buildAdvanceRequestsTab(),
),
// Action buttons
Positioned(
bottom: 30,
right: 20,
child: Column(
children: [
// Money icon (custom size)
_HolidayActionButton(
label: "طلب سلفة",
svgPath: "assets/images/money2.svg",
iconWidth: 28,
iconHeight: 20,
onTap: () async {
// Navigate to RequestAdvanceScreen
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RequestAdvanceScreen(),
),
);
// Data will be updated automatically through the stream
},
),
const SizedBox(height: 18),
// Plus icon (custom size)
_HolidayActionButton(
label: "طلب إجازة",
svgPath: "assets/images/plus.svg",
iconWidth: 28,
iconHeight: 20,
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RequestLeaveScreen(),
),
);
// Data will be updated automatically through the stream
},
),
],
),
),
],
),
);
}
Widget _buildLeaveRequestsTab() {
if (_leaveRequests.isEmpty) {
return const Center(
child: Text(
'لا توجد طلبات أجازة',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
);
}
return 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() {
if (_advanceRequests.isEmpty) {
return const Center(
child: Text(
'لا توجد طلبات سلف',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
);
}
return 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) {
// Base colors from the design
const bgColor = Color(0xFFEAF8F3);
// const activeColor = Color(0xFFFFC940); // yellow active state
// const inactiveColor = Color(0xFFD6D6D6); // grey inactive circles
const lineColor = Color(0xFF22A685); // green divider
// Status flags
final bool isWaiting = request.status == "waiting";
final bool isApproved = request.status == "approved";
final bool isDenied = request.status == "denied";
final String dateText =
"${request.fromDate.year}.${request.fromDate.month}.${request.fromDate.day}";
return Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: MediaQuery.of(context).size.width * 0.8, // 80% of screen width
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), // Reduced padding
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// ───────────────── STATUS ROW (centered) ─────────────────
Row(
mainAxisAlignment: MainAxisAlignment.center, // Center the status circles
children: [
buildStatusCircle(
active: isWaiting,
label: "قيد الانتظار",
svgPath: isWaiting ? "assets/images/waiting.svg" : null,
activeColor: const Color(0xFFF9C8A01B),
glowColor: const Color(0xB4FFDC69),
),
const SizedBox(width: 12), // Space between circles
buildStatusCircle(
active: isApproved,
label: "موافقة",
svgPath: isApproved ? "assets/images/yes.svg" : null,
activeColor: const Color(0xFF0A8A60),
glowColor: const Color(0xFF00D7A3),
),
const SizedBox(width: 12), // Space between circles
buildStatusCircle(
active: isDenied,
label: "تم الرفض",
svgPath: isDenied ? "assets/images/no.svg" : null,
activeColor: const Color(0xFFE63946),
glowColor: const Color(0xFFE63946),
),
],
),
const SizedBox(height: 8), // Reduced height
// ───────────── DATE (LEFT) + TYPE + TREE ICON (RIGHT) ─────────────
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Right cluster: tree icon + leave type
Row(
children: [
// Tree SVG in rounded square
SizedBox(
width: 32, // Smaller icon
height: 32,
child: Center(
child: SvgPicture.asset(
"assets/images/holiday.svg",
width: 32, // Smaller icon
height: 32,
color: const Color(0xFF11663C),
),
),
),
const SizedBox(width: 8),
Text(
request.leaveType,
style: const TextStyle(
fontSize: 12, // Smaller text
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
],
),
// Left: date
Text(
dateText,
style: const TextStyle(
fontSize: 14, // Smaller text
color: Colors.black87,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 10), // Reduced height
// Divider line
Container(width: double.infinity, height: 1.4, color: lineColor),
const SizedBox(height: 10), // Reduced height
// Reason label
// ───────────── Reason Row (inline with label) ─────────────
Padding(
padding: const EdgeInsets.only(top: 8), // Reduced padding
child: Row(
textDirection: TextDirection.ltr,
children: [
// Reason text (LEFT)
Expanded(
child: Text(
request.reason,
textAlign: TextAlign.right,
style: const TextStyle(
fontSize: 13, // Smaller text
color: Colors.black87,
),
),
),
const SizedBox(width: 6),
// Label (RIGHT)
const Text(
"السبب :",
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 14, // Smaller text
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
);
}
Widget _buildAdvanceRequestCard(AdvanceRequest request) {
// Base colors
const bgColor = Color(0xFFEAF8F3);
const lineColor = Color(0xFF22A685);
// const inactiveColor = Color(0xFFD6D6D6);
// Status flags
final bool isWaiting = request.status == "waiting";
final bool isApproved = request.status == "approved";
final bool isDenied = request.status == "denied";
// You can format a date if needed
final String dateText =
"${DateTime.now().year}.${DateTime.now().month}.${DateTime.now().day}";
return Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: MediaQuery.of(context).size.width * 0.8, // 80% of screen width
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 14), // Reduced padding
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// ───────────────── STATUS ROW (centered) ─────────────────
Row(
mainAxisAlignment: MainAxisAlignment.center, // Center the status circles
children: [
buildStatusCircle(
active: isWaiting,
label: "قيد الانتظار",
svgPath: isWaiting ? "assets/images/waiting.svg" : null,
activeColor: const Color(0xFFF9C8A01B),
glowColor: const Color(0xB4FFDC69),
),
const SizedBox(width: 12), // Space between circles
buildStatusCircle(
active: isApproved,
label: "موافقة",
svgPath: isApproved ? "assets/images/yes.svg" : null,
activeColor: const Color(0xFF0A8A60),
glowColor: const Color(0xFF00D7A3),
),
const SizedBox(width: 12), // Space between circles
buildStatusCircle(
active: isDenied,
label: "تم الرفض",
svgPath: isDenied ? "assets/images/no.svg" : null,
activeColor: const Color(0xFFE63946),
glowColor: const Color(0xFFE63946),
),
],
),
const SizedBox(height: 8), // Reduced height
// ───────────────── TYPE + ICON + AMOUNT (same layout as leave card) ─────────────────
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Right: money icon + amount label
Row(
children: [
SizedBox(
width: 32, // Smaller icon
height: 32,
child: Center(
child: SvgPicture.asset(
"assets/images/money.svg",
width: 32, // Smaller icon
height: 32,
color: const Color(0xFF11663C),
),
),
),
const SizedBox(width: 8),
Text(
"سلفة: ${request.amount.toStringAsFixed(0)} دع",
style: const TextStyle(
fontSize: 12, // Smaller text
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
],
),
// Left: dummy date (or request.requestDate)
Text(
dateText,
style: const TextStyle(
fontSize: 14, // Smaller text
color: Colors.black87,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 10), // Reduced height
// Divider line
Container(width: double.infinity, height: 1, color: lineColor),
const SizedBox(height: 10), // Reduced height
// ───────────────── REASON ROW (identical to leave card) ─────────────────
Padding(
padding: const EdgeInsets.only(top: 8), // Reduced padding
child: Row(
textDirection: TextDirection.ltr,
children: [
// Reason text (LEFT)
Expanded(
child: Text(
request.reason,
textAlign: TextAlign.right,
style: const TextStyle(
fontSize: 13, // Smaller text
color: Colors.black87,
),
),
),
const SizedBox(width: 6),
// Label (RIGHT)
const Text(
"السبب :",
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 14, // Smaller text
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
);
}
}
class _HolidayActionButton extends StatelessWidget {
final String label;
final String svgPath;
final VoidCallback onTap;
final double iconWidth;
final double iconHeight;
const _HolidayActionButton({
required this.label,
required this.svgPath,
required this.onTap,
required this.iconWidth,
required this.iconHeight,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
width: 80,
height: 70,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: const [
BoxShadow(
color: Color(0x4B00C68B),
blurRadius: 20,
offset: Offset(0, 6),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: iconWidth,
height: iconHeight,
child: SvgPicture.asset(svgPath, fit: BoxFit.contain),
),
const SizedBox(height: 6),
Text(
label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
],
),
),
);
}
}