This commit is contained in:
Mohammed Al-Samarraie
2026-01-13 15:14:30 +03:00
parent 7cbf65e6c1
commit 3b3ed5e640
27 changed files with 36 additions and 36 deletions

View File

@@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import '../widgets/app_background.dart';
import '../widgets/settings_bar.dart';
class AboutScreen extends StatelessWidget {
const AboutScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: AppBackground(
child: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
/// -------------------- SETTINGS BAR --------------------
SettingsBar(
selectedIndex: 0,
onTap: (_) {},
showBackButton: true,
onBackTap: () => Navigator.pop(context),
iconPaths: const [],
),
const SizedBox(height: 12),
/// -------------------- CENTER CONTENT --------------------
///
Center(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24.0,
vertical: 10,
),
child: Align(
alignment: Alignment.topCenter,
child: Directionality(
textDirection: TextDirection.rtl,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"عن الشركة",
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 12),
Container(
width: 200,
height: 1,
color: const Color(0x8732C599),
),
const SizedBox(height: 14),
const Text(
"شركة كودا ذ.م.م هي شركة عراقية رائدة تقدم حلولاً متميزة في البرمجيات المخصصة والتطبيقات وإدارة الخوادم والمواقع، ومنظومات الاتصال وخدمات الأمن السيبراني. تأسست عام 2008 ولديها عقود واتفاقيات عديدة مع مؤسسات حكومية عراقية والقطاع الخاص.\n\nوسعت الشركة شراكاتها مع وكالات عالمية مثل CODACS الأمريكية و Laipac الكندية و Teltonika اللتوانية وWolf Team الصينية وLibelium الإسبانية وOdoo العالمية وغيرها. مما أتاح لها تقديم أحدث التقنيات والمنتجات المبتكرة لعملائها.\n\nتوفر كودا حلولاً برمجية مخصصة للمؤسسات الكبيرة والدوائر الحكومية، مما يسهم في تبسيط الإجراءات وتقديم الخدمات للمواطنين بشكل إلكتروني وفعال.\n\nكما تقدم كودا مجموعة واسعة من المنتجات والخدمات الحديثة، بما في ذلك الشاشات الإعلانية، والبوابات الذكية، والمواقف الذكية، والأقفال، والمباني الذكية، وأجهزة الكشك (الخدمة الإلكترونية الذاتية)، وأجهزة محمولة تخصصية، وحساسات، وأجهزة نداء واستغاثة.\n\nبفضل هذه المنتجات والخدمات المبتكرة، تعزز كودا مكانتها الرائدة في السوق المحلي والإقليمي، وتساهم في تطوير التكنولوجيا وتعزيز جودة الحياة في العراق .",
style: TextStyle(
fontSize: 18,
color: Colors.white70,
height:
1.5, // Added line height for better readability
),
),
const SizedBox(height: 20), // Bottom padding
],
),
),
),
),
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,269 @@
import 'package:coda_project/presentation/screens/face_screen.dart';
import 'package:coda_project/presentation/screens/notifications_screen.dart';
import 'package:coda_project/presentation/screens/user_settings_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/settings_bar.dart';
class AttendanceScreen extends StatelessWidget {
const AttendanceScreen({super.key});
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.sizeOf(context).width;
final screenHeight = MediaQuery.sizeOf(context).height;
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: [
SizedBox(height: MediaQuery.of(context).size.height),
/// ------------------------------
/// SETTINGS BAR (STATIC)
/// ------------------------------
SafeArea(
child: SettingsBar(
selectedIndex: 0,
showBackButton: false,
iconPaths: ['assets/images/user.svg', 'assets/images/ball.svg'],
onTap: (index) {
if (index == 0) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserSettingsScreen(),
),
);
} else if (index == 1) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationsScreen(),
),
);
}
},
),
),
/// ------------------------------
/// GREETING TEXT
/// ------------------------------
Positioned(
top:
screenHeight *
0.14, // moved down because settings bar now exists
left: 0,
right: 0,
child: Center(
child: Text(
"صباح الخير, محمد",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.white,
shadows: [Shadow(color: Color(0x42000000), blurRadius: 6)],
),
),
),
),
/// ------------------------------
/// MAIN CARD AREA
/// ------------------------------
Positioned(
top:
screenHeight *
0.2, // pushed down because of settings bar + greeting
left: 0,
right: 0,
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: screenHeight * 0.05),
child: Stack(
children: [
Container(
height: screenHeight * 0.5,
width: screenWidth * 0.7,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
boxShadow: [
BoxShadow(
color: Color(0x1F2B2B2B),
blurRadius: 5,
offset: Offset(10, -10),
),
BoxShadow(
color: Color(0xABCECECE),
blurRadius: 5,
offset: Offset(-2, 5),
),
BoxShadow(
color: Color.fromARGB(148, 2, 70, 35),
blurRadius: 80,
offset: Offset(0, 10),
),
],
),
),
Container(
height: screenHeight * 0.5,
width: screenWidth * 0.7,
decoration: BoxDecoration(
color: Color(0x92757575),
borderRadius: BorderRadius.circular(32),
),
),
],
),
),
),
),
/// ------------------------------
/// LOGIN BUTTON
/// ------------------------------
Positioned(
top: screenHeight * 0.21,
left: screenWidth * 0.05,
child: _ShadowedCard(
shadow: [
BoxShadow(
color: Color(0x62000000),
blurRadius: 10,
spreadRadius: 5,
offset: Offset(5, 5),
),
],
child: _FingerButton(
icon: "assets/images/faceLogin.svg",
label: "تسجيل الدخول",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => OvalCameraCapturePage(isLogin: true),
),
);
},
),
),
),
/// ------------------------------
/// LOGOUT BUTTON
/// ------------------------------
Positioned(
bottom: screenHeight * 0.2,
right: screenWidth * 0.1,
child: _ShadowedCard(
shadow: [
BoxShadow(
color: Color(0xABCECECE),
blurRadius: 5,
spreadRadius: 3,
offset: Offset(-6, -6),
),
BoxShadow(
color: Color(0x92014221),
blurRadius: 10,
offset: Offset(-5, -5),
),
BoxShadow(
color: Color(0x7D1A1A1A),
blurRadius: 10,
spreadRadius: 3,
offset: Offset(5, 5),
),
],
child: _FingerButton(
icon: "assets/images/faceLogout.svg",
label: "تسجيل خروج",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => OvalCameraCapturePage(isLogin: false),
),
);
},
),
),
),
],
),
);
}
}
/// ---------------------------------------------
/// SHADOW WRAPPER
/// ---------------------------------------------
class _ShadowedCard extends StatelessWidget {
final Widget child;
final List<BoxShadow> shadow;
const _ShadowedCard({required this.child, required this.shadow});
@override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 160,
width: 160,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
boxShadow: shadow,
),
),
child,
],
);
}
}
/// ---------------------------------------------
/// BUTTON WIDGET
/// ---------------------------------------------
class _FingerButton extends StatelessWidget {
final String icon;
final String label;
final VoidCallback onTap;
const _FingerButton({
required this.icon,
required this.label,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
height: 160,
width: 160,
decoration: BoxDecoration(
color: Color(0xFFEAFBF3),
borderRadius: BorderRadius.circular(32),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(icon, width: 75, height: 75),
SizedBox(height: 10),
Text(
label,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/app_background.dart';
import '../widgets/auth_form.dart';
import '../../core/di/injection_container.dart';
import '../blocs/login/login_bloc.dart';
class AuthScreen extends StatelessWidget {
const AuthScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => sl<LoginBloc>(),
child: Scaffold(
resizeToAvoidBottomInset: false,
body: AppBackground(
child: SafeArea(
child: Column(
children: [
const SizedBox(height: 60),
// Logo
Center(child: Image.asset("assets/images/logo2.png", width: 200)),
// const SizedBox(height: 15),
// Form - taking remaining space and centered
Expanded(child: Center(child: const AuthForm())),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,351 @@
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'dart:async';
class OvalCameraCapturePage extends StatefulWidget {
final bool isLogin;
const OvalCameraCapturePage({super.key, this.isLogin = true});
@override
State<OvalCameraCapturePage> createState() => _OvalCameraCapturePageState();
}
class _OvalCameraCapturePageState extends State<OvalCameraCapturePage> {
CameraController? _cameraController;
bool _isCameraInitialized = false;
String? _errorMessage;
bool _isSuccess = false;
Timer? _timer;
@override
void initState() {
super.initState();
_initializeCamera();
}
Future<void> _initializeCamera() async {
try {
// Dispose existing controller if any
await _cameraController?.dispose();
_cameraController = null;
// Get available cameras
final cameras = await availableCameras();
// Check if cameras list is available
if (cameras.isEmpty) {
setState(() {
_errorMessage = "لا توجد كاميرات متاحة";
_isCameraInitialized = false;
});
return;
}
// Try to find front camera, fallback to first available camera
CameraDescription? selectedCamera;
try {
selectedCamera = cameras.firstWhere(
(cam) => cam.lensDirection == CameraLensDirection.front,
);
} catch (e) {
// If no front camera found, use the first available camera
if (cameras.isNotEmpty) {
selectedCamera = cameras.first;
} else {
setState(() {
_errorMessage = "لا توجد كاميرات متاحة";
_isCameraInitialized = false;
});
return;
}
}
_cameraController = CameraController(
selectedCamera,
ResolutionPreset.medium,
enableAudio: false,
imageFormatGroup: ImageFormatGroup.jpeg,
);
await _cameraController!.initialize();
if (!mounted) return;
setState(() {
_isCameraInitialized = true;
_errorMessage = null;
});
_timer = Timer(const Duration(seconds: 3), () {
if (mounted) {
setState(() {
_isSuccess = true;
});
// Auto-close after 2 seconds
Future.delayed(const Duration(seconds: 2), () {
if (mounted) {
Navigator.of(context).pop();
}
});
}
});
} catch (e) {
if (!mounted) return;
setState(() {
_errorMessage = "خطأ في تهيئة الكاميرا: $e";
_isCameraInitialized = false;
});
print("Error initializing camera: $e");
}
}
@override
void dispose() {
_cameraController?.dispose();
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xff000000),
body:
_errorMessage != null
? Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.camera_alt_outlined,
size: 64,
color: Colors.white70,
),
SizedBox(height: 16),
Text(
_errorMessage!,
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
SizedBox(height: 24),
ElevatedButton(
onPressed: _initializeCamera,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xffE8001A),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 32,
vertical: 12,
),
),
child: Text("إعادة المحاولة"),
),
],
),
),
)
: _isCameraInitialized && _cameraController != null
? Stack(
children: [
SizedBox(height: MediaQuery.of(context).size.height),
// Camera Preview
Positioned(
child: Center(child: CameraPreview(_cameraController!)),
),
// Oval overlay with dimmed background
Positioned.fill(
child: CustomPaint(painter: _OvalOverlayPainter()),
),
// Top Text
Positioned(
top: 100,
left: 0,
right: 0,
child: Center(
child: Text(
widget.isLogin ? "تسجيل الدخول" : "تسجيل خروج",
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
fontFamily:
'Cairo', // Assuming Cairo font based on Arabic text
),
),
),
),
// Bottom Text and Logo
Positioned(
bottom: 80,
left: 0,
right: 0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_isSuccess
? (widget.isLogin
? "تم تسجيل دخولك بنجاح"
: "تم تسجيل خروجك بنجاح")
: (widget.isLogin
? "يتم تسجيل الدخول ..."
: "يتم تسجيل الخروج ..."),
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
// Logo
SizedBox(
height: _isSuccess ? 80 : 60,
width: _isSuccess ? 80 : 60,
child: SvgPicture.asset(
_isSuccess
? 'assets/images/logSuccess.svg'
: 'assets/images/logLoading.svg',
// ignore: deprecated_member_use
color: Colors.white,
fit: BoxFit.contain,
),
),
],
),
),
// // Capture button
// Positioned(
// bottom: 60,
// left: 0,
// right: 0,
// child: Center(
// child: GestureDetector(
// onTap: (){},
// child: Container(
// width: 72,
// height: 72,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: Colors.white,
// boxShadow: [
// BoxShadow(
// color: Colors.black26,
// blurRadius: 8,
// offset: Offset(0, 4),
// ),
// ],
// ),
// child: Icon(Icons.camera_alt, color: Color(0xffE8001A), size: 36),
// ),
// ),
// ),
// ),
],
)
: Center(
child: CircularProgressIndicator(color: Color(0xffE8001A)),
),
);
}
}
class _OvalOverlayPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final width = size.width * 0.75;
final height = size.height * 0.4;
final center = Offset(size.width / 2, size.height / 2);
final ovalRect = Rect.fromCenter(
center: center,
width: width,
height: height,
);
// Create a path for the whole screen
final screenPath =
Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
// Create a path for the oval
final ovalPath = Path()..addOval(ovalRect);
// Subtract the oval from the screen path
final overlayPath = Path.combine(
PathOperation.difference,
screenPath,
ovalPath,
);
// Draw the dimmed area outside the oval with gradient
final gradient = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromARGB(255, 41, 41, 41), // top dark gray
Color.fromARGB(255, 0, 20, 15), // bottom deep green
],
);
final paint =
Paint()
..shader = gradient.createShader(
Rect.fromLTWH(0, 0, size.width, size.height),
)
..style = PaintingStyle.fill;
canvas.drawPath(overlayPath, paint);
// Draw glowing circles effect (like AppBackground) - drawn after overlay
// Top circle - positioned similar to AppBackground (top: -250, left: 100, right: -200)
final topCircleCenter = Offset(size.width * 0.3, -250);
final topCircleRadius = 150.0;
// Draw multiple circles with different opacities for spread effect (spreadRadius: 160)
for (int i = 0; i < 5; i++) {
final spreadPaint =
Paint()
..color = Color.fromARGB(69 ~/ (i + 1), 62, 254, 190)
..maskFilter = MaskFilter.blur(BlurStyle.normal, 140 - (i * 20));
canvas.drawCircle(
topCircleCenter,
topCircleRadius + (i * 30),
spreadPaint,
);
}
// Bottom circle - positioned similar to AppBackground (bottom: 100, left: -140, right: -120)
final bottomCircleCenter = Offset(size.width * 0.2, size.height + 100);
final bottomCircleRadius = 160.0;
// Draw multiple circles with different opacities for spread effect (spreadRadius: 60)
for (int i = 0; i < 5; i++) {
final spreadPaint =
Paint()
..color = Color.fromARGB(83 ~/ (i + 1), 62, 254, 190)
..maskFilter = MaskFilter.blur(BlurStyle.normal, 180 - (i * 25));
canvas.drawCircle(
bottomCircleCenter,
bottomCircleRadius + (i * 40),
spreadPaint,
);
}
// Draw oval border
final borderPaint =
Paint()
..color = Colors.greenAccent
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawOval(ovalRect, borderPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,105 @@
import 'package:coda_project/presentation/screens/notifications_screen.dart';
import 'package:coda_project/presentation/screens/user_settings_screen.dart';
import 'package:flutter/material.dart';
import '../widgets/finance_summary_card.dart';
import '../widgets/work_day_card.dart';
import '../widgets/settings_bar.dart';
class FinanceScreen extends StatefulWidget {
final void Function(bool isScrollingDown)? onScrollEvent;
const FinanceScreen({super.key, this.onScrollEvent});
@override
State<FinanceScreen> createState() => _FinanceScreenState();
}
class _FinanceScreenState extends State<FinanceScreen> {
String dropdownValue = "الكل";
late ScrollController scrollController;
@override
void initState() {
super.initState();
scrollController = ScrollController();
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: SafeArea(
child: CustomScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(),
slivers: [
SliverToBoxAdapter(
child: SettingsBar(
selectedIndex: 0,
showBackButton: false,
iconPaths: [
'assets/images/user.svg',
'assets/images/ball.svg',
],
onTap: (index) {
if (index == 0) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserSettingsScreen(),
),
);
} else if (index == 1) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationsScreen(),
),
);
}
},
),
),
const SliverToBoxAdapter(child: SizedBox(height: 5)),
/// SUMMARY CARD
SliverToBoxAdapter(
child: FinanceSummaryCard(
totalAmount: "333,000",
dropdownValue: dropdownValue,
onCalendarTap: () => showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
),
onDropdownChanged: (value) {
setState(() => dropdownValue = value!);
},
),
),
/// WORK DAY CARDS
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return const WorkDayCard();
},
childCount: 3,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 120)),
],
),
),
);
}
}

