Files
apskel-owner-flutter/lib/presentation/pages/sales/sales_page.dart
T

214 lines
7.0 KiB
Dart
Raw Normal View History

2025-08-15 18:28:13 +07:00
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
2025-08-17 10:10:31 +07:00
import 'package:flutter_bloc/flutter_bloc.dart';
2025-08-15 18:28:13 +07:00
2026-06-23 23:18:22 +07:00
import '../../../application/analytic/category_analytic_loader/category_analytic_loader_bloc.dart';
import '../../../application/analytic/payment_method_analytic_loader/payment_method_analytic_loader_bloc.dart';
import '../../../application/analytic/product_analytic_loader/product_analytic_loader_bloc.dart';
2025-08-17 22:49:28 +07:00
import '../../../application/analytic/sales_loader/sales_loader_bloc.dart';
2025-08-15 18:28:13 +07:00
import '../../../common/theme/theme.dart';
2025-08-17 10:10:31 +07:00
import '../../../injection.dart';
2025-08-15 18:28:13 +07:00
import '../../components/spacer/spacer.dart';
2026-06-23 23:18:22 +07:00
import 'widgets/sales_category_card.dart';
import 'widgets/sales_header.dart';
import 'widgets/sales_payment_method_card.dart';
import 'widgets/sales_rincian_card.dart';
import 'widgets/sales_top_products_card.dart';
2025-08-15 18:28:13 +07:00
@RoutePage()
2025-08-17 10:10:31 +07:00
class SalesPage extends StatefulWidget implements AutoRouteWrapper {
2025-08-15 18:28:13 +07:00
const SalesPage({super.key});
@override
State<SalesPage> createState() => _SalesPageState();
2025-08-17 10:10:31 +07:00
@override
2026-06-23 23:18:22 +07:00
Widget wrappedRoute(BuildContext context) => MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>
getIt<SalesLoaderBloc>()..add(SalesLoaderEvent.fectched()),
),
BlocProvider(
create: (context) =>
getIt<PaymentMethodAnalyticLoaderBloc>()
..add(const PaymentMethodAnalyticLoaderEvent.fetched()),
),
BlocProvider(
create: (context) =>
getIt<CategoryAnalyticLoaderBloc>()
..add(const CategoryAnalyticLoaderEvent.fetched()),
),
BlocProvider(
create: (context) =>
getIt<ProductAnalyticLoaderBloc>()
..add(const ProductAnalyticLoaderEvent.fetched()),
),
],
2025-08-17 10:10:31 +07:00
child: this,
);
2025-08-15 18:28:13 +07:00
}
2026-06-23 23:18:22 +07:00
class _SalesPageState extends State<SalesPage>
with SingleTickerProviderStateMixin {
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
2025-08-17 14:53:52 +07:00
2025-08-15 18:28:13 +07:00
@override
void initState() {
super.initState();
2026-06-23 23:18:22 +07:00
_fadeController = AnimationController(
2025-08-15 18:28:13 +07:00
duration: const Duration(milliseconds: 600),
vsync: this,
);
2026-06-23 23:18:22 +07:00
_fadeAnimation = CurvedAnimation(
parent: _fadeController,
curve: Curves.easeOut,
2025-08-15 18:28:13 +07:00
);
Future.delayed(const Duration(milliseconds: 300), () {
2026-06-23 23:18:22 +07:00
_fadeController.forward();
2025-08-15 18:28:13 +07:00
});
}
@override
void dispose() {
2026-06-23 23:18:22 +07:00
_fadeController.dispose();
2025-08-15 18:28:13 +07:00
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
2026-06-23 23:18:22 +07:00
body: BlocBuilder<SalesLoaderBloc, SalesLoaderState>(
builder: (context, salesState) {
return CustomScrollView(
slivers: [
// Header
SliverToBoxAdapter(
child: SalesHeader(
state: salesState,
onDateRangeChanged: (startDate, endDate) {
_onDateRangeChanged(context, startDate, endDate);
},
2025-08-15 18:28:13 +07:00
),
2026-06-23 23:18:22 +07:00
),
2025-08-15 18:28:13 +07:00
2026-06-23 23:18:22 +07:00
// Rincian Penjualan
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.all(16),
child: SalesRincianCard(state: salesState),
),
),
),
// Metode Pembayaran
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child:
BlocBuilder<
PaymentMethodAnalyticLoaderBloc,
PaymentMethodAnalyticLoaderState
>(
builder: (context, paymentState) {
return SalesPaymentMethodCard(
paymentMethodAnalytic:
paymentState.paymentMethodAnalytic,
isFetching: paymentState.isFetching,
2025-08-18 16:43:07 +07:00
);
},
2025-08-15 18:28:13 +07:00
),
),
),
2026-06-23 23:18:22 +07:00
),
2025-08-15 18:28:13 +07:00
2026-06-23 23:18:22 +07:00
// Penjualan per Kategori
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.all(16),
child:
BlocBuilder<
CategoryAnalyticLoaderBloc,
CategoryAnalyticLoaderState
>(
builder: (context, categoryState) {
return SalesCategoryCard(
categoryAnalytic: categoryState.categoryAnalytic,
isFetching: categoryState.isFetching,
);
},
2025-08-18 16:43:07 +07:00
),
),
2025-08-17 14:53:52 +07:00
),
2026-06-23 23:18:22 +07:00
),
2025-08-18 16:43:07 +07:00
2026-06-23 23:18:22 +07:00
// Produk Terlaris
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child:
BlocBuilder<
ProductAnalyticLoaderBloc,
ProductAnalyticLoaderState
>(
builder: (context, productState) {
return SalesTopProductsCard(
productAnalytic: productState.productAnalytic,
isFetching: productState.isFetching,
);
},
2025-08-18 16:43:07 +07:00
),
),
),
2025-08-17 14:53:52 +07:00
),
2026-06-23 23:18:22 +07:00
// Bottom Padding
const SliverToBoxAdapter(child: SpaceHeight(32)),
2025-08-17 14:53:52 +07:00
],
);
},
),
);
}
2026-06-23 23:18:22 +07:00
void _onDateRangeChanged(
BuildContext context,
DateTime startDate,
DateTime endDate,
2025-08-15 18:28:13 +07:00
) {
2026-06-23 23:18:22 +07:00
// Update sales
context.read<SalesLoaderBloc>()
..add(SalesLoaderEvent.rangeDateChanged(startDate, endDate))
..add(SalesLoaderEvent.fectched());
2025-08-15 18:28:13 +07:00
2026-06-23 23:18:22 +07:00
// Update payment method
context.read<PaymentMethodAnalyticLoaderBloc>()
..add(
PaymentMethodAnalyticLoaderEvent.rangeDateChanged(startDate, endDate),
)
..add(const PaymentMethodAnalyticLoaderEvent.fetched());
2025-08-15 18:28:13 +07:00
2026-06-23 23:18:22 +07:00
// Update category
context.read<CategoryAnalyticLoaderBloc>()
..add(CategoryAnalyticLoaderEvent.rangeDateChanged(startDate, endDate))
..add(const CategoryAnalyticLoaderEvent.fetched());
// Update product
context.read<ProductAnalyticLoaderBloc>()
..add(ProductAnalyticLoaderEvent.rangeDateChanged(startDate, endDate))
..add(const ProductAnalyticLoaderEvent.fetched());
2025-08-15 18:28:13 +07:00
}
}