dev #1

Merged
aefril merged 128 commits from dev into main 2025-08-13 17:19:48 +00:00
7 changed files with 325 additions and 46 deletions
Showing only changes of commit ffe076e120 - Show all commits

View File

@ -587,4 +587,39 @@ class OrderRemoteDatasource {
return const Left('Terjadi kesalahan tak terduga');
}
}
Future<Either<String, PaymentSuccessResponseModel>> createPaymentSplitBill(
PaymentSplitBillRequest request) async {
final authData = await AuthLocalDataSource().getAuthData();
final url = '${Variables.baseUrl}/api/v1/orders/split-bill';
try {
final response = await dio.post(
url,
data: request.toMap(),
options: Options(
headers: {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
),
);
if (response.statusCode == 200) {
return Right(PaymentSuccessResponseModel.fromMap(response.data));
} else {
return const Left('Gagal membuat pembayaran');
}
} on DioException catch (e) {
final errorMessage =
e.response?.data['message'] ?? 'Terjadi kesalahan, coba lagi nanti.';
log("💥 Dio error: ${e.message}");
log("💥 Dio response: ${e.response?.data}");
return Left(errorMessage);
} catch (e) {
log("💥 Unexpected error: $e");
return const Left('Terjadi kesalahan tak terduga');
}
}
}

View File

@ -78,3 +78,57 @@ class PaymentOrderItemModel {
"amount": amount,
};
}
class PaymentSplitBillRequest {
final String orderId;
final String paymentMethodId;
final String customerId;
final String type; // e.g., "AMOUNT" or "ITEM"
final int amount;
final List<SplitItem> items;
PaymentSplitBillRequest({
required this.orderId,
required this.paymentMethodId,
required this.customerId,
required this.type,
required this.amount,
required this.items,
});
Map<String, dynamic> toMap() {
Map<String, dynamic> data = {
'order_id': orderId,
'payment_method_id': paymentMethodId,
'customer_id': customerId,
'type': type,
};
if (type == "AMOUNT") {
data['amount'] = amount;
}
if (type == "ITEM") {
data['items'] = items.map((e) => e.toJson()).toList();
}
return data;
}
}
class SplitItem {
final String orderItemId;
final int quantity;
SplitItem({
required this.orderItemId,
required this.quantity,
});
Map<String, dynamic> toJson() {
return {
'order_item_id': orderItemId,
'quantity': quantity,
};
}
}

View File

@ -17,7 +17,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class PaymentPage extends StatefulWidget {
final Order order;
const PaymentPage({Key? key, required this.order}) : super(key: key);
final bool isSplit;
final String? splitType;
const PaymentPage({
super.key,
required this.order,
this.isSplit = false,
this.splitType,
});
@override
State<PaymentPage> createState() => _PaymentPageState();
@ -434,7 +441,7 @@ class _PaymentPageState extends State<PaymentPage> {
final itemPending = widget.order.orderItems
?.where((item) => item.status == "pending")
.toList();
if (widget.isSplit == false) {
final request = PaymentRequestModel(
amount: widget.order.totalAmount ?? 0,
orderId: widget.order.id,
@ -452,5 +459,24 @@ class _PaymentPageState extends State<PaymentPage> {
);
context.read<PaymentFormBloc>().add(PaymentFormEvent.create(request));
} else {
final request = PaymentSplitBillRequest(
amount: widget.order.totalAmount ?? 0,
customerId: '',
items: itemPending
?.map((item) => SplitItem(
orderItemId: item.id ?? "",
quantity: item.quantity ?? 0,
))
.toList() ??
[],
orderId: widget.order.id ?? "",
paymentMethodId: selectedPaymentMethod?.id ?? "",
type: widget.splitType ?? "",
);
context.read<PaymentFormBloc>().add(
PaymentFormEvent.createSplitBill(request),
);
}
}
}

View File

@ -29,5 +29,22 @@ class PaymentFormBloc extends Bloc<PaymentFormEvent, PaymentFormState> {
}
},
);
on<_CreateSplitBill>(
(event, emit) async {
emit(const _Loading());
try {
final result = await _orderRemoteDatasource
.createPaymentSplitBill(event.payment);
result.fold(
(error) => emit(_Error(error)),
(success) => emit(_Success(success.data!)),
);
} catch (e) {
emit(_Error("Failed to create payment split bill: $e"));
}
},
);
}
}

