diff --git a/src/api/bookings.ts b/src/api/bookings.ts new file mode 100644 index 0000000..a853996 --- /dev/null +++ b/src/api/bookings.ts @@ -0,0 +1,36 @@ +import { api } from './client'; +import type { Booking, BookingStatus } from './types'; + +export type CreateBookingPayload = { + customerId: string; + businessId: string; + businessServiceId: string; + scheduledAt: string; + note?: string | null; +}; + +export function fetchBookings(params?: { + businessId?: string; + status?: BookingStatus; +}) { + const search = new URLSearchParams(); + if (params?.businessId) search.set('businessId', params.businessId); + if (params?.status) search.set('status', params.status); + const query = search.toString(); + return api(`/bookings${query ? `?${query}` : ''}`); +} + +export function createBooking(payload: CreateBookingPayload) { + return api('/bookings', { + method: 'POST', + body: JSON.stringify(payload), + }); +} + +export function cancelBooking(id: string) { + return api(`/bookings/${id}/cancel`, { method: 'POST' }); +} + +export function confirmBooking(id: string) { + return api(`/bookings/${id}/confirm`, { method: 'POST' }); +} diff --git a/src/api/types.ts b/src/api/types.ts index 12ef004..7bcb7df 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -82,3 +82,28 @@ export type BusinessService = { isActive: boolean; serviceTemplate?: ServiceTemplate; }; + +export type BookingStatus = + | 'pending' + | 'confirmed' + | 'cancelled' + | 'completed'; + +export type Booking = { + id: string; + customerId: string; + businessId: string; + businessServiceId: string; + scheduledAt: string; + status: BookingStatus; + note: string | null; + createdAt?: string; + updatedAt?: string; + customer?: { + id: string; + mobile: string; + fullName: string | null; + }; + business?: Business; + businessService?: BusinessService; +}; diff --git a/src/components/ui/AppModal.vue b/src/components/ui/AppModal.vue new file mode 100644 index 0000000..4b9717a --- /dev/null +++ b/src/components/ui/AppModal.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/utils/format.ts b/src/utils/format.ts index fc89a8e..2d259ab 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -7,6 +7,35 @@ export function formatPriceToman(price: string): string { return new Intl.NumberFormat('fa-IR').format(Math.round(value)); } +export function formatDate(value?: string | Date): string { + if (!value) return '—'; + const date = typeof value === 'string' ? new Date(value) : value; + if (Number.isNaN(date.getTime())) return '—'; + return new Intl.DateTimeFormat('fa-IR', { + dateStyle: 'short', + timeStyle: 'short', + }).format(date); +} + +export function bookingStatusLabel(status: string): string { + const map: Record = { + pending: 'در انتظار تأیید', + confirmed: 'تأیید شده', + completed: 'انجام شده', + cancelled: 'لغو شده', + }; + return map[status] ?? status; +} + +/** Default value for datetime-local (at least ~90 min ahead). */ +export function defaultBookingDateTimeLocal(): string { + const d = new Date(); + d.setMinutes(d.getMinutes() + 90); + d.setSeconds(0, 0); + const pad = (n: number) => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; +} + export function formatDuration(minutes: number | null, fallback?: number): string { const m = minutes ?? fallback; if (!m) { diff --git a/src/views/app/AppBookingsView.vue b/src/views/app/AppBookingsView.vue index 8c7be57..08dbee4 100644 --- a/src/views/app/AppBookingsView.vue +++ b/src/views/app/AppBookingsView.vue @@ -1,12 +1,185 @@ diff --git a/src/views/app/AppBusinessDetailView.vue b/src/views/app/AppBusinessDetailView.vue index cbbdcf6..10d568d 100644 --- a/src/views/app/AppBusinessDetailView.vue +++ b/src/views/app/AppBusinessDetailView.vue @@ -17,6 +17,13 @@ بازگشت +
+ این کسب‌وکار فعلاً غیرفعال است و امکان رزرو ندارد. +
+

خدمات

در حال بارگذاری…
+

+ {{ servicesError }} +

- خدمتی ثبت نشده است. + هنوز خدمتی برای این کسب‌وکار ثبت نشده است. + + از بخش «کسب‌وکار» در منو، خدمات و قیمت را اضافه کنید. +
- - رزرو (به‌زودی) + + رزرو + + +
+

+ {{ bookingError }} +

+ +
+

کسب‌وکار

+

{{ business.title }}

+

مبلغ تقریبی

+

+ {{ formatPriceToman(bookingService.price) }} تومان +

+
+ +
+ + +

حداقل یک ساعت بعد از الان

+
+ +
+ +