minor home screen fix

This commit is contained in:
2026-05-29 23:54:17 +02:00
parent 287cb37048
commit d700ad609d
4 changed files with 60 additions and 37 deletions

View File

@@ -108,8 +108,11 @@ class HomeController extends ChangeNotifier {
model.isLoading = true; model.isLoading = true;
if (!_disposed) notifyListeners(); if (!_disposed) notifyListeners();
List<dynamic> response = await http.get('/public/providers'); final response = await http.get('/public/providers?sort=priority,asc');
model.providers = response.map((e) => BillProvider.fromJson(e)).toList(); List<BillProvider> providers = PageableModel.fromJson(response).content
.map((e) => BillProvider.fromJson(e as Map<String, dynamic>))
.toList();
model.providers = providers;
model.filterableProviders = model.providers; model.filterableProviders = model.providers;
if (model.providers.isNotEmpty) { if (model.providers.isNotEmpty) {
@@ -141,7 +144,9 @@ class HomeController extends ChangeNotifier {
if (!_disposed) notifyListeners(); if (!_disposed) notifyListeners();
} }
Future<ApiResponse<List<Map<String, dynamic>>>> getTransactions(String userId) async { Future<ApiResponse<List<Map<String, dynamic>>>> getTransactions(
String userId,
) async {
model.isLoading = true; model.isLoading = true;
if (!_disposed) notifyListeners(); if (!_disposed) notifyListeners();
@@ -179,8 +184,10 @@ class HomeController extends ChangeNotifier {
// Simulate fetching categories // Simulate fetching categories
try { try {
List<dynamic> response = await http.get('/public/categories'); final response = await http.get('/public/categories');
model.categories = response.map((e) => Category.fromJson(e)).toList(); model.categories = (response as List<dynamic>)
.map((e) => Category.fromJson(e as Map<String, dynamic>))
.toList();
if (model.categories.isNotEmpty) { if (model.categories.isNotEmpty) {
updateSelectedCategory(model.categories[0]); updateSelectedCategory(model.categories[0]);

View File

@@ -35,6 +35,14 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
late Animation<Offset> _transactionsSlideAnimation; late Animation<Offset> _transactionsSlideAnimation;
late Animation<double> _transactionsFadeAnimation; late Animation<double> _transactionsFadeAnimation;
// ── Vibrant category color map ──────────────────────────────
static const Map<String, Color> _categoryColorMap = {
'ECONET': Color(0xFF1A73E8), // Deep green
'NETONE': Color.fromARGB(255, 230, 127, 0), // Bold blue
'TELECEL': Color(0xFF7B1FA2), // Vibrant purple
'ZESA': Color.fromARGB(255, 230, 58, 0), // Fiery orange
};
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -156,6 +164,28 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
super.dispose(); super.dispose();
} }
/// Derives a vibrant accent colour from the provider's category.
Color _resolveProviderColor(BillProvider provider) {
// Try exact match on category
final exact = _categoryColorMap[provider.category.toUpperCase()];
if (exact != null) return exact;
// Try matching on label
final labelMatch = _categoryColorMap[provider.label.toUpperCase()];
if (labelMatch != null) return labelMatch;
// Try matching on provider name keywords
final name = provider.name.toUpperCase();
for (final entry in _categoryColorMap.entries) {
if (name.contains(entry.key)) return entry.value;
}
// Hash-based fallback deterministic vibrant hue
final hash = provider.clientId.hashCode;
final hue = (hash % 360).toDouble();
return HSVColor.fromAHSV(1.0, hue, 0.75, 0.85).toColor();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
@@ -352,6 +382,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
double availableWidth, double availableWidth,
) { ) {
final cardWidth = (availableWidth / 2) - 6; final cardWidth = (availableWidth / 2) - 6;
final accent = _resolveProviderColor(provider);
return SizedBox( return SizedBox(
width: cardWidth, width: cardWidth,
@@ -368,26 +399,13 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
child: Container( child: Container(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( color: isDark ? const Color(0xFF1E1E1E) : Colors.white,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
theme.colorScheme.primary.withValues(alpha: 0.08),
theme.colorScheme.tertiary.withValues(alpha: 0.03),
theme.colorScheme.primary.withValues(alpha: 0.05),
],
stops: const [0.0, 0.5, 1.0],
),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(
color: theme.colorScheme.primary.withValues(alpha: 0.1),
width: 1,
),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: theme.colorScheme.primary.withValues(alpha: 0.06), color: accent.withValues(alpha: 0.15),
blurRadius: 10, blurRadius: 2,
offset: const Offset(0, 3), offset: const Offset(0, 1),
), ),
], ],
), ),
@@ -400,11 +418,9 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
Container( Container(
width: 40, width: 40,
height: 40, height: 40,
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(1),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.primary.withValues( color: accent.withValues(alpha: 0.12),
alpha: 0.12,
),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Image( child: Image(
@@ -412,14 +428,14 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
errorBuilder: (_, __, ___) => Icon( errorBuilder: (_, __, ___) => Icon(
Icons.payment_rounded, Icons.payment_rounded,
size: 20, size: 20,
color: theme.colorScheme.primary, color: accent,
), ),
), ),
), ),
Icon( Icon(
Icons.chevron_right_rounded, Icons.chevron_right_rounded,
size: 20, size: 20,
color: theme.colorScheme.primary.withValues(alpha: 0.5), color: accent.withValues(alpha: 0.55),
), ),
], ],
), ),

View File

@@ -74,9 +74,9 @@ class TransactionModel {
final String? providerImage; final String? providerImage;
final String? paymentProcessorImage; final String? paymentProcessorImage;
final String confirmationStatus; final String confirmationStatus;
final String paymentStatus; final String? paymentStatus;
final String integrationStatus; final String? integrationStatus;
final String pollingStatus; final String? pollingStatus;
final String? orderId; final String? orderId;
final String? orderTrace; final String? orderTrace;
final String? orderTransactionTrace; final String? orderTransactionTrace;
@@ -122,9 +122,9 @@ class TransactionModel {
this.providerImage, this.providerImage,
this.paymentProcessorImage, this.paymentProcessorImage,
required this.confirmationStatus, required this.confirmationStatus,
required this.paymentStatus, this.paymentStatus,
required this.integrationStatus, this.integrationStatus,
required this.pollingStatus, this.pollingStatus,
this.orderId, this.orderId,
this.orderTrace, this.orderTrace,
this.orderTransactionTrace, this.orderTransactionTrace,

View File

@@ -41,9 +41,9 @@ TransactionModel _$TransactionModelFromJson(Map<String, dynamic> json) =>
providerImage: json['providerImage'] as String?, providerImage: json['providerImage'] as String?,
paymentProcessorImage: json['paymentProcessorImage'] as String?, paymentProcessorImage: json['paymentProcessorImage'] as String?,
confirmationStatus: json['confirmationStatus'] as String, confirmationStatus: json['confirmationStatus'] as String,
paymentStatus: json['paymentStatus'] as String, paymentStatus: json['paymentStatus'] as String?,
integrationStatus: json['integrationStatus'] as String, integrationStatus: json['integrationStatus'] as String?,
pollingStatus: json['pollingStatus'] as String, pollingStatus: json['pollingStatus'] as String?,
orderId: json['orderId'] as String?, orderId: json['orderId'] as String?,
orderTrace: json['orderTrace'] as String?, orderTrace: json['orderTrace'] as String?,
orderTransactionTrace: json['orderTransactionTrace'] as String?, orderTransactionTrace: json['orderTransactionTrace'] as String?,