Efril 8d801e52d9
Some checks are pending
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
feat: update sales ui
2026-06-23 23:18:22 +07:00

214 lines
7.0 KiB
Dart

import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
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';
import '../../../application/analytic/sales_loader/sales_loader_bloc.dart';
import '../../../common/theme/theme.dart';
import '../../../injection.dart';
import '../../components/spacer/spacer.dart';
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';
@RoutePage()
class SalesPage extends StatefulWidget implements AutoRouteWrapper {
const SalesPage({super.key});
@override
State<SalesPage> createState() => _SalesPageState();
@override
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()),
),
],
child: this,
);
}
class _SalesPageState extends State<SalesPage>
with SingleTickerProviderStateMixin {
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
@override
void initState() {
super.initState();
_fadeController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_fadeAnimation = CurvedAnimation(
parent: _fadeController,
curve: Curves.easeOut,
);
Future.delayed(const Duration(milliseconds: 300), () {
_fadeController.forward();
});
}
@override
void dispose() {
_fadeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body: BlocBuilder<SalesLoaderBloc, SalesLoaderState>(
builder: (context, salesState) {
return CustomScrollView(
slivers: [
// Header
SliverToBoxAdapter(
child: SalesHeader(
state: salesState,
onDateRangeChanged: (startDate, endDate) {
_onDateRangeChanged(context, startDate, endDate);
},
),
),
// 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,
);
},
),
),
),
),
// 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,
);
},
),
),
),
),
// 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,
);
},
),
),
),
),
// Bottom Padding
const SliverToBoxAdapter(child: SpaceHeight(32)),
],
);
},
),
);
}
void _onDateRangeChanged(
BuildContext context,
DateTime startDate,
DateTime endDate,
) {
// Update sales
context.read<SalesLoaderBloc>()
..add(SalesLoaderEvent.rangeDateChanged(startDate, endDate))
..add(SalesLoaderEvent.fectched());
// Update payment method
context.read<PaymentMethodAnalyticLoaderBloc>()
..add(
PaymentMethodAnalyticLoaderEvent.rangeDateChanged(startDate, endDate),
)
..add(const PaymentMethodAnalyticLoaderEvent.fetched());
// 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());
}
}