feat: profit loss range date

This commit is contained in:
efrilm
2025-08-18 16:48:35 +07:00
parent 1f6e5e9a2b
commit ac8c2c0f54
5 changed files with 338 additions and 139 deletions
+96 -131
View File
@@ -10,6 +10,7 @@ import '../../../common/theme/theme.dart';
import '../../../domain/analytic/analytic.dart';
import '../../../injection.dart';
import '../../components/appbar/appbar.dart';
import '../../components/field/date_range_picker_field.dart';
import 'widgets/cash_flow.dart';
import 'widgets/category.dart';
import 'widgets/product.dart';
@@ -50,14 +51,6 @@ class _FinancePageState extends State<FinancePage>
late Animation<double> _fadeAnimation;
late Animation<double> _scaleAnimation;
String selectedPeriod = 'Hari ini';
final List<String> periods = [
'Hari ini',
'Minggu ini',
'Bulan ini',
'Tahun ini',
];
@override
void initState() {
super.initState();
@@ -113,136 +106,108 @@ class _FinancePageState extends State<FinancePage>
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body: BlocBuilder<ProfitLossLoaderBloc, ProfitLossLoaderState>(
builder: (context, state) {
return CustomScrollView(
slivers: [
// SliverAppBar with animated background
SliverAppBar(
expandedHeight: 120,
floating: false,
pinned: true,
backgroundColor: AppColor.primary,
elevation: 0,
flexibleSpace: CustomAppBar(title: 'Keuangan'),
),
// Header dengan filter periode
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: _buildPeriodSelector(),
),
),
// Summary Cards
SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: _buildSummaryCards(state.profitLoss.summary),
),
),
// Cash Flow Analysis
SliverToBoxAdapter(
child: ScaleTransition(
scale: _scaleAnimation,
child: FinanceCashFlow(dailyData: state.profitLoss.data),
),
),
// Profit Loss Detail
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: FinanceProfitLoss(data: state.profitLoss.summary),
),
),
BlocBuilder<
CategoryAnalyticLoaderBloc,
CategoryAnalyticLoaderState
>(
builder: (context, stateCategory) {
return SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: FinanceCategory(
categories: stateCategory.categoryAnalytic.data,
),
),
);
},
),
// Product Analysis Section
SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: _buildProductAnalysis(state.profitLoss.productData),
),
),
// Transaction Categories
// Bottom spacing
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
body: BlocListener<ProfitLossLoaderBloc, ProfitLossLoaderState>(
listenWhen: (previous, current) =>
previous.dateFrom != current.dateFrom &&
previous.dateTo != current.dateTo,
listener: (context, state) {
context.read<ProfitLossLoaderBloc>().add(
ProfitLossLoaderEvent.fetched(),
);
},
),
);
}
child: BlocBuilder<ProfitLossLoaderBloc, ProfitLossLoaderState>(
builder: (context, state) {
return CustomScrollView(
slivers: [
// SliverAppBar with animated background
SliverAppBar(
expandedHeight: 120,
floating: false,
pinned: true,
backgroundColor: AppColor.primary,
elevation: 0,
flexibleSpace: CustomAppBar(title: 'Keuangan'),
),
Widget _buildPeriodSelector() {
return Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.border),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: selectedPeriod,
isExpanded: true,
icon: const Icon(
LineIcons.angleDown,
color: AppColor.primary,
// Header dengan filter periode
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: DateRangePickerField(
maxDate: DateTime.now(),
startDate: state.dateFrom,
endDate: state.dateTo,
onChanged: (startDate, endDate) {
context.read<ProfitLossLoaderBloc>().add(
ProfitLossLoaderEvent.rangeDateChanged(
startDate!,
endDate!,
),
);
},
),
),
),
style: AppStyle.md,
items: periods.map((String period) {
return DropdownMenuItem<String>(
value: period,
child: Text(period),
),
// Summary Cards
SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: _buildSummaryCards(state.profitLoss.summary),
),
),
// Cash Flow Analysis
SliverToBoxAdapter(
child: ScaleTransition(
scale: _scaleAnimation,
child: FinanceCashFlow(dailyData: state.profitLoss.data),
),
),
// Profit Loss Detail
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: FinanceProfitLoss(data: state.profitLoss.summary),
),
),
BlocBuilder<
CategoryAnalyticLoaderBloc,
CategoryAnalyticLoaderState
>(
builder: (context, stateCategory) {
return SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: FinanceCategory(
categories: stateCategory.categoryAnalytic.data,
),
),
);
}).toList(),
onChanged: (String? newValue) {
setState(() {
selectedPeriod = newValue!;
});
},
),
),
),
),
const SizedBox(width: 12),
Container(
decoration: BoxDecoration(
color: AppColor.primary,
borderRadius: BorderRadius.circular(12),
),
child: IconButton(
onPressed: () {},
icon: const Icon(LineIcons.calendar, color: AppColor.white),
),
),
],
// Product Analysis Section
SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: _buildProductAnalysis(state.profitLoss.productData),
),
),
// Transaction Categories
// Bottom spacing
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
);
},
),
),
);
}