import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import '../models/laundry.dart'; import '../screens/laundry_detail_screen.dart'; import '../services/location_service.dart'; class LaundryCard extends StatelessWidget { final Laundry laundry; final int? ranking; final double? skor; final bool showRanking; final double? userLatitude; final double? userLongitude; final String? selectedJenisLayanan; const LaundryCard({ super.key, required this.laundry, this.ranking, this.skor, this.showRanking = false, this.userLatitude, this.userLongitude, this.selectedJenisLayanan, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => LaundryDetailScreen(laundry: laundry), ), ); }, child: Card( elevation: 2, shadowColor: Colors.black.withOpacity(0.1), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), clipBehavior: Clip.antiAlias, 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: laundry.hasPhoto && laundry.primaryPhoto.isNotEmpty ? 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, width: double.infinity, color: Colors.transparent, ), ) : Container( height: 180, width: double.infinity, color: Colors.transparent, ), ), 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: [ Flexible( child: Row( children: [ Text( laundry.formattedHargaFor(selectedJenisLayanan), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF00897B), ), ), Text( '/kg', style: TextStyle( fontSize: 12, color: Colors.grey[500], fontWeight: FontWeight.w500, ), ), ], ), ), 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.waktuProsesFor(selectedJenisLayanan)}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( _distanceText(), 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); } String _distanceText() { if (userLatitude != null && userLongitude != null && laundry.latitude != null && laundry.longitude != null) { final distance = LocationService.calculateDistance( userLatitude!, userLongitude!, laundry.latitude!, laundry.longitude!, ); if (distance < 1) { return '${(distance * 1000).toStringAsFixed(0)} m dari lokasi Anda'; } return '${distance.toStringAsFixed(1)} km dari lokasi Anda'; } return '${laundry.jarakKampus.toStringAsFixed(1)} km dari kampus'; } IconData _getRankingIcon(int ranking) { if (ranking <= 3) return Icons.emoji_events; return Icons.star; } }