View File

@@ -0,0 +1,536 @@
import 'package:coda_project/presentation/screens/notifications_screen.dart';
import 'package:coda_project/presentation/screens/user_settings_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/settings_bar.dart';
import 'request_leave_screen.dart';
import 'request_advance_scrren.dart';
import '../../models/leave_request.dart';
import '../../models/advance_request.dart';
import '../../core/services/request_service.dart';
class HolidayScreen extends StatefulWidget {
final void Function(bool isScrollingDown)? onScrollEvent;
const HolidayScreen({super.key, this.onScrollEvent});
@override
State<HolidayScreen> createState() => _HolidayScreenState();
}
class _HolidayScreenState extends State<HolidayScreen> {
int activeTab = 0;
final RequestService _requestService = RequestService();
List<LeaveRequest> _leaveRequests = [];
List<AdvanceRequest> _advanceRequests = [];
final ScrollController _scrollController = ScrollController();
bool _showActions = true;
@override
void initState() {
super.initState();
_initializeData();
_scrollController.addListener(() {
final direction = _scrollController.position.userScrollDirection;
if (direction == ScrollDirection.reverse) {
if (_showActions) setState(() => _showActions = false);
widget.onScrollEvent?.call(true);
} else if (direction == ScrollDirection.forward) {
if (!_showActions) setState(() => _showActions = true);
widget.onScrollEvent?.call(false);
}
if (_scrollController.position.pixels <= 5) {
setState(() {
_showActions = true;
});
widget.onScrollEvent?.call(false);
}
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _initializeData() async {
_leaveRequests = await _requestService.getLeaveRequests();
_advanceRequests = await _requestService.getAdvanceRequests();
_requestService.leaveRequestsStream.listen((requests) {
if (mounted) setState(() => _leaveRequests = requests);
});
_requestService.advanceRequestsStream.listen((requests) {
if (mounted) setState(() => _advanceRequests = requests);
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
// ---------------------------------------------------------
// ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW
// ---------------------------------------------------------
SafeArea(
child: CustomScrollView(
controller: _scrollController,
physics: const BouncingScrollPhysics(),
slivers: [
// SETTINGS BAR
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 8),
// First, make sure you have your screens defined
// Then in your main widget:
child: SettingsBar(
selectedIndex: 0,
showBackButton: false,
iconPaths: [
'assets/images/user.svg',
'assets/images/ball.svg',
],
onTap: (index) {
if (index == 0) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserSettingsScreen(),
),
);
} else if (index == 1) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationsScreen(),
),
);
}
},
),
),
),
const SliverToBoxAdapter(child: SizedBox(height: 5)),
// TABS SECTION
SliverToBoxAdapter(
child: SizedBox(
height: 55,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// الأجازات
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),
// السلف
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),
),
],
),
),
],
),
),
),
const SliverToBoxAdapter(child: SizedBox(height: 20)),
// CONTENT LISTS
activeTab == 1
? _buildLeaveRequestsSliver()
: _buildAdvanceRequestsSliver(),
const SliverToBoxAdapter(child: SizedBox(height: 120)),
],
),
),
// ---------------------------------------------------------
// ⭐ FLOATING ACTION BUTTONS
// ---------------------------------------------------------
Positioned(
bottom: 150,
right: 20,
child: AnimatedSlide(
offset: _showActions ? Offset.zero : const Offset(0, 1.3),
duration: const Duration(milliseconds: 300),
child: AnimatedOpacity(
opacity: _showActions ? 1 : 0,
duration: const Duration(milliseconds: 250),
child: Column(
children: [
_HolidayActionButton(
label: "طلب سلفة",
svgPath: "assets/images/money2.svg",
iconWidth: 28,
iconHeight: 20,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const RequestAdvanceScreen(),
),
);
},
),
const SizedBox(height: 18),
_HolidayActionButton(
label: "طلب إجازة",
svgPath: "assets/images/plus.svg",
iconWidth: 28,
iconHeight: 20,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const RequestLeaveScreen(),
),
);
},
),
],
),
),
),
),
],
);
}
// ----------------------------------------------------------------
// SLIVERS FOR LISTS
// ----------------------------------------------------------------
Widget _buildLeaveRequestsSliver() {
if (_leaveRequests.isEmpty) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
"لا توجد طلبات أجازة",
style: const TextStyle(color: Colors.white),
),
),
),
);
}
return SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 25),
sliver: SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: _buildLeaveRequestCard(_leaveRequests[index]),
);
}, childCount: _leaveRequests.length),
),
);
}
Widget _buildAdvanceRequestsSliver() {
if (_advanceRequests.isEmpty) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
"لا توجد طلبات سلف",
style: const TextStyle(color: Colors.white),
),
),
),
);
}
return SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 25),
sliver: SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: _buildAdvanceRequestCard(_advanceRequests[index]),
);
}, childCount: _advanceRequests.length),
),
);
}
// ----------------------------------------------------------------
// CARD BUILDERS (unchanged)
// ----------------------------------------------------------------
Widget _buildLeaveRequestCard(LeaveRequest request) {
const bgColor = Color(0xFFEAF8F3);
const lineColor = Color(0xFF22A685);
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: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// STATUS ROW
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_statusCircle(
isWaiting,
"قيد الانتظار",
isWaiting ? "assets/images/waiting.svg" : null,
const Color(0xFFA58A1B),
const Color(0xFFFFDC69),
),
const SizedBox(width: 12),
_statusCircle(
isApproved,
"موافقة",
isApproved ? "assets/images/yes.svg" : null,
const Color(0xFF0A8A60),
const Color(0xFF00D7A3),
),
const SizedBox(width: 12),
_statusCircle(
isDenied,
"تم الرفض",
isDenied ? "assets/images/no.svg" : null,
const Color(0xFFE63946),
const Color(0xFFE63946),
),
],
),
const SizedBox(height: 8),
// TITLE + DATE
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SvgPicture.asset(
"assets/images/holiday.svg",
width: 32,
height: 32,
color: const Color(0xFF11663C),
),
const SizedBox(width: 8),
Text(
request.leaveType,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13,
),
),
],
),
Text(dateText),
],
),
const SizedBox(height: 10),
Container(height: 1.4, width: double.infinity, color: lineColor),
const SizedBox(height: 10),
// REASON ROW
Row(
textDirection: TextDirection.ltr, // Add this
children: [
Expanded(
child: Text(request.reason, textAlign: TextAlign.right),
),
const SizedBox(width: 6),
const Text(
"السبب:",
style: TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
),
],
),
],
),
),
);
}
Widget _statusCircle(
bool active,
String label,
String? svg,
Color color,
Color glow,
) {
return Column(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: active ? color : const Color(0xFFD6D6D6),
boxShadow:
active
? [BoxShadow(color: glow.withOpacity(0.6), blurRadius: 12)]
: [],
),
child:
svg != null
? Center(child: SvgPicture.asset(svg, width: 18, height: 18))
: null,
),
const SizedBox(height: 4),
Text(label, style: const TextStyle(fontSize: 10)),
],
);
}
Widget _buildAdvanceRequestCard(AdvanceRequest request) {
// Same card logic as before (unchanged)
return _buildLeaveRequestCard(
LeaveRequest(
id: request.id,
leaveType: "سلفة ${request.amount}",
isTimedLeave: false,
fromDate: DateTime.now(),
toDate: DateTime.now(),
fromTime: TimeOfDay.now(),
toTime: TimeOfDay.now(),
reason: request.reason,
requestDate: DateTime.now(),
status: request.status,
),
);
}
}
// ----------------------------------------------------------------
// FLOATING BUTTON WIDGET (unchanged)
// ----------------------------------------------------------------
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,
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import '../widgets/app_background.dart';
import '../../widgets/floatingnavbar.dart';
import 'attendence_screen.dart';
import 'finance_screen.dart';
import 'holiday_screen.dart';
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
final screenHeight = MediaQuery.sizeOf(context).height;
return Scaffold(
body: Stack(
children: [
/// BACKGROUND
const AppBackground(child: SizedBox()),
/// ACTIVE SCREEN (fills entire screen - content will extend behind navbar)
///
Positioned.fill(
child: IndexedStack(
index: _currentIndex,
children: [
const AttendanceScreen(),
const FinanceScreen(),
const HolidayScreen(),
],
),
),
/// FLOATING NAVBAR (positioned above content)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Floatingnavbar(
items: [
NavBarItem(
iconPath: 'assets/images/attendance.svg',
label: 'الحضور',
),
NavBarItem(
iconPath: 'assets/images/finance.svg',
label: 'المالية',
),
NavBarItem(
iconPath: 'assets/images/holiday.svg',
label: 'الإجازة',
),
],
selectedIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
),
],
),
);
}
}

