import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:shimmer/shimmer.dart'; import '../../../application/analytic/profit_sharing_detail_loader/profit_sharing_detail_loader_bloc.dart'; import '../../../common/extension/extension.dart'; import '../../../common/theme/theme.dart'; import '../../../domain/analytic/analytic.dart'; import '../../../injection.dart'; import '../../components/spacer/spacer.dart'; import 'widgets/period_label.dart'; import 'widgets/profit_sharing_allocation.dart'; import 'widgets/profit_sharing_detail_header.dart'; import 'widgets/profit_sharing_sdl.dart'; import 'widgets/profit_sharing_status.dart'; import 'widgets/profit_sharing_subcategories.dart'; @RoutePage() class ProfitSharingDetailPage extends StatelessWidget implements AutoRouteWrapper { final String parentCategoryId; final String parentCategoryName; final DateTime dateFrom; final DateTime dateTo; const ProfitSharingDetailPage({ super.key, required this.parentCategoryId, required this.parentCategoryName, required this.dateFrom, required this.dateTo, }); @override Widget wrappedRoute(BuildContext context) => BlocProvider( create: (_) => getIt()..add(_fetchEvent()), child: this, ); ProfitSharingDetailLoaderEvent _fetchEvent() => ProfitSharingDetailLoaderEvent.fetched( parentCategoryId: parentCategoryId, dateFrom: dateFrom, dateTo: dateTo, ); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColor.background, body: BlocListener< ProfitSharingDetailLoaderBloc, ProfitSharingDetailLoaderState >( listenWhen: (previous, current) => previous.failureOption != current.failureOption, listener: (context, state) { final isNotTeam = state.failureOption.fold( () => false, (f) => f.maybeMap(notTeam: (_) => true, orElse: () => false), ); // Kategori induk dimatikan dari team setelah list dimuat: // kembali ke list dengan hasil `true` supaya list memuat ulang. if (isNotTeam) context.router.maybePop(true); }, child: _buildContent(), ), ); } Widget _buildContent() { return BlocBuilder< ProfitSharingDetailLoaderBloc, ProfitSharingDetailLoaderState >( builder: (context, state) { return RefreshIndicator( backgroundColor: AppColor.white, color: AppColor.primary, onRefresh: () async { context.read().add(_fetchEvent()); await context .read() .stream .firstWhere((s) => !s.isFetching); }, child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverToBoxAdapter( child: ProfitSharingDetailHeader( state: state, fallbackTitle: parentCategoryName, ), ), const SliverToBoxAdapter(child: SpaceHeight(16)), if (state.isFetching) SliverToBoxAdapter(child: _buildShimmer()) else ...[ SliverToBoxAdapter( child: _SummaryCard(summary: state.detail.summary), ), const SliverToBoxAdapter(child: SpaceHeight(16)), SliverToBoxAdapter( child: _buildSdlSection(context, state.detail), ), const SliverToBoxAdapter(child: SpaceHeight(16)), SliverToBoxAdapter( child: ProfitSharingAllocation(budget: state.detail.budget), ), const SliverToBoxAdapter(child: SpaceHeight(16)), SliverToBoxAdapter( child: ProfitSharingSubCategories( categories: state.detail.categories, expandedCategoryId: state.expandedCategoryId, onToggle: (categoryId) => context.read().add( ProfitSharingDetailLoaderEvent.expandedCategoryChanged( categoryId, ), ), ), ), ], const SliverToBoxAdapter(child: SizedBox(height: 100)), ], ), ); }, ); } Widget _buildSdlSection(BuildContext context, ProfitSharingDetail detail) { final summary = detail.summary; return ProfitSharingSdlSection( sdl: summary.sdl, revenue: summary.totalRevenue, percent: summary.ownerFeePercent, breakdownTitle: context.lang.sdl_per_week, items: [ for (final period in detail.budget.weekly.where((e) => e.hasRevenue)) ProfitSharingSdlItem( label: formatPeriodLabel(period.periodStart, period.periodEnd), sdl: period.sdl, revenue: period.revenue, ), ], ); } Widget _buildShimmer() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( children: [ _shimmerBox(height: 160), const SpaceHeight(16), _shimmerBox(height: 240), ], ), ); } Widget _shimmerBox({required double height}) { return Shimmer.fromColors( baseColor: Colors.grey[300]!, highlightColor: Colors.grey[100]!, child: Container( height: height, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), ), ); } } class _SummaryCard extends StatelessWidget { final ProfitSharingCategory summary; const _SummaryCard({required this.summary}); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: AppColor.textLight.withOpacity(0.08), spreadRadius: 1, blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.lang.summary, style: AppStyle.lg.copyWith( fontWeight: FontWeight.w700, color: AppColor.textPrimary, ), ), const SpaceHeight(16), Row( children: [ Expanded( child: ProfitSharingStatTile( icon: Icons.category_rounded, label: context.lang.sub_category, value: '${summary.categoryCount}', color: ProfitSharingColor.subCategory, ), ), const SpaceWidth(10), Expanded( child: ProfitSharingStatTile( icon: Icons.inventory_2_rounded, label: context.lang.products, value: '${summary.productCount}', color: ProfitSharingColor.product, ), ), const SpaceWidth(10), Expanded( child: ProfitSharingStatTile( icon: Icons.receipt_long_rounded, label: context.lang.orders, value: summary.orderCount.thousandFormat, color: ProfitSharingColor.order, ), ), ], ), const SpaceHeight(16), Row( children: [ Expanded( child: Text( context.lang.gross_profit, style: AppStyle.sm.copyWith(color: AppColor.textSecondary), ), ), Text( summary.grossProfit.currencyFormatRp, style: AppStyle.md.copyWith( color: AppColor.textPrimary, fontWeight: FontWeight.w800, ), ), ], ), ], ), ); } }