Update: 2026-08-08 12:58:16
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
// fuel_drawdowns_page.dart — حركة القسائم وملخّص الائتمان
|
||||
//
|
||||
// الشاشة التي تُجيب: كم أقرضنا؟ كم استُرد؟ وكم بقي في الشارع؟
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../controller/fuel/fuel_admin_controller.dart';
|
||||
import '../../../controller/fuel/fuel_admin_models.dart';
|
||||
|
||||
class FuelDrawdownsPageView extends StatefulWidget {
|
||||
const FuelDrawdownsPageView({super.key});
|
||||
|
||||
@override
|
||||
State<FuelDrawdownsPageView> createState() => _FuelDrawdownsPageViewState();
|
||||
}
|
||||
|
||||
class _FuelDrawdownsPageViewState extends State<FuelDrawdownsPageView> {
|
||||
String? _status;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Get.find<FuelAdminController>().fetchDrawdowns();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
|
||||
return GetBuilder<FuelAdminController>(
|
||||
builder: (c) => Scaffold(
|
||||
backgroundColor: cs.surface,
|
||||
appBar: AppBar(
|
||||
backgroundColor: cs.surface,
|
||||
foregroundColor: cs.onSurface,
|
||||
title: const Text('حركة القسائم'),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
if (c.drawdowns != null) _summary(c.drawdowns!, cs),
|
||||
_filters(c, cs),
|
||||
Expanded(
|
||||
child: c.isLoadingDrawdowns
|
||||
? Center(child: CircularProgressIndicator(color: cs.primary))
|
||||
: (c.drawdowns?.vouchers.isEmpty ?? true)
|
||||
? Center(
|
||||
child: Text('لا توجد قسائم',
|
||||
style: TextStyle(color: cs.onSurfaceVariant)))
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
itemCount: c.drawdowns!.vouchers.length,
|
||||
itemBuilder: (_, i) =>
|
||||
_voucherCard(c.drawdowns!.vouchers[i], cs),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// الملخّص على كامل المنتج لا على القائمة المعروضة: رقم يتغيّر بتغيّر
|
||||
/// الفلتر يُقرأ كأنه حقيقة عن المنصة وهو ليس كذلك.
|
||||
Widget _summary(FuelDrawdownsPage d, ColorScheme cs) => Container(
|
||||
margin: const EdgeInsets.fromLTRB(12, 12, 12, 4),
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: cs.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_stat('أُقرض', d.lentTotal.toString(), cs.onSurface, cs),
|
||||
_stat('استُرد', d.collectedTotal.toString(), cs.primary, cs),
|
||||
_stat('قائم', d.outstandingTotal.toString(), cs.tertiary, cs),
|
||||
_stat('سائق', d.driversCount.toString(), cs.onSurfaceVariant, cs),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _stat(String label, String value, Color color, ColorScheme cs) =>
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(value,
|
||||
style: TextStyle(
|
||||
color: color, fontSize: 16, fontWeight: FontWeight.w700)),
|
||||
const SizedBox(height: 2),
|
||||
Text(label,
|
||||
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 11)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _filters(FuelAdminController c, ColorScheme cs) {
|
||||
const options = {
|
||||
null: 'الكل',
|
||||
'issued': 'سارية',
|
||||
'redeemed': 'مصروفة',
|
||||
'expired': 'منتهية',
|
||||
'cancelled': 'ملغاة',
|
||||
};
|
||||
|
||||
return SizedBox(
|
||||
height: 46,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
children: options.entries.map((e) {
|
||||
final selected = _status == e.key;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: ChoiceChip(
|
||||
label: Text(e.value),
|
||||
selected: selected,
|
||||
onSelected: (_) {
|
||||
setState(() => _status = e.key);
|
||||
c.fetchDrawdowns(status: e.key);
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _voucherCard(FuelVoucher v, ColorScheme cs) {
|
||||
final (color, label) = switch (v.status) {
|
||||
'redeemed' => (cs.primary, 'مصروفة'),
|
||||
'issued' => (cs.tertiary, 'سارية'),
|
||||
'cancelled' => (cs.error, 'ملغاة'),
|
||||
_ => (cs.onSurfaceVariant, 'منتهية'),
|
||||
};
|
||||
|
||||
// المصروف لا المأذون: السائق قد يطلب عشرين ويتزوّد بخمسة عشر،
|
||||
// والدَّين على ما صُرف. عرض المأذون هنا يضخّم الأرقام في عين القارئ.
|
||||
final amount = v.status == 'redeemed' ? v.amountRedeemed : v.amountAuthorized;
|
||||
|
||||
return Card(
|
||||
color: cs.surfaceContainerHighest,
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: ListTile(
|
||||
title: Row(
|
||||
children: [
|
||||
Text('$amount ${v.currency}',
|
||||
style: TextStyle(
|
||||
color: cs.onSurface, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(label, style: TextStyle(color: color, fontSize: 11)),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
[
|
||||
'سائق ${v.driverId}',
|
||||
if (v.station != null && v.station!.isNotEmpty) v.station!,
|
||||
v.redeemedAt ?? v.createdAt ?? '',
|
||||
].where((s) => s.isNotEmpty).join(' · '),
|
||||
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 11),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user