attendence records, extra hours , rewards and punishment funnctionality have been added
This commit is contained in:
99
lib/presentation/bloc/finance_bloc.dart
Normal file
99
lib/presentation/bloc/finance_bloc.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../domain/usecases/get_attendance_records_usecase.dart';
|
||||
import '../../../domain/usecases/get_extra_hours_usecase.dart';
|
||||
import '../../../domain/usecases/get_rewards_usecase.dart';
|
||||
import '../../../domain/usecases/get_punishments_usecase.dart';
|
||||
import '../../../domain/models/finance_record.dart';
|
||||
import '../../../domain/models/finance_category.dart';
|
||||
import '../../../core/error/exceptions.dart';
|
||||
|
||||
// Events
|
||||
abstract class FinanceEvent {}
|
||||
|
||||
class LoadFinanceDataEvent extends FinanceEvent {
|
||||
final String employeeId;
|
||||
final FinanceCategory category;
|
||||
|
||||
LoadFinanceDataEvent({required this.employeeId, required this.category});
|
||||
}
|
||||
|
||||
// States
|
||||
abstract class FinanceState {}
|
||||
|
||||
class FinanceInitial extends FinanceState {}
|
||||
|
||||
class FinanceLoading extends FinanceState {}
|
||||
|
||||
class FinanceLoaded extends FinanceState {
|
||||
final List<FinanceRecord> records;
|
||||
final FinanceCategory category;
|
||||
|
||||
FinanceLoaded({required this.records, required this.category});
|
||||
}
|
||||
|
||||
class FinanceError extends FinanceState {
|
||||
final String message;
|
||||
|
||||
FinanceError({required this.message});
|
||||
}
|
||||
|
||||
// Bloc
|
||||
class FinanceBloc extends Bloc<FinanceEvent, FinanceState> {
|
||||
final GetAttendanceRecordsUseCase getAttendanceRecordsUseCase;
|
||||
final GetExtraHoursUseCase getExtraHoursUseCase;
|
||||
final GetRewardsUseCase getRewardsUseCase;
|
||||
final GetPunishmentsUseCase getPunishmentsUseCase;
|
||||
|
||||
FinanceBloc({
|
||||
required this.getAttendanceRecordsUseCase,
|
||||
required this.getExtraHoursUseCase,
|
||||
required this.getRewardsUseCase,
|
||||
required this.getPunishmentsUseCase,
|
||||
}) : super(FinanceInitial()) {
|
||||
on<LoadFinanceDataEvent>(_onLoadFinanceData);
|
||||
}
|
||||
|
||||
Future<void> _onLoadFinanceData(
|
||||
LoadFinanceDataEvent event,
|
||||
Emitter<FinanceState> emit,
|
||||
) async {
|
||||
if (event.employeeId.isEmpty) {
|
||||
emit(FinanceError(message: 'Employee ID is missing or invalid'));
|
||||
return;
|
||||
}
|
||||
|
||||
emit(FinanceLoading());
|
||||
try {
|
||||
List<FinanceRecord> records;
|
||||
switch (event.category) {
|
||||
case FinanceCategory.attendance:
|
||||
records = await getAttendanceRecordsUseCase.execute(
|
||||
employeeId: event.employeeId,
|
||||
);
|
||||
break;
|
||||
case FinanceCategory.overtime:
|
||||
records = await getExtraHoursUseCase.execute(
|
||||
employeeId: event.employeeId,
|
||||
);
|
||||
break;
|
||||
case FinanceCategory.bonus:
|
||||
records = await getRewardsUseCase.execute(
|
||||
employeeId: event.employeeId,
|
||||
);
|
||||
break;
|
||||
case FinanceCategory.penalty:
|
||||
records = await getPunishmentsUseCase.execute(
|
||||
employeeId: event.employeeId,
|
||||
);
|
||||
break;
|
||||
}
|
||||
emit(FinanceLoaded(records: records, category: event.category));
|
||||
} on ServerException catch (e) {
|
||||
emit(FinanceError(message: e.message));
|
||||
} on NetworkException catch (e) {
|
||||
emit(FinanceError(message: e.message));
|
||||
} catch (e) {
|
||||
emit(FinanceError(message: 'Unexpected error occurred: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
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_bloc/flutter_bloc.dart';
|
||||
import '../widgets/finance_summary_card.dart';
|
||||
import '../widgets/work_day_card.dart';
|
||||
import '../widgets/settings_bar.dart';
|
||||
import '../bloc/finance_bloc.dart';
|
||||
import '../../core/di/injection_container.dart';
|
||||
import '../../data/datasources/user_local_data_source.dart';
|
||||
import '../../domain/models/finance_category.dart';
|
||||
|
||||
class FinanceScreen extends StatefulWidget {
|
||||
final void Function(bool isScrollingDown)? onScrollEvent;
|
||||
@@ -15,13 +20,15 @@ class FinanceScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FinanceScreenState extends State<FinanceScreen> {
|
||||
String dropdownValue = "الكل";
|
||||
FinanceCategory currentCategory = FinanceCategory.attendance;
|
||||
late ScrollController scrollController;
|
||||
String? _employeeId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
scrollController = ScrollController();
|
||||
_loadInitialData();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -30,74 +37,152 @@ class _FinanceScreenState extends State<FinanceScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _loadInitialData() async {
|
||||
_employeeId = await sl<UserLocalDataSource>().getCachedEmployeeId();
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _triggerLoad(BuildContext context) {
|
||||
if (_employeeId != null && _employeeId!.isNotEmpty) {
|
||||
final bloc = context.read<FinanceBloc>();
|
||||
if (bloc.state is FinanceInitial) {
|
||||
bloc.add(
|
||||
LoadFinanceDataEvent(
|
||||
employeeId: _employeeId!,
|
||||
category: currentCategory,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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(),
|
||||
return BlocProvider(
|
||||
create: (context) => sl<FinanceBloc>(),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: SafeArea(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
// Trigger initial load when bloc is ready
|
||||
_triggerLoad(context);
|
||||
|
||||
return CustomScrollView(
|
||||
controller: scrollController,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsBar(
|
||||
selectedIndex: 0,
|
||||
showBackButton: false,
|
||||
iconPaths: const [
|
||||
'assets/images/user.svg',
|
||||
'assets/images/ball.svg',
|
||||
],
|
||||
onTap: (index) {
|
||||
if (index == 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const UserSettingsScreen(),
|
||||
),
|
||||
);
|
||||
} else if (index == 1) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NotificationsScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
||||
|
||||
/// SUMMARY CARD
|
||||
SliverToBoxAdapter(
|
||||
child: FinanceSummaryCard(
|
||||
totalAmount: "333,000",
|
||||
currentCategory: currentCategory,
|
||||
onCalendarTap:
|
||||
() => showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
),
|
||||
onCategoryChanged: (category) {
|
||||
if (category != null) {
|
||||
setState(() => currentCategory = category);
|
||||
context.read<FinanceBloc>().add(
|
||||
LoadFinanceDataEvent(
|
||||
employeeId: _employeeId ?? '',
|
||||
category: currentCategory,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
/// DATA LIST
|
||||
BlocBuilder<FinanceBloc, FinanceState>(
|
||||
builder: (context, state) {
|
||||
if (state is FinanceLoading || state is FinanceInitial) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (index == 1) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NotificationsScreen(),
|
||||
} else if (state is FinanceLoaded) {
|
||||
if (state.records.isEmpty) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Text("لا توجد سجلات"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((
|
||||
context,
|
||||
index,
|
||||
) {
|
||||
return WorkDayCard(record: state.records[index]);
|
||||
}, childCount: state.records.length),
|
||||
);
|
||||
} else if (state is FinanceError) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
state.message,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SliverToBoxAdapter(child: SizedBox());
|
||||
},
|
||||
),
|
||||
),
|
||||
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)),
|
||||
],
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../../domain/models/finance_category.dart';
|
||||
|
||||
class FinanceSummaryCard extends StatelessWidget {
|
||||
final String totalAmount;
|
||||
final String dropdownValue;
|
||||
final FinanceCategory currentCategory;
|
||||
final VoidCallback onCalendarTap;
|
||||
final ValueChanged<String?> onDropdownChanged;
|
||||
final ValueChanged<FinanceCategory?> onCategoryChanged;
|
||||
|
||||
const FinanceSummaryCard({
|
||||
super.key,
|
||||
required this.totalAmount,
|
||||
required this.dropdownValue,
|
||||
this.totalAmount = "0",
|
||||
required this.currentCategory,
|
||||
required this.onCalendarTap,
|
||||
required this.onDropdownChanged,
|
||||
required this.onCategoryChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -112,8 +113,8 @@ class FinanceSummaryCard extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: dropdownValue,
|
||||
child: DropdownButton<FinanceCategory>(
|
||||
value: currentCategory,
|
||||
icon: const Icon(
|
||||
Icons.arrow_drop_down,
|
||||
size: 35,
|
||||
@@ -121,45 +122,26 @@ class FinanceSummaryCard extends StatelessWidget {
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: Color.from(
|
||||
alpha: 1,
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
),
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
onChanged: onDropdownChanged,
|
||||
onChanged: onCategoryChanged,
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: "الكل",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Text("الكل"),
|
||||
),
|
||||
),
|
||||
|
||||
DropdownMenuItem(
|
||||
value: "ساعات أضافية",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Text("ساعات أضافية"),
|
||||
),
|
||||
),
|
||||
|
||||
DropdownMenuItem(
|
||||
value: "مكافئة",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Text("مكافئة"),
|
||||
),
|
||||
value: FinanceCategory.attendance,
|
||||
child: Text("الحظور"),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: " عقوبة",
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Text(" عقوبة"),
|
||||
),
|
||||
value: FinanceCategory.overtime,
|
||||
child: Text("ساعات أضافية"),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: FinanceCategory.bonus,
|
||||
child: Text("مكافئة"),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: FinanceCategory.penalty,
|
||||
child: Text("عقوبة"),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../domain/models/attendance_model.dart';
|
||||
import '../../domain/models/overtime_model.dart';
|
||||
import '../../domain/models/extra_payment_model.dart';
|
||||
import '../../domain/models/finance_record.dart';
|
||||
import 'gradient_line.dart';
|
||||
import 'status_circle.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class WorkDayCard extends StatelessWidget {
|
||||
const WorkDayCard({super.key});
|
||||
final FinanceRecord record;
|
||||
|
||||
const WorkDayCard({super.key, required this.record});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dateFormat = DateFormat('yyyy.MM.dd');
|
||||
|
||||
String title = "يوم عمل";
|
||||
if (record is OvertimeModel) title = "ساعات أضافية";
|
||||
if (record is ExtraPaymentModel) {
|
||||
title = (record as ExtraPaymentModel).isPenalty ? "عقوبة" : "مكافئة";
|
||||
}
|
||||
|
||||
final dateStr =
|
||||
record.date != null ? dateFormat.format(record.date!) : '--.--.--';
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 18, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
@@ -25,87 +43,152 @@ class WorkDayCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text(
|
||||
"يوم عمل",
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
/// 🔥 FIXED: CENTERED LINES BETWEEN CIRCLES
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_StatusItem(
|
||||
color: const Color(0xFFD16400),
|
||||
icon: SvgPicture.asset('assets/images/money3.svg', width: 20),
|
||||
label: "سعر كلي\n18,250 د.ع",
|
||||
Text(
|
||||
dateStr,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
|
||||
/// LINE CENTERED VERTICALLY
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: GradientLine(
|
||||
start: const Color(0xFFD16400),
|
||||
end: const Color(0xFF1266A8),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
_StatusItem(
|
||||
color: const Color(0xFF1266A8),
|
||||
icon: SvgPicture.asset('assets/images/watch.svg', width: 20),
|
||||
label: "عدد ساعات\n5.50",
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: GradientLine(
|
||||
start: const Color(0xFF1266A8),
|
||||
end: const Color(0xFFB00000),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
_StatusItem(
|
||||
color: const Color(0xFFB00000),
|
||||
icon: SvgPicture.asset('assets/images/out.svg', width: 20),
|
||||
label: "خروج\n1:14pm",
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: GradientLine(
|
||||
start: const Color(0xFFB00000),
|
||||
end: const Color(0xFF0A8F6B),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
_StatusItem(
|
||||
color: const Color(0xFF0A8F6B),
|
||||
icon: SvgPicture.asset('assets/images/in.svg', width: 20),
|
||||
label: "دخول\n1:14pm",
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
/// CONTENT
|
||||
_buildContent(context),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
const Divider(color: Colors.black38),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: const [
|
||||
Text("2025.12.1", style: TextStyle(fontSize: 12)),
|
||||
Text("ملاحظات ان وجدت", style: TextStyle(fontSize: 12)),
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
record.reason ??
|
||||
(record is ExtraPaymentModel
|
||||
? ((record as ExtraPaymentModel).note ??
|
||||
"لا يوجد ملاحظات")
|
||||
: "لا يوجد ملاحظات"),
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context) {
|
||||
if (record is AttendanceModel) {
|
||||
return _buildAttendanceContent(record as AttendanceModel);
|
||||
} else if (record is OvertimeModel) {
|
||||
return _buildOvertimeContent(record as OvertimeModel);
|
||||
} else if (record is ExtraPaymentModel) {
|
||||
return _buildExtraPaymentContent(record as ExtraPaymentModel);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
Widget _buildAttendanceContent(AttendanceModel attendance) {
|
||||
final timeFormat = DateFormat('h:mm a');
|
||||
final loginStr =
|
||||
attendance.loginTime != null
|
||||
? timeFormat.format(attendance.loginTime!)
|
||||
: '--:--';
|
||||
final logoutStr =
|
||||
attendance.logoutTime != null
|
||||
? timeFormat.format(attendance.logoutTime!)
|
||||
: '--:--';
|
||||
final hoursStr = attendance.workHours?.toString() ?? '0.00';
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_StatusItem(
|
||||
color: const Color(0xFF1266A8),
|
||||
icon: SvgPicture.asset('assets/images/watch.svg', width: 20),
|
||||
label: "عدد ساعات\n$hoursStr",
|
||||
),
|
||||
_buildDivider(const Color(0xFF1266A8), const Color(0xFFB00000)),
|
||||
_StatusItem(
|
||||
color: const Color(0xFFB00000),
|
||||
icon: SvgPicture.asset('assets/images/out.svg', width: 20),
|
||||
label: "خروج\n$logoutStr",
|
||||
),
|
||||
_buildDivider(const Color(0xFFB00000), const Color(0xFF0A8F6B)),
|
||||
_StatusItem(
|
||||
color: const Color(0xFF0A8F6B),
|
||||
icon: SvgPicture.asset('assets/images/in.svg', width: 20),
|
||||
label: "دخول\n$loginStr",
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOvertimeContent(OvertimeModel overtime) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_StatusItem(
|
||||
color: const Color(0xFFD16400),
|
||||
icon: SvgPicture.asset('assets/images/money3.svg', width: 20),
|
||||
label: "سعر كلي\n${overtime.totalAmount.toStringAsFixed(0)} د.ع",
|
||||
),
|
||||
_buildDivider(const Color(0xFFD16400), const Color(0xFF1266A8)),
|
||||
_StatusItem(
|
||||
color: const Color(0xFF1266A8),
|
||||
icon: SvgPicture.asset('assets/images/watch.svg', width: 20),
|
||||
label: "ساعات إضافية\n${overtime.hours.toStringAsFixed(2)}",
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExtraPaymentContent(ExtraPaymentModel payment) {
|
||||
// Reason goes in the middle column.
|
||||
// Note goes next to the amount (as part of the amount label).
|
||||
final reasonText = payment.reason ?? "لا يوجد سبب";
|
||||
final noteText = payment.note != null ? "\n(${payment.note})" : "";
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_StatusItem(
|
||||
color: const Color(0xFFD16400),
|
||||
icon: SvgPicture.asset('assets/images/money3.svg', width: 20),
|
||||
label: "المبلغ\n${payment.amount.toStringAsFixed(0)} د.ع$noteText",
|
||||
),
|
||||
_buildDivider(const Color(0xFFD16400), const Color(0xFF1266A8)),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
reasonText,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const Text("السبب", style: TextStyle(fontSize: 12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDivider(Color start, Color end) {
|
||||
return Expanded(child: Center(child: GradientLine(start: start, end: end)));
|
||||
}
|
||||
}
|
||||
|
||||
class _StatusItem extends StatelessWidget {
|
||||
@@ -125,7 +208,7 @@ class _StatusItem extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
StatusCircle(color: color, icon: icon),
|
||||
// const SizedBox(height: 3),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
label,
|
||||
textAlign: TextAlign.center,
|
||||
|
||||
Reference in New Issue
Block a user