import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../providers/dashboard_providers.dart'; import '../../../core/services/contract_service.dart'; import '../../../widgets/error_view.dart'; import '../../../widgets/status_badge.dart'; class ContractScreen extends ConsumerWidget { final String reservationId; const ContractScreen({super.key, required this.reservationId}); @override Widget build(BuildContext context, WidgetRef ref) { final async = ref.watch(contractProvider(reservationId)); return Scaffold( appBar: AppBar( title: const Text('Rental Contract'), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () => ref.invalidate(contractProvider(reservationId)), ), ], ), body: async.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => ErrorView( message: 'Could not load contract', onRetry: () => ref.invalidate(contractProvider(reservationId)), ), data: (c) => _ContractView(contract: c), ), ); } } class _ContractView extends StatelessWidget { final ContractPayload contract; const _ContractView({required this.contract}); @override Widget build(BuildContext context) { final fmt = DateFormat('MMM d, yyyy'); final fmtMoney = NumberFormat.currency(symbol: '\$', decimalDigits: 2); return ListView( padding: const EdgeInsets.all(16), children: [ // ── Header ────────────────────────────────────────────── _Section( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (contract.contractNumber != null) Text( 'Contract ${contract.contractNumber}', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF111928), ), ), if (contract.invoiceNumber != null) Text( 'Invoice ${contract.invoiceNumber}', style: const TextStyle( fontSize: 13, color: Color(0xFF6B7280)), ), ], ), ), StatusBadge(status: contract.status), ], ), const SizedBox(height: 8), Text( 'Generated ${fmt.format(contract.generatedAt)}', style: const TextStyle( fontSize: 12, color: Color(0xFF9CA3AF)), ), if (contract.notes != null && contract.notes!.isNotEmpty) ...[ const SizedBox(height: 8), Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFFFFF3CD), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon(Icons.notes_outlined, size: 14, color: Color(0xFF92400E)), const SizedBox(width: 6), Expanded( child: Text( contract.notes!, style: const TextStyle( fontSize: 12, color: Color(0xFF92400E)), ), ), ], ), ), ], ], ), ), const SizedBox(height: 12), // ── Rental Period ──────────────────────────────────────── _Section( title: 'Rental Period', icon: Icons.calendar_today_outlined, child: Column( children: [ _Row2( left: _LabelValue( label: 'Check-out', value: fmt.format(contract.startDate)), right: _LabelValue( label: 'Check-in', value: fmt.format(contract.endDate)), ), const SizedBox(height: 10), _Row2( left: _LabelValue( label: 'Duration', value: '${contract.totalDays} day${contract.totalDays == 1 ? '' : 's'}'), right: _LabelValue( label: 'Payment', value: _paymentLabel(contract.paymentStatus)), ), if (contract.pickupLocation != null || contract.returnLocation != null) ...[ const SizedBox(height: 10), if (contract.pickupLocation != null) _LabelValue( label: 'Pick-up location', value: contract.pickupLocation!), if (contract.returnLocation != null) ...[ const SizedBox(height: 6), _LabelValue( label: 'Return location', value: contract.returnLocation!), ], ], ], ), ), const SizedBox(height: 12), // ── Vehicle ────────────────────────────────────────────── _Section( title: 'Vehicle', icon: Icons.directions_car_outlined, child: Column( children: [ _Row2( left: _LabelValue( label: 'Vehicle', value: contract.vehicle.displayName), right: _LabelValue( label: 'Plate', value: contract.vehicle.licensePlate), ), const SizedBox(height: 10), _Row2( left: _LabelValue( label: 'Category', value: contract.vehicle.category), right: _LabelValue( label: 'Color', value: contract.vehicle.color ?? '—'), ), if (contract.vehicle.vin != null) ...[ const SizedBox(height: 10), _LabelValue( label: 'VIN', value: contract.vehicle.vin!), ], ], ), ), const SizedBox(height: 12), // ── Customer ───────────────────────────────────────────── _Section( title: 'Customer', icon: Icons.person_outline, child: Column( children: [ _Row2( left: _LabelValue( label: 'Name', value: contract.driver.fullName), right: _LabelValue( label: 'Email', value: contract.driver.email), ), if (contract.driver.phone != null) ...[ const SizedBox(height: 10), _LabelValue( label: 'Phone', value: contract.driver.phone!), ], if (contract.driver.driverLicense != null) ...[ const SizedBox(height: 10), _Row2( left: _LabelValue( label: 'License #', value: contract.driver.driverLicense!), right: _LabelValue( label: 'Country', value: contract.driver.licenseCountry ?? '—'), ), ], if (contract.driver.licenseExpiry != null) ...[ const SizedBox(height: 10), _LabelValue( label: 'License expiry', value: contract.driver.licenseExpiry!), ], ], ), ), const SizedBox(height: 12), // ── Company ────────────────────────────────────────────── _Section( title: 'Rental Company', icon: Icons.business_outlined, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( contract.company.name, style: const TextStyle( fontWeight: FontWeight.w600, color: Color(0xFF111928)), ), if (contract.company.address != null) ...[ const SizedBox(height: 4), Text( contract.company.address!, style: const TextStyle( fontSize: 13, color: Color(0xFF6B7280)), ), ], if (contract.company.email != null || contract.company.phone != null) ...[ const SizedBox(height: 8), _Row2( left: _LabelValue( label: 'Email', value: contract.company.email ?? '—'), right: _LabelValue( label: 'Phone', value: contract.company.phone ?? '—'), ), ], ], ), ), const SizedBox(height: 12), // ── Invoice ────────────────────────────────────────────── _Section( title: 'Invoice', icon: Icons.receipt_outlined, child: Column( children: [ ...contract.lineItems.map((item) => Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(item.description, style: const TextStyle( fontSize: 13, color: Color(0xFF374151))), if (item.qty > 1) Text( '${item.qty} × ${fmtMoney.format(item.unitPrice / 100)}', style: const TextStyle( fontSize: 11, color: Color(0xFF9CA3AF)), ), ], ), ), Text( fmtMoney.format(item.total / 100), style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: Color(0xFF111928)), ), ], ), )), const Divider(height: 16), if (contract.taxTotal > 0) ...[ _AmountRow( label: 'Subtotal', amount: contract.subtotal, currency: contract.currency), const SizedBox(height: 4), _AmountRow( label: 'Taxes', amount: contract.taxTotal, currency: contract.currency), const Divider(height: 12), ], _AmountRow( label: 'Total', amount: contract.total, currency: contract.currency, bold: true, color: const Color(0xFF1A56DB), ), const SizedBox(height: 4), _AmountRow( label: 'Amount paid', amount: contract.amountPaid, currency: contract.currency, color: const Color(0xFF057A55), ), if (contract.balanceDue > 0) ...[ const SizedBox(height: 4), _AmountRow( label: 'Balance due', amount: contract.balanceDue, currency: contract.currency, bold: true, color: const Color(0xFFE02424), ), ], ], ), ), // ── Payments ───────────────────────────────────────────── if (contract.payments.isNotEmpty) ...[ const SizedBox(height: 12), _Section( title: 'Payments', icon: Icons.payments_outlined, child: Column( children: contract.payments .map((p) => _PaymentRow(payment: p)) .toList(), ), ), ], // ── Inspections ────────────────────────────────────────── if (contract.checkIn != null || contract.checkOut != null) ...[ const SizedBox(height: 12), _Section( title: 'Vehicle Inspections', icon: Icons.fact_check_outlined, child: Column( children: [ if (contract.checkIn != null) ...[ _InspectionRow( label: 'Check-out', inspection: contract.checkIn!), if (contract.checkOut != null) const SizedBox(height: 10), ], if (contract.checkOut != null) _InspectionRow( label: 'Check-in', inspection: contract.checkOut!), ], ), ), ], // ── Terms ──────────────────────────────────────────────── if (contract.terms != null && contract.terms!.isNotEmpty) ...[ const SizedBox(height: 12), _Section( title: 'Terms & Conditions', icon: Icons.gavel_outlined, child: Text( contract.terms!, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), height: 1.5), ), ), ], if (contract.fuelPolicy != null && contract.fuelPolicy!.isNotEmpty) ...[ const SizedBox(height: 12), _Section( title: 'Fuel Policy', icon: Icons.local_gas_station_outlined, child: Text( contract.fuelPolicy!, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), height: 1.5), ), ), ], const SizedBox(height: 24), ], ); } } // ── Helpers ─────────────────────────────────────────────────────────────────── class _Section extends StatelessWidget { final String? title; final IconData? icon; final Widget child; const _Section({this.title, this.icon, required this.child}); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title != null) ...[ Row( children: [ if (icon != null) ...[ Icon(icon, size: 16, color: const Color(0xFF6B7280)), const SizedBox(width: 6), ], Text( title!, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 13, color: Color(0xFF374151), ), ), ], ), const SizedBox(height: 12), const Divider(height: 1), const SizedBox(height: 12), ], child, ], ), ), ); } } class _LabelValue extends StatelessWidget { final String label; final String value; const _LabelValue({required this.label, required this.value}); @override Widget build(BuildContext context) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle( fontSize: 10, color: Color(0xFF9CA3AF))), Text(value, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: Color(0xFF111928))), ], ); } class _Row2 extends StatelessWidget { final Widget left; final Widget right; const _Row2({required this.left, required this.right}); @override Widget build(BuildContext context) => Row( children: [ Expanded(child: left), const SizedBox(width: 8), Expanded(child: right), ], ); } class _AmountRow extends StatelessWidget { final String label; final int amount; final String currency; final bool bold; final Color? color; const _AmountRow({ required this.label, required this.amount, required this.currency, this.bold = false, this.color, }); @override Widget build(BuildContext context) { final fmt = NumberFormat.currency(symbol: '\$', decimalDigits: 2); final style = TextStyle( fontWeight: bold ? FontWeight.bold : FontWeight.normal, color: color ?? const Color(0xFF374151), fontSize: bold ? 15 : 13, ); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: style), Text(fmt.format(amount / 100), style: style), ], ); } } class _PaymentRow extends StatelessWidget { final ContractPayment payment; const _PaymentRow({required this.payment}); @override Widget build(BuildContext context) { final fmt = NumberFormat.currency(symbol: '\$', decimalDigits: 2); final fmtDate = DateFormat('MMM d, yyyy'); return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( payment.paymentMethod ?? payment.type, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: Color(0xFF374151)), ), if (payment.paidAt != null) Text( fmtDate.format(payment.paidAt!), style: const TextStyle( fontSize: 11, color: Color(0xFF9CA3AF)), ), ], ), ), Text( fmt.format(payment.amount / 100), style: TextStyle( fontWeight: FontWeight.w600, color: payment.status == 'PAID' ? const Color(0xFF057A55) : const Color(0xFF374151), ), ), ], ), ); } } class _InspectionRow extends StatelessWidget { final String label; final ContractInspection inspection; const _InspectionRow( {required this.label, required this.inspection}); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 12, color: Color(0xFF374151)), ), const SizedBox(height: 6), Row( children: [ if (inspection.mileage != null) Expanded( child: _LabelValue( label: 'Mileage', value: '${inspection.mileage} km'), ), if (inspection.fuelLevel != null) Expanded( child: _LabelValue( label: 'Fuel', value: inspection.fuelLevel!), ), ], ), if (inspection.generalCondition != null) ...[ const SizedBox(height: 6), _LabelValue( label: 'Condition', value: inspection.generalCondition!), ], if (inspection.employeeNotes != null && inspection.employeeNotes!.isNotEmpty) ...[ const SizedBox(height: 6), Text( inspection.employeeNotes!, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280)), ), ], ], ); } } String _paymentLabel(String status) { switch (status.toUpperCase()) { case 'PAID': return 'Paid'; case 'PARTIAL': return 'Partial'; case 'PENDING': return 'Pending'; default: return status[0] + status.substring(1).toLowerCase(); } }