From a3d2764a4ece899c47f3c48809cddfcbc4a54d6f Mon Sep 17 00:00:00 2001 From: Mahdi Date: Thu, 16 Jul 2026 12:42:59 +0330 Subject: [PATCH] AddFirstBookingDiscount --- src/api/types.ts | 13 +++ src/components/landing/DesktopLandingPage.vue | 31 +++++- .../landing/FirstBookingDiscountBanner.vue | 67 +++++++++++ .../landing/MobileBusinessLanding.vue | 74 ++++++------ src/components/shell/SiteFooter.vue | 2 - src/views/app/AppOwnerView.vue | 105 +++++++++++++++++- src/views/app/BookingPaymentView.vue | 20 ++++ tsconfig.app.json | 1 - 8 files changed, 277 insertions(+), 36 deletions(-) create mode 100644 src/components/landing/FirstBookingDiscountBanner.vue diff --git a/src/api/types.ts b/src/api/types.ts index dd7a1d9..b38ce54 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -38,6 +38,11 @@ export type Business = { serviceClassesEnabled?: boolean; autoApproveBookings?: boolean; tipsEnabled?: boolean; + promoDiscountEnabled?: boolean; + promoDiscountTitle?: string | null; + promoDiscountPercent?: number | null; + promoDiscountCode?: string | null; + promoDiscountMode?: 'first_booking' | 'always'; averageRating?: number | null; reviewCount?: number; createdAt?: string; @@ -57,6 +62,11 @@ export type UpdateBusinessPayload = { serviceClassesEnabled?: boolean; autoApproveBookings?: boolean; tipsEnabled?: boolean; + promoDiscountEnabled?: boolean; + promoDiscountTitle?: string | null; + promoDiscountPercent?: number | null; + promoDiscountCode?: string | null; + promoDiscountMode?: 'first_booking' | 'always'; }; export type ServiceTemplate = { @@ -122,6 +132,9 @@ export type Booking = { paymentToken?: string | null; paymentAmount?: string | null; loyaltyRedeemed?: boolean; + promoDiscountApplied?: boolean; + promoDiscountPercent?: number | null; + promoDiscountTitle?: string | null; business?: Business; businessService?: BusinessService; serviceClass?: BusinessServiceClass; diff --git a/src/components/landing/DesktopLandingPage.vue b/src/components/landing/DesktopLandingPage.vue index af41365..f80fc62 100644 --- a/src/components/landing/DesktopLandingPage.vue +++ b/src/components/landing/DesktopLandingPage.vue @@ -6,6 +6,15 @@ @browse="scrollToServices" /> + + diff --git a/src/components/landing/MobileBusinessLanding.vue b/src/components/landing/MobileBusinessLanding.vue index b379a60..c2972db 100644 --- a/src/components/landing/MobileBusinessLanding.vue +++ b/src/components/landing/MobileBusinessLanding.vue @@ -70,26 +70,14 @@ -
-
-
-

اولین رزرو شما

-

۱۰٪ تخفیف

-

- کد تخفیف: - NEW10 -

-
- -
-
+ landingBusiness.value?.title ?? ''); const businessSubtitle = computed(() => landingBusiness.value?.subtitle?.trim() ?? ''); const aboutText = computed(() => landingBusiness.value?.description?.trim() ?? ''); -const quickActions: { - id: string; - label: string; - icon: AppIconName; - targetId: string; -}[] = [ - { id: 'services', label: 'خدمات کارواش', icon: 'car', targetId: 'services' }, - { id: 'pricing', label: 'تعرفه‌ها', icon: 'badge-percent', targetId: 'services' }, - { id: 'club', label: 'باشگاه مشتریان', icon: 'sparkles', targetId: 'club' }, - { id: 'about', label: 'درباره ما', icon: 'info', targetId: 'about' }, -]; +const discountEnabled = computed( + () => + landingBusiness.value?.promoDiscountEnabled === true && + !!landingBusiness.value?.promoDiscountPercent, +); +const discountTitle = computed( + () => landingBusiness.value?.promoDiscountTitle?.trim() || 'تخفیف ویژه', +); +const discountPercent = computed( + () => landingBusiness.value?.promoDiscountPercent ?? null, +); +const discountCode = computed( + () => landingBusiness.value?.promoDiscountCode?.trim() ?? null, +); +const discountMode = computed( + () => landingBusiness.value?.promoDiscountMode ?? 'first_booking', +); + +const quickActions = computed(() => { + const items: { + id: string; + label: string; + icon: AppIconName; + targetId: string; + }[] = [ + { id: 'services', label: 'خدمات', icon: 'car', targetId: 'services' }, + { id: 'pricing', label: 'تعرفه‌ها', icon: 'badge-percent', targetId: 'services' }, + ]; + if (discountEnabled.value) { + items.push({ id: 'club', label: 'تخفیف ویژه', icon: 'sparkles', targetId: 'club' }); + } + items.push({ id: 'about', label: 'درباره ما', icon: 'info', targetId: 'about' }); + return items; +}); function scrollTo(targetId: string) { const el = document.getElementById(targetId); diff --git a/src/components/shell/SiteFooter.vue b/src/components/shell/SiteFooter.vue index 63eb44b..c8bd132 100644 --- a/src/components/shell/SiteFooter.vue +++ b/src/components/shell/SiteFooter.vue @@ -1,7 +1,5 @@ +

+ {{ promoDiscountLabel }} +

  • {{ booking.businessService?.serviceTemplate?.title ?? 'خدمت' }} (رایگان) + + ({{ booking.promoDiscountPercent.toLocaleString('fa-IR') }}٪ + {{ booking.promoDiscountTitle || 'تخفیف' }}) +
@@ -127,6 +140,13 @@ const canPay = computed(() => { return details.value.bookings.some((booking) => booking.status === 'awaiting_payment'); }); +const promoDiscountLabel = computed(() => { + const hit = details.value?.bookings.find((booking) => booking.promoDiscountApplied); + if (!hit?.promoDiscountPercent) return ''; + const title = hit.promoDiscountTitle?.trim() || 'تخفیف'; + return `${title} — ${hit.promoDiscountPercent.toLocaleString('fa-IR')}٪ اعمال شد.`; +}); + async function load() { const token = String(route.query.token ?? '').trim(); if (!token) { diff --git a/tsconfig.app.json b/tsconfig.app.json index 90b993f..b1e458e 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -15,7 +15,6 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "jsx": "preserve", - "baseUrl": ".", "paths": { "@/*": ["./src/*"] }