import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:url_launcher/url_launcher.dart'; import 'package:nearledaily/constants/color_constants.dart'; import '../../constants/font_constants.dart'; import '../../widgets/text_widget.dart'; // ───────────────────────────────────────────── // MODEL // ───────────────────────────────────────────── class TenantDetails { final int tenantid; final String tenantname; final String tenanttype; // "D" = delivery-only final String registrationno; final String companyname; final String primaryemail; final String primarycontact; final String address; final String city; final String state; final String postcode; final String latitude; final String longitude; final String status; // "Active" / else const TenantDetails({ required this.tenantid, required this.tenantname, required this.tenanttype, required this.registrationno, required this.companyname, required this.primaryemail, required this.primarycontact, required this.address, required this.city, required this.state, required this.postcode, required this.latitude, required this.longitude, required this.status, }); factory TenantDetails.fromJson(Map j) => TenantDetails( tenantid: j['tenantid'] ?? 0, tenantname: j['tenantname'] ?? '', tenanttype: j['tenanttype'] ?? '', registrationno: j['registrationno'] ?? '', companyname: j['companyname'] ?? '', primaryemail: j['primaryemail'] ?? '', primarycontact: j['primarycontact'] ?? '', address: j['address'] ?? '', city: j['city'] ?? '', state: j['state'] ?? '', postcode: j['postcode'] ?? '', latitude: j['latitude'] ?? '', longitude: j['longitude'] ?? '', status: j['status'] ?? '', ); bool get isDeliveryOnly => tenanttype == 'D'; bool get isActive => status == 'Active'; } // ───────────────────────────────────────────── // API SERVICE // ───────────────────────────────────────────── class TenantApiService { static Future fetch(int tenantId) async { final res = await http.get(Uri.parse( 'https://fiesta.nearle.app/live/api/v1/mob/tenants/gettenantinfo/?tenantid=$tenantId')); if (res.statusCode == 200) { final body = jsonDecode(res.body); if (body['status'] == true) { return TenantDetails.fromJson(body['details']); } throw Exception(body['message']); } throw Exception('HTTP ${res.statusCode}'); } } // ───────────────────────────────────────────── // SCREEN // ───────────────────────────────────────────── class StoreOverviewScreen extends StatefulWidget { final int tenantId; const StoreOverviewScreen({super.key, this.tenantId = 1091}); @override State createState() => _StoreOverviewScreenState(); } class _StoreOverviewScreenState extends State { late Future _future; @override void initState() { super.initState(); _future = TenantApiService.fetch(widget.tenantId); } // ── Dialer ─────────────────────────────────── Future _launchDialer(String phone) async { final uri = Uri(scheme: 'tel', path: phone); if (await canLaunchUrl(uri)) { await launchUrl(uri); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Could not open dialer')), ); } } // ── Maps ───────────────────────────────────── Future _openMap(String lat, String lng, String label) async { final encoded = Uri.encodeComponent(label); final uri = Uri.parse( 'https://www.google.com/maps/search/?api=1&query=$lat,$lng($encoded)', ); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Could not open maps')), ); } } // ── Bad-experience bottom sheet ────────────────── void _showBadExperienceSheet(TenantDetails tenant) { final reasons = [ 'Wrong items delivered', 'Poor food quality', 'Late delivery', 'Rude behaviour', 'Other', ]; String? selected; showModalBottomSheet( backgroundColor: Colors.white, context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (_) => StatefulBuilder( builder: (ctx, setSheet) => Padding( padding: EdgeInsets.only( left: 20, right: 20, top: 20, bottom: MediaQuery.of(ctx).viewInsets.bottom + 24, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Handle bar Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 16), ReusableTextWidget( text: 'What went wrong?', color: Colors.black87, fontFamily: FontConstants.fontFamily, fontSize: 17, fontWeight: FontWeight.bold, ), const SizedBox(height: 4), ReusableTextWidget( text: 'Tell us about your experience at ${tenant.tenantname}', color: Colors.grey, fontFamily: FontConstants.fontFamily, fontSize: 12, fontWeight: FontWeight.w500, ), const SizedBox(height: 16), // Reason chips Wrap( spacing: 8, runSpacing: 8, children: reasons.map((r) { final picked = selected == r; return GestureDetector( onTap: () => setSheet(() => selected = r), child: AnimatedContainer( duration: const Duration(milliseconds: 180), padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 8), decoration: BoxDecoration( color: picked ? const Color(0xFF6A1B9A) : Colors.grey.shade100, borderRadius: BorderRadius.circular(20), border: Border.all( color: picked ? const Color(0xFF6A1B9A) : Colors.grey.shade300, ), ), child: ReusableTextWidget( text: r, color: picked ? Colors.white : Colors.black87, fontFamily: FontConstants.fontFamily, fontSize: 12, fontWeight: FontWeight.w600, ), ), ); }).toList(), ), const SizedBox(height: 20), // Hide store option const SizedBox(height: 14), // Submit button SizedBox( width: double.infinity, height: 48, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF6A1B9A), disabledBackgroundColor: Colors.grey.shade200, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(13), ), ), onPressed: selected == null ? null : () { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Feedback submitted: $selected'), behavior: SnackBarBehavior.floating, ), ); }, child: ReusableTextWidget( text: 'Submit Feedback', color: selected == null ? Colors.grey : Colors.white, fontFamily: FontConstants.fontFamily, fontSize: 15, fontWeight: FontWeight.w600, ), ), ), ], ), ), ), ); } // ───────────────────────────────────────────── @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFFF9F9F9), Color(0xFFF1F1F1)], ), ), child: SafeArea( child: FutureBuilder( future: _future, builder: (context, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snap.hasError) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, color: Colors.red, size: 48), const SizedBox(height: 12), Text('Failed to load\n${snap.error}', textAlign: TextAlign.center), const SizedBox(height: 12), ElevatedButton( onPressed: () => setState( () => _future = TenantApiService.fetch(widget.tenantId)), child: const Text('Retry'), ), ], ), ); } final t = snap.data!; return Column( children: [ Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(12), child: Column( children: [ _topBar(), const SizedBox(height: 16), _storeCard(t), const SizedBox(height: 12), _badExperienceCard(t), const SizedBox(height: 12), _legalCard(t), ], ), ), ), _bottomButton(), ], ); }, ), ), ), ); } // ── Top bar ────────────────────────────────── Widget _topBar() => Row( children: [ CircleAvatar( backgroundColor: Colors.white, child: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context), ), ), const Spacer(), ], ); // ── Store card ─────────────────────────────── Widget _storeCard(TenantDetails t) { return Container( padding: const EdgeInsets.all(16), decoration: _card(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // tenantname ReusableTextWidget( text: t.tenantname, color: Colors.black.withOpacity(0.75), fontFamily: FontConstants.fontFamily, fontSize: 23, fontWeight: FontWeight.bold, overflow: TextOverflow.ellipsis, maxLines: 1, ), const SizedBox(height: 8), // address ReusableTextWidget( text: t.address, color: Colors.black87, fontFamily: FontConstants.fontFamily, fontSize: 11, fontWeight: FontWeight.w600, maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), // Call & Directions Row( children: [ GestureDetector( onTap: () => _launchDialer(t.primarycontact), child: _circleIcon(Icons.call), ), const SizedBox(width: 12), GestureDetector( onTap: () => _openMap(t.latitude, t.longitude, t.tenantname), child: _circleIcon(Icons.near_me_outlined), ), ], ), const Divider(height: 24, thickness: 0.5), // status → Open / Closed _infoRow( icon: Icons.access_time, title: t.isActive ? 'Open now' : 'Currently Closed', titleColor: t.isActive ? Colors.green : Colors.red, ), // tenanttype == "D" → delivery-only row if (t.isDeliveryOnly) ...[ const Divider(thickness: 0.5), _infoRow( icon: Icons.store_mall_directory_outlined, title: 'This is a delivery-only kitchen', subtitle: 'There are multiple brands delivering from this kitchen', ), ], const Divider(thickness: 0.5), // city + state + postcode _infoRow( icon: Icons.location_city_outlined, title: '${t.city}, ${t.state} – ${t.postcode}', ), ], ), ); } // ── Bad experience card ────────────────────── Widget _badExperienceCard(TenantDetails t) => Container( decoration: _card(), child: ListTile( leading: Container( width: 36, height: 36, decoration: BoxDecoration( color: Colors.red.shade50, shape: BoxShape.circle, ), child: Icon(Icons.sentiment_dissatisfied_outlined, color: Colors.red.shade400, size: 20), ), title: ReusableTextWidget( text: 'Had a bad experience here?', color: Colors.black87, fontFamily: FontConstants.fontFamily, fontSize: 13, fontWeight: FontWeight.w600, ), subtitle: ReusableTextWidget( text: 'Report an issue or hide this store', color: Colors.black54, fontFamily: FontConstants.fontFamily, fontSize: 12, fontWeight: FontWeight.w400, ), trailing: const Icon(Icons.chevron_right, color: Colors.black87), onTap: () => _showBadExperienceSheet(t), ), ); // ── Legal card — only real non-empty API fields ── Widget _legalCard(TenantDetails t) => Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: _card(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _labelText('Legal Name', t.companyname), const SizedBox(height: 12), _labelText('GST Number', t.registrationno), const SizedBox(height: 12), _labelText('Contact', t.primarycontact), const SizedBox(height: 12), _labelText('Email', t.primaryemail), ], ), ); // ── Bottom button ──────────────────────────── Widget _bottomButton() => Container( padding: const EdgeInsets.fromLTRB(16, 10, 16, 13), color: Colors.white, child: SizedBox( width: double.infinity, height: 48, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF6A1B9A), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(13)), ), onPressed: () => Navigator.pop(context), child: ReusableTextWidget( text: 'Go back to menu', color: Colors.white, fontFamily: FontConstants.fontFamily, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ); // ── Helpers ────────────────────────────────── Widget _circleIcon(IconData icon) => Container( width: 40, height: 40, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white, border: Border.all(color: Colors.grey.shade200), ), child: Center( child: Icon(icon, size: 22, color: ColorConstants.primaryColor), ), ); Widget _infoRow({ required IconData icon, required String title, String? subtitle, Color? titleColor, }) => Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 20, color: Colors.black87), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ReusableTextWidget( text: title, color: titleColor ?? Colors.black87, fontFamily: FontConstants.fontFamily, fontSize: 12, fontWeight: FontWeight.w600, ), if (subtitle != null) ReusableTextWidget( text: subtitle, color: Colors.grey, fontFamily: FontConstants.fontFamily, fontSize: 11, fontWeight: FontWeight.w600, maxLines: 2, ), ], ), ), const Icon(Icons.chevron_right, color: Colors.black87), ], ); Widget _labelText(String label, String value) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ReusableTextWidget( text: label, color: Colors.grey, fontFamily: FontConstants.fontFamily, fontSize: 11, fontWeight: FontWeight.w600, ), const SizedBox(height: 4), ReusableTextWidget( text: value, color: Colors.black87, fontFamily: FontConstants.fontFamily, fontSize: 11, fontWeight: FontWeight.w600, ), ], ); BoxDecoration _card() => BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 10, offset: const Offset(0, 4), ), ], ); }