From 54e2d31a517ee48c96f0340398f529f7c80e3009 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Mon, 29 Jun 2026 15:49:13 +0330 Subject: [PATCH] awaiting_payment --- src/api/bookings.ts | 26 +++ src/api/types.ts | 11 +- src/components/booking/BookingItem.vue | 15 +- src/components/booking/BookingSuccessView.vue | 27 ++- src/components/booking/DesktopBookingFlow.vue | 36 +++- src/router/index.ts | 6 + src/utils/booking-groups.ts | 10 +- src/utils/format.ts | 1 + src/views/app/AppBookingsView.vue | 55 +++++- src/views/app/AppOwnerView.vue | 26 ++- src/views/app/BookingPaymentView.vue | 165 ++++++++++++++++++ 11 files changed, 363 insertions(+), 15 deletions(-) create mode 100644 src/views/app/BookingPaymentView.vue diff --git a/src/api/bookings.ts b/src/api/bookings.ts index 4ccc192..97ce2c5 100644 --- a/src/api/bookings.ts +++ b/src/api/bookings.ts @@ -60,6 +60,32 @@ export function confirmBooking(id: string) { return api(`/bookings/${id}/confirm`, { method: 'POST' }); } +export function fetchPaymentByToken(token: string) { + return api<{ + bookings: Booking[]; + totalAmountToman: number; + }>(`/bookings/payment/by-token/${encodeURIComponent(token)}`); +} + +export type PaymentInitResult = { + startUrl: string; + amountToman: number; + mock?: boolean; +}; + +export function initiateBookingPayment(id: string) { + return api(`/bookings/${id}/payment/init`, { + method: 'POST', + }); +} + +export function initiateBookingPaymentByToken(token: string) { + return api( + `/bookings/payment/by-token/${encodeURIComponent(token)}/init`, + { method: 'POST' }, + ); +} + export function fetchAvailableSlots( businessId: string, dateYmd: string, diff --git a/src/api/types.ts b/src/api/types.ts index 683b590..83a7c73 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,4 +1,9 @@ -export type BookingStatus = 'pending' | 'confirmed' | 'cancelled' | 'completed'; +export type BookingStatus = + | 'pending' + | 'awaiting_payment' + | 'confirmed' + | 'cancelled' + | 'completed'; export type BusinessCategory = { id: string; @@ -31,6 +36,7 @@ export type Business = { concurrentCapacity?: number; slotStepMinutes?: number; serviceClassesEnabled?: boolean; + autoApproveBookings?: boolean; createdAt?: string; updatedAt?: string; }; @@ -46,6 +52,7 @@ export type UpdateBusinessPayload = { concurrentCapacity?: number; slotStepMinutes?: number; serviceClassesEnabled?: boolean; + autoApproveBookings?: boolean; }; export type ServiceTemplate = { @@ -98,6 +105,8 @@ export type Booking = { status: BookingStatus; note?: string | null; cancellationReason?: string | null; + paymentToken?: string | null; + paymentAmount?: string | null; business?: Business; businessService?: BusinessService; serviceClass?: BusinessServiceClass; diff --git a/src/components/booking/BookingItem.vue b/src/components/booking/BookingItem.vue index 066bfa5..f5253bd 100644 --- a/src/components/booking/BookingItem.vue +++ b/src/components/booking/BookingItem.vue @@ -65,9 +65,17 @@
+ + پرداخت + (); const emit = defineEmits<{ confirm: [bookings: Booking[]]; cancel: [bookings: Booking[]]; + pay: [bookings: Booking[]]; }>(); const primary = computed(() => props.bookings[0]!); @@ -139,6 +149,9 @@ function statusClassFor(status: BookingStatus | 'mixed') { if (status === 'confirmed' || status === 'completed') { return 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'; } + if (status === 'awaiting_payment') { + return 'bg-sky-500/15 text-sky-700 dark:text-sky-400'; + } if (status === 'pending') { return 'bg-amber-500/15 text-amber-700 dark:text-amber-400'; } diff --git a/src/components/booking/BookingSuccessView.vue b/src/components/booking/BookingSuccessView.vue index 762ccfd..c8d0dd4 100644 --- a/src/components/booking/BookingSuccessView.vue +++ b/src/components/booking/BookingSuccessView.vue @@ -16,9 +16,9 @@
-

رزرو شما با موفقیت ثبت شد!

+

{{ title }}

- جزئیات رزرو برای شما پیامک و در بخش رزروها ذخیره شد. + {{ subtitle }}

@@ -61,7 +61,10 @@
- + + پرداخت آنلاین + + مشاهده رزروها @@ -81,22 +84,38 @@