feat: profit sharing
Build & Deploy iOS to TestFlight / build-and-deploy (push) Canceled after 0s

This commit is contained in:
efrilm
2026-08-21 22:31:54 +07:00
parent f9bfb69254
commit 2e4c77888e
48 changed files with 12553 additions and 162 deletions
@@ -16,6 +16,7 @@ import 'widgets/feature.dart';
import 'widgets/header.dart';
import 'widgets/home_top_products.dart';
import 'widgets/home_warnings.dart';
import 'widgets/profit_sharing_banner.dart';
import 'widgets/stats.dart';
@RoutePage()
@@ -201,6 +202,7 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
HomeFeature(),
HomeProfitSharingBanner(),
HomeWarnings(),
HomeStats(overview: state.dashboard.overview),
HomeTopProducts(
@@ -0,0 +1,89 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hugeicons/hugeicons.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
import '../../../router/app_router.gr.dart';
/// Pintu masuk ke halaman bagi hasil dari beranda.
class HomeProfitSharingBanner extends StatelessWidget {
const HomeProfitSharingBanner({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
child: InkWell(
borderRadius: BorderRadius.circular(AppValue.radius),
onTap: () => context.router.push(const ProfitSharingRoute()),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: AppColor.primaryGradient,
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius: BorderRadius.circular(AppValue.radius),
boxShadow: [
BoxShadow(
color: AppColor.primary.withOpacity(0.25),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.18),
borderRadius: BorderRadius.circular(12),
),
child: const HugeIcon(
icon: HugeIcons.strokeRoundedPieChart,
color: AppColor.textWhite,
size: 24,
),
),
const SpaceWidth(14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.lang.profit_sharing,
style: AppStyle.md.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 2),
Text(
context.lang.profit_sharing_desc,
style: AppStyle.xs.copyWith(
color: AppColor.textWhite.withOpacity(0.8),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SpaceWidth(8),
const Icon(
Icons.chevron_right_rounded,
color: AppColor.textWhite,
size: 24,
),
],
),
),
),
);
}
}
@@ -0,0 +1,238 @@
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/profit_sharing_allocation.dart';
import 'widgets/profit_sharing_detail_header.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<ProfitSharingDetailLoaderBloc>()..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:
BlocBuilder<
ProfitSharingDetailLoaderBloc,
ProfitSharingDetailLoaderState
>(
builder: (context, state) {
return RefreshIndicator(
backgroundColor: AppColor.white,
color: AppColor.primary,
onRefresh: () async {
context.read<ProfitSharingDetailLoaderBloc>().add(
_fetchEvent(),
);
await context
.read<ProfitSharingDetailLoaderBloc>()
.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: ProfitSharingAllocation(
budget: state.detail.budget,
),
),
const SliverToBoxAdapter(child: SpaceHeight(16)),
SliverToBoxAdapter(
child: ProfitSharingSubCategories(
categories: state.detail.categories,
expandedCategoryId: state.expandedCategoryId,
onToggle: (categoryId) =>
context.read<ProfitSharingDetailLoaderBloc>().add(
ProfitSharingDetailLoaderEvent.expandedCategoryChanged(
categoryId,
),
),
),
),
],
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
),
);
},
),
);
}
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: ProfitSharingMetric(
label: context.lang.sub_category,
value: '${summary.categoryCount}',
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.products,
value: '${summary.productCount}',
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.orders,
value: summary.orderCount.thousandFormat,
),
),
],
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: Divider(height: 1, color: AppColor.borderLight),
),
Row(
children: [
Expanded(
child: ProfitSharingMetric(
label: context.lang.std_hpp,
value: formatPercent(summary.standardHppPercentage),
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.real_hpp,
value: formatPercent(summary.realHppPercentage),
valueColor: statusColor(summary.realHppPercentage),
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.gross_profit,
value: summary.grossProfit.currencyFormatRp,
),
),
],
),
],
),
);
}
}
@@ -0,0 +1,203 @@
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_loader/profit_sharing_loader_bloc.dart';
import '../../../common/theme/theme.dart';
import '../../../injection.dart';
import '../../components/field/date_range_picker_field.dart';
import '../../components/spacer/spacer.dart';
import '../../router/app_router.gr.dart';
import 'widgets/profit_sharing_allocation.dart';
import 'widgets/profit_sharing_categories.dart';
import 'widgets/profit_sharing_header.dart';
import 'widgets/profit_sharing_periods.dart';
@RoutePage()
class ProfitSharingPage extends StatefulWidget implements AutoRouteWrapper {
const ProfitSharingPage({super.key});
@override
State<ProfitSharingPage> createState() => _ProfitSharingPageState();
@override
Widget wrappedRoute(BuildContext context) => BlocProvider(
create: (_) =>
getIt<ProfitSharingLoaderBloc>()
..add(ProfitSharingLoaderEvent.fetched()),
child: this,
);
}
class _ProfitSharingPageState extends State<ProfitSharingPage>
with SingleTickerProviderStateMixin {
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
@override
void initState() {
super.initState();
_fadeController = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
);
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(parent: _fadeController, curve: Curves.easeIn));
_fadeController.forward();
}
@override
void dispose() {
_fadeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body: BlocListener<ProfitSharingLoaderBloc, ProfitSharingLoaderState>(
listenWhen: (previous, current) =>
previous.dateFrom != current.dateFrom ||
previous.dateTo != current.dateTo,
listener: (context, state) {
context.read<ProfitSharingLoaderBloc>().add(
const ProfitSharingLoaderEvent.fetched(),
);
},
child: BlocBuilder<ProfitSharingLoaderBloc, ProfitSharingLoaderState>(
builder: (context, state) {
return RefreshIndicator(
backgroundColor: AppColor.white,
color: AppColor.primary,
onRefresh: () async {
context.read<ProfitSharingLoaderBloc>().add(
const ProfitSharingLoaderEvent.fetched(),
);
await context.read<ProfitSharingLoaderBloc>().stream.firstWhere(
(s) => !s.isFetching,
);
},
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingHeader(state: state),
),
),
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.all(16),
child: DateRangePickerField(
startDate: state.dateFrom,
endDate: state.dateTo,
onChanged: (startDate, endDate) {
if (startDate == null || endDate == null) return;
context.read<ProfitSharingLoaderBloc>().add(
ProfitSharingLoaderEvent.rangeDateChanged(
startDate,
endDate,
),
);
},
),
),
),
),
if (state.isFetching)
SliverToBoxAdapter(child: _buildShimmer())
else ...[
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingCategories(
categories: state.profitSharing.categories,
onCategoryTap: (category) => context.router.push(
ProfitSharingDetailRoute(
parentCategoryId: category.parentCategoryId,
parentCategoryName: category.parentCategoryName,
dateFrom: state.dateFrom,
dateTo: state.dateTo,
),
),
),
),
),
const SliverToBoxAdapter(child: SpaceHeight(16)),
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingAllocation(
budget: state.profitSharing.budget,
),
),
),
const SliverToBoxAdapter(child: SpaceHeight(16)),
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingPeriods(
state: state,
onPeriodTypeChanged: (type) {
context.read<ProfitSharingLoaderBloc>().add(
ProfitSharingLoaderEvent.periodTypeChanged(type),
);
},
),
),
),
],
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
),
);
},
),
),
);
}
Widget _buildShimmer() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
_shimmerBox(height: 240),
const SpaceHeight(16),
_shimmerBox(height: 300),
],
),
);
}
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),
),
),
);
}
}
@@ -0,0 +1,31 @@
import 'package:intl/intl.dart';
/// Label rentang periode ringkas, contoh: "27 Jul - 2 Agu 2026".
String formatPeriodLabel(DateTime from, DateTime to) {
final dayMonth = DateFormat('d MMM', 'id_ID');
final dayMonthYear = DateFormat('d MMM yyyy', 'id_ID');
if (from.year == to.year && from.month == to.month && from.day == to.day) {
return dayMonthYear.format(from);
}
if (from.year == to.year) {
return '${dayMonth.format(from)} - ${dayMonthYear.format(to)}';
}
return '${dayMonthYear.format(from)} - ${dayMonthYear.format(to)}';
}
/// Label bulan dari format API "2026-08" menjadi "Agustus 2026".
String formatMonthLabel(String month, DateTime fallback) {
final parts = month.split('-');
if (parts.length == 2) {
final year = int.tryParse(parts[0]);
final monthNumber = int.tryParse(parts[1]);
if (year != null && monthNumber != null) {
return DateFormat(
'MMMM yyyy',
'id_ID',
).format(DateTime(year, monthNumber));
}
}
return DateFormat('MMMM yyyy', 'id_ID').format(fallback);
}
@@ -0,0 +1,195 @@
import 'package:flutter/material.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../../domain/analytic/analytic.dart';
import '../../../components/spacer/spacer.dart';
/// Warna tetap untuk tiap pos bagi hasil supaya konsisten di semua bagian.
class ProfitSharingColor {
static const Color purchase = Color(0xFF2196F3);
static const Color owner = Color(0xFF4CAF50);
static const Color team = Color(0xFFFF9800);
}
class ProfitSharingAllocation extends StatelessWidget {
final ProfitSharingBudget budget;
const ProfitSharingAllocation({super.key, required this.budget});
@override
Widget build(BuildContext context) {
final total = budget.total;
final percentages = budget.percentages;
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.profit_sharing_allocation,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
),
),
const SpaceHeight(16),
ProfitSharingBar(
purchase: percentages.purchase,
owner: percentages.owner,
team: percentages.team,
),
const SpaceHeight(20),
_row(
context: context,
color: ProfitSharingColor.purchase,
label: context.lang.share_purchase,
percentage: percentages.purchase,
amount: total.limitPurchase,
),
const Divider(height: 24, color: AppColor.borderLight),
_row(
context: context,
color: ProfitSharingColor.owner,
label: context.lang.share_owner,
percentage: percentages.owner,
amount: total.limitOwner,
),
const Divider(height: 24, color: AppColor.borderLight),
_row(
context: context,
color: ProfitSharingColor.team,
label: context.lang.share_team,
percentage: percentages.team,
amount: total.limitTeam,
),
],
),
);
}
Widget _row({
required BuildContext context,
required Color color,
required String label,
required double percentage,
required int amount,
}) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
const SpaceWidth(10),
Text(
label,
style: AppStyle.md.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w600,
),
),
const SpaceWidth(8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: color.withOpacity(0.12),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'${percentage.toStringAsFixed(percentage % 1 == 0 ? 0 : 1)}%',
style: AppStyle.xs.copyWith(
color: color,
fontWeight: FontWeight.w700,
),
),
),
const Spacer(),
Flexible(
child: Text(
amount.currencyFormatRp,
textAlign: TextAlign.right,
style: AppStyle.md.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w700,
),
overflow: TextOverflow.ellipsis,
),
),
],
);
}
}
/// Bar proporsi belanja / owner / tim.
class ProfitSharingBar extends StatelessWidget {
final double purchase;
final double owner;
final double team;
final double height;
const ProfitSharingBar({
super.key,
required this.purchase,
required this.owner,
required this.team,
this.height = 12,
});
@override
Widget build(BuildContext context) {
final total = purchase + owner + team;
if (total <= 0) {
return Container(
height: height,
decoration: BoxDecoration(
color: AppColor.borderLight,
borderRadius: BorderRadius.circular(height),
),
);
}
return ClipRRect(
borderRadius: BorderRadius.circular(height),
child: SizedBox(
height: height,
child: Row(
children: [
_segment(purchase, ProfitSharingColor.purchase),
_segment(owner, ProfitSharingColor.owner),
_segment(team, ProfitSharingColor.team),
].whereType<Widget>().toList(),
),
),
);
}
/// Segmen dilewati saat porsinya 0 supaya Expanded tidak dipakai dengan flex 0.
Widget? _segment(double value, Color color) {
final flex = (value * 100).round();
if (flex <= 0) return null;
return Expanded(
flex: flex,
child: Container(color: color),
);
}
}
@@ -0,0 +1,324 @@
import 'package:flutter/material.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../../domain/analytic/analytic.dart';
import '../../../components/spacer/spacer.dart';
import 'profit_sharing_status.dart';
/// Ringkasan omzet & HPP per kategori induk (parent category).
/// Tiap kartu bisa diketuk untuk melihat rincian sub kategori & produknya.
class ProfitSharingCategories extends StatelessWidget {
final List<ProfitSharingCategory> categories;
final ValueChanged<ProfitSharingCategory> onCategoryTap;
const ProfitSharingCategories({
super.key,
required this.categories,
required this.onCategoryTap,
});
@override
Widget build(BuildContext context) {
final sorted = [...categories]
..sort((a, b) => b.totalRevenue.compareTo(a.totalRevenue));
final totalRevenue = sorted.fold<int>(0, (sum, e) => sum + e.totalRevenue);
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.parent_category_summary,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
),
),
const SizedBox(height: 2),
Text(
context.lang.tap_row_for_detail,
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
),
const SpaceHeight(16),
if (sorted.isEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Center(
child: Text(
context.lang.no_data_yet,
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
),
),
)
else ...[
...sorted.map(
(category) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: _CategoryCard(
category: category,
share: totalRevenue == 0
? 0
: category.totalRevenue / totalRevenue,
onTap: () => onCategoryTap(category),
),
),
),
_TotalCard(categories: sorted),
],
],
),
);
}
}
class _CategoryCard extends StatelessWidget {
final ProfitSharingCategory category;
final double share;
final VoidCallback onTap;
const _CategoryCard({
required this.category,
required this.share,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColor.background,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.borderLight),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
category.parentCategoryName,
style: AppStyle.md.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w700,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SpaceWidth(8),
ProfitSharingStatusBadge(
realHppPercentage: category.realHppPercentage,
),
const Icon(
Icons.chevron_right_rounded,
color: AppColor.textSecondary,
size: 20,
),
],
),
const SpaceHeight(8),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Text(
category.totalRevenue.currencyFormatRp,
style: AppStyle.lg.copyWith(
color: AppColor.primary,
fontWeight: FontWeight.w800,
),
),
),
Text(
formatPercent(share * 100),
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
),
),
],
),
const SpaceHeight(8),
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: LinearProgressIndicator(
value: share.clamp(0.0, 1.0),
minHeight: 6,
backgroundColor: AppColor.borderLight,
valueColor: const AlwaysStoppedAnimation<Color>(
AppColor.primary,
),
),
),
const SpaceHeight(12),
Row(
children: [
Expanded(
child: ProfitSharingMetric(
label: context.lang.qty,
value: category.totalQuantity.thousandFormat,
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.products,
value: '${category.productCount}',
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.sub_category,
value: '${category.categoryCount}',
),
),
],
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 10),
child: Divider(height: 1, color: AppColor.border),
),
Row(
children: [
Expanded(
child: ProfitSharingMetric(
label: context.lang.std_hpp,
value: formatPercent(category.standardHppPercentage),
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.real_hpp,
value: formatPercent(category.realHppPercentage),
valueColor: statusColor(category.realHppPercentage),
),
),
],
),
],
),
),
);
}
}
class _TotalCard extends StatelessWidget {
final List<ProfitSharingCategory> categories;
const _TotalCard({required this.categories});
@override
Widget build(BuildContext context) {
final totalQuantity = categories.fold<int>(
0,
(sum, e) => sum + e.totalQuantity,
);
final totalRevenue = categories.fold<int>(
0,
(sum, e) => sum + e.totalRevenue,
);
final totalStandardHpp = categories.fold<int>(
0,
(sum, e) => sum + e.totalStandardHpp,
);
final totalFifoHpp = categories.fold<int>(
0,
(sum, e) => sum + e.totalFifoHpp,
);
double percentOf(int hpp) =>
totalRevenue == 0 ? 0 : (hpp / totalRevenue) * 100;
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColor.primary.withOpacity(0.06),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
context.lang.grand_total,
style: AppStyle.md.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w800,
),
),
),
Text(
totalRevenue.currencyFormatRp,
style: AppStyle.lg.copyWith(
color: AppColor.primary,
fontWeight: FontWeight.w900,
),
),
],
),
const SpaceHeight(12),
Row(
children: [
Expanded(
child: ProfitSharingMetric(
label: context.lang.qty,
value: totalQuantity.thousandFormat,
isBold: true,
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.std_hpp,
value: formatPercent(percentOf(totalStandardHpp)),
isBold: true,
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.real_hpp,
value: formatPercent(percentOf(totalFifoHpp)),
isBold: true,
valueColor: statusColor(percentOf(totalFifoHpp)),
),
),
],
),
],
),
);
}
}
@@ -0,0 +1,195 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.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 '../../../components/spacer/spacer.dart';
import 'period_label.dart';
import 'profit_sharing_status.dart';
class ProfitSharingDetailHeader extends StatelessWidget {
final ProfitSharingDetailLoaderState state;
final String fallbackTitle;
const ProfitSharingDetailHeader({
super.key,
required this.state,
required this.fallbackTitle,
});
@override
Widget build(BuildContext context) {
final detail = state.detail;
final summary = detail.summary;
final title = detail.parentCategoryName.isNotEmpty
? detail.parentCategoryName
: fallbackTitle;
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: AppColor.primaryGradient,
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24),
),
),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
GestureDetector(
onTap: () => context.router.maybePop(),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.15),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
Icons.chevron_left_rounded,
color: AppColor.textWhite,
size: 24,
),
),
),
const SpaceWidth(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: AppStyle.xl.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w700,
fontSize: 20,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
formatPeriodLabel(state.dateFrom, state.dateTo),
style: AppStyle.sm.copyWith(
color: AppColor.textWhite.withOpacity(0.75),
fontSize: 12,
),
),
],
),
),
],
),
const SpaceHeight(24),
if (state.isFetching)
_shimmerBox(width: 220, height: 36)
else
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Text(
summary.totalRevenue.currencyFormatRp,
style: AppStyle.h1.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w900,
fontSize: 30,
),
),
),
ProfitSharingStatusBadge(
realHppPercentage: summary.realHppPercentage,
onDarkBackground: true,
),
],
),
const SpaceHeight(4),
Text(
context.lang.total_revenue,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite.withOpacity(0.75),
fontSize: 13,
),
),
const SpaceHeight(16),
if (state.isFetching)
Row(
children: [
_shimmerBox(width: 120, height: 32, radius: 20),
const SpaceWidth(8),
_shimmerBox(width: 120, height: 32, radius: 20),
],
)
else
Wrap(
spacing: 8,
runSpacing: 8,
children: [
_chip(
'${context.lang.qty} ${summary.totalQuantity.thousandFormat}',
),
_chip(context.lang.order_count_label(summary.orderCount)),
],
),
],
),
),
),
);
}
Widget _chip(String label) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColor.textWhite.withOpacity(0.25)),
),
child: Text(
label,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
);
}
Widget _shimmerBox({
required double width,
required double height,
double radius = 8,
}) {
return Shimmer.fromColors(
baseColor: AppColor.textWhite.withOpacity(0.3),
highlightColor: AppColor.textWhite.withOpacity(0.6),
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.3),
borderRadius: BorderRadius.circular(radius),
),
),
);
}
}
@@ -0,0 +1,163 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
import '../../../../application/analytic/profit_sharing_loader/profit_sharing_loader_bloc.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class ProfitSharingHeader extends StatelessWidget {
final ProfitSharingLoaderState state;
const ProfitSharingHeader({super.key, required this.state});
@override
Widget build(BuildContext context) {
final budget = state.profitSharing.budget;
final outletLabel = state.profitSharing.outletName.isNotEmpty
? state.profitSharing.outletName
: context.lang.all_outlets;
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: AppColor.primaryGradient,
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24),
),
),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
if (context.router.canPop()) ...[
GestureDetector(
onTap: () => context.router.maybePop(),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.15),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
Icons.chevron_left_rounded,
color: AppColor.textWhite,
size: 24,
),
),
),
const SpaceWidth(12),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.lang.profit_sharing,
style: AppStyle.xl.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w700,
fontSize: 20,
),
),
const SizedBox(height: 2),
Text(
outletLabel,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite.withOpacity(0.75),
fontSize: 12,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
const SpaceHeight(24),
state.isFetching
? _shimmerBox(width: 220, height: 36)
: Text(
budget.total.revenue.currencyFormatRp,
style: AppStyle.h1.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w900,
fontSize: 32,
),
),
const SpaceHeight(4),
Text(
context.lang.total_revenue,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite.withOpacity(0.75),
fontSize: 13,
),
),
const SpaceHeight(16),
state.isFetching
? _shimmerBox(width: 140, height: 32, radius: 20)
: _chip(
context.lang.order_count_label(budget.total.orderCount),
),
],
),
),
),
);
}
Widget _chip(String label) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColor.textWhite.withOpacity(0.25)),
),
child: Text(
label,
style: AppStyle.sm.copyWith(
color: AppColor.textWhite,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
);
}
Widget _shimmerBox({
required double width,
required double height,
double radius = 8,
}) {
return Shimmer.fromColors(
baseColor: AppColor.textWhite.withOpacity(0.3),
highlightColor: AppColor.textWhite.withOpacity(0.6),
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
color: AppColor.textWhite.withOpacity(0.3),
borderRadius: BorderRadius.circular(radius),
),
),
);
}
}
@@ -0,0 +1,270 @@
import 'package:flutter/material.dart';
import '../../../../application/analytic/profit_sharing_loader/profit_sharing_loader_bloc.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../../domain/analytic/analytic.dart';
import '../../../components/spacer/spacer.dart';
import 'period_label.dart';
import 'profit_sharing_allocation.dart';
class ProfitSharingPeriods extends StatelessWidget {
final ProfitSharingLoaderState state;
final ValueChanged<ProfitSharingPeriodType> onPeriodTypeChanged;
const ProfitSharingPeriods({
super.key,
required this.state,
required this.onPeriodTypeChanged,
});
@override
Widget build(BuildContext context) {
final periods = state.periods;
final percentages = state.profitSharing.budget.percentages;
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.period_breakdown,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
),
),
const SpaceHeight(16),
_tabs(context),
const SpaceHeight(16),
if (periods.isEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Center(
child: Text(
context.lang.no_data_yet,
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
),
),
)
else
...periods.map(
(period) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: _PeriodCard(period: period, percentages: percentages),
),
),
],
),
);
}
Widget _tabs(BuildContext context) {
return Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: AppColor.background,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
_tab(
context: context,
label: context.lang.weekly,
type: ProfitSharingPeriodType.weekly,
),
_tab(
context: context,
label: context.lang.monthly,
type: ProfitSharingPeriodType.monthly,
),
],
),
);
}
Widget _tab({
required BuildContext context,
required String label,
required ProfitSharingPeriodType type,
}) {
final isSelected = state.periodType == type;
return Expanded(
child: GestureDetector(
onTap: () => onPeriodTypeChanged(type),
behavior: HitTestBehavior.opaque,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isSelected ? AppColor.primary : Colors.transparent,
borderRadius: BorderRadius.circular(10),
),
child: Text(
label,
textAlign: TextAlign.center,
style: AppStyle.sm.copyWith(
color: isSelected ? AppColor.textWhite : AppColor.textSecondary,
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
),
),
),
),
);
}
}
class _PeriodCard extends StatelessWidget {
final ProfitSharingPeriod period;
final ProfitSharingPercentage percentages;
const _PeriodCard({required this.period, required this.percentages});
@override
Widget build(BuildContext context) {
final isMonthly = period.month.isNotEmpty;
final title = isMonthly
? formatMonthLabel(period.month, period.periodStart)
: formatPeriodLabel(period.periodStart, period.periodEnd);
final subtitle = isMonthly
? '${formatPeriodLabel(period.periodStart, period.periodEnd)} - ${context.lang.week_count(period.weekCount)}'
: context.lang.order_count_label(period.orderCount);
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.background,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.borderLight),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
),
),
],
),
),
const SpaceWidth(8),
Text(
period.revenue.currencyFormatRp,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w800,
color: AppColor.primary,
),
),
],
),
const SpaceHeight(12),
ProfitSharingBar(
purchase: percentages.purchase,
owner: percentages.owner,
team: percentages.team,
height: 6,
),
const SpaceHeight(12),
Row(
children: [
_share(
context.lang.share_purchase,
period.limitPurchase,
ProfitSharingColor.purchase,
),
_share(
context.lang.share_owner,
period.limitOwner,
ProfitSharingColor.owner,
),
_share(
context.lang.share_team,
period.limitTeam,
ProfitSharingColor.team,
),
],
),
],
),
);
}
Widget _share(String label, int amount, Color color) {
return Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
const SpaceWidth(6),
Flexible(
child: Text(
label,
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 4),
Text(
amount.currencyFormatRp,
style: AppStyle.xs.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w700,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
);
}
}
@@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
/// Ambang kesehatan berdasarkan % HPP riil terhadap omzet.
/// Ubah di sini kalau target margin bisnis berubah.
class HppThreshold {
static const double healthy = 70;
static const double watch = 80;
}
String formatPercent(double value) => '${value.toStringAsFixed(1)}%';
Color statusColor(double realHppPercentage) {
if (realHppPercentage <= HppThreshold.healthy) return AppColor.success;
if (realHppPercentage <= HppThreshold.watch) return AppColor.warning;
return AppColor.error;
}
String statusLabel(BuildContext context, double realHppPercentage) {
if (realHppPercentage <= HppThreshold.healthy) {
return context.lang.status_healthy;
}
if (realHppPercentage <= HppThreshold.watch) return context.lang.status_watch;
return context.lang.status_critical;
}
/// Pill status margin (Sehat / Waspada / Tidak Sehat).
class ProfitSharingStatusBadge extends StatelessWidget {
final double realHppPercentage;
final bool onDarkBackground;
const ProfitSharingStatusBadge({
super.key,
required this.realHppPercentage,
this.onDarkBackground = false,
});
@override
Widget build(BuildContext context) {
final color = statusColor(realHppPercentage);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: onDarkBackground
? AppColor.textWhite.withOpacity(0.18)
: color.withOpacity(0.12),
borderRadius: BorderRadius.circular(20),
border: onDarkBackground
? Border.all(color: AppColor.textWhite.withOpacity(0.3))
: null,
),
child: Text(
statusLabel(context, realHppPercentage),
style: AppStyle.xs.copyWith(
color: onDarkBackground ? AppColor.textWhite : color,
fontWeight: FontWeight.w700,
),
),
);
}
}
/// Label kecil dua baris: judul di atas, nilai di bawah.
class ProfitSharingMetric extends StatelessWidget {
final String label;
final String value;
final Color? valueColor;
final bool isBold;
const ProfitSharingMetric({
super.key,
required this.label,
required this.value,
this.valueColor,
this.isBold = false,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
value,
style: AppStyle.sm.copyWith(
color: valueColor ?? AppColor.textPrimary,
fontWeight: isBold ? FontWeight.w800 : FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
);
}
}
@@ -0,0 +1,311 @@
import 'package:flutter/material.dart';
import '../../../../common/extension/extension.dart';
import '../../../../common/theme/theme.dart';
import '../../../../domain/analytic/analytic.dart';
import '../../../components/spacer/spacer.dart';
import 'profit_sharing_status.dart';
/// Daftar sub kategori; tiap kartu bisa dibuka untuk melihat produknya.
class ProfitSharingSubCategories extends StatelessWidget {
final List<ProfitSharingSubCategory> categories;
final String expandedCategoryId;
final ValueChanged<String> onToggle;
const ProfitSharingSubCategories({
super.key,
required this.categories,
required this.expandedCategoryId,
required this.onToggle,
});
@override
Widget build(BuildContext context) {
final sorted = [...categories]
..sort((a, b) => b.totalRevenue.compareTo(a.totalRevenue));
final totalRevenue = sorted.fold<int>(0, (sum, e) => sum + e.totalRevenue);
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.sub_category,
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
),
),
const SpaceHeight(16),
if (sorted.isEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Center(
child: Text(
context.lang.no_data_yet,
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
),
),
)
else
...sorted.map(
(category) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: _SubCategoryCard(
category: category,
share: totalRevenue == 0
? 0
: category.totalRevenue / totalRevenue,
isExpanded: expandedCategoryId == category.categoryId,
onToggle: () => onToggle(category.categoryId),
),
),
),
],
),
);
}
}
class _SubCategoryCard extends StatelessWidget {
final ProfitSharingSubCategory category;
final double share;
final bool isExpanded;
final VoidCallback onToggle;
const _SubCategoryCard({
required this.category,
required this.share,
required this.isExpanded,
required this.onToggle,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColor.background,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.borderLight),
),
child: Column(
children: [
InkWell(
onTap: onToggle,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
category.categoryName,
style: AppStyle.md.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w700,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SpaceWidth(8),
Text(
category.totalRevenue.currencyFormatRp,
style: AppStyle.md.copyWith(
color: AppColor.primary,
fontWeight: FontWeight.w800,
),
),
AnimatedRotation(
turns: isExpanded ? 0.5 : 0,
duration: const Duration(milliseconds: 200),
child: const Icon(
Icons.keyboard_arrow_down_rounded,
color: AppColor.textSecondary,
size: 22,
),
),
],
),
const SpaceHeight(8),
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: LinearProgressIndicator(
value: share.clamp(0.0, 1.0),
minHeight: 5,
backgroundColor: AppColor.borderLight,
valueColor: const AlwaysStoppedAnimation<Color>(
AppColor.primary,
),
),
),
const SpaceHeight(12),
Row(
children: [
Expanded(
child: ProfitSharingMetric(
label: context.lang.qty,
value: category.totalQuantity.thousandFormat,
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.products,
value: '${category.productCount}',
),
),
Expanded(
child: ProfitSharingMetric(
label: context.lang.real_hpp,
value: formatPercent(category.realHppPercentage),
valueColor: statusColor(category.realHppPercentage),
),
),
],
),
],
),
),
),
AnimatedCrossFade(
duration: const Duration(milliseconds: 200),
crossFadeState: isExpanded
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: _ProductList(products: category.products),
secondChild: const SizedBox(width: double.infinity),
),
],
),
);
}
}
class _ProductList extends StatelessWidget {
final List<ProfitSharingProduct> products;
const _ProductList({required this.products});
@override
Widget build(BuildContext context) {
final sorted = [...products]
..sort((a, b) => b.revenue.compareTo(a.revenue));
return Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(14, 0, 14, 14),
child: Column(
children: [
const Divider(height: 1, color: AppColor.border),
const SpaceHeight(10),
...sorted.map((product) => _ProductRow(product: product)),
],
),
);
}
}
class _ProductRow extends StatelessWidget {
final ProfitSharingProduct product;
const _ProductRow({required this.product});
@override
Widget build(BuildContext context) {
final color = statusColor(product.realHppPercentage);
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(top: 4),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: AppColor.primary.withOpacity(0.08),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'${product.quantitySold}x',
style: AppStyle.xs.copyWith(
color: AppColor.primary,
fontWeight: FontWeight.w700,
),
),
),
const SpaceWidth(10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.productName,
style: AppStyle.sm.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w600,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
'${product.productSku} · @${product.averagePrice.round().currencyFormatRp}',
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SpaceWidth(8),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
product.revenue.currencyFormatRp,
style: AppStyle.sm.copyWith(
color: AppColor.textPrimary,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 2),
Text(
'${context.lang.real_hpp} ${formatPercent(product.realHppPercentage)}',
style: AppStyle.xs.copyWith(
color: color,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
);
}
}
+4
View File
@@ -50,6 +50,10 @@ class AppRouter extends RootStackRouter {
// Finance page
AutoRoute(page: FinanceRoute.page),
// Profit Sharing
AutoRoute(page: ProfitSharingRoute.page),
AutoRoute(page: ProfitSharingDetailRoute.page),
// Exclusive Summary
AutoRoute(page: ExclusiveSummaryRoute.page),
+214 -127
View File
@@ -9,8 +9,8 @@
// coverage:ignore-file
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:apskel_owner_flutter/domain/order/order.dart' as _i29;
import 'package:apskel_owner_flutter/domain/user/user.dart' as _i30;
import 'package:apskel_owner_flutter/domain/order/order.dart' as _i31;
import 'package:apskel_owner_flutter/domain/user/user.dart' as _i32;
import 'package:apskel_owner_flutter/presentation/pages/about_app/about_app_page.dart'
as _i1;
import 'package:apskel_owner_flutter/presentation/pages/auth/login/login_page.dart'
@@ -53,28 +53,32 @@ import 'package:apskel_owner_flutter/presentation/pages/profile/pages/profile_ed
as _i20;
import 'package:apskel_owner_flutter/presentation/pages/profile/profile_page.dart'
as _i21;
import 'package:apskel_owner_flutter/presentation/pages/purchase/purchase_page.dart'
import 'package:apskel_owner_flutter/presentation/pages/profit_sharing/profit_sharing_detail_page.dart'
as _i22;
import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart'
import 'package:apskel_owner_flutter/presentation/pages/profit_sharing/profit_sharing_page.dart'
as _i23;
import 'package:apskel_owner_flutter/presentation/pages/sales/sales_page.dart'
import 'package:apskel_owner_flutter/presentation/pages/purchase/purchase_page.dart'
as _i24;
import 'package:apskel_owner_flutter/presentation/pages/schedule/schedule_page.dart'
import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart'
as _i25;
import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart'
import 'package:apskel_owner_flutter/presentation/pages/sales/sales_page.dart'
as _i26;
import 'package:auto_route/auto_route.dart' as _i27;
import 'package:flutter/material.dart' as _i28;
import 'package:apskel_owner_flutter/presentation/pages/schedule/schedule_page.dart'
as _i27;
import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart'
as _i28;
import 'package:auto_route/auto_route.dart' as _i29;
import 'package:flutter/material.dart' as _i30;
/// generated route for
/// [_i1.AboutAppPage]
class AboutAppRoute extends _i27.PageRouteInfo<void> {
const AboutAppRoute({List<_i27.PageRouteInfo>? children})
class AboutAppRoute extends _i29.PageRouteInfo<void> {
const AboutAppRoute({List<_i29.PageRouteInfo>? children})
: super(AboutAppRoute.name, initialChildren: children);
static const String name = 'AboutAppRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i1.AboutAppPage();
@@ -84,13 +88,13 @@ class AboutAppRoute extends _i27.PageRouteInfo<void> {
/// generated route for
/// [_i2.ComingSoonPage]
class ComingSoonRoute extends _i27.PageRouteInfo<void> {
const ComingSoonRoute({List<_i27.PageRouteInfo>? children})
class ComingSoonRoute extends _i29.PageRouteInfo<void> {
const ComingSoonRoute({List<_i29.PageRouteInfo>? children})
: super(ComingSoonRoute.name, initialChildren: children);
static const String name = 'ComingSoonRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i2.ComingSoonPage();
@@ -100,29 +104,29 @@ class ComingSoonRoute extends _i27.PageRouteInfo<void> {
/// generated route for
/// [_i3.CustomerPage]
class CustomerRoute extends _i27.PageRouteInfo<void> {
const CustomerRoute({List<_i27.PageRouteInfo>? children})
class CustomerRoute extends _i29.PageRouteInfo<void> {
const CustomerRoute({List<_i29.PageRouteInfo>? children})
: super(CustomerRoute.name, initialChildren: children);
static const String name = 'CustomerRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i3.CustomerPage());
return _i29.WrappedRoute(child: const _i3.CustomerPage());
},
);
}
/// generated route for
/// [_i4.DailyTasksFormPage]
class DailyTasksFormRoute extends _i27.PageRouteInfo<void> {
const DailyTasksFormRoute({List<_i27.PageRouteInfo>? children})
class DailyTasksFormRoute extends _i29.PageRouteInfo<void> {
const DailyTasksFormRoute({List<_i29.PageRouteInfo>? children})
: super(DailyTasksFormRoute.name, initialChildren: children);
static const String name = 'DailyTasksFormRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i4.DailyTasksFormPage();
@@ -132,32 +136,32 @@ class DailyTasksFormRoute extends _i27.PageRouteInfo<void> {
/// generated route for
/// [_i5.DownloadReportPage]
class DownloadReportRoute extends _i27.PageRouteInfo<void> {
const DownloadReportRoute({List<_i27.PageRouteInfo>? children})
class DownloadReportRoute extends _i29.PageRouteInfo<void> {
const DownloadReportRoute({List<_i29.PageRouteInfo>? children})
: super(DownloadReportRoute.name, initialChildren: children);
static const String name = 'DownloadReportRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i5.DownloadReportPage());
return _i29.WrappedRoute(child: const _i5.DownloadReportPage());
},
);
}
/// generated route for
/// [_i6.ErrorPage]
class ErrorRoute extends _i27.PageRouteInfo<ErrorRouteArgs> {
class ErrorRoute extends _i29.PageRouteInfo<ErrorRouteArgs> {
ErrorRoute({
_i28.Key? key,
_i30.Key? key,
String? title,
String? message,
_i28.VoidCallback? onRetry,
_i28.VoidCallback? onBack,
_i30.VoidCallback? onRetry,
_i30.VoidCallback? onBack,
String? errorCode,
_i28.IconData? errorIcon,
List<_i27.PageRouteInfo>? children,
_i30.IconData? errorIcon,
List<_i29.PageRouteInfo>? children,
}) : super(
ErrorRoute.name,
args: ErrorRouteArgs(
@@ -174,7 +178,7 @@ class ErrorRoute extends _i27.PageRouteInfo<ErrorRouteArgs> {
static const String name = 'ErrorRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
final args = data.argsAs<ErrorRouteArgs>(
@@ -204,19 +208,19 @@ class ErrorRouteArgs {
this.errorIcon,
});
final _i28.Key? key;
final _i30.Key? key;
final String? title;
final String? message;
final _i28.VoidCallback? onRetry;
final _i30.VoidCallback? onRetry;
final _i28.VoidCallback? onBack;
final _i30.VoidCallback? onBack;
final String? errorCode;
final _i28.IconData? errorIcon;
final _i30.IconData? errorIcon;
@override
String toString() {
@@ -226,77 +230,77 @@ class ErrorRouteArgs {
/// generated route for
/// [_i7.ExclusiveSummaryPage]
class ExclusiveSummaryRoute extends _i27.PageRouteInfo<void> {
const ExclusiveSummaryRoute({List<_i27.PageRouteInfo>? children})
class ExclusiveSummaryRoute extends _i29.PageRouteInfo<void> {
const ExclusiveSummaryRoute({List<_i29.PageRouteInfo>? children})
: super(ExclusiveSummaryRoute.name, initialChildren: children);
static const String name = 'ExclusiveSummaryRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i7.ExclusiveSummaryPage());
return _i29.WrappedRoute(child: const _i7.ExclusiveSummaryPage());
},
);
}
/// generated route for
/// [_i8.FinancePage]
class FinanceRoute extends _i27.PageRouteInfo<void> {
const FinanceRoute({List<_i27.PageRouteInfo>? children})
class FinanceRoute extends _i29.PageRouteInfo<void> {
const FinanceRoute({List<_i29.PageRouteInfo>? children})
: super(FinanceRoute.name, initialChildren: children);
static const String name = 'FinanceRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i8.FinancePage());
return _i29.WrappedRoute(child: const _i8.FinancePage());
},
);
}
/// generated route for
/// [_i9.HomePage]
class HomeRoute extends _i27.PageRouteInfo<void> {
const HomeRoute({List<_i27.PageRouteInfo>? children})
class HomeRoute extends _i29.PageRouteInfo<void> {
const HomeRoute({List<_i29.PageRouteInfo>? children})
: super(HomeRoute.name, initialChildren: children);
static const String name = 'HomeRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i9.HomePage());
return _i29.WrappedRoute(child: const _i9.HomePage());
},
);
}
/// generated route for
/// [_i10.InventoryPage]
class InventoryRoute extends _i27.PageRouteInfo<void> {
const InventoryRoute({List<_i27.PageRouteInfo>? children})
class InventoryRoute extends _i29.PageRouteInfo<void> {
const InventoryRoute({List<_i29.PageRouteInfo>? children})
: super(InventoryRoute.name, initialChildren: children);
static const String name = 'InventoryRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i10.InventoryPage());
return _i29.WrappedRoute(child: const _i10.InventoryPage());
},
);
}
/// generated route for
/// [_i11.LanguagePage]
class LanguageRoute extends _i27.PageRouteInfo<void> {
const LanguageRoute({List<_i27.PageRouteInfo>? children})
class LanguageRoute extends _i29.PageRouteInfo<void> {
const LanguageRoute({List<_i29.PageRouteInfo>? children})
: super(LanguageRoute.name, initialChildren: children);
static const String name = 'LanguageRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i11.LanguagePage();
@@ -306,29 +310,29 @@ class LanguageRoute extends _i27.PageRouteInfo<void> {
/// generated route for
/// [_i12.LoginPage]
class LoginRoute extends _i27.PageRouteInfo<void> {
const LoginRoute({List<_i27.PageRouteInfo>? children})
class LoginRoute extends _i29.PageRouteInfo<void> {
const LoginRoute({List<_i29.PageRouteInfo>? children})
: super(LoginRoute.name, initialChildren: children);
static const String name = 'LoginRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i12.LoginPage());
return _i29.WrappedRoute(child: const _i12.LoginPage());
},
);
}
/// generated route for
/// [_i13.MainPage]
class MainRoute extends _i27.PageRouteInfo<void> {
const MainRoute({List<_i27.PageRouteInfo>? children})
class MainRoute extends _i29.PageRouteInfo<void> {
const MainRoute({List<_i29.PageRouteInfo>? children})
: super(MainRoute.name, initialChildren: children);
static const String name = 'MainRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i13.MainPage();
@@ -338,11 +342,11 @@ class MainRoute extends _i27.PageRouteInfo<void> {
/// generated route for
/// [_i14.OrderDetailPage]
class OrderDetailRoute extends _i27.PageRouteInfo<OrderDetailRouteArgs> {
class OrderDetailRoute extends _i29.PageRouteInfo<OrderDetailRouteArgs> {
OrderDetailRoute({
_i28.Key? key,
required _i29.Order order,
List<_i27.PageRouteInfo>? children,
_i30.Key? key,
required _i31.Order order,
List<_i29.PageRouteInfo>? children,
}) : super(
OrderDetailRoute.name,
args: OrderDetailRouteArgs(key: key, order: order),
@@ -351,7 +355,7 @@ class OrderDetailRoute extends _i27.PageRouteInfo<OrderDetailRouteArgs> {
static const String name = 'OrderDetailRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
final args = data.argsAs<OrderDetailRouteArgs>();
@@ -363,9 +367,9 @@ class OrderDetailRoute extends _i27.PageRouteInfo<OrderDetailRouteArgs> {
class OrderDetailRouteArgs {
const OrderDetailRouteArgs({this.key, required this.order});
final _i28.Key? key;
final _i30.Key? key;
final _i29.Order order;
final _i31.Order order;
@override
String toString() {
@@ -375,91 +379,91 @@ class OrderDetailRouteArgs {
/// generated route for
/// [_i15.OrderPage]
class OrderRoute extends _i27.PageRouteInfo<void> {
const OrderRoute({List<_i27.PageRouteInfo>? children})
class OrderRoute extends _i29.PageRouteInfo<void> {
const OrderRoute({List<_i29.PageRouteInfo>? children})
: super(OrderRoute.name, initialChildren: children);
static const String name = 'OrderRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i15.OrderPage());
return _i29.WrappedRoute(child: const _i15.OrderPage());
},
);
}
/// generated route for
/// [_i16.OutletInformationPage]
class OutletInformationRoute extends _i27.PageRouteInfo<void> {
const OutletInformationRoute({List<_i27.PageRouteInfo>? children})
class OutletInformationRoute extends _i29.PageRouteInfo<void> {
const OutletInformationRoute({List<_i29.PageRouteInfo>? children})
: super(OutletInformationRoute.name, initialChildren: children);
static const String name = 'OutletInformationRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i16.OutletInformationPage());
return _i29.WrappedRoute(child: const _i16.OutletInformationPage());
},
);
}
/// generated route for
/// [_i17.ProductAnalyticPage]
class ProductAnalyticRoute extends _i27.PageRouteInfo<void> {
const ProductAnalyticRoute({List<_i27.PageRouteInfo>? children})
class ProductAnalyticRoute extends _i29.PageRouteInfo<void> {
const ProductAnalyticRoute({List<_i29.PageRouteInfo>? children})
: super(ProductAnalyticRoute.name, initialChildren: children);
static const String name = 'ProductAnalyticRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i17.ProductAnalyticPage());
return _i29.WrappedRoute(child: const _i17.ProductAnalyticPage());
},
);
}
/// generated route for
/// [_i18.ProductPage]
class ProductRoute extends _i27.PageRouteInfo<void> {
const ProductRoute({List<_i27.PageRouteInfo>? children})
class ProductRoute extends _i29.PageRouteInfo<void> {
const ProductRoute({List<_i29.PageRouteInfo>? children})
: super(ProductRoute.name, initialChildren: children);
static const String name = 'ProductRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i18.ProductPage());
return _i29.WrappedRoute(child: const _i18.ProductPage());
},
);
}
/// generated route for
/// [_i19.ProfileChangePasswordPage]
class ProfileChangePasswordRoute extends _i27.PageRouteInfo<void> {
const ProfileChangePasswordRoute({List<_i27.PageRouteInfo>? children})
class ProfileChangePasswordRoute extends _i29.PageRouteInfo<void> {
const ProfileChangePasswordRoute({List<_i29.PageRouteInfo>? children})
: super(ProfileChangePasswordRoute.name, initialChildren: children);
static const String name = 'ProfileChangePasswordRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i19.ProfileChangePasswordPage());
return _i29.WrappedRoute(child: const _i19.ProfileChangePasswordPage());
},
);
}
/// generated route for
/// [_i20.ProfileEditPage]
class ProfileEditRoute extends _i27.PageRouteInfo<ProfileEditRouteArgs> {
class ProfileEditRoute extends _i29.PageRouteInfo<ProfileEditRouteArgs> {
ProfileEditRoute({
_i28.Key? key,
required _i30.User user,
List<_i27.PageRouteInfo>? children,
_i30.Key? key,
required _i32.User user,
List<_i29.PageRouteInfo>? children,
}) : super(
ProfileEditRoute.name,
args: ProfileEditRouteArgs(key: key, user: user),
@@ -468,11 +472,11 @@ class ProfileEditRoute extends _i27.PageRouteInfo<ProfileEditRouteArgs> {
static const String name = 'ProfileEditRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
final args = data.argsAs<ProfileEditRouteArgs>();
return _i27.WrappedRoute(
return _i29.WrappedRoute(
child: _i20.ProfileEditPage(key: args.key, user: args.user),
);
},
@@ -482,9 +486,9 @@ class ProfileEditRoute extends _i27.PageRouteInfo<ProfileEditRouteArgs> {
class ProfileEditRouteArgs {
const ProfileEditRouteArgs({this.key, required this.user});
final _i28.Key? key;
final _i30.Key? key;
final _i30.User user;
final _i32.User user;
@override
String toString() {
@@ -494,96 +498,179 @@ class ProfileEditRouteArgs {
/// generated route for
/// [_i21.ProfilePage]
class ProfileRoute extends _i27.PageRouteInfo<void> {
const ProfileRoute({List<_i27.PageRouteInfo>? children})
class ProfileRoute extends _i29.PageRouteInfo<void> {
const ProfileRoute({List<_i29.PageRouteInfo>? children})
: super(ProfileRoute.name, initialChildren: children);
static const String name = 'ProfileRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i21.ProfilePage());
return _i29.WrappedRoute(child: const _i21.ProfilePage());
},
);
}
/// generated route for
/// [_i22.PurchasePage]
class PurchaseRoute extends _i27.PageRouteInfo<void> {
const PurchaseRoute({List<_i27.PageRouteInfo>? children})
/// [_i22.ProfitSharingDetailPage]
class ProfitSharingDetailRoute
extends _i29.PageRouteInfo<ProfitSharingDetailRouteArgs> {
ProfitSharingDetailRoute({
_i30.Key? key,
required String parentCategoryId,
required String parentCategoryName,
required DateTime dateFrom,
required DateTime dateTo,
List<_i29.PageRouteInfo>? children,
}) : super(
ProfitSharingDetailRoute.name,
args: ProfitSharingDetailRouteArgs(
key: key,
parentCategoryId: parentCategoryId,
parentCategoryName: parentCategoryName,
dateFrom: dateFrom,
dateTo: dateTo,
),
initialChildren: children,
);
static const String name = 'ProfitSharingDetailRoute';
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
final args = data.argsAs<ProfitSharingDetailRouteArgs>();
return _i29.WrappedRoute(
child: _i22.ProfitSharingDetailPage(
key: args.key,
parentCategoryId: args.parentCategoryId,
parentCategoryName: args.parentCategoryName,
dateFrom: args.dateFrom,
dateTo: args.dateTo,
),
);
},
);
}
class ProfitSharingDetailRouteArgs {
const ProfitSharingDetailRouteArgs({
this.key,
required this.parentCategoryId,
required this.parentCategoryName,
required this.dateFrom,
required this.dateTo,
});
final _i30.Key? key;
final String parentCategoryId;
final String parentCategoryName;
final DateTime dateFrom;
final DateTime dateTo;
@override
String toString() {
return 'ProfitSharingDetailRouteArgs{key: $key, parentCategoryId: $parentCategoryId, parentCategoryName: $parentCategoryName, dateFrom: $dateFrom, dateTo: $dateTo}';
}
}
/// generated route for
/// [_i23.ProfitSharingPage]
class ProfitSharingRoute extends _i29.PageRouteInfo<void> {
const ProfitSharingRoute({List<_i29.PageRouteInfo>? children})
: super(ProfitSharingRoute.name, initialChildren: children);
static const String name = 'ProfitSharingRoute';
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i29.WrappedRoute(child: const _i23.ProfitSharingPage());
},
);
}
/// generated route for
/// [_i24.PurchasePage]
class PurchaseRoute extends _i29.PageRouteInfo<void> {
const PurchaseRoute({List<_i29.PageRouteInfo>? children})
: super(PurchaseRoute.name, initialChildren: children);
static const String name = 'PurchaseRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i22.PurchasePage());
return _i29.WrappedRoute(child: const _i24.PurchasePage());
},
);
}
/// generated route for
/// [_i23.ReportPage]
class ReportRoute extends _i27.PageRouteInfo<void> {
const ReportRoute({List<_i27.PageRouteInfo>? children})
/// [_i25.ReportPage]
class ReportRoute extends _i29.PageRouteInfo<void> {
const ReportRoute({List<_i29.PageRouteInfo>? children})
: super(ReportRoute.name, initialChildren: children);
static const String name = 'ReportRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i23.ReportPage());
return _i29.WrappedRoute(child: const _i25.ReportPage());
},
);
}
/// generated route for
/// [_i24.SalesPage]
class SalesRoute extends _i27.PageRouteInfo<void> {
const SalesRoute({List<_i27.PageRouteInfo>? children})
/// [_i26.SalesPage]
class SalesRoute extends _i29.PageRouteInfo<void> {
const SalesRoute({List<_i29.PageRouteInfo>? children})
: super(SalesRoute.name, initialChildren: children);
static const String name = 'SalesRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return _i27.WrappedRoute(child: const _i24.SalesPage());
return _i29.WrappedRoute(child: const _i26.SalesPage());
},
);
}
/// generated route for
/// [_i25.SchedulePage]
class ScheduleRoute extends _i27.PageRouteInfo<void> {
const ScheduleRoute({List<_i27.PageRouteInfo>? children})
/// [_i27.SchedulePage]
class ScheduleRoute extends _i29.PageRouteInfo<void> {
const ScheduleRoute({List<_i29.PageRouteInfo>? children})
: super(ScheduleRoute.name, initialChildren: children);
static const String name = 'ScheduleRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i25.SchedulePage();
return const _i27.SchedulePage();
},
);
}
/// generated route for
/// [_i26.SplashPage]
class SplashRoute extends _i27.PageRouteInfo<void> {
const SplashRoute({List<_i27.PageRouteInfo>? children})
/// [_i28.SplashPage]
class SplashRoute extends _i29.PageRouteInfo<void> {
const SplashRoute({List<_i29.PageRouteInfo>? children})
: super(SplashRoute.name, initialChildren: children);
static const String name = 'SplashRoute';
static _i27.PageInfo page = _i27.PageInfo(
static _i29.PageInfo page = _i29.PageInfo(
name,
builder: (data) {
return const _i26.SplashPage();
return const _i28.SplashPage();
},
);
}