import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import '../models/laundry.dart'; import '../screens/laundry_detail_screen.dart'; class LaundryCard extends StatelessWidget { final Laundry laundry; final int? ranking; final double? skor; final bool showRanking; const LaundryCard({ super.key, required this.laundry, this.ranking, this.skor, this.showRanking = false, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => LaundryDetailScreen(laundry: laundry), ), ); }, child: Card( elevation: 3, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Ranking Badge (if showRanking is true) if (showRanking && ranking != null) ...[ Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), decoration: BoxDecoration( color: _getRankingColor(ranking!), borderRadius: const BorderRadius.only( topLeft: Radius.circular(16), topRight: Radius.circular(16), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon( _getRankingIcon(ranking!), color: Colors.white, size: 18, ), const SizedBox(width: 6), Text( 'Ranking #$ranking', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), if (skor != null) Text( '${(skor! * 100).toStringAsFixed(0)}%', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ), ], // Image Stack( children: [ ClipRRect( borderRadius: BorderRadius.vertical( top: Radius.circular(showRanking && ranking != null ? 0 : 16), ), child: CachedNetworkImage( imageUrl: laundry.primaryPhoto, height: 180, width: double.infinity, fit: BoxFit.cover, placeholder: (context, url) => Container( height: 180, color: Colors.grey[300], child: const Center( child: CircularProgressIndicator(), ), ), errorWidget: (context, url, error) => Container( height: 180, color: Colors.grey[300], child: const Icon(Icons.local_laundry_service, size: 50), ), ), ), Positioned( top: 10, right: 10, child: Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: laundry.status == 'buka' ? Colors.green : Colors.red, borderRadius: BorderRadius.circular(20), ), child: Text( laundry.status == 'buka' ? 'Buka' : 'Tutup', style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ), ], ), // Content Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( laundry.nama, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Row( children: [ const Icon(Icons.location_on, size: 14, color: Colors.grey), const SizedBox(width: 4), Expanded( child: Text( laundry.alamat, style: const TextStyle( fontSize: 12, color: Colors.grey, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( laundry.formattedHarga, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF00897B), ), ), const Text( '/kg', style: TextStyle( fontSize: 11, color: Colors.grey, ), ), ], ), Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: const Color(0xFF00897B).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon( Icons.access_time, size: 14, color: Color(0xFF00897B), ), const SizedBox(width: 4), Text( '${laundry.waktuProses}h', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Color(0xFF00897B), ), ), ], ), ), ], ), const SizedBox(height: 8), Row( children: [ const Icon( Icons.directions_walk, size: 14, color: Colors.orange, ), const SizedBox(width: 4), Text( '${laundry.jarakKampus.toStringAsFixed(1)} km dari kampus', style: const TextStyle( fontSize: 11, color: Colors.grey, ), ), ], ), const SizedBox(height: 6), Row( children: [ const Icon( Icons.schedule, size: 14, color: Colors.green, ), const SizedBox(width: 4), Text( '${laundry.jamBuka} - ${laundry.jamTutup}', style: const TextStyle( fontSize: 11, color: Colors.grey, ), ), ], ), if (laundry.avgRating != null) ...[ const SizedBox(height: 6), Row( children: [ const Icon(Icons.star, size: 14, color: Colors.amber), const SizedBox(width: 4), Text( '${laundry.avgRating!.toStringAsFixed(1)}', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( ' (${laundry.totalReviews ?? 0} review)', style: const TextStyle( fontSize: 11, color: Colors.grey, ), ), ], ), ], ], ), ), ], ), ), ); } Color _getRankingColor(int ranking) { if (ranking == 1) return Colors.amber; if (ranking == 2) return Colors.grey[400]!; if (ranking == 3) return Colors.brown[300]!; return const Color(0xFF00897B); } IconData _getRankingIcon(int ranking) { if (ranking <= 3) return Icons.emoji_events; return Icons.star; } }