From 0b043ed91e54714ca6e92e5f8a15d4cbf6c4c8e8 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Sun, 25 Jan 2026 16:47:41 -0500 Subject: [PATCH] feat: add staff payments feature with mock data source and UI components --- .../datasources/payments_mock_datasource.dart | 67 +++++ .../payments_remote_datasource.dart | 7 + .../payments_repository_impl.dart | 22 ++ .../src/domain/entities/payment_summary.dart | 23 ++ .../domain/entities/payment_transaction.dart | 41 +++ .../repositories/payments_repository.dart | 10 + .../usecases/get_payment_history_usecase.dart | 12 + .../usecases/get_payment_summary_usecase.dart | 12 + .../payments/lib/src/payments_module.dart | 32 +++ .../blocs/payments/payments_bloc.dart | 60 ++++ .../blocs/payments/payments_event.dart | 20 ++ .../blocs/payments/payments_state.dart | 50 ++++ .../src/presentation/pages/payments_page.dart | 259 ++++++++++++++++++ .../widgets/payment_history_item.dart | 209 ++++++++++++++ .../widgets/payment_stats_card.dart | 62 +++++ .../widgets/pending_pay_card.dart | 98 +++++++ .../staff/payments/lib/staff_payements.dart | 1 + .../features/staff/payments/pubspec.yaml | 28 ++ .../staff_main/lib/src/staff_main_module.dart | 6 +- .../features/staff/staff_main/pubspec.yaml | 4 +- apps/mobile/pubspec.lock | 7 + 21 files changed, 1025 insertions(+), 5 deletions(-) create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_mock_datasource.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_remote_datasource.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_transaction.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/domain/repositories/payments_repository.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_history_usecase.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_summary_usecase.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/payments_module.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_bloc.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_event.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_state.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_stats_card.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/pending_pay_card.dart create mode 100644 apps/mobile/packages/features/staff/payments/lib/staff_payements.dart create mode 100644 apps/mobile/packages/features/staff/payments/pubspec.yaml diff --git a/apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_mock_datasource.dart b/apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_mock_datasource.dart new file mode 100644 index 00000000..2b084c46 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_mock_datasource.dart @@ -0,0 +1,67 @@ +// ignore: depend_on_referenced_packages +import 'package:flutter/foundation.dart'; +import '../../domain/entities/payment_summary.dart'; +import '../../domain/entities/payment_transaction.dart'; +import 'payments_remote_datasource.dart'; + +class PaymentsMockDataSource implements PaymentsRemoteDataSource { + @override + Future fetchPaymentSummary() async { + // Simulate network delay + await Future.delayed(const Duration(milliseconds: 800)); + + // Mock data matching the prototype + return const PaymentSummary( + weeklyEarnings: 847.50, + monthlyEarnings: 3240.0, + pendingEarnings: 285.0, + totalEarnings: 12450.0, + ); + } + + @override + Future> fetchPaymentHistory(String period) async { + await Future.delayed(const Duration(milliseconds: 1000)); + + // Mock data matching the prototype + // In a real scenario, this would filter by 'period' (week/month/year) + return [ + PaymentTransaction( + id: '1', + title: 'Cook', + location: 'LA Convention Center', + address: '1201 S Figueroa St, Los Angeles, CA 90015', + workedTime: '2:00 PM - 10:00 PM', + amount: 160.00, + status: 'PAID', + hours: 8, + rate: 20.0, + date: DateTime(2025, 12, 6), // "Sat, Dec 6" (Using future date to match context if needed, but keeping it simple) + ), + PaymentTransaction( + id: '2', + title: 'Server', + location: 'The Grand Hotel', + address: '456 Main St, Los Angeles, CA 90012', + workedTime: '5:00 PM - 11:00 PM', + amount: 176.00, + status: 'PAID', + hours: 8, + rate: 22.0, + date: DateTime(2025, 12, 5), // "Fri, Dec 5" + ), + PaymentTransaction( + id: '3', + title: 'Bartender', + location: 'Club Luxe', + address: '789 Sunset Blvd, Los Angeles, CA 90028', + workedTime: '6:00 PM - 2:00 AM', + amount: 225.00, + status: 'PAID', + hours: 9, + rate: 25.0, + date: DateTime(2025, 12, 4), // "Thu, Dec 4" + ), + ]; + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_remote_datasource.dart b/apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_remote_datasource.dart new file mode 100644 index 00000000..37f24a9e --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/data/datasources/payments_remote_datasource.dart @@ -0,0 +1,7 @@ +import '../../domain/entities/payment_summary.dart'; +import '../../domain/entities/payment_transaction.dart'; + +abstract class PaymentsRemoteDataSource { + Future fetchPaymentSummary(); + Future> fetchPaymentHistory(String period); +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart b/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart new file mode 100644 index 00000000..303fca7f --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart @@ -0,0 +1,22 @@ +// ignore: unused_import +// import 'package:data_connect/data_connect.dart'; +import '../../domain/entities/payment_summary.dart'; +import '../../domain/entities/payment_transaction.dart'; +import '../../domain/repositories/payments_repository.dart'; +import '../datasources/payments_remote_datasource.dart'; + +class PaymentsRepositoryImpl implements PaymentsRepository { + final PaymentsRemoteDataSource remoteDataSource; + + PaymentsRepositoryImpl({required this.remoteDataSource}); + + @override + Future getPaymentSummary() async { + return await remoteDataSource.fetchPaymentSummary(); + } + + @override + Future> getPaymentHistory(String period) async { + return await remoteDataSource.fetchPaymentHistory(period); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart b/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart new file mode 100644 index 00000000..ad575833 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart @@ -0,0 +1,23 @@ +import 'package:equatable/equatable.dart'; + +class PaymentSummary extends Equatable { + final double weeklyEarnings; + final double monthlyEarnings; + final double pendingEarnings; + final double totalEarnings; + + const PaymentSummary({ + required this.weeklyEarnings, + required this.monthlyEarnings, + required this.pendingEarnings, + required this.totalEarnings, + }); + + @override + List get props => [ + weeklyEarnings, + monthlyEarnings, + pendingEarnings, + totalEarnings, + ]; +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_transaction.dart b/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_transaction.dart new file mode 100644 index 00000000..0b0eb7b2 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_transaction.dart @@ -0,0 +1,41 @@ +import 'package:equatable/equatable.dart'; + +class PaymentTransaction extends Equatable { + final String id; + final String title; + final String location; + final String address; + final DateTime date; + final String workedTime; + final double amount; + final String status; + final int hours; + final double rate; + + const PaymentTransaction({ + required this.id, + required this.title, + required this.location, + required this.address, + required this.date, + required this.workedTime, + required this.amount, + required this.status, + required this.hours, + required this.rate, + }); + + @override + List get props => [ + id, + title, + location, + address, + date, + workedTime, + amount, + status, + hours, + rate, + ]; +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/domain/repositories/payments_repository.dart b/apps/mobile/packages/features/staff/payments/lib/src/domain/repositories/payments_repository.dart new file mode 100644 index 00000000..078291cb --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/domain/repositories/payments_repository.dart @@ -0,0 +1,10 @@ +import '../entities/payment_summary.dart'; +import '../entities/payment_transaction.dart'; + +abstract class PaymentsRepository { + /// Fetches the summary of earnings (weekly, monthly, total, pending). + Future getPaymentSummary(); + + /// Fetches the list of recent payment transactions (history). + Future> getPaymentHistory(String period); +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_history_usecase.dart b/apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_history_usecase.dart new file mode 100644 index 00000000..ff549034 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_history_usecase.dart @@ -0,0 +1,12 @@ +import '../entities/payment_transaction.dart'; +import '../repositories/payments_repository.dart'; + +class GetPaymentHistoryUseCase { + final PaymentsRepository repository; + + GetPaymentHistoryUseCase(this.repository); + + Future> call({String period = 'week'}) async { + return await repository.getPaymentHistory(period); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_summary_usecase.dart b/apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_summary_usecase.dart new file mode 100644 index 00000000..e846d0be --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/domain/usecases/get_payment_summary_usecase.dart @@ -0,0 +1,12 @@ +import '../entities/payment_summary.dart'; +import '../repositories/payments_repository.dart'; + +class GetPaymentSummaryUseCase { + final PaymentsRepository repository; + + GetPaymentSummaryUseCase(this.repository); + + Future call() async { + return await repository.getPaymentSummary(); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/payments_module.dart b/apps/mobile/packages/features/staff/payments/lib/src/payments_module.dart new file mode 100644 index 00000000..06796c83 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/payments_module.dart @@ -0,0 +1,32 @@ +import 'package:flutter_modular/flutter_modular.dart'; +import 'domain/repositories/payments_repository.dart'; +import 'domain/usecases/get_payment_summary_usecase.dart'; +import 'domain/usecases/get_payment_history_usecase.dart'; +import 'data/datasources/payments_remote_datasource.dart'; +import 'data/datasources/payments_mock_datasource.dart'; +import 'data/repositories/payments_repository_impl.dart'; +import 'presentation/blocs/payments/payments_bloc.dart'; +import 'presentation/pages/payments_page.dart'; + +class StaffPaymentsModule extends Module { + @override + void binds(Injector i) { + // Data Sources + i.add(PaymentsMockDataSource.new); + + // Repositories + i.add(PaymentsRepositoryImpl.new); + + // Use Cases + i.add(GetPaymentSummaryUseCase.new); + i.add(GetPaymentHistoryUseCase.new); + + // Blocs + i.add(PaymentsBloc.new); + } + + @override + void routes(RouteManager r) { + r.child('/', child: (context) => const PaymentsPage()); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_bloc.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_bloc.dart new file mode 100644 index 00000000..710623cc --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_bloc.dart @@ -0,0 +1,60 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../domain/entities/payment_summary.dart'; +import '../../../domain/entities/payment_transaction.dart'; +import '../../../domain/usecases/get_payment_summary_usecase.dart'; +import '../../../domain/usecases/get_payment_history_usecase.dart'; +import 'payments_event.dart'; +import 'payments_state.dart'; + +class PaymentsBloc extends Bloc { + final GetPaymentSummaryUseCase getPaymentSummary; + final GetPaymentHistoryUseCase getPaymentHistory; + + PaymentsBloc({ + required this.getPaymentSummary, + required this.getPaymentHistory, + }) : super(PaymentsInitial()) { + on(_onLoadPayments); + on(_onChangePeriod); + } + + Future _onLoadPayments( + LoadPaymentsEvent event, + Emitter emit, + ) async { + emit(PaymentsLoading()); + try { + final PaymentSummary summary = await getPaymentSummary(); + final List history = await getPaymentHistory(period: 'week'); + emit(PaymentsLoaded( + summary: summary, + history: history, + activePeriod: 'week', + )); + } catch (e) { + emit(PaymentsError(e.toString())); + } + } + + Future _onChangePeriod( + ChangePeriodEvent event, + Emitter emit, + ) async { + final PaymentsState currentState = state; + if (currentState is PaymentsLoaded) { + if (currentState.activePeriod == event.period) return; + + // Optimistic update or set loading state if expecting delay + // For now, we'll keep the current data and fetch new history + try { + final List newHistory = await getPaymentHistory(period: event.period); + emit(currentState.copyWith( + history: newHistory, + activePeriod: event.period, + )); + } catch (e) { + emit(PaymentsError(e.toString())); + } + } + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_event.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_event.dart new file mode 100644 index 00000000..86aceffd --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_event.dart @@ -0,0 +1,20 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +abstract class PaymentsEvent extends Equatable { + const PaymentsEvent(); + + @override + List get props => []; +} + +class LoadPaymentsEvent extends PaymentsEvent {} + +class ChangePeriodEvent extends PaymentsEvent { + final String period; + + const ChangePeriodEvent(this.period); + + @override + List get props => [period]; +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_state.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_state.dart new file mode 100644 index 00000000..f3742ca3 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/blocs/payments/payments_state.dart @@ -0,0 +1,50 @@ +import 'package:equatable/equatable.dart'; +import '../../../domain/entities/payment_summary.dart'; +import '../../../domain/entities/payment_transaction.dart'; + +abstract class PaymentsState extends Equatable { + const PaymentsState(); + + @override + List get props => []; +} + +class PaymentsInitial extends PaymentsState {} + +class PaymentsLoading extends PaymentsState {} + +class PaymentsLoaded extends PaymentsState { + final PaymentSummary summary; + final List history; + final String activePeriod; + + const PaymentsLoaded({ + required this.summary, + required this.history, + this.activePeriod = 'week', + }); + + PaymentsLoaded copyWith({ + PaymentSummary? summary, + List? history, + String? activePeriod, + }) { + return PaymentsLoaded( + summary: summary ?? this.summary, + history: history ?? this.history, + activePeriod: activePeriod ?? this.activePeriod, + ); + } + + @override + List get props => [summary, history, activePeriod]; +} + +class PaymentsError extends PaymentsState { + final String message; + + const PaymentsError(this.message); + + @override + List get props => [message]; +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart new file mode 100644 index 00000000..188f8285 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart @@ -0,0 +1,259 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_modular/flutter_modular.dart'; +import 'package:lucide_icons/lucide_icons.dart'; +import 'package:intl/intl.dart'; +import '../../domain/entities/payment_transaction.dart'; +import '../blocs/payments/payments_bloc.dart'; +import '../blocs/payments/payments_event.dart'; +import '../blocs/payments/payments_state.dart'; +import '../widgets/payment_stats_card.dart'; +import '../widgets/pending_pay_card.dart'; +import '../widgets/payment_history_item.dart'; + +class PaymentsPage extends StatefulWidget { + const PaymentsPage({super.key}); + + @override + State createState() => _PaymentsPageState(); +} + +class _PaymentsPageState extends State { + final PaymentsBloc _bloc = Modular.get(); + + @override + void initState() { + super.initState(); + _bloc.add(LoadPaymentsEvent()); + } + + @override + Widget build(BuildContext context) { + return BlocProvider.value( + value: _bloc, + child: Scaffold( + backgroundColor: const Color(0xFFF8FAFC), + body: BlocBuilder( + builder: (BuildContext context, PaymentsState state) { + if (state is PaymentsLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is PaymentsError) { + return Center(child: Text('Error: ${state.message}')); + } else if (state is PaymentsLoaded) { + return _buildContent(context, state); + } + return const SizedBox.shrink(); + }, + ), + ), + ); + } + + Widget _buildContent(BuildContext context, PaymentsLoaded state) { + return SingleChildScrollView( + child: Column( + children: [ + // Header Section with Gradient + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [Color(0xFF0032A0), Color(0xFF333F48)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + padding: EdgeInsets.fromLTRB( + 20, + MediaQuery.of(context).padding.top + 24, + 20, + 32, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Earnings", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 24), + + // Main Balance + Center( + child: Column( + children: [ + const Text( + "Total Earnings", + style: TextStyle( + color: Color(0xFFF8E08E), + fontSize: 14, + ), + ), + const SizedBox(height: 4), + Text( + "\$${state.summary.totalEarnings.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},')}", + style: const TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ], + ), + ), + const SizedBox(height: 16), + + // Period Tabs + Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.2), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + _buildTab("Week", 'week', state.activePeriod), + _buildTab("Month", 'month', state.activePeriod), + _buildTab("Year", 'year', state.activePeriod), + ], + ), + ), + ], + ), + ), + + // Main Content - Offset upwards + Transform.translate( + offset: const Offset(0, -16), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Quick Stats + Row( + children: [ + Expanded( + child: PaymentStatsCard( + icon: LucideIcons.trendingUp, + iconColor: const Color(0xFF059669), + label: "This Week", + amount: "\$${state.summary.weeklyEarnings}", + ), + ), + const SizedBox(width: 12), + Expanded( + child: PaymentStatsCard( + icon: LucideIcons.calendar, + iconColor: const Color(0xFF2563EB), + label: "This Month", + amount: "\$${state.summary.monthlyEarnings.toStringAsFixed(0)}", + ), + ), + ], + ), + const SizedBox(height: 16), + + // Pending Pay + PendingPayCard( + amount: state.summary.pendingEarnings, + onCashOut: () { + Modular.to.pushNamed('/early-pay'); + }, + ), + const SizedBox(height: 24), + + // Recent Payments + const Text( + "Recent Payments", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF0F172A), + ), + ), + const SizedBox(height: 12), + Column( + children: state.history.map((PaymentTransaction payment) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: PaymentHistoryItem( + amount: payment.amount, + title: payment.title, + location: payment.location, + address: payment.address, + date: DateFormat('E, MMM d').format(payment.date), + workedTime: payment.workedTime, + hours: payment.hours, + rate: payment.rate, + status: payment.status, + ), + ); + }).toList(), + ), + const SizedBox(height: 16), + + // Export History Button + SizedBox( + width: double.infinity, + height: 48, + child: OutlinedButton.icon( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('PDF Exported'), + duration: Duration(seconds: 2), + ), + ); + }, + icon: const Icon(LucideIcons.download, size: 16), + label: const Text("Export History"), + style: OutlinedButton.styleFrom( + foregroundColor: const Color(0xFF0F172A), + side: const BorderSide(color: Color(0xFFE2E8F0)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + const SizedBox(height: 32), + ], + ), + ), + ), + ], + ), + ); + } + + Widget _buildTab(String label, String value, String activePeriod) { + final bool isSelected = activePeriod == value; + return Expanded( + child: GestureDetector( + onTap: () => _bloc.add(ChangePeriodEvent(value)), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: isSelected ? Colors.white : Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text( + label, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: isSelected ? const Color(0xFF0032A0) : Colors.white, + ), + ), + ), + ), + ), + ); + } +} + diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart new file mode 100644 index 00000000..9c49df1e --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:lucide_icons/lucide_icons.dart'; + +class PaymentHistoryItem extends StatelessWidget { + final double amount; + final String title; + final String location; + final String address; + final String date; + final String workedTime; + final int hours; + final double rate; + final String status; + + const PaymentHistoryItem({ + super.key, + required this.amount, + required this.title, + required this.location, + required this.address, + required this.date, + required this.workedTime, + required this.hours, + required this.rate, + required this.status, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status Badge + Row( + children: [ + Container( + width: 6, + height: 6, + decoration: const BoxDecoration( + color: Color(0xFF3B82F6), // blue-500 + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 6), + const Text( + "PAID", + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w700, + color: Color(0xFF2563EB), // blue-600 + letterSpacing: 0.5, + ), + ), + ], + ), + const SizedBox(height: 12), + + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Icon + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: const Color(0xFFF1F5F9), // slate-100 + borderRadius: BorderRadius.circular(12), + ), + child: const Icon( + LucideIcons.dollarSign, + color: Color(0xFF334155), // slate-700 + size: 24, + ), + ), + const SizedBox(width: 12), + + // Content + Expanded( + child: Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF0F172A), // slate-900 + ), + ), + Text( + location, + style: const TextStyle( + fontSize: 12, + color: Color(0xFF475569), // slate-600 + ), + ), + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "\$${amount.toStringAsFixed(0)}", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF0F172A), // slate-900 + ), + ), + Text( + "\$${rate.toStringAsFixed(0)}/hr ยท ${hours}h", + style: const TextStyle( + fontSize: 10, + color: Color(0xFF64748B), // slate-500 + ), + ), + ], + ), + ], + ), + const SizedBox(height: 8), + + // Date and Time + Row( + children: [ + const Icon( + LucideIcons.calendar, + size: 12, + color: Color(0xFF64748B), + ), + const SizedBox(width: 8), + Text( + date, + style: const TextStyle( + fontSize: 12, + color: Color(0xFF64748B), + ), + ), + const SizedBox(width: 8), + const Icon( + LucideIcons.clock, + size: 12, + color: Color(0xFF64748B), + ), + const SizedBox(width: 8), + Text( + workedTime, + style: const TextStyle( + fontSize: 12, + color: Color(0xFF64748B), + ), + ), + ], + ), + const SizedBox(height: 4), + + // Address + Row( + children: [ + const Icon( + LucideIcons.mapPin, + size: 12, + color: Color(0xFF64748B), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + address, + style: const TextStyle( + fontSize: 12, + color: Color(0xFF64748B), + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_stats_card.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_stats_card.dart new file mode 100644 index 00000000..aad2cf9b --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_stats_card.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:lucide_icons/lucide_icons.dart'; + +class PaymentStatsCard extends StatelessWidget { + final IconData icon; + final Color iconColor; + final String label; + final String amount; + + const PaymentStatsCard({ + super.key, + required this.icon, + required this.iconColor, + required this.label, + required this.amount, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(icon, size: 16, color: iconColor), + const SizedBox(width: 8), + Text( + label, + style: const TextStyle( + fontSize: 12, + color: Color(0xFF64748B), // slate-500 + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + amount, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Color(0xFF0F172A), // slate-900 + ), + ), + ], + ), + ); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/pending_pay_card.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/pending_pay_card.dart new file mode 100644 index 00000000..3ca7c602 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/pending_pay_card.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:lucide_icons/lucide_icons.dart'; + +class PendingPayCard extends StatelessWidget { + final double amount; + final VoidCallback onCashOut; + + const PendingPayCard({ + super.key, + required this.amount, + required this.onCashOut, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFFEFF6FF), Color(0xFFEFF6FF)], // blue-50 to blue-50 + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: const Color(0xFFE8F0FF), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon( + LucideIcons.dollarSign, + color: Color(0xFF0047FF), + size: 20, + ), + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Pending", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Color(0xFF0F172A), // slate-900 + fontSize: 14, + ), + ), + Text( + "\$${amount.toStringAsFixed(0)} available", + style: const TextStyle( + fontSize: 12, + color: Color(0xFF475569), // slate-600 + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + ElevatedButton.icon( + onPressed: onCashOut, + icon: const Icon(LucideIcons.zap, size: 14), + label: const Text("Early Pay"), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF0047FF), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + elevation: 4, + shadowColor: Colors.black.withOpacity(0.2), + textStyle: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ); + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/staff_payements.dart b/apps/mobile/packages/features/staff/payments/lib/staff_payements.dart new file mode 100644 index 00000000..e2e80b64 --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/lib/staff_payements.dart @@ -0,0 +1 @@ +export 'src/payments_module.dart'; diff --git a/apps/mobile/packages/features/staff/payments/pubspec.yaml b/apps/mobile/packages/features/staff/payments/pubspec.yaml new file mode 100644 index 00000000..b51bcfee --- /dev/null +++ b/apps/mobile/packages/features/staff/payments/pubspec.yaml @@ -0,0 +1,28 @@ +name: staff_payments +description: Staff Payments feature +version: 0.0.1 +publish_to: 'none' + +environment: + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_modular: ^6.3.2 + lucide_icons: ^0.257.0 + intl: ^0.20.0 + + # Internal packages + design_system: + path: ../../../design_system + core_localization: + path: ../../../core_localization + krow_domain: + path: ../../../domain + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 diff --git a/apps/mobile/packages/features/staff/staff_main/lib/src/staff_main_module.dart b/apps/mobile/packages/features/staff/staff_main/lib/src/staff_main_module.dart index 551b4f69..223c03c4 100644 --- a/apps/mobile/packages/features/staff/staff_main/lib/src/staff_main_module.dart +++ b/apps/mobile/packages/features/staff/staff_main/lib/src/staff_main_module.dart @@ -11,6 +11,7 @@ import 'package:staff_documents/staff_documents.dart'; import 'package:staff_certificates/staff_certificates.dart'; import 'package:staff_attire/staff_attire.dart'; import 'package:staff_shifts/staff_shifts.dart'; +import 'package:staff_payments/staff_payements.dart'; import 'package:staff_main/src/presentation/blocs/staff_main_cubit.dart'; import 'package:staff_main/src/presentation/constants/staff_main_routes.dart'; @@ -33,10 +34,9 @@ class StaffMainModule extends Module { StaffMainRoutes.shifts, module: StaffShiftsModule(), ), - ChildRoute( + ModuleRoute( StaffMainRoutes.payments, - child: (BuildContext context) => - const PlaceholderPage(title: 'Payments'), + module: StaffPaymentsModule(), ), ModuleRoute( StaffMainRoutes.home, diff --git a/apps/mobile/packages/features/staff/staff_main/pubspec.yaml b/apps/mobile/packages/features/staff/staff_main/pubspec.yaml index 8d1d349e..616df90c 100644 --- a/apps/mobile/packages/features/staff/staff_main/pubspec.yaml +++ b/apps/mobile/packages/features/staff/staff_main/pubspec.yaml @@ -45,8 +45,8 @@ dependencies: path: ../profile_sections/onboarding/attire staff_shifts: path: ../shifts - # staff_payments: - # path: ../payments + staff_payments: + path: ../payments dev_dependencies: flutter_test: diff --git a/apps/mobile/pubspec.lock b/apps/mobile/pubspec.lock index 254b32d0..3fb0cc1a 100644 --- a/apps/mobile/pubspec.lock +++ b/apps/mobile/pubspec.lock @@ -1100,6 +1100,13 @@ packages: relative: true source: path version: "0.0.1" + staff_payments: + dependency: transitive + description: + path: "packages/features/staff/payments" + relative: true + source: path + version: "0.0.1" staff_shifts: dependency: transitive description: