// ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/widgets/home_right_title.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/core/extensions/int_ext.dart'; import 'package:enaklo_pos/data/models/response/table_model.dart'; import 'package:enaklo_pos/presentation/home/pages/confirm_payment_page.dart'; import 'package:enaklo_pos/data/models/response/product_response_model.dart'; import '../../../core/assets/assets.gen.dart'; import '../../../core/components/buttons.dart'; import '../../../core/components/spaces.dart'; import '../../../core/constants/colors.dart'; import '../bloc/checkout/checkout_bloc.dart'; import '../widgets/custom_tab_bar.dart'; import '../widgets/home_title.dart'; import '../widgets/order_menu.dart'; import '../widgets/product_card.dart'; class HomePage extends StatefulWidget { final bool isTable; final TableModel? table; const HomePage({ super.key, required this.isTable, this.table, }); @override State createState() => _HomePageState(); } class _HomePageState extends State { final searchController = TextEditingController(); String searchQuery = ''; @override void initState() { // First sync products from API, then load local products _syncAndLoadProducts(); super.initState(); } void _syncAndLoadProducts() { // Trigger sync from API first // context.read().add(const SyncProductEvent.syncProduct()); // Also load local products initially in case sync fails or takes time // context // .read() // .add(const LocalProductEvent.getLocalProduct()); context .read() .add(const ProductLoaderEvent.getProduct()); // Initialize checkout with tax and service charge settings context.read().add(const CheckoutEvent.started()); } void onCategoryTap(int index) { searchController.clear(); setState(() { searchQuery = ''; }); } List _filterProducts(List products) { if (searchQuery.isEmpty) { return products; } return products.where((product) { final productName = product.name?.toLowerCase() ?? ''; final queryLower = searchQuery.toLowerCase(); return productName.contains(queryLower); }).toList(); } List _filterProductsByCategory( List products, int categoryId) { final filteredBySearch = _filterProducts(products); return filteredBySearch .where((element) => element.price == categoryId) .toList(); } @override Widget build(BuildContext context) { return Hero( tag: 'confirmation_screen', child: Scaffold( backgroundColor: AppColors.white, body: Row( children: [ Expanded( flex: 3, child: Align( alignment: AlignmentDirectional.topStart, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ HomeTitle( controller: searchController, onChanged: (value) { setState(() { searchQuery = value; }); }, ), BlocBuilder( builder: (context, state) { return Expanded( child: CustomTabBarV2( tabTitles: const [ 'Semua', 'Makanan', 'Minuman', 'Paket' ], tabViews: [ // All Products Tab SizedBox( child: state.maybeWhen(orElse: () { return const Center( child: CircularProgressIndicator(), ); }, loading: () { return const Center( child: CircularProgressIndicator(), ); }, loaded: (products) { final filteredProducts = _filterProducts(products); if (filteredProducts.isEmpty) { return const Center( child: Text('No Items Found'), ); } return GridView.builder( itemCount: filteredProducts.length, padding: const EdgeInsets.all(16), gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 180, mainAxisSpacing: 30, crossAxisSpacing: 30, childAspectRatio: 180 / 240, ), itemBuilder: (context, index) => ProductCard( data: filteredProducts[index], onCartButton: () {}, ), ); }), ), // Makanan Tab SizedBox( child: state.maybeWhen(orElse: () { return const Center( child: CircularProgressIndicator(), ); }, loading: () { return const Center( child: CircularProgressIndicator(), ); }, loaded: (products) { if (products.isEmpty) { return const Center( child: Text('No Items'), ); } final filteredProducts = _filterProductsByCategory(products, 1); return filteredProducts.isEmpty ? const _IsEmpty() : GridView.builder( itemCount: filteredProducts.length, padding: const EdgeInsets.all(16), gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200, // Lebar maksimal tiap item (bisa kamu ubah) mainAxisSpacing: 30, crossAxisSpacing: 30, childAspectRatio: 0.85, ), itemBuilder: (context, index) => ProductCard( data: filteredProducts[index], onCartButton: () {}, ), ); }), ), // Minuman Tab SizedBox( child: state.maybeWhen(orElse: () { return const Center( child: CircularProgressIndicator(), ); }, loading: () { return const Center( child: CircularProgressIndicator(), ); }, loaded: (products) { if (products.isEmpty) { return const Center( child: Text('No Items'), ); } final filteredProducts = _filterProductsByCategory(products, 2); return filteredProducts.isEmpty ? const _IsEmpty() : GridView.builder( itemCount: filteredProducts.length, padding: const EdgeInsets.all(16), gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200, // Lebar maksimal tiap item (bisa kamu ubah) mainAxisSpacing: 30, crossAxisSpacing: 30, childAspectRatio: 0.85, ), itemBuilder: (context, index) { return ProductCard( data: filteredProducts[index], onCartButton: () {}, ); }, ); }), ), // Snack Tab SizedBox( child: state.maybeWhen(orElse: () { return const Center( child: CircularProgressIndicator(), ); }, loading: () { return const Center( child: CircularProgressIndicator(), ); }, loaded: (products) { if (products.isEmpty) { return const Center( child: Text('No Items'), ); } final filteredProducts = _filterProductsByCategory(products, 3); return filteredProducts.isEmpty ? const _IsEmpty() : GridView.builder( itemCount: filteredProducts.length, padding: const EdgeInsets.all(16), gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200, // Lebar maksimal tiap item (bisa kamu ubah) mainAxisSpacing: 30, crossAxisSpacing: 30, childAspectRatio: 0.85, ), itemBuilder: (context, index) { return ProductCard( data: filteredProducts[index], onCartButton: () {}, ); }, ); }), ), ], ), ); }, ), ], ), ), ), Expanded( flex: 2, child: Align( alignment: Alignment.topCenter, child: Material( color: Colors.white, child: Column( children: [ HomeRightTitle( table: widget.table, ), Padding( padding: const EdgeInsets.all(16.0) .copyWith(bottom: 0, top: 27), child: Column( children: [ const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Item', style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), SizedBox( width: 130, ), SizedBox( width: 50.0, child: Text( 'Qty', style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), SizedBox( child: Text( 'Price', style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ], ), const SpaceHeight(8), const Divider(), ], ), ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16.0).copyWith(top: 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ BlocBuilder( builder: (context, state) { return state.maybeWhen( orElse: () => const Center( child: Text('No Items'), ), loaded: (products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType) { if (products.isEmpty) { return const Center( child: Text('No Items'), ); } return ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => OrderMenu(data: products[index]), separatorBuilder: (context, index) => const SpaceHeight(1.0), itemCount: products.length, ); }, ); }, ), const SpaceHeight(8.0), ], ), ), ), Padding( padding: const EdgeInsets.all(16.0).copyWith(top: 0), child: Column( children: [ const Divider(), const SpaceHeight(16.0), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Pajak', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.bold, ), ), BlocBuilder( builder: (context, state) { final tax = state.maybeWhen( orElse: () => 0, loaded: (products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType) { if (products.isEmpty) { return 0; } return tax; }); return Text( '$tax %', style: const TextStyle( color: AppColors.primary, fontWeight: FontWeight.w600, ), ); }, ), ], ), const SpaceHeight(16.0), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Sub total', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.bold, ), ), BlocBuilder( builder: (context, state) { final price = state.maybeWhen( orElse: () => 0, loaded: (products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType) { if (products.isEmpty) { return 0; } return products .map((e) => e.product.price! * e.quantity) .reduce((value, element) => value + element); }); return Text( price.currencyFormatRp, style: const TextStyle( color: AppColors.primary, fontWeight: FontWeight.w900, ), ); }, ), ], ), SpaceHeight(16.0), Align( alignment: Alignment.bottomCenter, child: Expanded( child: Button.filled( borderRadius: 12, elevation: 1, onPressed: () { context.push(ConfirmPaymentPage( isTable: widget.table == null ? false : true, table: widget.table, )); }, label: 'Lanjutkan Pembayaran', ), ), ), ], ), ), ], ), ), ), ), ], ), ), ); } } class _IsEmpty extends StatelessWidget { const _IsEmpty(); @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SpaceHeight(40), Assets.icons.noProduct.svg(), const SizedBox(height: 40.0), const Text( 'Belum Ada Produk', textAlign: TextAlign.center, style: TextStyle(fontSize: 16), ), ], ); } }