feat: sync payment methods
This commit is contained in:
parent
7678b4791d
commit
39e3fdc81c
@ -1,34 +1,40 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:enaklo_pos/core/constants/variables.dart';
|
import 'package:enaklo_pos/core/constants/variables.dart';
|
||||||
|
import 'package:enaklo_pos/core/network/dio_client.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
class PaymentMethodsRemoteDatasource {
|
class PaymentMethodsRemoteDatasource {
|
||||||
Future<Either<String, PaymentMethodsResponseModel>> getPaymentMethods() async {
|
final Dio dio = DioClient.instance;
|
||||||
|
Future<Either<String, PaymentMethodsResponseModel>>
|
||||||
|
getPaymentMethods() async {
|
||||||
try {
|
try {
|
||||||
final authData = await AuthLocalDataSource().getAuthData();
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
final response = await http.get(
|
final response = await dio.get(
|
||||||
Uri.parse('${Variables.baseUrl}/api/payment-methods'),
|
'${Variables.baseUrl}/api/v1/payment-methods',
|
||||||
headers: {
|
options: Options(
|
||||||
'Authorization': 'Bearer ${authData.token}',
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Authorization': 'Bearer ${authData.token}',
|
||||||
},
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
log("Payment Methods Response Status: ${response.statusCode}");
|
|
||||||
log("Payment Methods Response Body: ${response.body}");
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return Right(PaymentMethodsResponseModel.fromJson(response.body));
|
return Right(PaymentMethodsResponseModel.fromMap(response.data));
|
||||||
} else {
|
} else {
|
||||||
return const Left('Failed to get payment methods');
|
return const Left('Failed to get payment methods');
|
||||||
}
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
|
log("Dio error: ${e.message}");
|
||||||
|
return Left(
|
||||||
|
e.response?.data['message'] ?? 'Failed to get payment methods');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("Error getting payment methods: $e");
|
log("Error getting payment methods: $e");
|
||||||
return Left('Error: $e');
|
return Left('Unexpected error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,14 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
class PaymentMethodsResponseModel {
|
class PaymentMethodsResponseModel {
|
||||||
final String? status;
|
final bool? success;
|
||||||
final List<PaymentMethod>? data;
|
final PaymentMethodsData? data;
|
||||||
|
final dynamic errors;
|
||||||
|
|
||||||
PaymentMethodsResponseModel({
|
PaymentMethodsResponseModel({
|
||||||
this.status,
|
this.success,
|
||||||
this.data,
|
this.data,
|
||||||
|
this.errors,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PaymentMethodsResponseModel.fromJson(String str) =>
|
factory PaymentMethodsResponseModel.fromJson(String str) =>
|
||||||
@ -16,51 +18,83 @@ class PaymentMethodsResponseModel {
|
|||||||
|
|
||||||
factory PaymentMethodsResponseModel.fromMap(Map<String, dynamic> json) =>
|
factory PaymentMethodsResponseModel.fromMap(Map<String, dynamic> json) =>
|
||||||
PaymentMethodsResponseModel(
|
PaymentMethodsResponseModel(
|
||||||
status: json["status"],
|
success: json["success"],
|
||||||
data: json["data"] == null
|
data: json["data"] == null
|
||||||
? []
|
? null
|
||||||
: List<PaymentMethod>.from(
|
: PaymentMethodsData.fromMap(json["data"]),
|
||||||
json["data"]!.map((x) => PaymentMethod.fromMap(x))),
|
errors: json["errors"],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
"status": status,
|
"success": success,
|
||||||
"data": data == null
|
"data": data?.toMap(),
|
||||||
|
"errors": errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentMethodsData {
|
||||||
|
final List<PaymentMethod>? paymentMethods;
|
||||||
|
final int? totalCount;
|
||||||
|
final int? page;
|
||||||
|
final int? limit;
|
||||||
|
final int? totalPages;
|
||||||
|
|
||||||
|
PaymentMethodsData({
|
||||||
|
this.paymentMethods,
|
||||||
|
this.totalCount,
|
||||||
|
this.page,
|
||||||
|
this.limit,
|
||||||
|
this.totalPages,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PaymentMethodsData.fromMap(Map<String, dynamic> json) =>
|
||||||
|
PaymentMethodsData(
|
||||||
|
paymentMethods: json["payment_methods"] == null
|
||||||
? []
|
? []
|
||||||
: List<dynamic>.from(data!.map((x) => x.toMap())),
|
: List<PaymentMethod>.from(
|
||||||
|
json["payment_methods"].map((x) => PaymentMethod.fromMap(x))),
|
||||||
|
totalCount: json["total_count"],
|
||||||
|
page: json["page"],
|
||||||
|
limit: json["limit"],
|
||||||
|
totalPages: json["total_pages"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() => {
|
||||||
|
"payment_methods": paymentMethods == null
|
||||||
|
? []
|
||||||
|
: List<dynamic>.from(paymentMethods!.map((x) => x.toMap())),
|
||||||
|
"total_count": totalCount,
|
||||||
|
"page": page,
|
||||||
|
"limit": limit,
|
||||||
|
"total_pages": totalPages,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaymentMethod {
|
class PaymentMethod {
|
||||||
final int? id;
|
final String? id;
|
||||||
|
final String? organizationId;
|
||||||
final String? name;
|
final String? name;
|
||||||
final String? description;
|
final String? type;
|
||||||
final bool? isActive;
|
final bool? isActive;
|
||||||
final int? sortOrder;
|
|
||||||
final DateTime? createdAt;
|
final DateTime? createdAt;
|
||||||
final DateTime? updatedAt;
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
PaymentMethod({
|
PaymentMethod({
|
||||||
this.id,
|
this.id,
|
||||||
|
this.organizationId,
|
||||||
this.name,
|
this.name,
|
||||||
this.description,
|
this.type,
|
||||||
this.isActive,
|
this.isActive,
|
||||||
this.sortOrder,
|
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PaymentMethod.fromJson(String str) =>
|
|
||||||
PaymentMethod.fromMap(json.decode(str));
|
|
||||||
|
|
||||||
String toJson() => json.encode(toMap());
|
|
||||||
|
|
||||||
factory PaymentMethod.fromMap(Map<String, dynamic> json) => PaymentMethod(
|
factory PaymentMethod.fromMap(Map<String, dynamic> json) => PaymentMethod(
|
||||||
id: json["id"],
|
id: json["id"],
|
||||||
|
organizationId: json["organization_id"],
|
||||||
name: json["name"],
|
name: json["name"],
|
||||||
description: json["description"],
|
type: json["type"],
|
||||||
isActive: json["is_active"],
|
isActive: json["is_active"],
|
||||||
sortOrder: json["sort_order"],
|
|
||||||
createdAt: json["created_at"] == null
|
createdAt: json["created_at"] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json["created_at"]),
|
: DateTime.parse(json["created_at"]),
|
||||||
@ -71,10 +105,10 @@ class PaymentMethod {
|
|||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
"id": id,
|
"id": id,
|
||||||
|
"organization_id": organizationId,
|
||||||
"name": name,
|
"name": name,
|
||||||
"description": description,
|
"type": type,
|
||||||
"is_active": isActive,
|
"is_active": isActive,
|
||||||
"sort_order": sortOrder,
|
|
||||||
"created_at": createdAt?.toIso8601String(),
|
"created_at": createdAt?.toIso8601String(),
|
||||||
"updated_at": updatedAt?.toIso8601String(),
|
"updated_at": updatedAt?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,8 @@ part 'payment_methods_event.dart';
|
|||||||
part 'payment_methods_state.dart';
|
part 'payment_methods_state.dart';
|
||||||
part 'payment_methods_bloc.freezed.dart';
|
part 'payment_methods_bloc.freezed.dart';
|
||||||
|
|
||||||
class PaymentMethodsBloc extends Bloc<PaymentMethodsEvent, PaymentMethodsState> {
|
class PaymentMethodsBloc
|
||||||
|
extends Bloc<PaymentMethodsEvent, PaymentMethodsState> {
|
||||||
final PaymentMethodsRemoteDatasource datasource;
|
final PaymentMethodsRemoteDatasource datasource;
|
||||||
|
|
||||||
PaymentMethodsBloc(this.datasource) : super(const _Initial()) {
|
PaymentMethodsBloc(this.datasource) : super(const _Initial()) {
|
||||||
@ -17,7 +18,7 @@ class PaymentMethodsBloc extends Bloc<PaymentMethodsEvent, PaymentMethodsState>
|
|||||||
final response = await datasource.getPaymentMethods();
|
final response = await datasource.getPaymentMethods();
|
||||||
response.fold(
|
response.fold(
|
||||||
(l) => emit(_Error(l)),
|
(l) => emit(_Error(l)),
|
||||||
(r) => emit(_Loaded(r.data ?? [])),
|
(r) => emit(_Loaded(r.data?.paymentMethods ?? [])),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,11 +72,13 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
|
|
||||||
// Set a default payment method in case API fails
|
// Set a default payment method in case API fails
|
||||||
selectedPaymentMethod = PaymentMethod(
|
selectedPaymentMethod = PaymentMethod(
|
||||||
id: 1,
|
id: "4b1c0d21-c98a-4fc0-a2f9-8d90a0c9d905",
|
||||||
name: 'Cash',
|
organizationId: "3e8b1793-d18b-40c4-a03d-0c6480b630c7",
|
||||||
description: 'Cash payment',
|
name: "CASH",
|
||||||
|
type: "cash",
|
||||||
isActive: true,
|
isActive: true,
|
||||||
sortOrder: 1,
|
createdAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'),
|
||||||
|
updatedAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'),
|
||||||
);
|
);
|
||||||
// if (selectTable == null && widget.table != null) {
|
// if (selectTable == null && widget.table != null) {
|
||||||
// selectTable = tables.firstWhere(
|
// selectTable = tables.firstWhere(
|
||||||
@ -1041,41 +1043,37 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
8.0),
|
8.0),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
child: Tooltip(
|
child: isSelected
|
||||||
message: method.description ??
|
? Button.filled(
|
||||||
'No description available',
|
width: double.infinity,
|
||||||
child: isSelected
|
height: 50.0,
|
||||||
? Button.filled(
|
onPressed: () {
|
||||||
width: double.infinity,
|
setState(() {
|
||||||
height: 50.0,
|
selectedPaymentMethod =
|
||||||
onPressed: () {
|
method;
|
||||||
setState(() {
|
});
|
||||||
selectedPaymentMethod =
|
},
|
||||||
method;
|
label: method.name
|
||||||
});
|
?.isNotEmpty ==
|
||||||
},
|
true
|
||||||
label: method.name
|
? method.name!
|
||||||
?.isNotEmpty ==
|
: 'Unknown',
|
||||||
true
|
)
|
||||||
? method.name!
|
: Button.outlined(
|
||||||
: 'Unknown',
|
width: double.infinity,
|
||||||
)
|
height: 50.0,
|
||||||
: Button.outlined(
|
onPressed: () {
|
||||||
width: double.infinity,
|
setState(() {
|
||||||
height: 50.0,
|
selectedPaymentMethod =
|
||||||
onPressed: () {
|
method;
|
||||||
setState(() {
|
});
|
||||||
selectedPaymentMethod =
|
},
|
||||||
method;
|
label: method.name
|
||||||
});
|
?.isNotEmpty ==
|
||||||
},
|
true
|
||||||
label: method.name
|
? method.name!
|
||||||
?.isNotEmpty ==
|
: 'Unknown',
|
||||||
true
|
),
|
||||||
? method.name!
|
|
||||||
: 'Unknown',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -65,11 +65,13 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
|
|
||||||
// Set a default payment method in case API fails
|
// Set a default payment method in case API fails
|
||||||
selectedPaymentMethod = PaymentMethod(
|
selectedPaymentMethod = PaymentMethod(
|
||||||
id: 1,
|
id: "4b1c0d21-c98a-4fc0-a2f9-8d90a0c9d905",
|
||||||
name: 'Cash',
|
organizationId: "3e8b1793-d18b-40c4-a03d-0c6480b630c7",
|
||||||
description: 'Cash payment',
|
name: "CASH",
|
||||||
|
type: "cash",
|
||||||
isActive: true,
|
isActive: true,
|
||||||
sortOrder: 1,
|
createdAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'),
|
||||||
|
updatedAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'),
|
||||||
);
|
);
|
||||||
// if (selectTable == null && widget.table != null) {
|
// if (selectTable == null && widget.table != null) {
|
||||||
// selectTable = tables.firstWhere(
|
// selectTable = tables.firstWhere(
|
||||||
@ -517,149 +519,149 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
subtitle: 'Silahkan lakukan pembayaran',
|
subtitle: 'Silahkan lakukan pembayaran',
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: SingleChildScrollView(
|
||||||
children: [
|
child: Column(
|
||||||
Container(
|
children: [
|
||||||
padding: const EdgeInsets.all(16),
|
Container(
|
||||||
decoration: BoxDecoration(
|
padding: const EdgeInsets.all(16),
|
||||||
color: AppColors.white,
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
color: AppColors.white,
|
||||||
bottom: BorderSide(
|
border: Border(
|
||||||
color: AppColors.grey,
|
bottom: BorderSide(
|
||||||
width: 1.0,
|
color: AppColors.grey,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
const Text(
|
||||||
const Text(
|
'Pelanggan',
|
||||||
'Pelanggan',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
color: AppColors.black,
|
||||||
color: AppColors.black,
|
fontSize: 16,
|
||||||
fontSize: 16,
|
fontWeight: FontWeight.w600,
|
||||||
fontWeight: FontWeight.w600,
|
),
|
||||||
),
|
),
|
||||||
),
|
const SpaceHeight(6.0),
|
||||||
const SpaceHeight(6.0),
|
TextFormField(
|
||||||
TextFormField(
|
controller: customerController,
|
||||||
controller: customerController,
|
decoration: InputDecoration(
|
||||||
decoration: InputDecoration(
|
hintText: 'Nama Pelanggan',
|
||||||
hintText: 'Nama Pelanggan',
|
),
|
||||||
|
textCapitalization:
|
||||||
|
TextCapitalization.words,
|
||||||
),
|
),
|
||||||
textCapitalization:
|
],
|
||||||
TextCapitalization.words,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
width: double.infinity,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColors.white,
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: AppColors.grey,
|
|
||||||
width: 1.0,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
Container(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding: const EdgeInsets.all(16),
|
||||||
children: [
|
width: double.infinity,
|
||||||
const Text(
|
decoration: BoxDecoration(
|
||||||
'Metode Pembayaran',
|
color: AppColors.white,
|
||||||
style: TextStyle(
|
border: Border(
|
||||||
color: AppColors.black,
|
bottom: BorderSide(
|
||||||
fontSize: 16,
|
color: AppColors.grey,
|
||||||
fontWeight: FontWeight.w600,
|
width: 1.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SpaceHeight(12.0),
|
),
|
||||||
BlocBuilder<PaymentMethodsBloc,
|
child: Column(
|
||||||
PaymentMethodsState>(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
builder: (context, state) {
|
children: [
|
||||||
return state.maybeWhen(
|
const Text(
|
||||||
orElse: () => const Center(
|
'Metode Pembayaran',
|
||||||
child: CircularProgressIndicator(),
|
style: TextStyle(
|
||||||
),
|
color: AppColors.black,
|
||||||
loading: () => const Center(
|
fontSize: 16,
|
||||||
child: Column(
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceHeight(12.0),
|
||||||
|
BlocBuilder<PaymentMethodsBloc,
|
||||||
|
PaymentMethodsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.maybeWhen(
|
||||||
|
orElse: () => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
loading: () => const Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(),
|
||||||
|
SizedBox(height: 8.0),
|
||||||
|
Text(
|
||||||
|
'Loading payment methods...'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
error: (message) => Column(
|
||||||
children: [
|
children: [
|
||||||
CircularProgressIndicator(),
|
Center(
|
||||||
SizedBox(height: 8.0),
|
child: Text(
|
||||||
Text(
|
'Error loading payment methods: $message'),
|
||||||
'Loading payment methods...'),
|
),
|
||||||
|
const SpaceHeight(16.0),
|
||||||
|
Button.filled(
|
||||||
|
onPressed: () {
|
||||||
|
context
|
||||||
|
.read<
|
||||||
|
PaymentMethodsBloc>()
|
||||||
|
.add(PaymentMethodsEvent
|
||||||
|
.fetchPaymentMethods());
|
||||||
|
},
|
||||||
|
label: 'Retry',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
loaded: (paymentMethods) {
|
||||||
error: (message) => Column(
|
log("Loaded ${paymentMethods.length} payment methods");
|
||||||
children: [
|
paymentMethods.forEach((method) {
|
||||||
Center(
|
log("Payment method: ${method.name} (ID: ${method.id})");
|
||||||
child: Text(
|
});
|
||||||
'Error loading payment methods: $message'),
|
if (paymentMethods.isEmpty) {
|
||||||
),
|
return Column(
|
||||||
const SpaceHeight(16.0),
|
children: [
|
||||||
Button.filled(
|
const Center(
|
||||||
onPressed: () {
|
child: Text(
|
||||||
context
|
'No payment methods available'),
|
||||||
.read<PaymentMethodsBloc>()
|
),
|
||||||
.add(PaymentMethodsEvent
|
const SpaceHeight(16.0),
|
||||||
.fetchPaymentMethods());
|
Button.filled(
|
||||||
},
|
onPressed: () {
|
||||||
label: 'Retry',
|
context
|
||||||
),
|
.read<
|
||||||
],
|
PaymentMethodsBloc>()
|
||||||
),
|
.add(PaymentMethodsEvent
|
||||||
loaded: (paymentMethods) {
|
.fetchPaymentMethods());
|
||||||
log("Loaded ${paymentMethods.length} payment methods");
|
},
|
||||||
paymentMethods.forEach((method) {
|
label: 'Retry',
|
||||||
log("Payment method: ${method.name} (ID: ${method.id})");
|
),
|
||||||
});
|
],
|
||||||
if (paymentMethods.isEmpty) {
|
);
|
||||||
return Column(
|
}
|
||||||
children: [
|
|
||||||
const Center(
|
|
||||||
child: Text(
|
|
||||||
'No payment methods available'),
|
|
||||||
),
|
|
||||||
const SpaceHeight(16.0),
|
|
||||||
Button.filled(
|
|
||||||
onPressed: () {
|
|
||||||
context
|
|
||||||
.read<
|
|
||||||
PaymentMethodsBloc>()
|
|
||||||
.add(PaymentMethodsEvent
|
|
||||||
.fetchPaymentMethods());
|
|
||||||
},
|
|
||||||
label: 'Retry',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default selected payment method if none selected or if current selection is not in the list
|
// Set default selected payment method if none selected or if current selection is not in the list
|
||||||
if (selectedPaymentMethod == null ||
|
if (selectedPaymentMethod == null ||
|
||||||
!paymentMethods.any((method) =>
|
!paymentMethods.any((method) =>
|
||||||
method.id ==
|
method.id ==
|
||||||
selectedPaymentMethod?.id)) {
|
selectedPaymentMethod
|
||||||
selectedPaymentMethod =
|
?.id)) {
|
||||||
paymentMethods.first;
|
selectedPaymentMethod =
|
||||||
}
|
paymentMethods.first;
|
||||||
|
}
|
||||||
|
|
||||||
return Wrap(
|
return Wrap(
|
||||||
spacing: 12.0,
|
spacing: 12.0,
|
||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children:
|
children:
|
||||||
paymentMethods.map((method) {
|
paymentMethods.map((method) {
|
||||||
final isSelected =
|
final isSelected =
|
||||||
selectedPaymentMethod?.id ==
|
selectedPaymentMethod?.id ==
|
||||||
method.id;
|
method.id;
|
||||||
return Tooltip(
|
return GestureDetector(
|
||||||
message: method.description ??
|
|
||||||
'No description available',
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedPaymentMethod =
|
selectedPaymentMethod =
|
||||||
@ -699,122 +701,124 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
TextAlign.center,
|
TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}).toList(),
|
||||||
}).toList(),
|
);
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (selectedPaymentMethod?.id == 1)
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColors.white,
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: AppColors.grey,
|
|
||||||
width: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Total Bayar',
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.black,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SpaceHeight(8.0),
|
|
||||||
BlocBuilder<CheckoutBloc, CheckoutState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return TextFormField(
|
|
||||||
controller: totalPriceController,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(8.0),
|
|
||||||
),
|
|
||||||
hintText: 'Total harga',
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
priceValue =
|
|
||||||
value.toIntegerFromText;
|
|
||||||
final int newValue =
|
|
||||||
value.toIntegerFromText;
|
|
||||||
totalPriceController.text =
|
|
||||||
newValue.currencyFormatRp;
|
|
||||||
totalPriceController.selection =
|
|
||||||
TextSelection.fromPosition(
|
|
||||||
TextPosition(
|
|
||||||
offset:
|
|
||||||
totalPriceController
|
|
||||||
.text.length));
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SpaceHeight(20.0),
|
|
||||||
BlocBuilder<CheckoutBloc, CheckoutState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Button.outlined(
|
|
||||||
width: 150.0,
|
|
||||||
onPressed: () {
|
|
||||||
totalPriceController.text =
|
|
||||||
uangPas
|
|
||||||
.toString()
|
|
||||||
.currencyFormatRpV2;
|
|
||||||
priceValue = uangPas;
|
|
||||||
},
|
|
||||||
label: 'UANG PAS',
|
|
||||||
),
|
|
||||||
const SpaceWidth(20.0),
|
|
||||||
Button.outlined(
|
|
||||||
width: 150.0,
|
|
||||||
onPressed: () {
|
|
||||||
totalPriceController.text =
|
|
||||||
uangPas2
|
|
||||||
.toString()
|
|
||||||
.currencyFormatRpV2;
|
|
||||||
priceValue = uangPas2;
|
|
||||||
},
|
|
||||||
label: uangPas2
|
|
||||||
.toString()
|
|
||||||
.currencyFormatRpV2,
|
|
||||||
),
|
|
||||||
const SpaceWidth(20.0),
|
|
||||||
Button.outlined(
|
|
||||||
width: 150.0,
|
|
||||||
onPressed: () {
|
|
||||||
totalPriceController.text =
|
|
||||||
uangPas3
|
|
||||||
.toString()
|
|
||||||
.currencyFormatRpV2;
|
|
||||||
priceValue = uangPas3;
|
|
||||||
},
|
|
||||||
label: uangPas3
|
|
||||||
.toString()
|
|
||||||
.currencyFormatRpV2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
if (selectedPaymentMethod?.type == "cash")
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.white,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: AppColors.grey,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Total Bayar',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SpaceHeight(8.0),
|
||||||
|
BlocBuilder<CheckoutBloc, CheckoutState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextFormField(
|
||||||
|
controller: totalPriceController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
hintText: 'Total harga',
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
priceValue =
|
||||||
|
value.toIntegerFromText;
|
||||||
|
final int newValue =
|
||||||
|
value.toIntegerFromText;
|
||||||
|
totalPriceController.text =
|
||||||
|
newValue.currencyFormatRp;
|
||||||
|
totalPriceController.selection =
|
||||||
|
TextSelection.fromPosition(
|
||||||
|
TextPosition(
|
||||||
|
offset:
|
||||||
|
totalPriceController
|
||||||
|
.text
|
||||||
|
.length));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SpaceHeight(20.0),
|
||||||
|
BlocBuilder<CheckoutBloc, CheckoutState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Button.outlined(
|
||||||
|
width: 150.0,
|
||||||
|
onPressed: () {
|
||||||
|
totalPriceController.text =
|
||||||
|
uangPas
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2;
|
||||||
|
priceValue = uangPas;
|
||||||
|
},
|
||||||
|
label: 'UANG PAS',
|
||||||
|
),
|
||||||
|
const SpaceWidth(20.0),
|
||||||
|
Button.outlined(
|
||||||
|
width: 150.0,
|
||||||
|
onPressed: () {
|
||||||
|
totalPriceController.text =
|
||||||
|
uangPas2
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2;
|
||||||
|
priceValue = uangPas2;
|
||||||
|
},
|
||||||
|
label: uangPas2
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
),
|
||||||
|
const SpaceWidth(20.0),
|
||||||
|
Button.outlined(
|
||||||
|
width: 150.0,
|
||||||
|
onPressed: () {
|
||||||
|
totalPriceController.text =
|
||||||
|
uangPas3
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2;
|
||||||
|
priceValue = uangPas3;
|
||||||
|
},
|
||||||
|
label: uangPas3
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
|||||||
@ -31,10 +31,10 @@ class PaymentTablePage extends StatefulWidget {
|
|||||||
final DraftOrderModel? draftOrder;
|
final DraftOrderModel? draftOrder;
|
||||||
final TableModel? table;
|
final TableModel? table;
|
||||||
const PaymentTablePage({
|
const PaymentTablePage({
|
||||||
Key? key,
|
super.key,
|
||||||
this.draftOrder,
|
this.draftOrder,
|
||||||
this.table,
|
this.table,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PaymentTablePage> createState() => _PaymentTablePageState();
|
State<PaymentTablePage> createState() => _PaymentTablePageState();
|
||||||
@ -98,11 +98,13 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
|
|||||||
|
|
||||||
// Set a default payment method in case API fails
|
// Set a default payment method in case API fails
|
||||||
selectedPaymentMethod = PaymentMethod(
|
selectedPaymentMethod = PaymentMethod(
|
||||||
id: 1,
|
id: "4b1c0d21-c98a-4fc0-a2f9-8d90a0c9d905",
|
||||||
name: 'Cash',
|
organizationId: "3e8b1793-d18b-40c4-a03d-0c6480b630c7",
|
||||||
description: 'Cash payment',
|
name: "CASH",
|
||||||
|
type: "cash",
|
||||||
isActive: true,
|
isActive: true,
|
||||||
sortOrder: 1,
|
createdAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'),
|
||||||
|
updatedAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,7 +446,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
|
|||||||
),
|
),
|
||||||
BlocBuilder<CheckoutBloc, CheckoutState>(
|
BlocBuilder<CheckoutBloc, CheckoutState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final tax = state.maybeWhen(
|
state.maybeWhen(
|
||||||
orElse: () => 0,
|
orElse: () => 0,
|
||||||
loaded: (products,
|
loaded: (products,
|
||||||
discountModel,
|
discountModel,
|
||||||
@ -805,41 +807,37 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
|
|||||||
8.0),
|
8.0),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
child: Tooltip(
|
child: isSelected
|
||||||
message: method.description ??
|
? Button.filled(
|
||||||
'No description available',
|
width: double.infinity,
|
||||||
child: isSelected
|
height: 50.0,
|
||||||
? Button.filled(
|
onPressed: () {
|
||||||
width: double.infinity,
|
setState(() {
|
||||||
height: 50.0,
|
selectedPaymentMethod =
|
||||||
onPressed: () {
|
method;
|
||||||
setState(() {
|
});
|
||||||
selectedPaymentMethod =
|
},
|
||||||
method;
|
label: method.name
|
||||||
});
|
?.isNotEmpty ==
|
||||||
},
|
true
|
||||||
label: method.name
|
? method.name!
|
||||||
?.isNotEmpty ==
|
: 'Unknown',
|
||||||
true
|
)
|
||||||
? method.name!
|
: Button.outlined(
|
||||||
: 'Unknown',
|
width: double.infinity,
|
||||||
)
|
height: 50.0,
|
||||||
: Button.outlined(
|
onPressed: () {
|
||||||
width: double.infinity,
|
setState(() {
|
||||||
height: 50.0,
|
selectedPaymentMethod =
|
||||||
onPressed: () {
|
method;
|
||||||
setState(() {
|
});
|
||||||
selectedPaymentMethod =
|
},
|
||||||
method;
|
label: method.name
|
||||||
});
|
?.isNotEmpty ==
|
||||||
},
|
true
|
||||||
label: method.name
|
? method.name!
|
||||||
?.isNotEmpty ==
|
: 'Unknown',
|
||||||
true
|
),
|
||||||
? method.name!
|
|
||||||
: 'Unknown',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
@ -1052,7 +1050,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
|
|||||||
if (discountModel == null) {
|
if (discountModel == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return discountModel!.value!
|
return discountModel.value!
|
||||||
.replaceAll('.00', '')
|
.replaceAll('.00', '')
|
||||||
.toIntegerFromText;
|
.toIntegerFromText;
|
||||||
});
|
});
|
||||||
@ -1135,8 +1133,6 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
|
|||||||
orderType,
|
orderType,
|
||||||
);
|
);
|
||||||
|
|
||||||
final totalPrice = subTotal + finalTax;
|
|
||||||
|
|
||||||
return Flexible(
|
return Flexible(
|
||||||
child: Button.filled(
|
child: Button.filled(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user