Intégration fonctionnalites V1
This commit is contained in:
@@ -1,130 +1,138 @@
|
||||
import 'package:flutter/material.dart';␍
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';␍
|
||||
import 'package:intl/intl.dart';␍
|
||||
␍
|
||||
import '../../../core/theme/app_colors.dart';␍
|
||||
import '../../../core/widgets/empty_state.dart';␍
|
||||
import '../data/wallet_repository.dart';␍
|
||||
␍
|
||||
/// Écran du portefeuille électronique — solde et historique.␍
|
||||
class WalletScreen extends ConsumerWidget {␍
|
||||
const WalletScreen({super.key});␍
|
||||
␍
|
||||
@override␍
|
||||
Widget build(BuildContext context, WidgetRef ref) {␍
|
||||
final walletAsync = ref.watch(walletProvider);␍
|
||||
final transactionsAsync = ref.watch(walletTransactionsProvider);␍
|
||||
final currencyFormat = NumberFormat.currency(locale: 'fr_FR', symbol: '€');␍
|
||||
␍
|
||||
return RefreshIndicator(␍
|
||||
onRefresh: () async {␍
|
||||
ref.invalidate(walletProvider);␍
|
||||
ref.invalidate(walletTransactionsProvider);␍
|
||||
},␍
|
||||
child: ListView(␍
|
||||
padding: const EdgeInsets.all(16),␍
|
||||
children: [␍
|
||||
walletAsync.when(␍
|
||||
loading: () => const SizedBox(␍
|
||||
height: 120,␍
|
||||
child: Center(child: CircularProgressIndicator()),␍
|
||||
),␍
|
||||
error: (error, _) => Text('Erreur solde : $error'),␍
|
||||
data: (wallet) => Container(␍
|
||||
padding: const EdgeInsets.all(20),␍
|
||||
decoration: BoxDecoration(␍
|
||||
gradient: AppColors.gradientAccent,␍
|
||||
borderRadius: BorderRadius.circular(14),␍
|
||||
boxShadow: [␍
|
||||
BoxShadow(␍
|
||||
color: AppColors.primary.withValues(alpha: 0.2),␍
|
||||
blurRadius: 10,␍
|
||||
offset: const Offset(0, 4),␍
|
||||
),␍
|
||||
],␍
|
||||
),␍
|
||||
child: Column(␍
|
||||
crossAxisAlignment: CrossAxisAlignment.start,␍
|
||||
children: [␍
|
||||
Text(␍
|
||||
'Solde disponible',␍
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: 0.9), fontSize: 14),␍
|
||||
),␍
|
||||
const SizedBox(height: 8),␍
|
||||
Text(␍
|
||||
currencyFormat.format(wallet.currentBalance),␍
|
||||
style: const TextStyle(␍
|
||||
color: Colors.white,␍
|
||||
fontSize: 32,␍
|
||||
fontWeight: FontWeight.w700,␍
|
||||
),␍
|
||||
),␍
|
||||
const SizedBox(height: 4),␍
|
||||
Text(␍
|
||||
wallet.status == 'active' ? 'Compte actif' : wallet.status,␍
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: 0.85), fontSize: 13),␍
|
||||
),␍
|
||||
],␍
|
||||
),␍
|
||||
),␍
|
||||
),␍
|
||||
const SizedBox(height: 16),␍
|
||||
ElevatedButton.icon(␍
|
||||
onPressed: () {␍
|
||||
ScaffoldMessenger.of(context).showSnackBar(␍
|
||||
const SnackBar(content: Text('Rechargement — à connecter à l\'API')),␍
|
||||
);␍
|
||||
},␍
|
||||
icon: const Icon(Icons.add),␍
|
||||
label: const Text('Recharger'),␍
|
||||
),␍
|
||||
const SizedBox(height: 24),␍
|
||||
Text('Historique', style: Theme.of(context).textTheme.titleMedium),␍
|
||||
const SizedBox(height: 8),␍
|
||||
transactionsAsync.when(␍
|
||||
loading: () => const Center(child: CircularProgressIndicator()),␍
|
||||
error: (error, _) => Text('Erreur historique : $error'),␍
|
||||
data: (transactions) {␍
|
||||
if (transactions.isEmpty) {␍
|
||||
return const EmptyState(␍
|
||||
icon: Icons.receipt_long_outlined,␍
|
||||
title: 'Aucune transaction',␍
|
||||
subtitle: 'Vos rechargements et débits apparaîtront ici.',␍
|
||||
);␍
|
||||
}␍
|
||||
␍
|
||||
return Column(␍
|
||||
children: transactions.map((tx) {␍
|
||||
final isCredit = tx.type == 'credit' || tx.type == 'refund';␍
|
||||
final color = isCredit ? AppColors.success : AppColors.error;␍
|
||||
␍
|
||||
return Card(␍
|
||||
margin: const EdgeInsets.only(bottom: 8),␍
|
||||
child: ListTile(␍
|
||||
leading: CircleAvatar(␍
|
||||
backgroundColor: color.withValues(alpha: 0.1),␍
|
||||
child: Icon(␍
|
||||
isCredit ? Icons.add : Icons.remove,␍
|
||||
color: color,␍
|
||||
size: 20,␍
|
||||
),␍
|
||||
),␍
|
||||
title: Text(tx.type),␍
|
||||
subtitle: tx.createdAt != null␍
|
||||
? Text(DateFormat('dd/MM/yyyy HH:mm').format(tx.createdAt!))␍
|
||||
: null,␍
|
||||
trailing: Text(␍
|
||||
'${isCredit ? '+' : '-'}${currencyFormat.format(tx.amount)}',␍
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: color),␍
|
||||
),␍
|
||||
),␍
|
||||
);␍
|
||||
}).toList(),␍
|
||||
);␍
|
||||
},␍
|
||||
),␍
|
||||
],␍
|
||||
),␍
|
||||
);␍
|
||||
}␍
|
||||
}␍
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../core/router/app_router.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/time/server_time.dart';
|
||||
import '../../../core/widgets/empty_state.dart';
|
||||
import '../data/wallet_repository.dart';
|
||||
|
||||
/// Écran du portefeuille électronique — solde et historique.
|
||||
class WalletScreen extends ConsumerWidget {
|
||||
const WalletScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final walletAsync = ref.watch(walletProvider);
|
||||
final transactionsAsync = ref.watch(walletTransactionsProvider);
|
||||
final currencyFormat = NumberFormat.currency(locale: 'fr_FR', symbol: '€');
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
ref.invalidate(walletProvider);
|
||||
ref.invalidate(walletTransactionsProvider);
|
||||
},
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
walletAsync.when(
|
||||
loading: () => const SizedBox(
|
||||
height: 120,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (error, _) => Text('Erreur solde : $error'),
|
||||
data: (wallet) => Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: AppColors.gradientAccent,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.primary.withValues(alpha: 0.2),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Solde disponible',
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: 0.9), fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
currencyFormat.format(wallet.currentBalance),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
wallet.status == 'active' ? 'Compte actif' : wallet.status,
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: 0.85), fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => context.push(AppRoutes.walletTopUp),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Recharger'),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text('Historique', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
transactionsAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (error, _) => Text('Erreur historique : $error'),
|
||||
data: (transactions) {
|
||||
if (transactions.isEmpty) {
|
||||
return const EmptyState(
|
||||
icon: Icons.receipt_long_outlined,
|
||||
title: 'Aucune transaction',
|
||||
subtitle: 'Vos rechargements et débits apparaîtront ici.',
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: transactions.map((tx) {
|
||||
final isCredit = tx.type == 'credit' || tx.type == 'refund';
|
||||
final color = isCredit ? AppColors.success : AppColors.error;
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: color.withValues(alpha: 0.1),
|
||||
child: Icon(
|
||||
isCredit ? Icons.add : Icons.remove,
|
||||
color: color,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
title: Text(tx.displayLabel),
|
||||
subtitle: tx.createdAt != null
|
||||
? Text(
|
||||
[
|
||||
ServerTime.format(tx.createdAt, pattern: 'dd/MM/yyyy HH:mm'),
|
||||
if (tx.displaySubtitle != null) tx.displaySubtitle,
|
||||
].join(' · '),
|
||||
)
|
||||
: (tx.displaySubtitle != null ? Text(tx.displaySubtitle!) : null),
|
||||
trailing: Text(
|
||||
'${isCredit ? '+' : '-'}${currencyFormat.format(tx.amount)}',
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: color),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user