View File

@@ -0,0 +1,139 @@
import 'package:flutter/material.dart';
import '../widgets/app_background.dart';
import '../widgets/settings_bar.dart';
class NotificationsScreen extends StatelessWidget {
const NotificationsScreen({super.key});
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: AppBackground(
child: Column(
children: [
/// -------------------- SETTINGS BAR --------------------
SettingsBar(
selectedIndex: 0,
onTap: (_) {},
showBackButton: true,
onBackTap: () => Navigator.pop(context),
iconPaths: const [],
),
const SizedBox(height: 12),
/// -------------------- CONTENT --------------------
Expanded(
child: Directionality(
textDirection: TextDirection.rtl,
child: Column(
children: [
/// Title
Padding(
padding: const EdgeInsets.only(right: 40),
child: Align(
alignment: Alignment.topRight,
child: const Text(
"الأشعارات",
style: TextStyle(
fontSize: 26,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 20),
/// -------------------- NOTIFICATION CARD --------------------
Padding(
padding: const EdgeInsets.symmetric(horizontal: 22),
child: Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFFEFFFFA),
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 12,
offset: Offset(0, 6),
),
],
),
child: Stack(
children: [
/// ✅ DATE — TOP LEFT
const Positioned(
top: 0,
left: 0,
child: Text(
"2025.12.1",
style: TextStyle(
fontSize: 12,
color: Color(0x9E000000),
fontWeight: FontWeight.w900,
),
),
),
/// ✅ MAIN CONTENT — RTL
Align(
alignment: Alignment.centerRight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"عنوان الاشعار",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
// const SizedBox(height: 6),
/// ✅ DETAILS WITH GREEN LINE
Row(
mainAxisSize: MainAxisSize.min,
children: [
// Green line
Container(
width: 20,
height: 2,
color: Color(0xFF025A42),
),
SizedBox(width: 8),
Text(
"تفاصيل الاشعار",
style: TextStyle(
fontSize: 16,
color: Colors.black87,
),
),
],
),
],
),
),
],
),
),
),
],
),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,154 @@
import 'dart:async';
import 'package:coda_project/presentation/screens/auth_screen.dart';
import 'package:flutter/material.dart';
import '../widgets/onboarding_page.dart';
import '../widgets/onboarding_button.dart';
class OnboardingScreen extends StatefulWidget {
const OnboardingScreen({super.key});
@override
State<OnboardingScreen> createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends State<OnboardingScreen> {
final PageController controller = PageController();
int index = 0;
@override
void initState() {
super.initState();
// auto slide
Timer.periodic(const Duration(seconds: 4), (timer) {
if (!mounted) return;
int next = index == 1 ? 0 : index + 1;
controller.animateToPage(
next,
duration: const Duration(milliseconds: 600),
curve: Curves.easeInOut,
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
/// BACKGROUND GRADIENT (base layer)
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.0, 0.45, 0.75, 1.0],
colors: [
Color(0xFF2E2E2E),
Color(0xFF00271D),
Color(0xFF005841),
Color.fromARGB(176, 62, 254, 203),
],
),
),
),
/// BLURRED CIRCLE (subtle rounded glow)
Positioned(
bottom: -120,
left: -60,
right: -60,
child: Container(
height: 300,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color.fromARGB(
0,
62,
254,
203,
), // same green with opacity
boxShadow: [
BoxShadow(
color: Color.fromARGB(
69,
62,
254,
142,
), // stronger outer glow
blurRadius: 60,
spreadRadius: 100,
),
],
),
),
),
Column(
children: [
const SizedBox(height: 70),
Image.asset("assets/images/logo2.png", width: 200),
/// PAGEVIEW (SVG + TEXT ONLY)
Expanded(
child: PageView(
physics: BouncingScrollPhysics(),
controller: controller,
onPageChanged: (i) => setState(() => index = i),
children: const [
OnboardingPage(
imagePath: "assets/images/Onboarding1.svg",
text:
"سجل دخولك وخروجك بسهولة وتابع دوامك\nيومياً بدون تعقيد",
),
OnboardingPage(
imagePath: "assets/images/Onboarding2.svg",
text:
"اعرف تفاصيل راتبك وقدّم طلب الإجازة\nوتابع حالته بكل شفافية",
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 40),
child: OnboardingButton(
text: "تسجيل دخول",
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AuthScreen(),
),
);
},
),
),
/// DOTS INDICATOR
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(2, (i) {
bool active = i == index;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 10,
height: 10,
decoration: BoxDecoration(
color: active ? Colors.white : Colors.white60,
borderRadius: BorderRadius.circular(50),
),
);
}),
),
const SizedBox(height: 100),
],
),
],
),
);
}
}

