diff --git a/mobile/meezi_app/lib/features/discover/cafe_detail_screen.dart b/mobile/meezi_app/lib/features/discover/cafe_detail_screen.dart index fa0bfe3..964f33b 100644 --- a/mobile/meezi_app/lib/features/discover/cafe_detail_screen.dart +++ b/mobile/meezi_app/lib/features/discover/cafe_detail_screen.dart @@ -93,11 +93,64 @@ class _CafeDetailScreenState extends ConsumerState { final description = cafe['description'] as String?; final address = cafe['address'] as String?; final city = cafe['city'] as String?; + // Defensive parsing — public DTO key names may vary. + final cover = (cafe['coverImageUrl'] ?? cafe['coverUrl'] ?? cafe['cover']) as String?; + final isOpen = cafe['isOpenNow'] as bool?; + final gallery = (cafe['galleryUrls'] ?? cafe['gallery']) is List + ? ((cafe['galleryUrls'] ?? cafe['gallery']) as List) + .map((e) => e.toString()) + .where((e) => e.isNotEmpty) + .toList() + : []; + // WorkingHoursPublicDto: a day-keyed object {sat..fri}, each {isOpen,open,close}. + final hours = cafe['workingHours'] is Map + ? (cafe['workingHours'] as Map) + : const {}; return ListView( padding: const EdgeInsets.all(16), children: [ - Text(name, style: Theme.of(context).textTheme.headlineSmall), + if (cover != null && cover.isNotEmpty) ...[ + ClipRRect( + borderRadius: BorderRadius.circular(16), + child: AspectRatio( + aspectRatio: 16 / 9, + child: Image.network( + cover, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => + Container(color: Colors.black12), + ), + ), + ), + const SizedBox(height: 12), + ], + Row( + children: [ + Expanded( + child: Text(name, + style: Theme.of(context).textTheme.headlineSmall), + ), + if (isOpen != null) + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: (isOpen ? Colors.green : Colors.red) + .withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(20), + ), + child: Text( + isOpen ? 'باز است' : 'بسته است', + style: TextStyle( + color: isOpen ? Colors.green[800] : Colors.red[800], + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ], + ), const SizedBox(height: 8), Row( children: [ @@ -117,6 +170,59 @@ class _CafeDetailScreenState extends ConsumerState { const SizedBox(height: 12), Text(description), ], + if (gallery.isNotEmpty) ...[ + const SizedBox(height: 12), + SizedBox( + height: 110, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: gallery.length, + separatorBuilder: (_, __) => const SizedBox(width: 8), + itemBuilder: (_, i) => ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.network( + gallery[i], + width: 150, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => + Container(width: 150, color: Colors.black12), + ), + ), + ), + ), + ], + if (hours.isNotEmpty) ...[ + const SizedBox(height: 16), + Text('ساعات کاری', + style: Theme.of(context).textTheme.titleMedium), + const SizedBox(height: 8), + ...const [ + ('sat', 'شنبه'), + ('sun', 'یکشنبه'), + ('mon', 'دوشنبه'), + ('tue', 'سه‌شنبه'), + ('wed', 'چهارشنبه'), + ('thu', 'پنجشنبه'), + ('fri', 'جمعه'), + ].map((d) { + final m = hours[d.$1] is Map + ? hours[d.$1] as Map + : const {}; + final open = (m['open'] ?? '').toString(); + final close = (m['close'] ?? '').toString(); + final isOpen = m['isOpen'] == true && open.isNotEmpty; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(d.$2), + Text(isOpen ? '$open - $close' : 'تعطیل'), + ], + ), + ); + }), + ], const SizedBox(height: 16), Row( children: [