diff --git a/apps/mobile/packages/domain/lib/krow_domain.dart b/apps/mobile/packages/domain/lib/krow_domain.dart index bc5e3d77..8bab1718 100644 --- a/apps/mobile/packages/domain/lib/krow_domain.dart +++ b/apps/mobile/packages/domain/lib/krow_domain.dart @@ -49,6 +49,7 @@ export 'src/entities/financial/time_card.dart'; export 'src/entities/financial/invoice_item.dart'; export 'src/entities/financial/invoice_decline.dart'; export 'src/entities/financial/staff_payment.dart'; +export 'src/entities/financial/payment_summary.dart'; // Profile export 'src/entities/profile/staff_document.dart'; @@ -91,3 +92,4 @@ export 'src/adapters/profile/experience_adapter.dart'; export 'src/entities/profile/experience_skill.dart'; export 'src/adapters/profile/bank_account_adapter.dart'; export 'src/adapters/profile/tax_form_adapter.dart'; +export 'src/adapters/financial/payment_adapter.dart'; diff --git a/apps/mobile/packages/domain/lib/src/adapters/financial/payment_adapter.dart b/apps/mobile/packages/domain/lib/src/adapters/financial/payment_adapter.dart new file mode 100644 index 00000000..66446058 --- /dev/null +++ b/apps/mobile/packages/domain/lib/src/adapters/financial/payment_adapter.dart @@ -0,0 +1,19 @@ +import '../../entities/financial/staff_payment.dart'; + +/// Adapter for Payment related data. +class PaymentAdapter { + + /// Converts string status to [PaymentStatus]. + static PaymentStatus toPaymentStatus(String status) { + switch (status) { + case 'PAID': + return PaymentStatus.paid; + case 'PENDING': + return PaymentStatus.pending; + case 'FAILED': + return PaymentStatus.failed; + default: + return PaymentStatus.unknown; + } + } +} diff --git a/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart b/apps/mobile/packages/domain/lib/src/entities/financial/payment_summary.dart similarity index 94% rename from apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart rename to apps/mobile/packages/domain/lib/src/entities/financial/payment_summary.dart index de815145..0a202449 100644 --- a/apps/mobile/packages/features/staff/payments/lib/src/domain/entities/payment_summary.dart +++ b/apps/mobile/packages/domain/lib/src/entities/financial/payment_summary.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; +/// Summary of staff earnings. class PaymentSummary extends Equatable { final double weeklyEarnings; final double monthlyEarnings; diff --git a/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart b/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart index bd890a77..d6126de8 100644 --- a/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart +++ b/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart @@ -13,6 +13,9 @@ enum PaymentStatus { /// Transfer failed. failed, + + /// Status unknown. + unknown, } /// Represents a payout to a [Staff] member for a completed [Assignment]. 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 index 51bf5504..d5ec6910 100644 --- 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 @@ -1,76 +1,111 @@ import 'package:firebase_data_connect/firebase_data_connect.dart'; -import 'package:krow_data_connect/krow_data_connect.dart'; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/src/session/staff_session_store.dart'; import 'package:krow_domain/krow_domain.dart'; -import '../../domain/entities/payment_summary.dart'; import '../../domain/repositories/payments_repository.dart'; class PaymentsRepositoryImpl implements PaymentsRepository { - PaymentsRepositoryImpl(); + final dc.ExampleConnector _dataConnect; + + PaymentsRepositoryImpl() : _dataConnect = dc.ExampleConnector.instance; + + /// Helper to convert Data Connect Timestamp to DateTime + DateTime? _toDateTime(dynamic t) { + if (t == null) return null; + try { + // Attempt to deserialize via standard methods + return DateTime.tryParse(t.toJson() as String); + } catch (_) { + try { + return DateTime.tryParse(t.toString()); + } catch (e) { + return null; + } + } + } @override Future getPaymentSummary() async { - // Current requirement: Mock data only for summary - await Future.delayed(const Duration(milliseconds: 500)); - return const PaymentSummary( - weeklyEarnings: 847.50, - monthlyEarnings: 3240.0, - pendingEarnings: 285.0, - totalEarnings: 12450.0, + final StaffSession? session = StaffSessionStore.instance.session; + if (session?.staff?.id == null) { + return const PaymentSummary( + weeklyEarnings: 0, + monthlyEarnings: 0, + pendingEarnings: 0, + totalEarnings: 0, + ); + } + + final String currentStaffId = session!.staff!.id; + + // Fetch recent payments with a limit + // Note: limit is chained on the query builder + final QueryResult result = + await _dataConnect.listRecentPaymentsByStaffId( + staffId: currentStaffId, + ).limit(100).execute(); + + final List payments = result.data.recentPayments; + + double weekly = 0; + double monthly = 0; + double pending = 0; + double total = 0; + + final DateTime now = DateTime.now(); + final DateTime startOfWeek = now.subtract(const Duration(days: 7)); + final DateTime startOfMonth = DateTime(now.year, now.month, 1); + + for (final dc.ListRecentPaymentsByStaffIdRecentPayments p in payments) { + final DateTime? date = _toDateTime(p.invoice.issueDate) ?? _toDateTime(p.createdAt); + final double amount = p.invoice.amount; + final String? status = p.status?.stringValue; + + if (status == 'PENDING') { + pending += amount; + } else if (status == 'PAID') { + total += amount; + if (date != null) { + if (date.isAfter(startOfWeek)) weekly += amount; + if (date.isAfter(startOfMonth)) monthly += amount; + } + } + } + + return PaymentSummary( + weeklyEarnings: weekly, + monthlyEarnings: monthly, + pendingEarnings: pending, + totalEarnings: total, ); } @override Future> getPaymentHistory(String period) async { - final session = StaffSessionStore.instance.session; - if (session?.staff?.id == null) return []; + final StaffSession? session = StaffSessionStore.instance.session; + if (session?.staff?.id == null) return []; final String currentStaffId = session!.staff!.id; try { - final response = await ExampleConnector.instance + final QueryResult response = + await _dataConnect .listRecentPaymentsByStaffId(staffId: currentStaffId) .execute(); - return response.data.recentPayments.map((payment) { + return response.data.recentPayments.map((dc.ListRecentPaymentsByStaffIdRecentPayments payment) { return StaffPayment( id: payment.id, staffId: payment.staffId, assignmentId: payment.applicationId, amount: payment.invoice.amount, - status: _mapStatus(payment.status), - paidAt: payment.invoice.issueDate?.toDate(), + status: PaymentAdapter.toPaymentStatus(payment.status?.stringValue ?? 'UNKNOWN'), + paidAt: _toDateTime(payment.invoice.issueDate), ); }).toList(); } catch (e) { - return []; - } - } - - PaymentStatus _mapStatus(EnumValue? status) { - if (status == null || status is! Known) return PaymentStatus.pending; - - switch ((status as Known).value) { - case RecentPaymentStatus.PAID: - return PaymentStatus.paid; - case RecentPaymentStatus.PENDING: - return PaymentStatus.pending; - case RecentPaymentStatus.FAILED: - return PaymentStatus.failed; - default: - return PaymentStatus.pending; + return []; } } } -extension on DateTime { - // Simple toDate if needed, but Data Connect Timestamp has toDate() usually - // or we need the extension from earlier -} - -extension TimestampExt on Timestamp { - DateTime toDate() { - return DateTime.fromMillisecondsSinceEpoch(seconds.toInt() * 1000 + nanoseconds ~/ 1000000); - } -} - 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 index d813a8ae..227c783e 100644 --- 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 @@ -1,5 +1,4 @@ import 'package:krow_domain/krow_domain.dart'; -import '../entities/payment_summary.dart'; /// Repository interface for Payments feature. /// 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 index b810454b..84c54d59 100644 --- 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 @@ -1,5 +1,5 @@ import 'package:krow_core/core.dart'; -import '../entities/payment_summary.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../repositories/payments_repository.dart'; /// Use case to retrieve payment summary information. 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 index d2885d44..c25e98e8 100644 --- 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 @@ -1,7 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:krow_domain/krow_domain.dart'; import '../../../domain/arguments/get_payment_history_arguments.dart'; -import '../../../domain/entities/payment_summary.dart'; import '../../../domain/usecases/get_payment_history_usecase.dart'; import '../../../domain/usecases/get_payment_summary_usecase.dart'; import 'payments_event.dart'; 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 index 4bba5691..6e100f83 100644 --- 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 @@ -1,6 +1,5 @@ import 'package:equatable/equatable.dart'; import 'package:krow_domain/krow_domain.dart'; -import '../../../domain/entities/payment_summary.dart'; abstract class PaymentsState extends Equatable { const PaymentsState(); 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 index 82123957..2d867507 100644 --- 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 @@ -165,7 +165,7 @@ class _PaymentsPageState extends State { const SizedBox(height: 16), // Pending Pay - PendingPayCard( + if(state.summary.pendingEarnings > 0) PendingPayCard( amount: state.summary.pendingEarnings, onCashOut: () { Modular.to.pushNamed('/early-pay'); @@ -173,62 +173,43 @@ class _PaymentsPageState extends State { ), 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((StaffPayment payment) { - return Padding( - padding: const EdgeInsets.only(bottom: 8), - child: PaymentHistoryItem( - amount: payment.amount, - title: "Shift Payment", - location: "Varies", - address: "Payment ID: ${payment.id}", - date: payment.paidAt != null - ? DateFormat('E, MMM d').format(payment.paidAt!) - : 'Pending', - workedTime: "Completed", - hours: 0, - rate: 0.0, - status: payment.status.name.toUpperCase(), - ), - ); - }).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), + + // Recent Payments + if (state.history.isNotEmpty) Column( + children: [ + const Text( + "Recent Payments", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF0F172A), ), ), - ), + const SizedBox(height: 12), + Column( + children: state.history.map((StaffPayment payment) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: PaymentHistoryItem( + amount: payment.amount, + title: "Shift Payment", + location: "Varies", + address: "Payment ID: ${payment.id}", + date: payment.paidAt != null + ? DateFormat('E, MMM d').format(payment.paidAt!) + : 'Pending', + workedTime: "Completed", + hours: 0, + rate: 0.0, + status: payment.status.name.toUpperCase(), + ), + ); + }).toList(), + ), + ], ), + const SizedBox(height: 32), ], ),