View File

@@ -0,0 +1,258 @@
import 'package:flutter/material.dart';
// 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 '../../core/services/request_service.dart';
class RequestAdvanceScreen extends StatefulWidget {
const RequestAdvanceScreen({super.key});
@override
State<RequestAdvanceScreen> createState() => _RequestAdvanceScreenState();
}
class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
// Text controller for amount
final TextEditingController amountController = TextEditingController();
// Text controller for reason
final TextEditingController reasonController = TextEditingController();
// Use the singleton instance
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 with default status "waiting"
final advanceRequest = AdvanceRequest(
id: DateTime.now().millisecondsSinceEpoch.toString(),
amount: amount,
reason: reasonController.text,
status: "waiting", // Default status
);
try {
// 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);
} catch (e) {
// Show an error message if something went wrong
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('حدث خطأ: $e'),
backgroundColor: Colors.red,
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: AppBackground(
child: SafeArea(
child: Column(
children: [
// Settings bar
SettingsBar(
selectedIndex: -1,
onTap: (_) {},
showBackButton: true,
onBackTap: () => Navigator.pop(context),
iconPaths: const [
"assets/images/user.svg",
"assets/images/bell.svg",
],
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title for advance request
const Align(
alignment: Alignment.topRight,
child: Text(
"طلب سلفة",
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 40),
//=============================
// AMOUNT INPUT
//=============================
Align(
alignment: Alignment.centerRight,
child: const Text(
"المبلغ المطلوب",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(height: 6),
Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20),
height: 58,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
boxShadow: const [
BoxShadow(
color: Color(0x22000000),
blurRadius: 8,
offset: Offset(0, 3),
),
],
),
child: TextField(
controller: amountController,
textAlign: TextAlign.right,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: "دع .000 ",
hintStyle: TextStyle(color: Colors.grey),
),
),
),
),
const SizedBox(height: 25),
// =============================
// REASON TEXTFIELD
// =============================
Align(
alignment: Alignment.centerRight,
child: Directionality(
textDirection: TextDirection.rtl,
child: const Text(
"السبب",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
// OUTER BORDER CONTAINER
Container(
padding: const EdgeInsets.all(
15,
), // border thickness space
decoration: BoxDecoration(
border: Border.all(
color: Color(0xFF00FFAA), // green border
width: 0.5,
),
borderRadius: BorderRadius.circular(14),
),
// INNER TEXTFIELD CONTAINER
child: Container(
height: 100,
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 10,
),
decoration: BoxDecoration(
color: const Color(0xFFEAEAEA),
borderRadius: BorderRadius.circular(12),
),
// Added Directionality to fix text direction
child: Directionality(
textDirection: TextDirection.rtl,
child: TextField(
controller: reasonController,
maxLines: 5,
textAlign:
TextAlign
.right, // Added this to align text to the right
decoration: const InputDecoration(
border: InputBorder.none,
hintText:
"اكتب السبب", // Added placeholder text
hintStyle: TextStyle(color: Colors.grey),
),
),
),
),
),
const SizedBox(height: 70),
// CONFIRM BUTTON
Center(
child: OnboardingButton(
text: "تأكيد الطلب",
backgroundColor: const Color(0xFFD1FEF0),
textColor: Colors.black,
onPressed: _saveAdvanceRequest, // Call the save method
),
),
const SizedBox(height: 40),
],
),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,642 @@
import 'package:flutter/material.dart';
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 '../../core/services/request_service.dart';
class RequestLeaveScreen extends StatefulWidget {
const RequestLeaveScreen({super.key});
@override
State<RequestLeaveScreen> createState() => _RequestLeaveScreenState();
}
class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
// Dropdown value - initialize with default
String leaveType = "إجازة مرضية ";
// Toggle switch
bool isTimedLeave = false;
// Date & time selectors
DateTime? fromDate = DateTime.now();
DateTime? toDate = DateTime.now();
TimeOfDay? fromTime = const TimeOfDay(hour: 12, minute: 00);
TimeOfDay? toTime = const TimeOfDay(hour: 12, minute: 00);
// Text controller for reason
final TextEditingController reasonController = TextEditingController();
// Use the singleton instance
final RequestService _requestService = RequestService();
/// PICK DATE
Future<void> pickDate(bool isFrom) async {
DateTime initial = isFrom ? fromDate! : toDate!;
DateTime? newDate = await showDatePicker(
context: context,
initialDate: initial,
firstDate: DateTime(2020),
lastDate: DateTime(2035),
builder: (context, child) {
return Theme(data: ThemeData.dark(), child: child!);
},
);
if (newDate != null) {
setState(() {
if (isFrom) {
fromDate = newDate;
} else {
toDate = newDate;
}
});
}
}
/// PICK TIME
Future<void> pickTime(bool isFrom) async {
TimeOfDay initial = isFrom ? fromTime! : toTime!;
TimeOfDay? newTime = await showTimePicker(
context: context,
initialTime: initial,
builder: (context, child) {
return Theme(data: ThemeData.dark(), child: child!);
},
);
if (newTime != null) {
setState(() {
if (isFrom) {
fromTime = newTime;
} else {
toTime = newTime;
}
});
}
}
// 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 with default status "waiting"
final leaveRequest = LeaveRequest(
id: DateTime.now().millisecondsSinceEpoch.toString(),
leaveType: leaveType, // Use the current leaveType value
isTimedLeave: isTimedLeave,
fromDate: fromDate!,
toDate: toDate!,
fromTime: fromTime!,
toTime: toTime!,
reason: reasonController.text,
requestDate: DateTime.now(),
status: "waiting", // Default status
);
try {
// 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);
} catch (e) {
// Show an error message if something went wrong
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('حدث خطأ: $e'), backgroundColor: Colors.red),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true, // ✅ IMPORTANT
body: AppBackground(
child: SafeArea(
child: Column(
children: [
// In your RequestLeaveScreen's build method, replace the SettingsBar with this:
SettingsBar(
selectedIndex: -1,
onTap: (_) {},
showBackButton: true,
onBackTap: () => Navigator.pop(context),
iconPaths: const [
"assets/images/user.svg",
"assets/images/bell.svg",
],
),
// Title
// const SizedBox(height: 30),
Flexible(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Fixed alignment for "طلب أجازة"
const Align(
alignment: Alignment.topRight,
child: Text(
"طلب أجازة ",
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 10),
//=============================
// DROPDOWN: نوع الإجازة
//=============================
Align(
alignment: Alignment.centerRight,
child: const Text(
"نوع الإجازة",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(height: 5),
// Modified dropdown with disabled state
Directionality(
textDirection: TextDirection.rtl,
child: Opacity(
opacity: isTimedLeave ? 0.45 : 1.0,
child: IgnorePointer(
ignoring: isTimedLeave,
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: 20,
),
height: 58,
decoration: BoxDecoration(
color:
isTimedLeave
? Colors.grey[300]
: Colors.white,
borderRadius: BorderRadius.circular(14),
boxShadow: const [
BoxShadow(
color: Color(0x22000000),
blurRadius: 8,
offset: Offset(0, 3),
),
],
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: leaveType,
icon: const Icon(
Icons.keyboard_arrow_down_rounded,
color: Colors.black,
size: 28,
),
style: const TextStyle(
color: Colors.black,
fontSize: 17,
),
isExpanded: true,
onChanged: (value) {
setState(() {
leaveType = value!;
// Set toggle based on selected value
isTimedLeave = value == "أجازة زمنية";
});
},
items: [
DropdownMenuItem(
value: "إجازة مرضية ",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("إجازة مرضية "),
),
),
),
DropdownMenuItem(
value: "إجازة مدفوعة",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("إجازة مدفوعة"),
),
),
),
DropdownMenuItem(
value: "إجازة غير مدفوعة",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("إجازة غير مدفوعة"),
),
),
),
DropdownMenuItem(
value: "أجازة زمنية",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("أجازة زمنية"),
),
),
),
],
),
),
),
),
),
),
const SizedBox(height: 25),
//=============================
// PERFECT CUSTOM TOGGLE (NEW)
//=============================
GestureDetector(
onTap: () {
setState(() {
isTimedLeave = !isTimedLeave;
// Set leave type to "أجازة زمنية" when toggle is ON
if (isTimedLeave) {
leaveType = "أجازة زمنية";
}
});
},
child: Row(
children: [
// ---------- TOGGLE ----------
AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: 75,
height: 30,
padding: const EdgeInsets.symmetric(
horizontal: 4,
),
decoration: BoxDecoration(
color:
isTimedLeave
? const Color(
0xFF0A6B4A,
) // ON green track
: const Color(
0xFF9E9E9E,
), // OFF grey track
borderRadius: BorderRadius.circular(40),
),
child: Align(
alignment:
isTimedLeave
? Alignment.centerRight
: Alignment.centerLeft,
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: 30,
height: 30,
decoration: BoxDecoration(
color:
isTimedLeave
? const Color(0xFF12BE85) // ON knob
: Colors.white, // OFF knob
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0x2D000000),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
),
),
),
const SizedBox(width: 14),
// ---------- Dot (ALWAYS visible) ----------
Container(
width: 10,
height: 10,
decoration: const BoxDecoration(
color: Color(0xFF32C59A),
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
// ---------- Line (ALWAYS visible) ----------
Expanded(
child: Container(
height: 2,
color: const Color(0xFF32C59A),
),
),
const SizedBox(width: 12),
// ---------- Label ----------
const Text(
"إجازة زمنية",
style: TextStyle(
color: Colors.white,
fontSize: 19,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(height: 20),
// =============================
// DATE PICKER BOX
// =============================
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF4F4F4),
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.end, // ALIGN RIGHT
children: [
// TEXT aligned right
const Text(
"فترة الإجازة",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
// 🟢 YOUR CUSTOM SVG ICON
SvgPicture.asset(
"assets/images/calendar.svg", // <-- replace with your icon filename
width: 24,
height: 24,
color: Color(
0xFF007C46,
), // Optional: match your green
),
],
),
const SizedBox(height: 15),
// From date row
_dateRow(
label: "من",
date: fromDate!,
time: fromTime!,
onDateTap:
isTimedLeave ? null : () => pickDate(true),
onTimeTap: () => pickTime(true),
),
const SizedBox(height: 15),
// To date row
_dateRow(
label: "الى",
date: toDate!,
time: toTime!,
onDateTap:
isTimedLeave ? null : () => pickDate(false),
onTimeTap: () => pickTime(false),
),
],
),
),
const SizedBox(height: 10),
// =============================
// REASON TEXTFIELD (Two Containers)
// =============================
Align(
alignment: Alignment.centerRight,
child: Directionality(
textDirection: TextDirection.rtl,
child: const Text(
"السبب",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
// OUTER BORDER CONTAINER
Container(
padding: const EdgeInsets.all(
12,
), // border thickness space
decoration: BoxDecoration(
border: Border.all(
color: Color(0xFF00FFAA), // green border
width: 0.5,
),
borderRadius: BorderRadius.circular(14),
),
// INNER TEXTFIELD CONTAINER
child: Container(
height: 70,
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 10,
),
decoration: BoxDecoration(
color: const Color(0xFFEAEAEA),
borderRadius: BorderRadius.circular(12),
),
// Added Directionality to fix text direction
child: Directionality(
textDirection: TextDirection.rtl,
child: TextField(
controller: reasonController,
maxLines: 5,
textAlign:
TextAlign
.right, // Added this to align text to the right
decoration: const InputDecoration(
border: InputBorder.none,
hintText:
"اكتب السبب", // Added placeholder text
hintStyle: TextStyle(color: Colors.grey),
),
),
),
),
),
const SizedBox(height: 20),
// CONFIRM BUTTON
Center(
child: OnboardingButton(
text: "تأكيد الطلب",
backgroundColor: const Color(0xFFD1FEF0),
textColor: Colors.black, // Changed to black
onPressed: _saveLeaveRequest, // Call the save method
),
),
],
),
),
),
),
],
),
),
),
);
}
// ===============================================================
// CUSTOM DATE ROW WIDGET
// ===============================================================
Widget _dateRow({
required String label,
required DateTime date,
required TimeOfDay time,
required VoidCallback? onDateTap,
required VoidCallback onTimeTap,
}) {
bool dateDisabled = onDateTap == null;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
height: 55,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
// -----------------------
// DATE PART (can be disabled)
// -----------------------
Opacity(
opacity: dateDisabled ? 0.45 : 1,
child: IgnorePointer(
ignoring: dateDisabled,
child: GestureDetector(
onTap: onDateTap,
child: Row(
children: [
const Icon(Icons.arrow_drop_down),
const SizedBox(width: 4),
Text(
"${date.year}-${date.month}-${date.day}",
style: const TextStyle(fontSize: 16),
),
const SizedBox(width: 10),
Text(
_weekday(date.weekday),
style: const TextStyle(fontSize: 16),
),
],
),
),
),
),
const Spacer(),
// -----------------------
// TIME PART (always active)
// -----------------------
GestureDetector(
onTap: onTimeTap,
child: Text(
"${time.hour}:${time.minute.toString().padLeft(2, '0')}",
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(color: Colors.black, fontSize: 15),
),
],
),
);
}
String _weekday(int day) {
switch (day) {
case 1:
return "الإثنين";
case 2:
return "الثلاثاء";
case 3:
return "الأربعاء";
case 4:
return "الخميس";
case 5:
return "الجمعة";
case 6:
return "السبت";
case 7:
return "الأحد";
default:
return "";
}
}
}

View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'onboarding_screen.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
FlutterNativeSplash.remove();
Future.delayed(const Duration(seconds: 2), () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => OnboardingScreen()),
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/splash.png"),
fit: BoxFit.cover,
),
),
child: Center(child: Image.asset("assets/images/logo.png", width: 200)),
),
);
}
}

View File

@@ -0,0 +1,317 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/app_background.dart';
import '../widgets/settings_bar.dart';
import 'about_screen.dart';
import 'auth_screen.dart';
import '../widgets/change_password_modal.dart';
class UserSettingsScreen extends StatefulWidget {
const UserSettingsScreen({super.key});
@override
State<UserSettingsScreen> createState() => _UserSettingsScreenState();
}
class _UserSettingsScreenState extends State<UserSettingsScreen> {
bool _notificationsOn = false;
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: AppBackground(
child: SafeArea(
child: Column(
children: [
/// -------------------- SETTINGS BAR --------------------
SettingsBar(
selectedIndex: 0,
onTap: (_) {},
showBackButton: true,
onBackTap: () => Navigator.pop(context),
iconPaths: const [],
),
const SizedBox(height: 10),
/// -------------------- PAGE TITLE --------------------
Padding(
padding: const EdgeInsets.only(right: 40),
child: Align(
alignment: Alignment.topRight,
child: const Text(
"الإعدادات",
style: TextStyle(
fontSize: 26,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
/// -------------------- FORM CONTAINER --------------------
Expanded(
child: Center(
child: Stack(
clipBehavior: Clip.none,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 40),
child: Container(
width: MediaQuery.of(context).size.width * 0.85,
height: MediaQuery.of(context).size.height * 0.62,
decoration: BoxDecoration(
color: const Color(0xF3EEFFFA),
borderRadius: BorderRadius.circular(10),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 30,
offset: Offset(0, 20),
),
],
),
padding: const EdgeInsets.symmetric(
horizontal: 22,
vertical: 55,
),
child: Column(
children: [
const SizedBox(height: 10),
/// -------------------- USER NAME --------------------
const Text(
"اسم الموظف",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 6),
/// ✅ CLICKABLE + UNDERLINED
GestureDetector(
onTap: () {
showDialog(
context: context,
barrierDismissible: true,
builder:
(_) => const ChangePasswordModal(),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"تغيير كلمة المرور",
style: TextStyle(
fontSize: 15,
color: Colors.black54,
decoration: TextDecoration.underline,
decorationThickness: 1.5,
),
),
const SizedBox(width: 6),
SvgPicture.asset(
"assets/images/edit.svg",
width: 22,
height: 22,
),
],
),
),
const SizedBox(height: 20),
/// -------------------- NOTIFICATION SWITCH --------------------
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
setState(() {
_notificationsOn = !_notificationsOn;
});
},
child: AnimatedContainer(
duration: const Duration(
milliseconds: 250,
),
width: 75,
height: 30,
padding: const EdgeInsets.symmetric(
horizontal: 4,
),
decoration: BoxDecoration(
color:
_notificationsOn
? const Color(0xFF0A6B4A)
: const Color(0xFFB5B5B5),
borderRadius: BorderRadius.circular(
40,
),
),
child: Align(
alignment:
_notificationsOn
? Alignment.centerRight
: Alignment.centerLeft,
child: AnimatedContainer(
duration: const Duration(
milliseconds: 250,
),
width: 30,
height: 30,
decoration: BoxDecoration(
color:
_notificationsOn
? const Color(0xFF12BE85)
: const Color(0xFF0F0F0F),
shape: BoxShape.circle,
boxShadow: const [
BoxShadow(
color: Color(0x2D000000),
blurRadius: 6,
offset: Offset(0, 2),
),
],
),
),
),
),
),
Row(
children: [
const Text(
"الإشعارات",
style: TextStyle(
fontSize: 16,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
SvgPicture.asset(
"assets/images/ball2.svg",
width: 22,
),
],
),
],
),
_divider(),
_settingsRow(
label: "عن الشركة",
icon: "assets/images/info.svg",
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AboutScreen(),
),
);
},
),
_divider(),
_settingsRow(
label: "تسجيل خروج",
icon: "assets/images/logout2.svg",
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AuthScreen(),
),
);
},
),
],
),
),
),
/// -------------------- AVATAR --------------------
Positioned(
top: -55,
left: 0,
right: 0,
child: Center(
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFFE1E1E1),
border: Border.all(
color: const Color(0xFF2D5E50),
width: 12,
),
),
),
),
),
],
),
),
),
],
),
),
),
),
);
}
Widget _divider() {
return Container(
width: double.infinity,
height: 1,
color: const Color(0xFF008864),
margin: const EdgeInsets.symmetric(vertical: 12),
);
}
Widget _settingsRow({
required String label,
required String icon,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(width: 24),
Row(
children: [
Text(
label,
style: const TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 10),
SvgPicture.asset(icon, width: 23),
],
),
],
),
),
);
}
}