View File

@ -16,45 +16,45 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$PaymentFormEvent {
PaymentRequestModel get payment => throw _privateConstructorUsedError;
Object get payment => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(PaymentRequestModel payment) create,
required TResult Function(PaymentSplitBillRequest payment) createSplitBill,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(PaymentRequestModel payment)? create,
TResult? Function(PaymentSplitBillRequest payment)? createSplitBill,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(PaymentRequestModel payment)? create,
TResult Function(PaymentSplitBillRequest payment)? createSplitBill,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Create value) create,
required TResult Function(_CreateSplitBill value) createSplitBill,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Create value)? create,
TResult? Function(_CreateSplitBill value)? createSplitBill,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Create value)? create,
TResult Function(_CreateSplitBill value)? createSplitBill,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
/// Create a copy of PaymentFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$PaymentFormEventCopyWith<PaymentFormEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
@ -62,8 +62,6 @@ abstract class $PaymentFormEventCopyWith<$Res> {
factory $PaymentFormEventCopyWith(
PaymentFormEvent value, $Res Function(PaymentFormEvent) then) =
_$PaymentFormEventCopyWithImpl<$Res, PaymentFormEvent>;
@useResult
$Res call({PaymentRequestModel payment});
}
/// @nodoc
@ -78,27 +76,13 @@ class _$PaymentFormEventCopyWithImpl<$Res, $Val extends PaymentFormEvent>
/// Create a copy of PaymentFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? payment = null,
}) {
return _then(_value.copyWith(
payment: null == payment
? _value.payment
: payment // ignore: cast_nullable_to_non_nullable
as PaymentRequestModel,
) as $Val);
}
}
/// @nodoc
abstract class _$$CreateImplCopyWith<$Res>
implements $PaymentFormEventCopyWith<$Res> {
abstract class _$$CreateImplCopyWith<$Res> {
factory _$$CreateImplCopyWith(
_$CreateImpl value, $Res Function(_$CreateImpl) then) =
__$$CreateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({PaymentRequestModel payment});
}
@ -163,6 +147,7 @@ class _$CreateImpl implements _Create {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(PaymentRequestModel payment) create,
required TResult Function(PaymentSplitBillRequest payment) createSplitBill,
}) {
return create(payment);
}
@ -171,6 +156,7 @@ class _$CreateImpl implements _Create {
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(PaymentRequestModel payment)? create,
TResult? Function(PaymentSplitBillRequest payment)? createSplitBill,
}) {
return create?.call(payment);
}
@ -179,6 +165,7 @@ class _$CreateImpl implements _Create {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(PaymentRequestModel payment)? create,
TResult Function(PaymentSplitBillRequest payment)? createSplitBill,
required TResult orElse(),
}) {
if (create != null) {
@ -191,6 +178,7 @@ class _$CreateImpl implements _Create {
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Create value) create,
required TResult Function(_CreateSplitBill value) createSplitBill,
}) {
return create(this);
}
@ -199,6 +187,7 @@ class _$CreateImpl implements _Create {
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Create value)? create,
TResult? Function(_CreateSplitBill value)? createSplitBill,
}) {
return create?.call(this);
}
@ -207,6 +196,7 @@ class _$CreateImpl implements _Create {
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Create value)? create,
TResult Function(_CreateSplitBill value)? createSplitBill,
required TResult orElse(),
}) {
if (create != null) {
@ -224,12 +214,154 @@ abstract class _Create implements PaymentFormEvent {
/// Create a copy of PaymentFormEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CreateImplCopyWith<_$CreateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$CreateSplitBillImplCopyWith<$Res> {
factory _$$CreateSplitBillImplCopyWith(_$CreateSplitBillImpl value,
$Res Function(_$CreateSplitBillImpl) then) =
__$$CreateSplitBillImplCopyWithImpl<$Res>;
@useResult
$Res call({PaymentSplitBillRequest payment});
}
/// @nodoc
class __$$CreateSplitBillImplCopyWithImpl<$Res>
extends _$PaymentFormEventCopyWithImpl<$Res, _$CreateSplitBillImpl>
implements _$$CreateSplitBillImplCopyWith<$Res> {
__$$CreateSplitBillImplCopyWithImpl(
_$CreateSplitBillImpl _value, $Res Function(_$CreateSplitBillImpl) _then)
: super(_value, _then);
/// Create a copy of PaymentFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? payment = null,
}) {
return _then(_$CreateSplitBillImpl(
null == payment
? _value.payment
: payment // ignore: cast_nullable_to_non_nullable
as PaymentSplitBillRequest,
));
}
}
/// @nodoc
class _$CreateSplitBillImpl implements _CreateSplitBill {
const _$CreateSplitBillImpl(this.payment);
@override
final PaymentSplitBillRequest payment;
@override
String toString() {
return 'PaymentFormEvent.createSplitBill(payment: $payment)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CreateSplitBillImpl &&
(identical(other.payment, payment) || other.payment == payment));
}
@override
int get hashCode => Object.hash(runtimeType, payment);
/// Create a copy of PaymentFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CreateSplitBillImplCopyWith<_$CreateSplitBillImpl> get copyWith =>
__$$CreateSplitBillImplCopyWithImpl<_$CreateSplitBillImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(PaymentRequestModel payment) create,
required TResult Function(PaymentSplitBillRequest payment) createSplitBill,
}) {
return createSplitBill(payment);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(PaymentRequestModel payment)? create,
TResult? Function(PaymentSplitBillRequest payment)? createSplitBill,
}) {
return createSplitBill?.call(payment);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(PaymentRequestModel payment)? create,
TResult Function(PaymentSplitBillRequest payment)? createSplitBill,
required TResult orElse(),
}) {
if (createSplitBill != null) {
return createSplitBill(payment);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Create value) create,
required TResult Function(_CreateSplitBill value) createSplitBill,
}) {
return createSplitBill(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Create value)? create,
TResult? Function(_CreateSplitBill value)? createSplitBill,
}) {
return createSplitBill?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Create value)? create,
TResult Function(_CreateSplitBill value)? createSplitBill,
required TResult orElse(),
}) {
if (createSplitBill != null) {
return createSplitBill(this);
}
return orElse();
}
}
abstract class _CreateSplitBill implements PaymentFormEvent {
const factory _CreateSplitBill(final PaymentSplitBillRequest payment) =
_$CreateSplitBillImpl;
@override
PaymentSplitBillRequest get payment;
/// Create a copy of PaymentFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CreateSplitBillImplCopyWith<_$CreateSplitBillImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$PaymentFormState {
@optionalTypeArgs

View File

@ -2,5 +2,10 @@ part of 'payment_form_bloc.dart';
@freezed
class PaymentFormEvent with _$PaymentFormEvent {
const factory PaymentFormEvent.create(PaymentRequestModel payment) = _Create;
const factory PaymentFormEvent.create(
PaymentRequestModel payment,
) = _Create;
const factory PaymentFormEvent.createSplitBill(
PaymentSplitBillRequest payment,
) = _CreateSplitBill;
}

View File

@ -760,7 +760,11 @@ class _SplitBillPageState extends State<SplitBillPage> {
log("Split Order: ${splitItems.length}");
// Navigate to PaymentPage with the split order
context.push(PaymentPage(order: splitOrder));
context.push(PaymentPage(
order: splitOrder,
isSplit: true,
splitType: 'ITEM',
));
} else {
AppFlushbar.showError(context, "Pilih minimal satu produk untuk split");
}
@ -781,7 +785,13 @@ class _SplitBillPageState extends State<SplitBillPage> {
);
// Navigate to PaymentPage with the split order
context.push(PaymentPage(order: splitOrder));
context.push(
PaymentPage(
order: splitOrder,
isSplit: true,
splitType: 'AMOUNT',
),
);
} else if (splitAmount > totalAmount) {
AppFlushbar.showError(
context, "Jumlah split tidak boleh melebihi total bill");