diff --git a/lib/screens/menu_browse_screen.dart b/lib/screens/menu_browse_screen.dart index 11b29d9..531d9cf 100644 --- a/lib/screens/menu_browse_screen.dart +++ b/lib/screens/menu_browse_screen.dart @@ -115,22 +115,63 @@ class _MenuBrowseScreenState extends State { return Scaffold( appBar: AppBar( - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + title: Row( children: [ - Text( - businessName, - style: const TextStyle(fontSize: 18), - ), - if (appState.selectedServicePointName != null) - Text( - appState.selectedServicePointName!, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, + // Business logo + if (_businessId != null) + Padding( + padding: const EdgeInsets.only(right: 12), + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: SizedBox( + width: 36, + height: 36, + child: Image.network( + "$_imageBaseUrl/logos/$_businessId.png", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.network( + "$_imageBaseUrl/logos/$_businessId.jpg", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(6), + ), + child: Icon( + Icons.store, + size: 20, + color: Theme.of(context).colorScheme.onPrimaryContainer, + ), + ); + }, + ); + }, + ), + ), ), ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + businessName, + style: const TextStyle(fontSize: 18), + ), + if (appState.selectedServicePointName != null) + Text( + appState.selectedServicePointName!, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), ], ), actions: [ @@ -184,9 +225,15 @@ class _MenuBrowseScreenState extends State { final categoryIds = _getUniqueCategoryIds(); return ListView.builder( - itemCount: categoryIds.length, + itemCount: categoryIds.length + 1, // +1 for header itemBuilder: (context, index) { - final categoryId = categoryIds[index]; + // First item is the business header + if (index == 0) { + return _buildBusinessHeader(); + } + + final categoryIndex = index - 1; + final categoryId = categoryIds[categoryIndex]; final items = _itemsByCategory[categoryId] ?? []; final categoryName = items.isNotEmpty ? items.first.categoryName @@ -195,27 +242,9 @@ class _MenuBrowseScreenState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - width: double.infinity, - padding: const EdgeInsets.fromLTRB(16, 24, 16, 12), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHighest, - border: Border( - bottom: BorderSide( - color: Theme.of(context).colorScheme.outlineVariant, - width: 1, - ), - ), - ), - child: Text( - categoryName, - style: Theme.of(context).textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ), + _buildCategoryHeader(categoryId, categoryName), ...items.map((item) => _buildMenuItem(item)), - const Divider(height: 32), + const SizedBox(height: 16), ], ); }, @@ -225,6 +254,234 @@ class _MenuBrowseScreenState extends State { ); } + static const String _imageBaseUrl = "https://biz.payfrit.com/uploads"; + + Widget _buildBusinessHeader() { + if (_businessId == null) return const SizedBox.shrink(); + + final appState = context.read(); + final businessName = appState.selectedBusinessName ?? "Restaurant"; + + return Container( + width: double.infinity, + height: 180, + margin: const EdgeInsets.only(bottom: 8), + child: Stack( + fit: StackFit.expand, + children: [ + // Header background image + Image.network( + "$_imageBaseUrl/headers/$_businessId.png", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.network( + "$_imageBaseUrl/headers/$_businessId.jpg", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // No header image - show gradient background + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Theme.of(context).colorScheme.primary, + Theme.of(context).colorScheme.secondary, + ], + ), + ), + ); + }, + ); + }, + ), + // Dark gradient overlay + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withAlpha(0), + Colors.black.withAlpha(179), + ], + ), + ), + ), + // Business info overlay + Positioned( + left: 16, + right: 16, + bottom: 16, + child: Row( + children: [ + // Logo + ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + width: 56, + height: 56, + child: Image.network( + "$_imageBaseUrl/logos/$_businessId.png", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.network( + "$_imageBaseUrl/logos/$_businessId.jpg", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Container( + color: Colors.white24, + child: const Icon( + Icons.store, + size: 32, + color: Colors.white, + ), + ); + }, + ); + }, + ), + ), + ), + const SizedBox(width: 12), + // Business name and info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + businessName, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + shadows: [ + const Shadow( + offset: Offset(1, 1), + blurRadius: 3, + color: Colors.black54, + ), + ], + ), + ), + if (appState.selectedServicePointName != null) + Text( + appState.selectedServicePointName!, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Colors.white70, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildItemImage(int itemId) { + return ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + width: 64, + height: 64, + child: Image.network( + "$_imageBaseUrl/items/$itemId.png", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // Try jpg if png fails + return Image.network( + "$_imageBaseUrl/items/$itemId.jpg", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // Show placeholder if no image + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + Icons.restaurant, + color: Theme.of(context).colorScheme.onPrimaryContainer, + size: 28, + ), + ); + }, + ); + }, + ), + ), + ); + } + + Widget _buildCategoryHeader(int categoryId, String categoryName) { + return Container( + width: double.infinity, + height: 120, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHighest, + ), + child: Stack( + fit: StackFit.expand, + children: [ + // Category image background + Image.network( + "$_imageBaseUrl/categories/$categoryId.png", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.network( + "$_imageBaseUrl/categories/$categoryId.jpg", + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // No image - just show solid color + return Container( + color: Theme.of(context).colorScheme.primaryContainer, + ); + }, + ); + }, + ), + // Dark gradient overlay for text readability + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withAlpha(0), + Colors.black.withAlpha(179), + ], + ), + ), + ), + // Category name + Positioned( + left: 16, + bottom: 12, + right: 16, + child: Text( + categoryName, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [ + const Shadow( + offset: Offset(1, 1), + blurRadius: 3, + color: Colors.black54, + ), + ], + ), + ), + ), + ], + ), + ); + } + Widget _buildMenuItem(MenuItem item) { final hasModifiers = _itemsByParent.containsKey(item.itemId); @@ -250,19 +507,8 @@ class _MenuBrowseScreenState extends State { padding: const EdgeInsets.all(16), child: Row( children: [ - // Food icon - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(8), - ), - child: Icon( - Icons.restaurant, - color: Theme.of(context).colorScheme.onPrimaryContainer, - ), - ), + // Item image or placeholder + _buildItemImage(item.itemId), const SizedBox(width: 16), // Item details Expanded(