purchasing
This commit is contained in:
parent
602647ff26
commit
823e009121
@ -28,22 +28,23 @@ class PurchasingAnalyticLoaderBloc
|
|||||||
rangeDateChanged: (e) async {
|
rangeDateChanged: (e) async {
|
||||||
emit(state.copyWith(dateFrom: e.dateFrom, dateTo: e.dateTo));
|
emit(state.copyWith(dateFrom: e.dateFrom, dateTo: e.dateTo));
|
||||||
},
|
},
|
||||||
|
outletChanged: (e) async {
|
||||||
|
emit(state.copyWith(outletId: e.outletId));
|
||||||
|
},
|
||||||
fetched: (e) async {
|
fetched: (e) async {
|
||||||
emit(
|
emit(state.copyWith(
|
||||||
state.copyWith(
|
isFetching: true,
|
||||||
isFetching: true,
|
failureOptionPurchasing: none(),
|
||||||
failureOptionPurchasing: none(),
|
));
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await _analyticRepository.getPurchasing(
|
final result = await _analyticRepository.getPurchasing(
|
||||||
dateFrom: state.dateFrom,
|
dateFrom: state.dateFrom,
|
||||||
dateTo: state.dateTo,
|
dateTo: state.dateTo,
|
||||||
|
outletId: state.outletId,
|
||||||
);
|
);
|
||||||
|
|
||||||
final newState = result.fold(
|
final newState = result.fold(
|
||||||
(f) =>
|
(f) => state.copyWith(failureOptionPurchasing: optionOf(f)),
|
||||||
state.copyWith(failureOptionPurchasing: optionOf(f)),
|
|
||||||
(purchasing) => state.copyWith(purchasing: purchasing),
|
(purchasing) => state.copyWith(purchasing: purchasing),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -21,32 +21,38 @@ mixin _$PurchasingAnalyticLoaderEvent {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
||||||
rangeDateChanged,
|
rangeDateChanged,
|
||||||
|
required TResult Function(String? outletId) outletChanged,
|
||||||
required TResult Function() fetched,
|
required TResult Function() fetched,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult? Function(String? outletId)? outletChanged,
|
||||||
TResult? Function()? fetched,
|
TResult? Function()? fetched,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult Function(String? outletId)? outletChanged,
|
||||||
TResult Function()? fetched,
|
TResult Function()? fetched,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
||||||
|
required TResult Function(_OutletChanged value) outletChanged,
|
||||||
required TResult Function(_Fetched value) fetched,
|
required TResult Function(_Fetched value) fetched,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult? Function(_OutletChanged value)? outletChanged,
|
||||||
TResult? Function(_Fetched value)? fetched,
|
TResult? Function(_Fetched value)? fetched,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult Function(_OutletChanged value)? outletChanged,
|
||||||
TResult Function(_Fetched value)? fetched,
|
TResult Function(_Fetched value)? fetched,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@ -168,6 +174,7 @@ class _$RangeDateChangedImpl implements _RangeDateChanged {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
||||||
rangeDateChanged,
|
rangeDateChanged,
|
||||||
|
required TResult Function(String? outletId) outletChanged,
|
||||||
required TResult Function() fetched,
|
required TResult Function() fetched,
|
||||||
}) {
|
}) {
|
||||||
return rangeDateChanged(dateFrom, dateTo);
|
return rangeDateChanged(dateFrom, dateTo);
|
||||||
@ -177,6 +184,7 @@ class _$RangeDateChangedImpl implements _RangeDateChanged {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult? Function(String? outletId)? outletChanged,
|
||||||
TResult? Function()? fetched,
|
TResult? Function()? fetched,
|
||||||
}) {
|
}) {
|
||||||
return rangeDateChanged?.call(dateFrom, dateTo);
|
return rangeDateChanged?.call(dateFrom, dateTo);
|
||||||
@ -186,6 +194,7 @@ class _$RangeDateChangedImpl implements _RangeDateChanged {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult Function(String? outletId)? outletChanged,
|
||||||
TResult Function()? fetched,
|
TResult Function()? fetched,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@ -199,6 +208,7 @@ class _$RangeDateChangedImpl implements _RangeDateChanged {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
||||||
|
required TResult Function(_OutletChanged value) outletChanged,
|
||||||
required TResult Function(_Fetched value) fetched,
|
required TResult Function(_Fetched value) fetched,
|
||||||
}) {
|
}) {
|
||||||
return rangeDateChanged(this);
|
return rangeDateChanged(this);
|
||||||
@ -208,6 +218,7 @@ class _$RangeDateChangedImpl implements _RangeDateChanged {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult? Function(_OutletChanged value)? outletChanged,
|
||||||
TResult? Function(_Fetched value)? fetched,
|
TResult? Function(_Fetched value)? fetched,
|
||||||
}) {
|
}) {
|
||||||
return rangeDateChanged?.call(this);
|
return rangeDateChanged?.call(this);
|
||||||
@ -217,6 +228,7 @@ class _$RangeDateChangedImpl implements _RangeDateChanged {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult Function(_OutletChanged value)? outletChanged,
|
||||||
TResult Function(_Fetched value)? fetched,
|
TResult Function(_Fetched value)? fetched,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@ -243,6 +255,157 @@ abstract class _RangeDateChanged implements PurchasingAnalyticLoaderEvent {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$OutletChangedImplCopyWith<$Res> {
|
||||||
|
factory _$$OutletChangedImplCopyWith(
|
||||||
|
_$OutletChangedImpl value,
|
||||||
|
$Res Function(_$OutletChangedImpl) then,
|
||||||
|
) = __$$OutletChangedImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({String? outletId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$OutletChangedImplCopyWithImpl<$Res>
|
||||||
|
extends
|
||||||
|
_$PurchasingAnalyticLoaderEventCopyWithImpl<$Res, _$OutletChangedImpl>
|
||||||
|
implements _$$OutletChangedImplCopyWith<$Res> {
|
||||||
|
__$$OutletChangedImplCopyWithImpl(
|
||||||
|
_$OutletChangedImpl _value,
|
||||||
|
$Res Function(_$OutletChangedImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of PurchasingAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({Object? outletId = freezed}) {
|
||||||
|
return _then(
|
||||||
|
_$OutletChangedImpl(
|
||||||
|
freezed == outletId
|
||||||
|
? _value.outletId
|
||||||
|
: outletId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$OutletChangedImpl implements _OutletChanged {
|
||||||
|
const _$OutletChangedImpl(this.outletId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String? outletId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'PurchasingAnalyticLoaderEvent.outletChanged(outletId: $outletId)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$OutletChangedImpl &&
|
||||||
|
(identical(other.outletId, outletId) ||
|
||||||
|
other.outletId == outletId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, outletId);
|
||||||
|
|
||||||
|
/// Create a copy of PurchasingAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$OutletChangedImplCopyWith<_$OutletChangedImpl> get copyWith =>
|
||||||
|
__$$OutletChangedImplCopyWithImpl<_$OutletChangedImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
||||||
|
rangeDateChanged,
|
||||||
|
required TResult Function(String? outletId) outletChanged,
|
||||||
|
required TResult Function() fetched,
|
||||||
|
}) {
|
||||||
|
return outletChanged(outletId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult? Function(String? outletId)? outletChanged,
|
||||||
|
TResult? Function()? fetched,
|
||||||
|
}) {
|
||||||
|
return outletChanged?.call(outletId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult Function(String? outletId)? outletChanged,
|
||||||
|
TResult Function()? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (outletChanged != null) {
|
||||||
|
return outletChanged(outletId);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
||||||
|
required TResult Function(_OutletChanged value) outletChanged,
|
||||||
|
required TResult Function(_Fetched value) fetched,
|
||||||
|
}) {
|
||||||
|
return outletChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult? Function(_OutletChanged value)? outletChanged,
|
||||||
|
TResult? Function(_Fetched value)? fetched,
|
||||||
|
}) {
|
||||||
|
return outletChanged?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult Function(_OutletChanged value)? outletChanged,
|
||||||
|
TResult Function(_Fetched value)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (outletChanged != null) {
|
||||||
|
return outletChanged(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _OutletChanged implements PurchasingAnalyticLoaderEvent {
|
||||||
|
const factory _OutletChanged(final String? outletId) = _$OutletChangedImpl;
|
||||||
|
|
||||||
|
String? get outletId;
|
||||||
|
|
||||||
|
/// Create a copy of PurchasingAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$OutletChangedImplCopyWith<_$OutletChangedImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class _$$FetchedImplCopyWith<$Res> {
|
abstract class _$$FetchedImplCopyWith<$Res> {
|
||||||
factory _$$FetchedImplCopyWith(
|
factory _$$FetchedImplCopyWith(
|
||||||
@ -288,6 +451,7 @@ class _$FetchedImpl implements _Fetched {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
required TResult Function(DateTime dateFrom, DateTime dateTo)
|
||||||
rangeDateChanged,
|
rangeDateChanged,
|
||||||
|
required TResult Function(String? outletId) outletChanged,
|
||||||
required TResult Function() fetched,
|
required TResult Function() fetched,
|
||||||
}) {
|
}) {
|
||||||
return fetched();
|
return fetched();
|
||||||
@ -297,6 +461,7 @@ class _$FetchedImpl implements _Fetched {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult? Function(String? outletId)? outletChanged,
|
||||||
TResult? Function()? fetched,
|
TResult? Function()? fetched,
|
||||||
}) {
|
}) {
|
||||||
return fetched?.call();
|
return fetched?.call();
|
||||||
@ -306,6 +471,7 @@ class _$FetchedImpl implements _Fetched {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
|
||||||
|
TResult Function(String? outletId)? outletChanged,
|
||||||
TResult Function()? fetched,
|
TResult Function()? fetched,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@ -319,6 +485,7 @@ class _$FetchedImpl implements _Fetched {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
required TResult Function(_RangeDateChanged value) rangeDateChanged,
|
||||||
|
required TResult Function(_OutletChanged value) outletChanged,
|
||||||
required TResult Function(_Fetched value) fetched,
|
required TResult Function(_Fetched value) fetched,
|
||||||
}) {
|
}) {
|
||||||
return fetched(this);
|
return fetched(this);
|
||||||
@ -328,6 +495,7 @@ class _$FetchedImpl implements _Fetched {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult? Function(_OutletChanged value)? outletChanged,
|
||||||
TResult? Function(_Fetched value)? fetched,
|
TResult? Function(_Fetched value)? fetched,
|
||||||
}) {
|
}) {
|
||||||
return fetched?.call(this);
|
return fetched?.call(this);
|
||||||
@ -337,6 +505,7 @@ class _$FetchedImpl implements _Fetched {
|
|||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
TResult Function(_RangeDateChanged value)? rangeDateChanged,
|
||||||
|
TResult Function(_OutletChanged value)? outletChanged,
|
||||||
TResult Function(_Fetched value)? fetched,
|
TResult Function(_Fetched value)? fetched,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@ -359,6 +528,7 @@ mixin _$PurchasingAnalyticLoaderState {
|
|||||||
bool get isFetching => throw _privateConstructorUsedError;
|
bool get isFetching => throw _privateConstructorUsedError;
|
||||||
DateTime get dateFrom => throw _privateConstructorUsedError;
|
DateTime get dateFrom => throw _privateConstructorUsedError;
|
||||||
DateTime get dateTo => throw _privateConstructorUsedError;
|
DateTime get dateTo => throw _privateConstructorUsedError;
|
||||||
|
String? get outletId => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of PurchasingAnalyticLoaderState
|
/// Create a copy of PurchasingAnalyticLoaderState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@ -384,6 +554,7 @@ abstract class $PurchasingAnalyticLoaderStateCopyWith<$Res> {
|
|||||||
bool isFetching,
|
bool isFetching,
|
||||||
DateTime dateFrom,
|
DateTime dateFrom,
|
||||||
DateTime dateTo,
|
DateTime dateTo,
|
||||||
|
String? outletId,
|
||||||
});
|
});
|
||||||
|
|
||||||
$PurchasingAnalyticCopyWith<$Res> get purchasing;
|
$PurchasingAnalyticCopyWith<$Res> get purchasing;
|
||||||
@ -412,6 +583,7 @@ class _$PurchasingAnalyticLoaderStateCopyWithImpl<
|
|||||||
Object? isFetching = null,
|
Object? isFetching = null,
|
||||||
Object? dateFrom = null,
|
Object? dateFrom = null,
|
||||||
Object? dateTo = null,
|
Object? dateTo = null,
|
||||||
|
Object? outletId = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@ -435,6 +607,10 @@ class _$PurchasingAnalyticLoaderStateCopyWithImpl<
|
|||||||
? _value.dateTo
|
? _value.dateTo
|
||||||
: dateTo // ignore: cast_nullable_to_non_nullable
|
: dateTo // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,
|
as DateTime,
|
||||||
|
outletId: freezed == outletId
|
||||||
|
? _value.outletId
|
||||||
|
: outletId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@ -466,6 +642,7 @@ abstract class _$$PurchasingAnalyticLoaderStateImplCopyWith<$Res>
|
|||||||
bool isFetching,
|
bool isFetching,
|
||||||
DateTime dateFrom,
|
DateTime dateFrom,
|
||||||
DateTime dateTo,
|
DateTime dateTo,
|
||||||
|
String? outletId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -495,6 +672,7 @@ class __$$PurchasingAnalyticLoaderStateImplCopyWithImpl<$Res>
|
|||||||
Object? isFetching = null,
|
Object? isFetching = null,
|
||||||
Object? dateFrom = null,
|
Object? dateFrom = null,
|
||||||
Object? dateTo = null,
|
Object? dateTo = null,
|
||||||
|
Object? outletId = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$PurchasingAnalyticLoaderStateImpl(
|
_$PurchasingAnalyticLoaderStateImpl(
|
||||||
@ -518,6 +696,10 @@ class __$$PurchasingAnalyticLoaderStateImplCopyWithImpl<$Res>
|
|||||||
? _value.dateTo
|
? _value.dateTo
|
||||||
: dateTo // ignore: cast_nullable_to_non_nullable
|
: dateTo // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,
|
as DateTime,
|
||||||
|
outletId: freezed == outletId
|
||||||
|
? _value.outletId
|
||||||
|
: outletId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -533,6 +715,7 @@ class _$PurchasingAnalyticLoaderStateImpl
|
|||||||
this.isFetching = false,
|
this.isFetching = false,
|
||||||
required this.dateFrom,
|
required this.dateFrom,
|
||||||
required this.dateTo,
|
required this.dateTo,
|
||||||
|
this.outletId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -546,10 +729,12 @@ class _$PurchasingAnalyticLoaderStateImpl
|
|||||||
final DateTime dateFrom;
|
final DateTime dateFrom;
|
||||||
@override
|
@override
|
||||||
final DateTime dateTo;
|
final DateTime dateTo;
|
||||||
|
@override
|
||||||
|
final String? outletId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'PurchasingAnalyticLoaderState(purchasing: $purchasing, failureOptionPurchasing: $failureOptionPurchasing, isFetching: $isFetching, dateFrom: $dateFrom, dateTo: $dateTo)';
|
return 'PurchasingAnalyticLoaderState(purchasing: $purchasing, failureOptionPurchasing: $failureOptionPurchasing, isFetching: $isFetching, dateFrom: $dateFrom, dateTo: $dateTo, outletId: $outletId)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -568,7 +753,9 @@ class _$PurchasingAnalyticLoaderStateImpl
|
|||||||
other.isFetching == isFetching) &&
|
other.isFetching == isFetching) &&
|
||||||
(identical(other.dateFrom, dateFrom) ||
|
(identical(other.dateFrom, dateFrom) ||
|
||||||
other.dateFrom == dateFrom) &&
|
other.dateFrom == dateFrom) &&
|
||||||
(identical(other.dateTo, dateTo) || other.dateTo == dateTo));
|
(identical(other.dateTo, dateTo) || other.dateTo == dateTo) &&
|
||||||
|
(identical(other.outletId, outletId) ||
|
||||||
|
other.outletId == outletId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -579,6 +766,7 @@ class _$PurchasingAnalyticLoaderStateImpl
|
|||||||
isFetching,
|
isFetching,
|
||||||
dateFrom,
|
dateFrom,
|
||||||
dateTo,
|
dateTo,
|
||||||
|
outletId,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of PurchasingAnalyticLoaderState
|
/// Create a copy of PurchasingAnalyticLoaderState
|
||||||
@ -603,6 +791,7 @@ abstract class _PurchasingAnalyticLoaderState
|
|||||||
final bool isFetching,
|
final bool isFetching,
|
||||||
required final DateTime dateFrom,
|
required final DateTime dateFrom,
|
||||||
required final DateTime dateTo,
|
required final DateTime dateTo,
|
||||||
|
final String? outletId,
|
||||||
}) = _$PurchasingAnalyticLoaderStateImpl;
|
}) = _$PurchasingAnalyticLoaderStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -615,6 +804,8 @@ abstract class _PurchasingAnalyticLoaderState
|
|||||||
DateTime get dateFrom;
|
DateTime get dateFrom;
|
||||||
@override
|
@override
|
||||||
DateTime get dateTo;
|
DateTime get dateTo;
|
||||||
|
@override
|
||||||
|
String? get outletId;
|
||||||
|
|
||||||
/// Create a copy of PurchasingAnalyticLoaderState
|
/// Create a copy of PurchasingAnalyticLoaderState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|||||||
@ -7,5 +7,9 @@ class PurchasingAnalyticLoaderEvent with _$PurchasingAnalyticLoaderEvent {
|
|||||||
DateTime dateTo,
|
DateTime dateTo,
|
||||||
) = _RangeDateChanged;
|
) = _RangeDateChanged;
|
||||||
|
|
||||||
|
const factory PurchasingAnalyticLoaderEvent.outletChanged(
|
||||||
|
String? outletId,
|
||||||
|
) = _OutletChanged;
|
||||||
|
|
||||||
const factory PurchasingAnalyticLoaderEvent.fetched() = _Fetched;
|
const factory PurchasingAnalyticLoaderEvent.fetched() = _Fetched;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ class PurchasingAnalyticLoaderState with _$PurchasingAnalyticLoaderState {
|
|||||||
@Default(false) bool isFetching,
|
@Default(false) bool isFetching,
|
||||||
required DateTime dateFrom,
|
required DateTime dateFrom,
|
||||||
required DateTime dateTo,
|
required DateTime dateTo,
|
||||||
|
String? outletId,
|
||||||
}) = _PurchasingAnalyticLoaderState;
|
}) = _PurchasingAnalyticLoaderState;
|
||||||
|
|
||||||
factory PurchasingAnalyticLoaderState.initial() =>
|
factory PurchasingAnalyticLoaderState.initial() =>
|
||||||
|
|||||||
@ -65,5 +65,8 @@ class ThemeApp {
|
|||||||
),
|
),
|
||||||
iconTheme: const IconThemeData(color: AppColor.white),
|
iconTheme: const IconThemeData(color: AppColor.white),
|
||||||
),
|
),
|
||||||
|
bottomSheetTheme: BottomSheetThemeData(
|
||||||
|
backgroundColor: AppColor.white,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,51 +1,70 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
|
||||||
|
import '../../../application/analytic/purchasing_analytic_loader/purchasing_analytic_loader_bloc.dart';
|
||||||
|
import '../../../application/outlet/outlet_list_loader/outlet_list_loader_bloc.dart';
|
||||||
import '../../../common/extension/extension.dart';
|
import '../../../common/extension/extension.dart';
|
||||||
import '../../../common/theme/theme.dart';
|
import '../../../common/theme/theme.dart';
|
||||||
|
import '../../../injection.dart';
|
||||||
import '../../components/appbar/appbar.dart';
|
import '../../components/appbar/appbar.dart';
|
||||||
import 'widgets/purchase_tile.dart';
|
import '../../components/field/date_range_picker_field.dart';
|
||||||
|
import '../../components/spacer/spacer.dart';
|
||||||
|
import 'widgets/ingredient_card.dart';
|
||||||
|
import 'widgets/outlet_selector_field.dart';
|
||||||
|
import 'widgets/purchase_daily_tile.dart';
|
||||||
import 'widgets/stat_card.dart';
|
import 'widgets/stat_card.dart';
|
||||||
import 'widgets/status_chip.dart';
|
import 'widgets/vendor_card.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class PurchasePage extends StatefulWidget {
|
class PurchasePage extends StatefulWidget implements AutoRouteWrapper {
|
||||||
const PurchasePage({super.key});
|
const PurchasePage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PurchasePage> createState() => _PurchasePageState();
|
State<PurchasePage> createState() => _PurchasePageState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget wrappedRoute(BuildContext context) => MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => getIt<PurchasingAnalyticLoaderBloc>()
|
||||||
|
..add(const PurchasingAnalyticLoaderEvent.fetched()),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => getIt<OutletListLoaderBloc>()
|
||||||
|
..add(const OutletListLoaderEvent.fetched()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: this,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PurchasePageState extends State<PurchasePage>
|
class _PurchasePageState extends State<PurchasePage>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late AnimationController cardAnimation;
|
late AnimationController _fadeController;
|
||||||
String selectedFilter = 'Semua';
|
late Animation<double> _fadeAnimation;
|
||||||
final List<String> filterOptions = [
|
|
||||||
'Semua',
|
|
||||||
'Pending',
|
|
||||||
'Completed',
|
|
||||||
'Cancelled',
|
|
||||||
];
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> purchaseData = [
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_fadeController = AnimationController(
|
||||||
cardAnimation = AnimationController(
|
duration: const Duration(milliseconds: 600),
|
||||||
duration: const Duration(milliseconds: 1200),
|
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
|
_fadeAnimation = CurvedAnimation(
|
||||||
cardAnimation.forward();
|
parent: _fadeController,
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
);
|
||||||
|
Future.delayed(const Duration(milliseconds: 200), () {
|
||||||
|
if (mounted) _fadeController.forward();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
cardAnimation.dispose();
|
_fadeController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,141 +72,521 @@ class _PurchasePageState extends State<PurchasePage>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColor.background,
|
backgroundColor: AppColor.background,
|
||||||
body: CustomScrollView(
|
body: MultiBlocListener(
|
||||||
slivers: [
|
listeners: [
|
||||||
SliverAppBar(
|
// Re-fetch when date range changes
|
||||||
expandedHeight: 120.0,
|
BlocListener<PurchasingAnalyticLoaderBloc,
|
||||||
floating: false,
|
PurchasingAnalyticLoaderState>(
|
||||||
pinned: true,
|
listenWhen: (prev, curr) =>
|
||||||
elevation: 0,
|
prev.dateFrom != curr.dateFrom ||
|
||||||
backgroundColor: AppColor.primary,
|
prev.dateTo != curr.dateTo,
|
||||||
|
listener: (context, _) => context
|
||||||
flexibleSpace: CustomAppBar(title: context.lang.purchase),
|
.read<PurchasingAnalyticLoaderBloc>()
|
||||||
|
.add(const PurchasingAnalyticLoaderEvent.fetched()),
|
||||||
),
|
),
|
||||||
|
// Re-fetch when outlet changes
|
||||||
// Stats Cards
|
BlocListener<PurchasingAnalyticLoaderBloc,
|
||||||
SliverToBoxAdapter(
|
PurchasingAnalyticLoaderState>(
|
||||||
child: Container(
|
listenWhen: (prev, curr) => prev.outletId != curr.outletId,
|
||||||
color: AppColor.background,
|
listener: (context, _) => context
|
||||||
padding: const EdgeInsets.all(16.0),
|
.read<PurchasingAnalyticLoaderBloc>()
|
||||||
child: Row(
|
.add(const PurchasingAnalyticLoaderEvent.fetched()),
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: PurchaseStatCard(
|
|
||||||
title: context.lang.total_purchase,
|
|
||||||
value: 'Rp 0',
|
|
||||||
icon: LineIcons.shoppingCart,
|
|
||||||
iconColor: AppColor.success,
|
|
||||||
cardAnimation: cardAnimation,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: PurchaseStatCard(
|
|
||||||
title: context.lang.pending_order,
|
|
||||||
value: '0 ${context.lang.orders}',
|
|
||||||
icon: LineIcons.clock,
|
|
||||||
iconColor: AppColor.warning,
|
|
||||||
cardAnimation: cardAnimation,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
child: BlocBuilder<OutletListLoaderBloc, OutletListLoaderState>(
|
||||||
|
builder: (context, outletListState) {
|
||||||
|
return BlocBuilder<PurchasingAnalyticLoaderBloc,
|
||||||
|
PurchasingAnalyticLoaderState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
// App Bar
|
||||||
|
SliverAppBar(
|
||||||
|
expandedHeight: 120.0,
|
||||||
|
floating: false,
|
||||||
|
pinned: true,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: AppColor.primary,
|
||||||
|
flexibleSpace:
|
||||||
|
CustomAppBar(title: context.lang.purchase),
|
||||||
|
),
|
||||||
|
|
||||||
// Filter Section
|
// Date Range + Outlet Picker
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: FadeTransition(
|
||||||
color: AppColor.surface,
|
opacity: _fadeAnimation,
|
||||||
padding: const EdgeInsets.all(16.0),
|
child: Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
DateRangePickerField(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
maxDate: DateTime.now(),
|
||||||
children: [
|
startDate: state.dateFrom,
|
||||||
Text(
|
endDate: state.dateTo,
|
||||||
context.lang.history_purchase,
|
onChanged: (startDate, endDate) {
|
||||||
style: AppStyle.lg.copyWith(
|
context
|
||||||
fontWeight: FontWeight.w600,
|
.read<PurchasingAnalyticLoaderBloc>()
|
||||||
|
.add(
|
||||||
|
PurchasingAnalyticLoaderEvent
|
||||||
|
.rangeDateChanged(
|
||||||
|
startDate!,
|
||||||
|
endDate!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SpaceHeight(8),
|
||||||
|
PurchaseOutletSelectorField(
|
||||||
|
selectedOutletId: state.outletId,
|
||||||
|
outlets: outletListState.outlets,
|
||||||
|
isLoading: outletListState.isFetching,
|
||||||
|
onOutletChanged: (outletId) {
|
||||||
|
context
|
||||||
|
.read<PurchasingAnalyticLoaderBloc>()
|
||||||
|
.add(
|
||||||
|
PurchasingAnalyticLoaderEvent
|
||||||
|
.outletChanged(outletId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
),
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
const SliverToBoxAdapter(child: SpaceHeight(16)),
|
||||||
vertical: 6,
|
|
||||||
),
|
// Summary Section
|
||||||
decoration: BoxDecoration(
|
SliverToBoxAdapter(
|
||||||
color: AppColor.primary,
|
child: FadeTransition(
|
||||||
borderRadius: BorderRadius.circular(20),
|
opacity: _fadeAnimation,
|
||||||
),
|
child: Padding(
|
||||||
child: Text(
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
'${purchaseData.length} ${context.lang.orders}',
|
child: Column(
|
||||||
style: AppStyle.sm.copyWith(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: AppColor.textWhite,
|
children: [
|
||||||
|
Text(
|
||||||
|
context.lang.summary,
|
||||||
|
style: AppStyle.xxl.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceHeight(16),
|
||||||
|
state.isFetching
|
||||||
|
? _buildSummaryShimmer()
|
||||||
|
: _buildSummaryCards(state),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(24)),
|
||||||
|
|
||||||
|
// Total Purchases Highlight Card
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: state.isFetching
|
||||||
|
? _buildHighlightShimmer()
|
||||||
|
: _buildTotalPurchasesCard(state),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(24)),
|
||||||
|
|
||||||
|
// Daily Breakdown Header
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Text(
|
||||||
|
context.lang.daily_breakdown,
|
||||||
|
style: AppStyle.xxl.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(12)),
|
||||||
|
|
||||||
|
state.isFetching
|
||||||
|
? _buildListShimmer()
|
||||||
|
: _buildDailyList(state),
|
||||||
|
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(24)),
|
||||||
|
|
||||||
|
// Ingredient Header
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Text(
|
||||||
|
'Bahan Baku',
|
||||||
|
style: AppStyle.xxl.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(12)),
|
||||||
|
|
||||||
|
state.isFetching
|
||||||
|
? _buildListShimmer()
|
||||||
|
: _buildIngredientList(state),
|
||||||
|
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(24)),
|
||||||
|
|
||||||
|
// Vendor Header
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Text(
|
||||||
|
'Vendor',
|
||||||
|
style: AppStyle.xxl.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(12)),
|
||||||
|
|
||||||
|
state.isFetching
|
||||||
|
? _buildListShimmer()
|
||||||
|
: _buildVendorList(state),
|
||||||
|
|
||||||
|
const SliverToBoxAdapter(child: SpaceHeight(80)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Summary Cards ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildSummaryCards(PurchasingAnalyticLoaderState state) {
|
||||||
|
final s = state.purchasing.summary;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: PurchaseStatCard(
|
||||||
|
title: 'Total Pembelian',
|
||||||
|
value: s.totalPurchases.currencyFormatRp,
|
||||||
|
icon: LineIcons.shoppingCart,
|
||||||
|
iconColor: AppColor.primary,
|
||||||
|
cardAnimation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: PurchaseStatCard(
|
||||||
|
title: 'Total PO',
|
||||||
|
value: '${s.totalPurchaseOrders} PO',
|
||||||
|
icon: LineIcons.fileAlt,
|
||||||
|
iconColor: AppColor.info,
|
||||||
|
cardAnimation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: PurchaseStatCard(
|
||||||
|
title: 'Total Qty',
|
||||||
|
value: '${s.totalQuantity} pcs',
|
||||||
|
icon: LineIcons.boxes,
|
||||||
|
iconColor: AppColor.warning,
|
||||||
|
cardAnimation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: PurchaseStatCard(
|
||||||
|
title: 'Rata-rata PO',
|
||||||
|
value: s.averagePurchaseOrderValue.round().currencyFormatRp,
|
||||||
|
icon: LineIcons.dollarSign,
|
||||||
|
iconColor: AppColor.secondary,
|
||||||
|
cardAnimation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: PurchaseStatCard(
|
||||||
|
title: 'Bahan Baku',
|
||||||
|
value: '${s.totalIngredients} item',
|
||||||
|
icon: LineIcons.leaf,
|
||||||
|
iconColor: AppColor.secondaryDark,
|
||||||
|
cardAnimation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: PurchaseStatCard(
|
||||||
|
title: 'Vendor',
|
||||||
|
value: '${s.totalVendors} vendor',
|
||||||
|
icon: LineIcons.truck,
|
||||||
|
iconColor: AppColor.primaryDark,
|
||||||
|
cardAnimation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTotalPurchasesCard(PurchasingAnalyticLoaderState state) {
|
||||||
|
return TweenAnimationBuilder<double>(
|
||||||
|
tween: Tween(begin: 0.0, end: 1.0),
|
||||||
|
duration: const Duration(milliseconds: 900),
|
||||||
|
curve: Curves.bounceOut,
|
||||||
|
builder: (context, value, _) {
|
||||||
|
return Transform.scale(
|
||||||
|
scale: value,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
colors: AppColor.primaryGradient,
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: AppColor.primary.withOpacity(0.35),
|
||||||
|
blurRadius: 16,
|
||||||
|
offset: const Offset(0, 6),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
LineIcons.shoppingBag,
|
||||||
|
color: AppColor.textWhite,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceWidth(16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.lang.total_purchase,
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColor.textWhite.withOpacity(0.9),
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceHeight(4),
|
||||||
|
Text(
|
||||||
|
state.purchasing.summary.totalPurchases
|
||||||
|
.currencyFormatRp,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColor.textWhite,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
),
|
||||||
SizedBox(
|
Column(
|
||||||
height: 40,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
child: ListView.builder(
|
children: [
|
||||||
scrollDirection: Axis.horizontal,
|
Text(
|
||||||
itemCount: filterOptions.length,
|
'${state.purchasing.summary.totalPurchaseOrders} PO',
|
||||||
itemBuilder: (context, index) {
|
style: const TextStyle(
|
||||||
final isSelected =
|
color: AppColor.textWhite,
|
||||||
selectedFilter == filterOptions[index];
|
fontSize: 16,
|
||||||
return PurchaseStatusChip(
|
fontWeight: FontWeight.bold,
|
||||||
isSelected: isSelected,
|
|
||||||
text: filterOptions[index],
|
|
||||||
onSelected: (selected) {
|
|
||||||
setState(() {
|
|
||||||
selectedFilter = filterOptions[index];
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Purchase List
|
|
||||||
SliverPadding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
sliver: SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
|
||||||
final purchase = purchaseData[index];
|
|
||||||
return AnimatedBuilder(
|
|
||||||
animation: cardAnimation,
|
|
||||||
builder: (context, child) {
|
|
||||||
final delay = index * 0.1;
|
|
||||||
final animValue = (cardAnimation.value - delay).clamp(
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Transform.translate(
|
|
||||||
offset: Offset(0, 30 * (1 - animValue)),
|
|
||||||
child: Opacity(
|
|
||||||
opacity: animValue,
|
|
||||||
child: PurchaseTile(purchase: purchase, index: index),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
Text(
|
||||||
);
|
'purchase order',
|
||||||
}, childCount: purchaseData.length),
|
style: TextStyle(
|
||||||
|
color: AppColor.textWhite.withOpacity(0.8),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Bottom spacing for FAB
|
// ─── Lists ────────────────────────────────────────────────────────────────
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 80)),
|
|
||||||
],
|
Widget _buildDailyList(PurchasingAnalyticLoaderState state) {
|
||||||
|
if (state.purchasing.data.isEmpty) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: _buildEmptyState('Tidak ada data harian'));
|
||||||
|
}
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) => PurchaseDailyTile(
|
||||||
|
data: state.purchasing.data[index],
|
||||||
|
index: index,
|
||||||
|
animation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
childCount: state.purchasing.data.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIngredientList(PurchasingAnalyticLoaderState state) {
|
||||||
|
if (state.purchasing.ingredientData.isEmpty) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: _buildEmptyState('Tidak ada data bahan baku'));
|
||||||
|
}
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) => PurchaseIngredientCard(
|
||||||
|
data: state.purchasing.ingredientData[index],
|
||||||
|
index: index,
|
||||||
|
animation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
childCount: state.purchasing.ingredientData.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildVendorList(PurchasingAnalyticLoaderState state) {
|
||||||
|
if (state.purchasing.vendorData.isEmpty) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: _buildEmptyState('Tidak ada data vendor'));
|
||||||
|
}
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) => PurchaseVendorCard(
|
||||||
|
data: state.purchasing.vendorData[index],
|
||||||
|
index: index,
|
||||||
|
animation: _fadeAnimation,
|
||||||
|
),
|
||||||
|
childCount: state.purchasing.vendorData.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Empty State ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildEmptyState(String message) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.surface,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: AppColor.border.withOpacity(0.3)),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
message,
|
||||||
|
style: AppStyle.md.copyWith(color: AppColor.textSecondary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Shimmer Loaders ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildSummaryShimmer() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _shimmerCard(height: 100)),
|
||||||
|
const SpaceWidth(12),
|
||||||
|
Expanded(child: _shimmerCard(height: 100)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _shimmerCard(height: 100)),
|
||||||
|
const SpaceWidth(12),
|
||||||
|
Expanded(child: _shimmerCard(height: 100)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SpaceHeight(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _shimmerCard(height: 100)),
|
||||||
|
const SpaceWidth(12),
|
||||||
|
Expanded(child: _shimmerCard(height: 100)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHighlightShimmer() {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: _shimmerCard(height: 88),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListShimmer() {
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(_, __) => Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
|
child: _shimmerCard(height: 72),
|
||||||
|
),
|
||||||
|
childCount: 4,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _shimmerCard({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(12),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
160
lib/presentation/pages/purchase/widgets/ingredient_card.dart
Normal file
160
lib/presentation/pages/purchase/widgets/ingredient_card.dart
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
|
||||||
|
import '../../../../common/extension/extension.dart';
|
||||||
|
import '../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../domain/analytic/analytic.dart';
|
||||||
|
|
||||||
|
class PurchaseIngredientCard extends StatelessWidget {
|
||||||
|
final PurchasingIngredientData data;
|
||||||
|
final int index;
|
||||||
|
final Animation<double> animation;
|
||||||
|
|
||||||
|
const PurchaseIngredientCard({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
required this.index,
|
||||||
|
required this.animation,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.surface,
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
border: Border.all(color: AppColor.border.withOpacity(0.25)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.04),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// Rank badge
|
||||||
|
Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
colors: AppColor.primaryGradient,
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'${index + 1}',
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.textWhite,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
|
// Ingredient icon
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.secondary.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
LineIcons.leaf,
|
||||||
|
color: AppColor.secondary,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
|
// Name & details
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
data.ingredientName,
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_Chip(
|
||||||
|
label: '${data.quantity} pcs',
|
||||||
|
color: AppColor.warning,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
_Chip(
|
||||||
|
label: '${data.purchaseOrderCount} PO',
|
||||||
|
color: AppColor.info,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Cost column
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
data.totalCost.currencyFormatRp,
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
color: AppColor.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
'@ ${data.averageUnitCost.round().currencyFormatRp}',
|
||||||
|
style: AppStyle.xs.copyWith(
|
||||||
|
color: AppColor.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Chip extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const _Chip({required this.label, required this.color});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: AppStyle.xs.copyWith(
|
||||||
|
color: color,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,264 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../domain/outlet/outlet.dart';
|
||||||
|
|
||||||
|
/// Outlet selector field — styled like DateRangePickerField.
|
||||||
|
/// Opens a bottom sheet to pick an outlet or "Semua Outlet".
|
||||||
|
class PurchaseOutletSelectorField extends StatefulWidget {
|
||||||
|
final String? selectedOutletId;
|
||||||
|
final List<Outlet> outlets;
|
||||||
|
final bool isLoading;
|
||||||
|
final ValueChanged<String?> onOutletChanged;
|
||||||
|
|
||||||
|
const PurchaseOutletSelectorField({
|
||||||
|
super.key,
|
||||||
|
required this.selectedOutletId,
|
||||||
|
required this.outlets,
|
||||||
|
required this.isLoading,
|
||||||
|
required this.onOutletChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PurchaseOutletSelectorField> createState() =>
|
||||||
|
_PurchaseOutletSelectorFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PurchaseOutletSelectorFieldState
|
||||||
|
extends State<PurchaseOutletSelectorField> {
|
||||||
|
bool _isPressed = false;
|
||||||
|
|
||||||
|
Outlet? get _selectedOutlet => widget.outlets
|
||||||
|
.where((o) => o.id == widget.selectedOutletId)
|
||||||
|
.firstOrNull;
|
||||||
|
|
||||||
|
String get _label => _selectedOutlet?.name ?? 'Semua Outlet';
|
||||||
|
|
||||||
|
bool get _hasValue => widget.selectedOutletId != null;
|
||||||
|
|
||||||
|
void _showSheet() {
|
||||||
|
if (widget.isLoading) return;
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
|
),
|
||||||
|
builder: (_) => _OutletBottomSheet(
|
||||||
|
outlets: widget.outlets,
|
||||||
|
selectedOutletId: widget.selectedOutletId,
|
||||||
|
onSelected: (outletId) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
widget.onOutletChanged(outletId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: _showSheet,
|
||||||
|
onTapDown: (_) => setState(() => _isPressed = true),
|
||||||
|
onTapUp: (_) => setState(() => _isPressed = false),
|
||||||
|
onTapCancel: () => setState(() => _isPressed = false),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 150),
|
||||||
|
height: 52,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _isPressed ? AppColor.backgroundLight : AppColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: _isPressed ? AppColor.primary : AppColor.border,
|
||||||
|
width: _isPressed ? 2 : 1,
|
||||||
|
),
|
||||||
|
boxShadow: _isPressed
|
||||||
|
? [
|
||||||
|
BoxShadow(
|
||||||
|
color: AppColor.primary.withOpacity(0.1),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight:
|
||||||
|
_hasValue ? FontWeight.w500 : FontWeight.w400,
|
||||||
|
color: _hasValue
|
||||||
|
? AppColor.textPrimary
|
||||||
|
: AppColor.textSecondary,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.primary.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: widget.isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: AppColor.primary,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const Icon(
|
||||||
|
Icons.store_rounded,
|
||||||
|
size: 20,
|
||||||
|
color: AppColor.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Bottom Sheet ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class _OutletBottomSheet extends StatelessWidget {
|
||||||
|
final List<Outlet> outlets;
|
||||||
|
final String? selectedOutletId;
|
||||||
|
final ValueChanged<String?> onSelected;
|
||||||
|
|
||||||
|
const _OutletBottomSheet({
|
||||||
|
required this.outlets,
|
||||||
|
required this.selectedOutletId,
|
||||||
|
required this.onSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 16, 20, 20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Handle bar
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Pilih Outlet',
|
||||||
|
style: AppStyle.lg.copyWith(fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
// "Semua Outlet" option
|
||||||
|
_OutletItem(
|
||||||
|
label: 'Semua Outlet',
|
||||||
|
icon: Icons.store_rounded,
|
||||||
|
isSelected: selectedOutletId == null,
|
||||||
|
onTap: () => onSelected(null),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
|
||||||
|
// Individual outlets
|
||||||
|
...outlets.map(
|
||||||
|
(outlet) => Column(
|
||||||
|
children: [
|
||||||
|
_OutletItem(
|
||||||
|
label: outlet.name,
|
||||||
|
icon: Icons.storefront_rounded,
|
||||||
|
isSelected: selectedOutletId == outlet.id,
|
||||||
|
isActive: outlet.isActive,
|
||||||
|
onTap: () => onSelected(outlet.id),
|
||||||
|
),
|
||||||
|
if (outlet != outlets.last) const Divider(height: 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── List Item ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class _OutletItem extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final IconData icon;
|
||||||
|
final bool isSelected;
|
||||||
|
final bool? isActive;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const _OutletItem({
|
||||||
|
required this.label,
|
||||||
|
required this.icon,
|
||||||
|
required this.isSelected,
|
||||||
|
required this.onTap,
|
||||||
|
this.isActive,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
onTap: onTap,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||||
|
leading: Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected
|
||||||
|
? AppColor.primary.withOpacity(0.1)
|
||||||
|
: AppColor.background,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
icon,
|
||||||
|
size: 18,
|
||||||
|
color: isSelected ? AppColor.primary : AppColor.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
label,
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
|
||||||
|
color: isSelected ? AppColor.primary : AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (isActive != null) ...[
|
||||||
|
Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: isActive! ? AppColor.success : AppColor.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
if (isSelected)
|
||||||
|
const Icon(Icons.check_rounded, color: AppColor.primary, size: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
165
lib/presentation/pages/purchase/widgets/purchase_daily_tile.dart
Normal file
165
lib/presentation/pages/purchase/widgets/purchase_daily_tile.dart
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
|
||||||
|
import '../../../../common/extension/extension.dart';
|
||||||
|
import '../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../domain/analytic/analytic.dart';
|
||||||
|
|
||||||
|
class PurchaseDailyTile extends StatelessWidget {
|
||||||
|
final PurchasingAnalyticData data;
|
||||||
|
final int index;
|
||||||
|
final Animation<double> animation;
|
||||||
|
|
||||||
|
const PurchaseDailyTile({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
required this.index,
|
||||||
|
required this.animation,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.surface,
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
border: Border.all(color: AppColor.border.withOpacity(0.25)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.04),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: ExpansionTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
),
|
||||||
|
collapsedShape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
),
|
||||||
|
leading: Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.primary.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
LineIcons.calendar,
|
||||||
|
color: AppColor.primary,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
data.date.toDate,
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
data.purchases.currencyFormatRp,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.primary,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.info.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'${data.purchaseOrders} PO',
|
||||||
|
style: AppStyle.xs.copyWith(
|
||||||
|
color: AppColor.info,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _DetailItem(
|
||||||
|
icon: LineIcons.boxes,
|
||||||
|
label: 'Qty',
|
||||||
|
value: '${data.quantity} pcs',
|
||||||
|
color: AppColor.warning,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _DetailItem(
|
||||||
|
icon: LineIcons.leaf,
|
||||||
|
label: 'Bahan',
|
||||||
|
value: '${data.ingredients} item',
|
||||||
|
color: AppColor.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _DetailItem(
|
||||||
|
icon: LineIcons.truck,
|
||||||
|
label: 'Vendor',
|
||||||
|
value: '${data.vendors}',
|
||||||
|
color: AppColor.primaryDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DetailItem extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String label;
|
||||||
|
final String value;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const _DetailItem({
|
||||||
|
required this.icon,
|
||||||
|
required this.label,
|
||||||
|
required this.value,
|
||||||
|
required this.color,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Icon(icon, color: color, size: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,314 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:line_icons/line_icons.dart';
|
|
||||||
|
|
||||||
import '../../../../common/extension/extension.dart';
|
|
||||||
import '../../../../common/theme/theme.dart';
|
|
||||||
|
|
||||||
class PurchaseTile extends StatelessWidget {
|
|
||||||
final Map<String, dynamic> purchase;
|
|
||||||
final int index;
|
|
||||||
const PurchaseTile({super.key, required this.purchase, required this.index});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Color statusColor;
|
|
||||||
|
|
||||||
switch (purchase['status']) {
|
|
||||||
case 'Completed':
|
|
||||||
statusColor = AppColor.success;
|
|
||||||
break;
|
|
||||||
case 'Pending':
|
|
||||||
statusColor = AppColor.warning;
|
|
||||||
break;
|
|
||||||
case 'Cancelled':
|
|
||||||
statusColor = AppColor.error;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
statusColor = AppColor.textSecondary;
|
|
||||||
}
|
|
||||||
return AnimatedContainer(
|
|
||||||
duration: Duration(milliseconds: 300 + (index * 50)),
|
|
||||||
curve: Curves.easeOutCubic,
|
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
|
||||||
child: Material(
|
|
||||||
elevation: 0,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [AppColor.surface, AppColor.surface.withOpacity(0.95)],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
border: Border.all(
|
|
||||||
color: AppColor.border.withOpacity(0.2),
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: AppColor.primary.withOpacity(0.08),
|
|
||||||
blurRadius: 25,
|
|
||||||
offset: const Offset(0, 10),
|
|
||||||
spreadRadius: 0,
|
|
||||||
),
|
|
||||||
BoxShadow(
|
|
||||||
color: AppColor.black.withOpacity(0.04),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 6,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: AppColor.primaryGradient,
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: AppColor.primary.withOpacity(0.3),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
purchase['id'],
|
|
||||||
style: AppStyle.sm.copyWith(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
color: AppColor.textWhite,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
statusColor.withOpacity(0.15),
|
|
||||||
statusColor.withOpacity(0.05),
|
|
||||||
],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
border: Border.all(
|
|
||||||
color: statusColor.withOpacity(0.2),
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: statusColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
purchase['status'],
|
|
||||||
style: AppStyle.xs.copyWith(
|
|
||||||
color: statusColor,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.background.withOpacity(0.5),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(
|
|
||||||
color: AppColor.border.withOpacity(0.3),
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.primary.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
LineIcons.building,
|
|
||||||
color: AppColor.primary,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
purchase['supplier'],
|
|
||||||
style: AppStyle.md.copyWith(
|
|
||||||
color: AppColor.textPrimary,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.info.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
LineIcons.calendar,
|
|
||||||
color: AppColor.info,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
|
||||||
purchase['date'],
|
|
||||||
style: AppStyle.sm.copyWith(
|
|
||||||
color: AppColor.textSecondary,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8,
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.secondary.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LineIcons.shoppingBag,
|
|
||||||
color: AppColor.secondary,
|
|
||||||
size: 14,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'${purchase['items']} ${context.lang.items}',
|
|
||||||
style: AppStyle.xs.copyWith(
|
|
||||||
color: AppColor.secondary,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
context.lang.total_purchase,
|
|
||||||
style: AppStyle.xs.copyWith(
|
|
||||||
color: AppColor.textSecondary,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Rp ${purchase['total'].toString().replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]}.')}',
|
|
||||||
style: AppStyle.lg.copyWith(
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
color: AppColor.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
_buildActionButton(LineIcons.eye, AppColor.info, () {}),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildActionButton(
|
|
||||||
LineIcons.edit,
|
|
||||||
AppColor.warning,
|
|
||||||
() {},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_buildActionButton(
|
|
||||||
LineIcons.trash,
|
|
||||||
AppColor.error,
|
|
||||||
() {},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildActionButton(
|
|
||||||
IconData icon,
|
|
||||||
Color color,
|
|
||||||
VoidCallback onPressed,
|
|
||||||
) {
|
|
||||||
return Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [color.withOpacity(0.15), color.withOpacity(0.05)],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(color: color.withOpacity(0.2), width: 1),
|
|
||||||
),
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
onTap: onPressed,
|
|
||||||
child: Center(child: Icon(icon, color: color, size: 18)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,6 +8,7 @@ class PurchaseStatCard extends StatelessWidget {
|
|||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Color iconColor;
|
final Color iconColor;
|
||||||
final Animation<double> cardAnimation;
|
final Animation<double> cardAnimation;
|
||||||
|
|
||||||
const PurchaseStatCard({
|
const PurchaseStatCard({
|
||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
@ -25,65 +26,41 @@ class PurchaseStatCard extends StatelessWidget {
|
|||||||
return Transform.scale(
|
return Transform.scale(
|
||||||
scale: 0.8 + (cardAnimation.value * 0.2),
|
scale: 0.8 + (cardAnimation.value * 0.2),
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: cardAnimation.value,
|
opacity: cardAnimation.value.clamp(0.0, 1.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
color: AppColor.surface,
|
||||||
colors: [AppColor.surface, AppColor.surface.withOpacity(0.9)],
|
borderRadius: BorderRadius.circular(16),
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColor.primary.withOpacity(0.1),
|
color: AppColor.primary.withOpacity(0.08),
|
||||||
blurRadius: 20,
|
blurRadius: 16,
|
||||||
offset: const Offset(0, 8),
|
offset: const Offset(0, 6),
|
||||||
spreadRadius: 0,
|
|
||||||
),
|
),
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColor.black.withOpacity(0.05),
|
color: AppColor.black.withOpacity(0.04),
|
||||||
blurRadius: 10,
|
blurRadius: 8,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: AppColor.border.withOpacity(0.3),
|
color: AppColor.border.withOpacity(0.25),
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
padding: const EdgeInsets.all(10),
|
||||||
children: [
|
decoration: BoxDecoration(
|
||||||
Container(
|
color: iconColor.withOpacity(0.12),
|
||||||
padding: const EdgeInsets.all(12),
|
borderRadius: BorderRadius.circular(10),
|
||||||
decoration: BoxDecoration(
|
),
|
||||||
gradient: LinearGradient(
|
child: Icon(icon, color: iconColor, size: 22),
|
||||||
colors: [
|
|
||||||
iconColor.withOpacity(0.15),
|
|
||||||
iconColor.withOpacity(0.05),
|
|
||||||
],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: iconColor.withOpacity(0.2),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Icon(icon, color: iconColor, size: 24),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: AppStyle.sm.copyWith(
|
style: AppStyle.sm.copyWith(
|
||||||
@ -91,14 +68,16 @@ class PurchaseStatCard extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: AppStyle.lg.copyWith(
|
style: AppStyle.md.copyWith(
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
color: AppColor.textPrimary,
|
color: AppColor.textPrimary,
|
||||||
height: 1.2,
|
height: 1.2,
|
||||||
),
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../../../common/theme/theme.dart';
|
|
||||||
|
|
||||||
class PurchaseStatusChip extends StatelessWidget {
|
|
||||||
final bool isSelected;
|
|
||||||
final String text;
|
|
||||||
final Function(bool) onSelected;
|
|
||||||
const PurchaseStatusChip({
|
|
||||||
super.key,
|
|
||||||
required this.isSelected,
|
|
||||||
required this.text,
|
|
||||||
required this.onSelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8),
|
|
||||||
child: FilterChip(
|
|
||||||
selected: isSelected,
|
|
||||||
label: Text(
|
|
||||||
text,
|
|
||||||
style: AppStyle.sm.copyWith(
|
|
||||||
color: isSelected ? AppColor.textWhite : AppColor.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
backgroundColor: AppColor.backgroundLight,
|
|
||||||
selectedColor: AppColor.primary,
|
|
||||||
onSelected: onSelected,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
153
lib/presentation/pages/purchase/widgets/vendor_card.dart
Normal file
153
lib/presentation/pages/purchase/widgets/vendor_card.dart
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
|
||||||
|
import '../../../../common/extension/extension.dart';
|
||||||
|
import '../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../domain/analytic/analytic.dart';
|
||||||
|
|
||||||
|
class PurchaseVendorCard extends StatelessWidget {
|
||||||
|
final PurchasingVendorData data;
|
||||||
|
final int index;
|
||||||
|
final Animation<double> animation;
|
||||||
|
|
||||||
|
const PurchaseVendorCard({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
required this.index,
|
||||||
|
required this.animation,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.surface,
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
border: Border.all(color: AppColor.border.withOpacity(0.25)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.04),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// Rank badge
|
||||||
|
Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
colors: AppColor.primaryGradient,
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'${index + 1}',
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.textWhite,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
|
// Vendor icon
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.info.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
LineIcons.truck,
|
||||||
|
color: AppColor.info,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
|
||||||
|
// Name & details
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
data.vendorName.trim(),
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_Chip(
|
||||||
|
label: '${data.purchaseOrderCount} PO',
|
||||||
|
color: AppColor.info,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
_Chip(
|
||||||
|
label: '${data.ingredientCount} bahan',
|
||||||
|
color: AppColor.secondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
_Chip(
|
||||||
|
label: '${data.quantity} pcs',
|
||||||
|
color: AppColor.warning,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Total cost
|
||||||
|
Text(
|
||||||
|
data.totalCost.currencyFormatRp,
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
color: AppColor.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Chip extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const _Chip({required this.label, required this.color});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: AppStyle.xs.copyWith(
|
||||||
|
color: color,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -501,7 +501,7 @@ class PurchaseRoute extends _i26.PageRouteInfo<void> {
|
|||||||
static _i26.PageInfo page = _i26.PageInfo(
|
static _i26.PageInfo page = _i26.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i21.PurchasePage();
|
return _i26.WrappedRoute(child: const _i21.PurchasePage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
38
pubspec.lock
38
pubspec.lock
@ -213,10 +213,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
checked_yaml:
|
checked_yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -865,26 +865,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.9"
|
version: "11.0.2"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.9"
|
version: "3.0.10"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_testing
|
name: leak_tracker_testing
|
||||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
line_icons:
|
line_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -921,26 +921,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.17"
|
version: "0.12.19"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.13.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1510,10 +1510,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
version: "0.7.10"
|
||||||
time:
|
time:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1646,10 +1646,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.2.0"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1731,5 +1731,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.1 <4.0.0"
|
dart: ">=3.9.0-0 <4.0.0"
|
||||||
flutter: ">=3.29.0"
|
flutter: ">=3.29.0"
|
||||||
|
|||||||
@ -3,7 +3,7 @@ description: "A new Flutter project."
|
|||||||
|
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.0.0+1
|
version: 1.0.1+2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user