report page

This commit is contained in:
efrilm 2025-11-01 04:11:29 +07:00
parent d111025aa6
commit 2f155a2f8d
12 changed files with 989 additions and 39 deletions

View File

@ -0,0 +1,34 @@
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../common/extension/extension.dart';
part 'report_event.dart';
part 'report_state.dart';
part 'report_bloc.freezed.dart';
@injectable
class ReportBloc extends Bloc<ReportEvent, ReportState> {
ReportBloc() : super(ReportState.initial()) {
on<ReportEvent>(_onReportEvent);
}
Future<void> _onReportEvent(ReportEvent event, Emitter<ReportState> emit) {
return event.map(
dateChanged: (e) async {
emit(
state.copyWith(
startDate: e.startDate,
endDate: e.endDate,
rangeDateFormatted:
'${e.startDate.toFormattedDate()} - ${e.endDate.toFormattedDate()}',
),
);
},
menuChanged: (e) async {
emit(state.copyWith(selectedMenu: e.index, title: e.title));
},
);
}
}

View File

@ -0,0 +1,616 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'report_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
/// @nodoc
mixin _$ReportEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int index, String title) menuChanged,
required TResult Function(DateTime startDate, DateTime endDate) dateChanged,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int index, String title)? menuChanged,
TResult? Function(DateTime startDate, DateTime endDate)? dateChanged,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int index, String title)? menuChanged,
TResult Function(DateTime startDate, DateTime endDate)? dateChanged,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_MenuChanged value) menuChanged,
required TResult Function(_DateChanged value) dateChanged,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_MenuChanged value)? menuChanged,
TResult? Function(_DateChanged value)? dateChanged,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_MenuChanged value)? menuChanged,
TResult Function(_DateChanged value)? dateChanged,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ReportEventCopyWith<$Res> {
factory $ReportEventCopyWith(
ReportEvent value,
$Res Function(ReportEvent) then,
) = _$ReportEventCopyWithImpl<$Res, ReportEvent>;
}
/// @nodoc
class _$ReportEventCopyWithImpl<$Res, $Val extends ReportEvent>
implements $ReportEventCopyWith<$Res> {
_$ReportEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$MenuChangedImplCopyWith<$Res> {
factory _$$MenuChangedImplCopyWith(
_$MenuChangedImpl value,
$Res Function(_$MenuChangedImpl) then,
) = __$$MenuChangedImplCopyWithImpl<$Res>;
@useResult
$Res call({int index, String title});
}
/// @nodoc
class __$$MenuChangedImplCopyWithImpl<$Res>
extends _$ReportEventCopyWithImpl<$Res, _$MenuChangedImpl>
implements _$$MenuChangedImplCopyWith<$Res> {
__$$MenuChangedImplCopyWithImpl(
_$MenuChangedImpl _value,
$Res Function(_$MenuChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? index = null, Object? title = null}) {
return _then(
_$MenuChangedImpl(
index: null == index
? _value.index
: index // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$MenuChangedImpl implements _MenuChanged {
const _$MenuChangedImpl({required this.index, required this.title});
@override
final int index;
@override
final String title;
@override
String toString() {
return 'ReportEvent.menuChanged(index: $index, title: $title)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MenuChangedImpl &&
(identical(other.index, index) || other.index == index) &&
(identical(other.title, title) || other.title == title));
}
@override
int get hashCode => Object.hash(runtimeType, index, title);
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MenuChangedImplCopyWith<_$MenuChangedImpl> get copyWith =>
__$$MenuChangedImplCopyWithImpl<_$MenuChangedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int index, String title) menuChanged,
required TResult Function(DateTime startDate, DateTime endDate) dateChanged,
}) {
return menuChanged(index, title);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int index, String title)? menuChanged,
TResult? Function(DateTime startDate, DateTime endDate)? dateChanged,
}) {
return menuChanged?.call(index, title);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int index, String title)? menuChanged,
TResult Function(DateTime startDate, DateTime endDate)? dateChanged,
required TResult orElse(),
}) {
if (menuChanged != null) {
return menuChanged(index, title);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_MenuChanged value) menuChanged,
required TResult Function(_DateChanged value) dateChanged,
}) {
return menuChanged(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_MenuChanged value)? menuChanged,
TResult? Function(_DateChanged value)? dateChanged,
}) {
return menuChanged?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_MenuChanged value)? menuChanged,
TResult Function(_DateChanged value)? dateChanged,
required TResult orElse(),
}) {
if (menuChanged != null) {
return menuChanged(this);
}
return orElse();
}
}
abstract class _MenuChanged implements ReportEvent {
const factory _MenuChanged({
required final int index,
required final String title,
}) = _$MenuChangedImpl;
int get index;
String get title;
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MenuChangedImplCopyWith<_$MenuChangedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$DateChangedImplCopyWith<$Res> {
factory _$$DateChangedImplCopyWith(
_$DateChangedImpl value,
$Res Function(_$DateChangedImpl) then,
) = __$$DateChangedImplCopyWithImpl<$Res>;
@useResult
$Res call({DateTime startDate, DateTime endDate});
}
/// @nodoc
class __$$DateChangedImplCopyWithImpl<$Res>
extends _$ReportEventCopyWithImpl<$Res, _$DateChangedImpl>
implements _$$DateChangedImplCopyWith<$Res> {
__$$DateChangedImplCopyWithImpl(
_$DateChangedImpl _value,
$Res Function(_$DateChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? startDate = null, Object? endDate = null}) {
return _then(
_$DateChangedImpl(
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
),
);
}
}
/// @nodoc
class _$DateChangedImpl implements _DateChanged {
const _$DateChangedImpl({required this.startDate, required this.endDate});
@override
final DateTime startDate;
@override
final DateTime endDate;
@override
String toString() {
return 'ReportEvent.dateChanged(startDate: $startDate, endDate: $endDate)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DateChangedImpl &&
(identical(other.startDate, startDate) ||
other.startDate == startDate) &&
(identical(other.endDate, endDate) || other.endDate == endDate));
}
@override
int get hashCode => Object.hash(runtimeType, startDate, endDate);
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$DateChangedImplCopyWith<_$DateChangedImpl> get copyWith =>
__$$DateChangedImplCopyWithImpl<_$DateChangedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int index, String title) menuChanged,
required TResult Function(DateTime startDate, DateTime endDate) dateChanged,
}) {
return dateChanged(startDate, endDate);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int index, String title)? menuChanged,
TResult? Function(DateTime startDate, DateTime endDate)? dateChanged,
}) {
return dateChanged?.call(startDate, endDate);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int index, String title)? menuChanged,
TResult Function(DateTime startDate, DateTime endDate)? dateChanged,
required TResult orElse(),
}) {
if (dateChanged != null) {
return dateChanged(startDate, endDate);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_MenuChanged value) menuChanged,
required TResult Function(_DateChanged value) dateChanged,
}) {
return dateChanged(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_MenuChanged value)? menuChanged,
TResult? Function(_DateChanged value)? dateChanged,
}) {
return dateChanged?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_MenuChanged value)? menuChanged,
TResult Function(_DateChanged value)? dateChanged,
required TResult orElse(),
}) {
if (dateChanged != null) {
return dateChanged(this);
}
return orElse();
}
}
abstract class _DateChanged implements ReportEvent {
const factory _DateChanged({
required final DateTime startDate,
required final DateTime endDate,
}) = _$DateChangedImpl;
DateTime get startDate;
DateTime get endDate;
/// Create a copy of ReportEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$DateChangedImplCopyWith<_$DateChangedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$ReportState {
String get title => throw _privateConstructorUsedError;
DateTime get startDate => throw _privateConstructorUsedError;
DateTime get endDate => throw _privateConstructorUsedError;
String get rangeDateFormatted => throw _privateConstructorUsedError;
int get selectedMenu => throw _privateConstructorUsedError;
/// Create a copy of ReportState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ReportStateCopyWith<ReportState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ReportStateCopyWith<$Res> {
factory $ReportStateCopyWith(
ReportState value,
$Res Function(ReportState) then,
) = _$ReportStateCopyWithImpl<$Res, ReportState>;
@useResult
$Res call({
String title,
DateTime startDate,
DateTime endDate,
String rangeDateFormatted,
int selectedMenu,
});
}
/// @nodoc
class _$ReportStateCopyWithImpl<$Res, $Val extends ReportState>
implements $ReportStateCopyWith<$Res> {
_$ReportStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ReportState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? title = null,
Object? startDate = null,
Object? endDate = null,
Object? rangeDateFormatted = null,
Object? selectedMenu = null,
}) {
return _then(
_value.copyWith(
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
rangeDateFormatted: null == rangeDateFormatted
? _value.rangeDateFormatted
: rangeDateFormatted // ignore: cast_nullable_to_non_nullable
as String,
selectedMenu: null == selectedMenu
? _value.selectedMenu
: selectedMenu // ignore: cast_nullable_to_non_nullable
as int,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$ReportStateImplCopyWith<$Res>
implements $ReportStateCopyWith<$Res> {
factory _$$ReportStateImplCopyWith(
_$ReportStateImpl value,
$Res Function(_$ReportStateImpl) then,
) = __$$ReportStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
String title,
DateTime startDate,
DateTime endDate,
String rangeDateFormatted,
int selectedMenu,
});
}
/// @nodoc
class __$$ReportStateImplCopyWithImpl<$Res>
extends _$ReportStateCopyWithImpl<$Res, _$ReportStateImpl>
implements _$$ReportStateImplCopyWith<$Res> {
__$$ReportStateImplCopyWithImpl(
_$ReportStateImpl _value,
$Res Function(_$ReportStateImpl) _then,
) : super(_value, _then);
/// Create a copy of ReportState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? title = null,
Object? startDate = null,
Object? endDate = null,
Object? rangeDateFormatted = null,
Object? selectedMenu = null,
}) {
return _then(
_$ReportStateImpl(
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
rangeDateFormatted: null == rangeDateFormatted
? _value.rangeDateFormatted
: rangeDateFormatted // ignore: cast_nullable_to_non_nullable
as String,
selectedMenu: null == selectedMenu
? _value.selectedMenu
: selectedMenu // ignore: cast_nullable_to_non_nullable
as int,
),
);
}
}
/// @nodoc
class _$ReportStateImpl implements _ReportState {
_$ReportStateImpl({
required this.title,
required this.startDate,
required this.endDate,
required this.rangeDateFormatted,
this.selectedMenu = 0,
});
@override
final String title;
@override
final DateTime startDate;
@override
final DateTime endDate;
@override
final String rangeDateFormatted;
@override
@JsonKey()
final int selectedMenu;
@override
String toString() {
return 'ReportState(title: $title, startDate: $startDate, endDate: $endDate, rangeDateFormatted: $rangeDateFormatted, selectedMenu: $selectedMenu)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ReportStateImpl &&
(identical(other.title, title) || other.title == title) &&
(identical(other.startDate, startDate) ||
other.startDate == startDate) &&
(identical(other.endDate, endDate) || other.endDate == endDate) &&
(identical(other.rangeDateFormatted, rangeDateFormatted) ||
other.rangeDateFormatted == rangeDateFormatted) &&
(identical(other.selectedMenu, selectedMenu) ||
other.selectedMenu == selectedMenu));
}
@override
int get hashCode => Object.hash(
runtimeType,
title,
startDate,
endDate,
rangeDateFormatted,
selectedMenu,
);
/// Create a copy of ReportState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ReportStateImplCopyWith<_$ReportStateImpl> get copyWith =>
__$$ReportStateImplCopyWithImpl<_$ReportStateImpl>(this, _$identity);
}
abstract class _ReportState implements ReportState {
factory _ReportState({
required final String title,
required final DateTime startDate,
required final DateTime endDate,
required final String rangeDateFormatted,
final int selectedMenu,
}) = _$ReportStateImpl;
@override
String get title;
@override
DateTime get startDate;
@override
DateTime get endDate;
@override
String get rangeDateFormatted;
@override
int get selectedMenu;
/// Create a copy of ReportState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ReportStateImplCopyWith<_$ReportStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,13 @@
part of 'report_bloc.dart';
@freezed
class ReportEvent with _$ReportEvent {
const factory ReportEvent.menuChanged({
required int index,
required String title,
}) = _MenuChanged;
const factory ReportEvent.dateChanged({
required DateTime startDate,
required DateTime endDate,
}) = _DateChanged;
}

View File

@ -0,0 +1,20 @@
part of 'report_bloc.dart';
@freezed
class ReportState with _$ReportState {
factory ReportState({
required String title,
required DateTime startDate,
required DateTime endDate,
required String rangeDateFormatted,
@Default(0) int selectedMenu,
}) = _ReportState;
factory ReportState.initial() => ReportState(
title: 'Ringkasan Laporan Penjualan',
startDate: DateTime.now(),
endDate: DateTime.now(),
rangeDateFormatted:
'${DateTime.now().toFormattedDate()} - ${DateTime.now().toFormattedDate()}',
);
}

View File

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ReportMenu {
ReportMenu({
required this.index,
required this.title,
required this.subtitle,
required this.icons,
});
final int index;
final String title;
final String subtitle;
final IconData icons;
}
List<ReportMenu> reportMenus = [
ReportMenu(
index: 0,
title: 'Ringkasan Laporan Penjualan',
subtitle: 'Ringkasan total penjualan dalam periode tertentu.',
icons: Icons.insert_drive_file_outlined,
),
ReportMenu(
index: 1,
title: 'Laporan Transaksi',
subtitle:
'Menampilkan riwayat lengkap semua transaksi yang telah dilakukan.',
icons: Icons.insert_drive_file_outlined,
),
ReportMenu(
index: 2,
title: 'Laporan Penjualan Item',
subtitle: 'Laporan penjualan berdasarkan masing-masing item atau produk.',
icons: Icons.inventory_2_outlined,
),
ReportMenu(
index: 3,
title: 'Laporan Penjualan Produk',
subtitle: 'Laporan penjualan berdasarkan masing-masing produk.',
icons: Icons.bar_chart_outlined,
),
ReportMenu(
index: 4,
title: 'Laporan Metode Pembayaran',
subtitle: 'Laporan metode pembayaran yang digunakan.',
icons: Icons.payment_outlined,
),
ReportMenu(
index: 5,
title: 'Laporan Untung Rugi',
subtitle: 'Laporan untung rugi penjualan.',
icons: Icons.trending_down_outlined,
),
ReportMenu(
index: 6,
title: 'Laporan Inventori',
subtitle: 'Laporan inventori produk',
icons: Icons.archive_outlined,
),
ReportMenu(
index: 7,
title: 'Laporan Kategori',
subtitle: 'Laporan kategori produk',
icons: Icons.category_outlined,
),
];

View File

@ -30,6 +30,8 @@ import 'package:apskel_pos_flutter_v2/application/payment_method/payment_method_
as _i952;
import 'package:apskel_pos_flutter_v2/application/product/product_loader/product_loader_bloc.dart'
as _i13;
import 'package:apskel_pos_flutter_v2/application/report/report_bloc.dart'
as _i257;
import 'package:apskel_pos_flutter_v2/application/split_bill/split_bill_form/split_bill_form_bloc.dart'
as _i334;
import 'package:apskel_pos_flutter_v2/application/sync/sync_bloc.dart' as _i741;
@ -129,6 +131,7 @@ extension GetItInjectableX on _i174.GetIt {
);
gh.factory<_i13.CheckoutFormBloc>(() => _i13.CheckoutFormBloc());
gh.factory<_i334.SplitBillFormBloc>(() => _i334.SplitBillFormBloc());
gh.factory<_i257.ReportBloc>(() => _i257.ReportBloc());
gh.singleton<_i487.DatabaseHelper>(() => databaseDi.databaseHelper);
gh.lazySingleton<_i361.Dio>(() => dioDi.dio);
gh.lazySingleton<_i800.AppRouter>(() => autoRouteDi.appRouter);

View File

@ -5,3 +5,4 @@ import '../../../common/theme/theme.dart';
import '../spaces/space.dart';
part 'elevated_button.dart';
part 'icon_button.dart';

View File

@ -0,0 +1,22 @@
part of 'button.dart';
class AppIconButton extends StatelessWidget {
final IconData icons;
final Function()? onTap;
const AppIconButton({super.key, required this.icons, required this.onTap});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(color: AppColor.border),
),
child: Icon(icons, color: AppColor.primary, size: 28),
),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
import '../../../common/theme/theme.dart';
import '../spaces/space.dart';
class DateRangePickerModal {
static Future<DateRangePickerSelectionChangedArgs?> show({
@ -217,42 +218,7 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog>
child: SingleChildScrollView(
child: Column(
children: [
// Selection Info
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: widget.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: widget.primaryColor.withOpacity(0.2),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tanggal Terpilih:',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: widget.primaryColor,
),
),
const SizedBox(height: 4),
Text(
_getSelectionText(),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
SpaceHeight(16),
// Date Picker
Padding(
padding: const EdgeInsets.symmetric(
@ -320,6 +286,41 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog>
),
),
),
// Selection Info
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: widget.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: widget.primaryColor.withOpacity(0.2),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tanggal Terpilih:',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: widget.primaryColor,
),
),
const SizedBox(height: 4),
Text(
_getSelectionText(),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
],
),
),
],
),
),

View File

@ -1,12 +1,123 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../application/report/report_bloc.dart';
import '../../../../../common/data/report_menu.dart';
import '../../../../../common/theme/theme.dart';
import '../../../../../injection.dart';
import '../../../../components/button/button.dart';
import '../../../../components/page/page_title.dart';
import '../../../../components/picker/date_range_picker.dart';
import '../../../../components/spaces/space.dart';
import '../../../../router/app_router.gr.dart';
import 'widgets/report_menu_card.dart';
@RoutePage()
class ReportPage extends StatelessWidget {
class ReportPage extends StatelessWidget implements AutoRouteWrapper {
const ReportPage({super.key});
@override
Widget build(BuildContext context) {
return const Center(child: Text('Report Page'));
return Scaffold(
backgroundColor: AppColor.white,
body: SafeArea(
child: BlocBuilder<ReportBloc, ReportState>(
builder: (context, state) {
return Column(
children: [
PageTitle(
title: 'Report',
subtitle: state.rangeDateFormatted,
actionWidget: [
AppIconButton(
icons: Icons.calendar_month_outlined,
onTap: () {
DateRangePickerModal.show(
context: context,
initialEndDate: state.endDate,
initialStartDate: state.startDate,
primaryColor: AppColor.primary,
onChanged: (startDate, endDate) {
context.read<ReportBloc>().add(
ReportEvent.dateChanged(
startDate: startDate!,
endDate: endDate!,
),
);
},
);
},
),
SpaceWidth(8),
AppIconButton(icons: Icons.download_outlined, onTap: () {}),
],
),
Expanded(
child: Row(
children: [
Expanded(
flex: 2,
child: Material(
color: AppColor.white,
child: SingleChildScrollView(
child: Column(
children: List.generate(
reportMenus.length,
(index) => ReportMenuCard(
menu: reportMenus[index],
isActive:
reportMenus[index].index ==
state.selectedMenu,
onTap: () {
if (reportMenus[index].index == 1) {
context.router.push(
OrderRoute(status: 'completed'),
);
} else {
context.read<ReportBloc>().add(
ReportEvent.menuChanged(
index: index,
title: reportMenus[index].title,
),
);
}
},
),
),
),
),
),
),
Expanded(
flex: 4,
child: Material(
color: AppColor.background,
child: switch (state.selectedMenu) {
0 => Text(state.title),
1 => Text(state.title),
2 => Text(state.title),
3 => Text(state.title),
4 => Text(state.title),
5 => Text(state.title),
6 => Text(state.title),
7 => Text(state.title),
_ => Container(),
},
),
),
],
),
),
],
);
},
),
),
);
}
@override
Widget wrappedRoute(BuildContext context) =>
BlocProvider(create: (context) => getIt<ReportBloc>(), child: this);
}

View File

@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import '../../../../../../common/data/report_menu.dart';
import '../../../../../../common/theme/theme.dart';
import '../../../../../components/spaces/space.dart';
class ReportMenuCard extends StatelessWidget {
final ReportMenu menu;
final bool isActive;
final Function() onTap;
const ReportMenuCard({
super.key,
required this.menu,
required this.isActive,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
border: Border(
right: BorderSide(
color: isActive ? AppColor.primary : Colors.transparent,
width: 4.0,
),
),
),
child: Row(
children: [
Icon(
menu.icons,
size: 24.0,
color: isActive ? AppColor.primary : AppColor.textSecondary,
),
const SpaceWidth(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
menu.title,
style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold),
),
SpaceHeight(4.0),
Text(
menu.subtitle,
style: AppStyle.md.copyWith(color: AppColor.textSecondary),
),
],
),
),
],
),
),
);
}
}

View File

@ -336,7 +336,7 @@ class ReportRoute extends _i20.PageRouteInfo<void> {
static _i20.PageInfo page = _i20.PageInfo(
name,
builder: (data) {
return const _i10.ReportPage();
return _i20.WrappedRoute(child: const _i10.ReportPage());
},
);
}