chnages has been made
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import '../dto/advances_list_response_dto.dart';
|
|||||||
|
|
||||||
abstract class AdvanceRemoteDataSource {
|
abstract class AdvanceRemoteDataSource {
|
||||||
Future<AdvanceResponseDto> createAdvance(AdvanceRequestDto request);
|
Future<AdvanceResponseDto> createAdvance(AdvanceRequestDto request);
|
||||||
Future<AdvancesListResponseDto> getAdvances();
|
Future<AdvancesListResponseDto> getAdvances(String employeeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AdvanceRemoteDataSourceImpl implements AdvanceRemoteDataSource {
|
class AdvanceRemoteDataSourceImpl implements AdvanceRemoteDataSource {
|
||||||
@@ -71,9 +71,12 @@ class AdvanceRemoteDataSourceImpl implements AdvanceRemoteDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<AdvancesListResponseDto> getAdvances() async {
|
Future<AdvancesListResponseDto> getAdvances(String employeeId) async {
|
||||||
try {
|
try {
|
||||||
final response = await apiClient.get('/SalaryInAdvance');
|
final response = await apiClient.get(
|
||||||
|
'/SalaryInAdvance',
|
||||||
|
queryParameters: {'EmployeeId': employeeId},
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseData = response.data;
|
final responseData = response.data;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import '../dto/vacations_list_response_dto.dart';
|
|||||||
abstract class VacationRemoteDataSource {
|
abstract class VacationRemoteDataSource {
|
||||||
Future<VacationResponseDto> createVacation(VacationRequestDto request);
|
Future<VacationResponseDto> createVacation(VacationRequestDto request);
|
||||||
Future<VacationTypesResponseDto> getVacationTypes();
|
Future<VacationTypesResponseDto> getVacationTypes();
|
||||||
Future<VacationsListResponseDto> getVacations();
|
Future<VacationsListResponseDto> getVacations(String employeeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class VacationRemoteDataSourceImpl implements VacationRemoteDataSource {
|
class VacationRemoteDataSourceImpl implements VacationRemoteDataSource {
|
||||||
@@ -125,9 +125,12 @@ class VacationRemoteDataSourceImpl implements VacationRemoteDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<VacationsListResponseDto> getVacations() async {
|
Future<VacationsListResponseDto> getVacations(String employeeId) async {
|
||||||
try {
|
try {
|
||||||
final response = await apiClient.get('/Vacation');
|
final response = await apiClient.get(
|
||||||
|
'/Vacation',
|
||||||
|
queryParameters: {'EmployeeId': employeeId},
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseData = response.data;
|
final responseData = response.data;
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ class AdvanceRepositoryImpl implements AdvanceRepository {
|
|||||||
statusCode: responseDto.statusCode,
|
statusCode: responseDto.statusCode,
|
||||||
isSuccess: responseDto.isSuccess,
|
isSuccess: responseDto.isSuccess,
|
||||||
message: responseDto.message,
|
message: responseDto.message,
|
||||||
data: responseDto.data != null
|
data:
|
||||||
|
responseDto.data != null
|
||||||
? AdvanceDataModel(
|
? AdvanceDataModel(
|
||||||
employeeId: responseDto.data!.employeeId,
|
employeeId: responseDto.data!.employeeId,
|
||||||
employeeFullName: responseDto.data!.employeeFullName,
|
employeeFullName: responseDto.data!.employeeFullName,
|
||||||
@@ -61,19 +62,24 @@ class AdvanceRepositoryImpl implements AdvanceRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Either<Failure, AdvancesListResponseModel>> getAdvances() async {
|
Future<Either<Failure, AdvancesListResponseModel>> getAdvances(
|
||||||
|
String employeeId,
|
||||||
|
) async {
|
||||||
try {
|
try {
|
||||||
final responseDto = await remoteDataSource.getAdvances();
|
final responseDto = await remoteDataSource.getAdvances(employeeId);
|
||||||
|
|
||||||
// Convert DTO to Model
|
// Convert DTO to Model
|
||||||
final responseModel = AdvancesListResponseModel(
|
final responseModel = AdvancesListResponseModel(
|
||||||
statusCode: responseDto.statusCode,
|
statusCode: responseDto.statusCode,
|
||||||
isSuccess: responseDto.isSuccess,
|
isSuccess: responseDto.isSuccess,
|
||||||
message: responseDto.message,
|
message: responseDto.message,
|
||||||
data: responseDto.data != null
|
data:
|
||||||
|
responseDto.data != null
|
||||||
? AdvancesListDataModel(
|
? AdvancesListDataModel(
|
||||||
items: responseDto.data!.items
|
items:
|
||||||
.map((dto) => AdvanceDataModel(
|
responseDto.data!.items
|
||||||
|
.map(
|
||||||
|
(dto) => AdvanceDataModel(
|
||||||
employeeId: dto.employeeId,
|
employeeId: dto.employeeId,
|
||||||
employeeFullName: dto.employeeFullName,
|
employeeFullName: dto.employeeFullName,
|
||||||
date: dto.date,
|
date: dto.date,
|
||||||
@@ -87,7 +93,8 @@ class AdvanceRepositoryImpl implements AdvanceRepository {
|
|||||||
updatedAt: dto.updatedAt,
|
updatedAt: dto.updatedAt,
|
||||||
deletedAt: dto.deletedAt,
|
deletedAt: dto.deletedAt,
|
||||||
isDeleted: dto.isDeleted,
|
isDeleted: dto.isDeleted,
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
pageNumber: responseDto.data!.pageNumber,
|
pageNumber: responseDto.data!.pageNumber,
|
||||||
pageSize: responseDto.data!.pageSize,
|
pageSize: responseDto.data!.pageSize,
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ class VacationRepositoryImpl implements VacationRepository {
|
|||||||
statusCode: responseDto.statusCode,
|
statusCode: responseDto.statusCode,
|
||||||
isSuccess: responseDto.isSuccess,
|
isSuccess: responseDto.isSuccess,
|
||||||
message: responseDto.message,
|
message: responseDto.message,
|
||||||
data: responseDto.data != null
|
data:
|
||||||
|
responseDto.data != null
|
||||||
? VacationDataModel(
|
? VacationDataModel(
|
||||||
employeeId: responseDto.data!.employeeId,
|
employeeId: responseDto.data!.employeeId,
|
||||||
employeeFullName: responseDto.data!.employeeFullName,
|
employeeFullName: responseDto.data!.employeeFullName,
|
||||||
@@ -74,11 +75,11 @@ class VacationRepositoryImpl implements VacationRepository {
|
|||||||
statusCode: responseDto.statusCode,
|
statusCode: responseDto.statusCode,
|
||||||
isSuccess: responseDto.isSuccess,
|
isSuccess: responseDto.isSuccess,
|
||||||
message: responseDto.message,
|
message: responseDto.message,
|
||||||
data: responseDto.data
|
data:
|
||||||
.map((dto) => VacationTypeModel(
|
responseDto.data
|
||||||
value: dto.value,
|
.map(
|
||||||
name: dto.name,
|
(dto) => VacationTypeModel(value: dto.value, name: dto.name),
|
||||||
))
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -93,19 +94,24 @@ class VacationRepositoryImpl implements VacationRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Either<Failure, VacationsListResponseModel>> getVacations() async {
|
Future<Either<Failure, VacationsListResponseModel>> getVacations(
|
||||||
|
String employeeId,
|
||||||
|
) async {
|
||||||
try {
|
try {
|
||||||
final responseDto = await remoteDataSource.getVacations();
|
final responseDto = await remoteDataSource.getVacations(employeeId);
|
||||||
|
|
||||||
// Convert DTO to Model
|
// Convert DTO to Model
|
||||||
final responseModel = VacationsListResponseModel(
|
final responseModel = VacationsListResponseModel(
|
||||||
statusCode: responseDto.statusCode,
|
statusCode: responseDto.statusCode,
|
||||||
isSuccess: responseDto.isSuccess,
|
isSuccess: responseDto.isSuccess,
|
||||||
message: responseDto.message,
|
message: responseDto.message,
|
||||||
data: responseDto.data != null
|
data:
|
||||||
|
responseDto.data != null
|
||||||
? VacationsListDataModel(
|
? VacationsListDataModel(
|
||||||
items: responseDto.data!.items
|
items:
|
||||||
.map((dto) => VacationDataModel(
|
responseDto.data!.items
|
||||||
|
.map(
|
||||||
|
(dto) => VacationDataModel(
|
||||||
employeeId: dto.employeeId,
|
employeeId: dto.employeeId,
|
||||||
employeeFullName: dto.employeeFullName,
|
employeeFullName: dto.employeeFullName,
|
||||||
startDate: dto.startDate,
|
startDate: dto.startDate,
|
||||||
@@ -120,7 +126,8 @@ class VacationRepositoryImpl implements VacationRepository {
|
|||||||
updatedAt: dto.updatedAt,
|
updatedAt: dto.updatedAt,
|
||||||
deletedAt: dto.deletedAt,
|
deletedAt: dto.deletedAt,
|
||||||
isDeleted: dto.isDeleted,
|
isDeleted: dto.isDeleted,
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
pageNumber: responseDto.data!.pageNumber,
|
pageNumber: responseDto.data!.pageNumber,
|
||||||
pageSize: responseDto.data!.pageSize,
|
pageSize: responseDto.data!.pageSize,
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ abstract class AdvanceRepository {
|
|||||||
Future<Either<Failure, AdvanceResponseModel>> createAdvance(
|
Future<Either<Failure, AdvanceResponseModel>> createAdvance(
|
||||||
AdvanceRequestModel request,
|
AdvanceRequestModel request,
|
||||||
);
|
);
|
||||||
Future<Either<Failure, AdvancesListResponseModel>> getAdvances();
|
Future<Either<Failure, AdvancesListResponseModel>> getAdvances(
|
||||||
|
String employeeId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,7 @@ abstract class VacationRepository {
|
|||||||
VacationRequest request,
|
VacationRequest request,
|
||||||
);
|
);
|
||||||
Future<Either<Failure, VacationTypesResponseModel>> getVacationTypes();
|
Future<Either<Failure, VacationTypesResponseModel>> getVacationTypes();
|
||||||
Future<Either<Failure, VacationsListResponseModel>> getVacations();
|
Future<Either<Failure, VacationsListResponseModel>> getVacations(
|
||||||
|
String employeeId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class GetAdvancesUseCase {
|
|||||||
|
|
||||||
GetAdvancesUseCase({required this.repository});
|
GetAdvancesUseCase({required this.repository});
|
||||||
|
|
||||||
Future<Either<Failure, AdvancesListResponseModel>> call() {
|
Future<Either<Failure, AdvancesListResponseModel>> call(String employeeId) {
|
||||||
return repository.getAdvances();
|
return repository.getAdvances(employeeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class GetVacationsUseCase {
|
|||||||
|
|
||||||
GetVacationsUseCase({required this.repository});
|
GetVacationsUseCase({required this.repository});
|
||||||
|
|
||||||
Future<Either<Failure, VacationsListResponseModel>> call() {
|
Future<Either<Failure, VacationsListResponseModel>> call(String employeeId) {
|
||||||
return repository.getVacations();
|
return repository.getVacations(employeeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'core/di/injection_container.dart';
|
|||||||
import 'presentation/screens/splash_screen.dart';
|
import 'presentation/screens/splash_screen.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
try {
|
||||||
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
|
|
||||||
@@ -12,6 +13,28 @@ void main() async {
|
|||||||
await initializeDependencies();
|
await initializeDependencies();
|
||||||
|
|
||||||
runApp(const CodaApp());
|
runApp(const CodaApp());
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('CRITICAL INITIALIZATION ERROR: $e');
|
||||||
|
// If initialization fails, show a simple error screen instead of a broken app
|
||||||
|
runApp(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: Text(
|
||||||
|
'Failed to start the app. Please try: \n1. flutter clean\n2. flutter pub get\n\nError: $e',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Ensure splash is removed so user can see the error
|
||||||
|
FlutterNativeSplash.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CodaApp extends StatelessWidget {
|
class CodaApp extends StatelessWidget {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import '../../domain/usecases/get_advances_usecase.dart';
|
|||||||
import '../../domain/models/vacation_response_model.dart';
|
import '../../domain/models/vacation_response_model.dart';
|
||||||
// import '../../domain/models/advances_list_response_model.dart';
|
// import '../../domain/models/advances_list_response_model.dart';
|
||||||
import '../../domain/models/advance_request_model.dart';
|
import '../../domain/models/advance_request_model.dart';
|
||||||
|
import '../../data/datasources/user_local_data_source.dart';
|
||||||
// import '../../core/error/failures.dart';
|
// import '../../core/error/failures.dart';
|
||||||
|
|
||||||
class HolidayScreen extends StatefulWidget {
|
class HolidayScreen extends StatefulWidget {
|
||||||
@@ -38,6 +39,7 @@ class _HolidayScreenState extends State<HolidayScreen> {
|
|||||||
List<AdvanceRequest> _advanceRequests = [];
|
List<AdvanceRequest> _advanceRequests = [];
|
||||||
bool _isLoadingVacations = false;
|
bool _isLoadingVacations = false;
|
||||||
bool _isLoadingAdvances = false;
|
bool _isLoadingAdvances = false;
|
||||||
|
String? _employeeId;
|
||||||
|
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
bool _showActions = true;
|
bool _showActions = true;
|
||||||
@@ -74,9 +76,13 @@ class _HolidayScreenState extends State<HolidayScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initializeData() async {
|
void _initializeData() async {
|
||||||
|
_employeeId = await sl<UserLocalDataSource>().getCachedEmployeeId();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
// Load from API
|
// Load from API
|
||||||
_loadVacationsFromAPI();
|
_loadVacationsFromAPI();
|
||||||
_loadAdvancesFromAPI();
|
_loadAdvancesFromAPI();
|
||||||
|
}
|
||||||
|
|
||||||
// Also listen to local changes (for newly created requests)
|
// Also listen to local changes (for newly created requests)
|
||||||
_requestService.leaveRequestsStream.listen((requests) {
|
_requestService.leaveRequestsStream.listen((requests) {
|
||||||
@@ -94,11 +100,13 @@ class _HolidayScreenState extends State<HolidayScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadVacationsFromAPI() async {
|
Future<void> _loadVacationsFromAPI() async {
|
||||||
|
if (_employeeId == null || _employeeId!.isEmpty) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingVacations = true;
|
_isLoadingVacations = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
final result = await _getVacationsUseCase();
|
final result = await _getVacationsUseCase(_employeeId!);
|
||||||
result.fold(
|
result.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -178,11 +186,13 @@ class _HolidayScreenState extends State<HolidayScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadAdvancesFromAPI() async {
|
Future<void> _loadAdvancesFromAPI() async {
|
||||||
|
if (_employeeId == null || _employeeId!.isEmpty) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingAdvances = true;
|
_isLoadingAdvances = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
final result = await _getAdvancesUseCase();
|
final result = await _getAdvancesUseCase(_employeeId!);
|
||||||
result.fold(
|
result.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class RequestLeaveScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
||||||
// Dropdown value - initialize with default
|
// Dropdown value - initialize with default
|
||||||
String leaveType = "إجازة مرضية ";
|
String leaveType = "إجازة مرضية";
|
||||||
|
|
||||||
// Toggle switch
|
// Toggle switch
|
||||||
bool isTimedLeave = false;
|
bool isTimedLeave = false;
|
||||||
@@ -41,12 +41,133 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
final RequestService _requestService = RequestService();
|
final RequestService _requestService = RequestService();
|
||||||
|
|
||||||
// Use cases
|
// Use cases
|
||||||
final CreateVacationUseCase _createVacationUseCase = sl<CreateVacationUseCase>();
|
final CreateVacationUseCase _createVacationUseCase =
|
||||||
final GetVacationTypesUseCase _getVacationTypesUseCase = sl<GetVacationTypesUseCase>();
|
sl<CreateVacationUseCase>();
|
||||||
|
final GetVacationTypesUseCase _getVacationTypesUseCase =
|
||||||
|
sl<GetVacationTypesUseCase>();
|
||||||
|
|
||||||
// Vacation types from API
|
// Vacation types from API
|
||||||
List<VacationTypeModel> _vacationTypes = [];
|
List<VacationTypeModel> _vacationTypes = [];
|
||||||
int? _selectedVacationTypeValue; // Store selected type value instead of string
|
|
||||||
|
// Store selected type value instead of string
|
||||||
|
int? _selectedVacationTypeValue;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadVacationTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------
|
||||||
|
// STEP A: single helper for selecting type safely
|
||||||
|
// -------------------------------
|
||||||
|
void _setSelectedVacationType(int value) {
|
||||||
|
_selectedVacationTypeValue = value;
|
||||||
|
|
||||||
|
if (_vacationTypes.isNotEmpty) {
|
||||||
|
final selectedType = _vacationTypes.firstWhere(
|
||||||
|
(t) => t.value == value,
|
||||||
|
orElse: () => _vacationTypes.first,
|
||||||
|
);
|
||||||
|
leaveType = _getArabicName(selectedType.name);
|
||||||
|
} else {
|
||||||
|
// Fallback text (when API not loaded yet)
|
||||||
|
if (value == 0) leaveType = "إجازة زمنية";
|
||||||
|
if (value == 1) leaveType = "إجازة مرضية";
|
||||||
|
if (value == 2) leaveType = "إجازة مدفوعة";
|
||||||
|
if (value == 3) leaveType = "إجازة غير مدفوعة";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------
|
||||||
|
// STEP B: single helper for timed leave rule
|
||||||
|
// -------------------------------
|
||||||
|
void _setTimedLeave(bool on) {
|
||||||
|
setState(() {
|
||||||
|
isTimedLeave = on;
|
||||||
|
|
||||||
|
if (isTimedLeave) {
|
||||||
|
// FORCE TimeOff (value = 1)
|
||||||
|
_setSelectedVacationType(0);
|
||||||
|
|
||||||
|
// Optional: keep dates consistent for timed leave (same day)
|
||||||
|
toDate = fromDate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadVacationTypes() async {
|
||||||
|
final result = await _getVacationTypesUseCase();
|
||||||
|
result.fold(
|
||||||
|
(failure) {
|
||||||
|
print('Failed to load vacation types: ${_getFailureMessage(failure)}');
|
||||||
|
},
|
||||||
|
(response) {
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_vacationTypes = response.data;
|
||||||
|
|
||||||
|
if (_vacationTypes.isEmpty) return;
|
||||||
|
|
||||||
|
// IMPORTANT:
|
||||||
|
// If toggle already ON -> force TimeOff
|
||||||
|
if (isTimedLeave) {
|
||||||
|
final timeOff = _vacationTypes.firstWhere(
|
||||||
|
(t) => t.value == 1,
|
||||||
|
orElse: () => _vacationTypes.first,
|
||||||
|
);
|
||||||
|
_setSelectedVacationType(timeOff.value);
|
||||||
|
} else {
|
||||||
|
// Otherwise default to SickLeave (2) if available
|
||||||
|
final sickLeave = _vacationTypes.firstWhere(
|
||||||
|
(type) => type.value == 2,
|
||||||
|
orElse: () => _vacationTypes.first,
|
||||||
|
);
|
||||||
|
_setSelectedVacationType(sickLeave.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getArabicName(String apiName) {
|
||||||
|
switch (apiName) {
|
||||||
|
case 'TimeOff':
|
||||||
|
return 'إجازة زمنية';
|
||||||
|
case 'SickLeave':
|
||||||
|
return 'إجازة مرضية';
|
||||||
|
case 'PaidLeave':
|
||||||
|
return 'إجازة مدفوعة';
|
||||||
|
case 'UnpaidLeave':
|
||||||
|
return 'إجازة غير مدفوعة';
|
||||||
|
default:
|
||||||
|
return apiName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getVacationTypeValue() {
|
||||||
|
if (_selectedVacationTypeValue != null) {
|
||||||
|
return _selectedVacationTypeValue!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaveType.contains("مرضية") || leaveType == "SickLeave") {
|
||||||
|
return 1;
|
||||||
|
} else if (leaveType.contains("مدفوعة") && !leaveType.contains("غير")) {
|
||||||
|
return 2;
|
||||||
|
} else if (leaveType.contains("غير مدفوعة") || leaveType == "UnpaidLeave") {
|
||||||
|
return 3;
|
||||||
|
} else if (leaveType.contains("زمنية") || leaveType == "TimeOff") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getFailureMessage(Failure failure) {
|
||||||
|
if (failure is ServerFailure) return failure.message;
|
||||||
|
if (failure is NetworkFailure) return failure.message;
|
||||||
|
return 'حدث خطأ غير متوقع';
|
||||||
|
}
|
||||||
|
|
||||||
/// PICK DATE
|
/// PICK DATE
|
||||||
Future<void> pickDate(bool isFrom) async {
|
Future<void> pickDate(bool isFrom) async {
|
||||||
@@ -56,15 +177,15 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
initialDate: initial,
|
initialDate: initial,
|
||||||
firstDate: DateTime(2020),
|
firstDate: DateTime(2020),
|
||||||
lastDate: DateTime(2035),
|
lastDate: DateTime(2035),
|
||||||
builder: (context, child) {
|
builder: (context, child) => Theme(data: ThemeData.dark(), child: child!),
|
||||||
return Theme(data: ThemeData.dark(), child: child!);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newDate != null) {
|
if (newDate != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (isFrom) {
|
if (isFrom) {
|
||||||
fromDate = newDate;
|
fromDate = newDate;
|
||||||
|
// If timed leave, keep toDate same as fromDate
|
||||||
|
if (isTimedLeave) toDate = newDate;
|
||||||
} else {
|
} else {
|
||||||
toDate = newDate;
|
toDate = newDate;
|
||||||
}
|
}
|
||||||
@@ -78,9 +199,7 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
TimeOfDay? newTime = await showTimePicker(
|
TimeOfDay? newTime = await showTimePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialTime: initial,
|
initialTime: initial,
|
||||||
builder: (context, child) {
|
builder: (context, child) => Theme(data: ThemeData.dark(), child: child!),
|
||||||
return Theme(data: ThemeData.dark(), child: child!);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newTime != null) {
|
if (newTime != null) {
|
||||||
@@ -94,85 +213,8 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_loadVacationTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadVacationTypes() async {
|
|
||||||
final result = await _getVacationTypesUseCase();
|
|
||||||
result.fold(
|
|
||||||
(failure) {
|
|
||||||
// Silently fail - user can still submit with default types
|
|
||||||
print('Failed to load vacation types: ${_getFailureMessage(failure)}');
|
|
||||||
},
|
|
||||||
(response) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_vacationTypes = response.data;
|
|
||||||
// Set default to SickLeave (value: 2) if available
|
|
||||||
if (_vacationTypes.isNotEmpty) {
|
|
||||||
final sickLeave = _vacationTypes.firstWhere(
|
|
||||||
(type) => type.value == 2,
|
|
||||||
orElse: () => _vacationTypes.first,
|
|
||||||
);
|
|
||||||
_selectedVacationTypeValue = sickLeave.value;
|
|
||||||
leaveType = _getArabicName(sickLeave.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getArabicName(String apiName) {
|
|
||||||
// Map API names to Arabic display names
|
|
||||||
switch (apiName) {
|
|
||||||
case 'TimeOff':
|
|
||||||
return 'أجازة زمنية';
|
|
||||||
case 'SickLeave':
|
|
||||||
return 'إجازة مرضية';
|
|
||||||
case 'PaidLeave':
|
|
||||||
return 'إجازة مدفوعة';
|
|
||||||
case 'UnpaidLeave':
|
|
||||||
return 'إجازة غير مدفوعة';
|
|
||||||
default:
|
|
||||||
return apiName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _getVacationTypeValue() {
|
|
||||||
// Use selected value if available, otherwise fallback to mapping
|
|
||||||
if (_selectedVacationTypeValue != null) {
|
|
||||||
return _selectedVacationTypeValue!;
|
|
||||||
}
|
|
||||||
// Fallback: Map display names to API type values
|
|
||||||
if (leaveType.contains("مرضية") || leaveType == "SickLeave") {
|
|
||||||
return 2; // SickLeave
|
|
||||||
} else if (leaveType.contains("مدفوعة") && !leaveType.contains("غير")) {
|
|
||||||
return 3; // PaidLeave
|
|
||||||
} else if (leaveType.contains("غير مدفوعة") || leaveType == "UnpaidLeave") {
|
|
||||||
return 4; // UnpaidLeave
|
|
||||||
} else if (leaveType.contains("زمنية") || leaveType == "TimeOff") {
|
|
||||||
return 1; // TimeOff
|
|
||||||
}
|
|
||||||
return 1; // Default to TimeOff
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getFailureMessage(Failure failure) {
|
|
||||||
if (failure is ServerFailure) {
|
|
||||||
return failure.message;
|
|
||||||
} else if (failure is NetworkFailure) {
|
|
||||||
return failure.message;
|
|
||||||
}
|
|
||||||
return 'حدث خطأ غير متوقع';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to save the leave request
|
|
||||||
Future<void> _saveLeaveRequest() async {
|
Future<void> _saveLeaveRequest() async {
|
||||||
if (reasonController.text.isEmpty) {
|
if (reasonController.text.isEmpty) {
|
||||||
// Show an error message if reason is empty
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
@@ -184,7 +226,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get employee ID
|
|
||||||
final employeeId = await sl<UserLocalDataSource>().getCachedEmployeeId();
|
final employeeId = await sl<UserLocalDataSource>().getCachedEmployeeId();
|
||||||
if (employeeId == null) {
|
if (employeeId == null) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -198,12 +239,10 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare dates - if timed leave, use same date with time differences
|
|
||||||
DateTime finalStartDate = fromDate!;
|
DateTime finalStartDate = fromDate!;
|
||||||
DateTime finalEndDate = toDate!;
|
DateTime finalEndDate = toDate!;
|
||||||
|
|
||||||
if (isTimedLeave) {
|
if (isTimedLeave) {
|
||||||
// For timed leave, use the same date but with different times
|
|
||||||
finalStartDate = DateTime(
|
finalStartDate = DateTime(
|
||||||
fromDate!.year,
|
fromDate!.year,
|
||||||
fromDate!.month,
|
fromDate!.month,
|
||||||
@@ -219,15 +258,12 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
toTime!.minute,
|
toTime!.minute,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// For regular leave, use dates at midnight
|
|
||||||
finalStartDate = DateTime(fromDate!.year, fromDate!.month, fromDate!.day);
|
finalStartDate = DateTime(fromDate!.year, fromDate!.month, fromDate!.day);
|
||||||
finalEndDate = DateTime(toDate!.year, toDate!.month, toDate!.day);
|
finalEndDate = DateTime(toDate!.year, toDate!.month, toDate!.day);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get vacation type value
|
|
||||||
final typeValue = _getVacationTypeValue();
|
final typeValue = _getVacationTypeValue();
|
||||||
|
|
||||||
// Show loading indicator
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -237,7 +273,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create vacation request
|
|
||||||
final vacationRequest = VacationRequest(
|
final vacationRequest = VacationRequest(
|
||||||
employeeId: employeeId,
|
employeeId: employeeId,
|
||||||
startDate: finalStartDate,
|
startDate: finalStartDate,
|
||||||
@@ -248,8 +283,8 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
final result = await _createVacationUseCase(vacationRequest);
|
final result = await _createVacationUseCase(vacationRequest);
|
||||||
|
|
||||||
if (mounted) {
|
if (!mounted) return;
|
||||||
Navigator.pop(context); // Close loading dialog
|
Navigator.pop(context);
|
||||||
|
|
||||||
result.fold(
|
result.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
@@ -261,9 +296,10 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
(response) {
|
(response) {
|
||||||
// Also save locally for UI display
|
|
||||||
final leaveRequest = LeaveRequest(
|
final leaveRequest = LeaveRequest(
|
||||||
id: response.data?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
|
id:
|
||||||
|
response.data?.id ??
|
||||||
|
DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
leaveType: leaveType,
|
leaveType: leaveType,
|
||||||
isTimedLeave: isTimedLeave,
|
isTimedLeave: isTimedLeave,
|
||||||
fromDate: fromDate!,
|
fromDate: fromDate!,
|
||||||
@@ -272,11 +308,10 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
toTime: toTime!,
|
toTime: toTime!,
|
||||||
reason: reasonController.text,
|
reason: reasonController.text,
|
||||||
requestDate: DateTime.now(),
|
requestDate: DateTime.now(),
|
||||||
status: "waiting", // Default status
|
status: "waiting",
|
||||||
);
|
);
|
||||||
_requestService.addLeaveRequest(leaveRequest);
|
_requestService.addLeaveRequest(leaveRequest);
|
||||||
|
|
||||||
// Show success message
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('تم إرسال طلب الأجازة بنجاح'),
|
content: Text('تم إرسال طلب الأجازة بنجاح'),
|
||||||
@@ -284,14 +319,12 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Navigate back to the previous screen
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.pop(context); // Close loading dialog
|
Navigator.pop(context);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text('حدث خطأ غير متوقع: $e'),
|
content: Text('حدث خطأ غير متوقع: $e'),
|
||||||
@@ -305,12 +338,11 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
resizeToAvoidBottomInset: true, // ✅ IMPORTANT
|
resizeToAvoidBottomInset: true,
|
||||||
body: AppBackground(
|
body: AppBackground(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// In your RequestLeaveScreen's build method, replace the SettingsBar with this:
|
|
||||||
SettingsBar(
|
SettingsBar(
|
||||||
selectedIndex: -1,
|
selectedIndex: -1,
|
||||||
onTap: (_) {},
|
onTap: (_) {},
|
||||||
@@ -322,8 +354,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Title
|
|
||||||
// const SizedBox(height: 30),
|
|
||||||
Flexible(
|
Flexible(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -331,7 +361,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Fixed alignment for "طلب أجازة"
|
|
||||||
const Align(
|
const Align(
|
||||||
alignment: Alignment.topRight,
|
alignment: Alignment.topRight,
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -345,9 +374,7 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
//=============================
|
|
||||||
// DROPDOWN: نوع الإجازة
|
|
||||||
//=============================
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
@@ -362,7 +389,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
|
|
||||||
// Modified dropdown with disabled state
|
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
@@ -390,11 +416,13 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: DropdownButtonHideUnderline(
|
child: DropdownButtonHideUnderline(
|
||||||
child: _vacationTypes.isEmpty
|
child:
|
||||||
|
_vacationTypes.isEmpty
|
||||||
? const Center(
|
? const Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: EdgeInsets.all(8.0),
|
||||||
child: CircularProgressIndicator(),
|
child:
|
||||||
|
CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: DropdownButton<int>(
|
: DropdownButton<int>(
|
||||||
@@ -410,25 +438,35 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
),
|
),
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value == null) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedVacationTypeValue = value;
|
_setSelectedVacationType(value);
|
||||||
final selectedType = _vacationTypes
|
|
||||||
.firstWhere((t) => t.value == value);
|
|
||||||
leaveType = _getArabicName(selectedType.name);
|
|
||||||
// Set toggle based on selected value (TimeOff = 1)
|
|
||||||
isTimedLeave = value == 1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sync toggle with selection
|
||||||
|
if (value == 0 && !isTimedLeave) {
|
||||||
|
_setTimedLeave(true);
|
||||||
|
} else if (value != 0 &&
|
||||||
|
isTimedLeave) {
|
||||||
|
setState(
|
||||||
|
() => isTimedLeave = false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
items: _vacationTypes.map((type) {
|
items:
|
||||||
final arabicName = _getArabicName(type.name);
|
_vacationTypes.map((type) {
|
||||||
|
final arabicName =
|
||||||
|
_getArabicName(type.name);
|
||||||
return DropdownMenuItem<int>(
|
return DropdownMenuItem<int>(
|
||||||
value: type.value,
|
value: type.value,
|
||||||
child: Directionality(
|
child: Directionality(
|
||||||
textDirection: TextDirection.rtl,
|
textDirection:
|
||||||
|
TextDirection.rtl,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment:
|
||||||
|
Alignment
|
||||||
|
.centerRight,
|
||||||
child: Text(arabicName),
|
child: Text(arabicName),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -443,25 +481,12 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
|
|
||||||
//=============================
|
|
||||||
// PERFECT CUSTOM TOGGLE (NEW)
|
|
||||||
//=============================
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
_setTimedLeave(!isTimedLeave);
|
||||||
isTimedLeave = !isTimedLeave;
|
|
||||||
// Set leave type to TimeOff (value: 1) when toggle is ON
|
|
||||||
if (isTimedLeave) {
|
|
||||||
final timeOffType = _vacationTypes
|
|
||||||
.firstWhere((t) => t.value == 1, orElse: () => _vacationTypes.first);
|
|
||||||
_selectedVacationTypeValue = timeOffType.value;
|
|
||||||
leaveType = _getArabicName(timeOffType.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// ---------- TOGGLE ----------
|
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
width: 75,
|
width: 75,
|
||||||
@@ -472,12 +497,8 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color:
|
||||||
isTimedLeave
|
isTimedLeave
|
||||||
? const Color(
|
? const Color(0xFF0A6B4A)
|
||||||
0xFF0A6B4A,
|
: const Color(0xFF9E9E9E),
|
||||||
) // ON green track
|
|
||||||
: const Color(
|
|
||||||
0xFF9E9E9E,
|
|
||||||
), // OFF grey track
|
|
||||||
borderRadius: BorderRadius.circular(40),
|
borderRadius: BorderRadius.circular(40),
|
||||||
),
|
),
|
||||||
child: Align(
|
child: Align(
|
||||||
@@ -492,8 +513,8 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color:
|
||||||
isTimedLeave
|
isTimedLeave
|
||||||
? const Color(0xFF12BE85) // ON knob
|
? const Color(0xFF12BE85)
|
||||||
: Colors.white, // OFF knob
|
: Colors.white,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -509,7 +530,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(width: 14),
|
const SizedBox(width: 14),
|
||||||
|
|
||||||
// ---------- Dot (ALWAYS visible) ----------
|
|
||||||
Container(
|
Container(
|
||||||
width: 10,
|
width: 10,
|
||||||
height: 10,
|
height: 10,
|
||||||
@@ -521,7 +541,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
|
|
||||||
// ---------- Line (ALWAYS visible) ----------
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
@@ -531,7 +550,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
// ---------- Label ----------
|
|
||||||
const Text(
|
const Text(
|
||||||
"إجازة زمنية",
|
"إجازة زمنية",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -546,9 +564,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// =============================
|
|
||||||
// DATE PICKER BOX
|
|
||||||
// =============================
|
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -565,10 +580,8 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
MainAxisAlignment.end, // ALIGN RIGHT
|
|
||||||
children: [
|
children: [
|
||||||
// TEXT aligned right
|
|
||||||
const Text(
|
const Text(
|
||||||
"فترة الإجازة",
|
"فترة الإجازة",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -577,24 +590,17 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
|
|
||||||
// 🟢 YOUR CUSTOM SVG ICON
|
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
"assets/images/calendar.svg", // <-- replace with your icon filename
|
"assets/images/calendar.svg",
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
color: Color(
|
color: const Color(0xFF007C46),
|
||||||
0xFF007C46,
|
|
||||||
), // Optional: match your green
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
// From date row
|
|
||||||
_dateRow(
|
_dateRow(
|
||||||
label: "من",
|
label: "من",
|
||||||
date: fromDate!,
|
date: fromDate!,
|
||||||
@@ -606,7 +612,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
// To date row
|
|
||||||
_dateRow(
|
_dateRow(
|
||||||
label: "الى",
|
label: "الى",
|
||||||
date: toDate!,
|
date: toDate!,
|
||||||
@@ -621,9 +626,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
// =============================
|
|
||||||
// REASON TEXTFIELD (Two Containers)
|
|
||||||
// =============================
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Directionality(
|
child: Directionality(
|
||||||
@@ -639,20 +641,15 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// OUTER BORDER CONTAINER
|
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(
|
padding: const EdgeInsets.all(12),
|
||||||
12,
|
|
||||||
), // border thickness space
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Color(0xFF00FFAA), // green border
|
color: const Color(0xFF00FFAA),
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
|
|
||||||
// INNER TEXTFIELD CONTAINER
|
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 70,
|
height: 70,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -663,19 +660,15 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
color: const Color(0xFFEAEAEA),
|
color: const Color(0xFFEAEAEA),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
// Added Directionality to fix text direction
|
|
||||||
child: Directionality(
|
child: Directionality(
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: reasonController,
|
controller: reasonController,
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
textAlign:
|
textAlign: TextAlign.right,
|
||||||
TextAlign
|
|
||||||
.right, // Added this to align text to the right
|
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText:
|
hintText: "اكتب السبب",
|
||||||
"اكتب السبب", // Added placeholder text
|
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -685,13 +678,12 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// CONFIRM BUTTON
|
|
||||||
Center(
|
Center(
|
||||||
child: OnboardingButton(
|
child: OnboardingButton(
|
||||||
text: "تأكيد الطلب",
|
text: "تأكيد الطلب",
|
||||||
backgroundColor: const Color(0xFFD1FEF0),
|
backgroundColor: const Color(0xFFD1FEF0),
|
||||||
textColor: Colors.black, // Changed to black
|
textColor: Colors.black,
|
||||||
onPressed: _saveLeaveRequest, // Call the save method
|
onPressed: _saveLeaveRequest,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -706,9 +698,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============================================================
|
|
||||||
// CUSTOM DATE ROW WIDGET
|
|
||||||
// ===============================================================
|
|
||||||
Widget _dateRow({
|
Widget _dateRow({
|
||||||
required String label,
|
required String label,
|
||||||
required DateTime date,
|
required DateTime date,
|
||||||
@@ -727,9 +716,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// -----------------------
|
|
||||||
// DATE PART (can be disabled)
|
|
||||||
// -----------------------
|
|
||||||
Opacity(
|
Opacity(
|
||||||
opacity: dateDisabled ? 0.45 : 1,
|
opacity: dateDisabled ? 0.45 : 1,
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
@@ -757,9 +743,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|
||||||
// -----------------------
|
|
||||||
// TIME PART (always active)
|
|
||||||
// -----------------------
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onTimeTap,
|
onTap: onTimeTap,
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -779,8 +762,11 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: DateTime.weekday is 1..7 (Mon..Sun)
|
||||||
String _weekday(int day) {
|
String _weekday(int day) {
|
||||||
switch (day) {
|
switch (day) {
|
||||||
|
case 7:
|
||||||
|
return "الأحد";
|
||||||
case 1:
|
case 1:
|
||||||
return "الإثنين";
|
return "الإثنين";
|
||||||
case 2:
|
case 2:
|
||||||
@@ -793,8 +779,6 @@ class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
|
|||||||
return "الجمعة";
|
return "الجمعة";
|
||||||
case 6:
|
case 6:
|
||||||
return "السبت";
|
return "السبت";
|
||||||
case 7:
|
|
||||||
return "الأحد";
|
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _checkTokenAndNavigate() async {
|
Future<void> _checkTokenAndNavigate() async {
|
||||||
|
try {
|
||||||
// Wait for splash screen display
|
// Wait for splash screen display
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
|
||||||
@@ -42,6 +43,16 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||||||
MaterialPageRoute(builder: (_) => const OnboardingScreen()),
|
MaterialPageRoute(builder: (_) => const OnboardingScreen()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error in _checkTokenAndNavigate: $e');
|
||||||
|
if (mounted) {
|
||||||
|
// Fallback to onboarding if anything fails
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (_) => const OnboardingScreen()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
Reference in New Issue
Block a user