AddFirstBookingDiscount

This commit is contained in:
2026-07-16 12:42:59 +03:30
parent a2d0232113
commit a3d2764a4e
8 changed files with 277 additions and 36 deletions
+30 -1
View File
@@ -6,6 +6,15 @@
@browse="scrollToServices"
/>
<FirstBookingDiscountBanner
:enabled="discountEnabled"
:title="discountTitle"
:percent="discountPercent"
:code="discountCode"
:mode="discountMode"
:compact="false"
/>
<HomeServicesSection
:items="services"
:loading="loadingServices"
@@ -18,11 +27,13 @@
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router';
import HomeFeatureBar from '@/components/home/HomeFeatureBar.vue';
import HomeHeroSection from '@/components/home/HomeHeroSection.vue';
import HomeServicesSection from '@/components/home/HomeServicesSection.vue';
import FirstBookingDiscountBanner from '@/components/landing/FirstBookingDiscountBanner.vue';
import DesktopSiteLayout from '@/layouts/DesktopSiteLayout.vue';
import { useAuthStore } from '@/stores/auth';
import { useTenantStore } from '@/stores/tenant';
@@ -42,7 +53,25 @@ const props = withDefaults(
const router = useRouter();
const auth = useAuthStore();
const tenant = useTenantStore();
const { landingBusinessId } = storeToRefs(tenant);
const { landingBusinessId, landingBusiness } = storeToRefs(tenant);
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',
);
function scrollToServices() {
const el = document.getElementById('services');
@@ -0,0 +1,67 @@
<template>
<section
v-if="enabled"
id="club"
class="scroll-mt-24"
:class="compact ? 'px-5 pb-6' : 'mx-auto max-w-[1200px] px-6 pb-10 lg:px-8'"
>
<div
class="flex items-center justify-between gap-3 rounded-2xl bg-brand-50 px-4 py-4 dark:bg-brand-500/10"
>
<div class="min-w-0 flex-1 space-y-1">
<p class="text-xs text-landing-muted dark:text-muted">{{ title }}</p>
<p class="text-2xl font-black text-brand-600 dark:text-brand-500">
{{ percentLabel }}٪ تخفیف
</p>
<p v-if="code" class="text-xs font-medium text-landing-ink dark:text-text">
کد تخفیف:
<span class="font-bold" dir="ltr">{{ code }}</span>
</p>
<p class="text-[11px] leading-5 text-landing-muted dark:text-muted">
{{ helperText }}
</p>
</div>
<div
class="flex h-16 w-16 shrink-0 items-center justify-center rounded-2xl bg-white shadow-sm dark:bg-surface dark:shadow-black/20"
aria-hidden="true"
>
<AppIcon name="gift" size="lg" class="text-brand-600 dark:text-brand-500" />
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import AppIcon from '@/components/ui/AppIcon.vue';
const props = withDefaults(
defineProps<{
enabled?: boolean;
title?: string | null;
percent?: number | null;
code?: string | null;
mode?: 'first_booking' | 'always' | null;
compact?: boolean;
}>(),
{
enabled: false,
title: 'تخفیف ویژه',
percent: null,
code: null,
mode: 'first_booking',
compact: true,
},
);
const percentLabel = computed(() => {
const n = props.percent ?? 0;
return n.toLocaleString('fa-IR');
});
const helperText = computed(() =>
props.mode === 'always'
? 'این تخفیف هنگام رزرو به‌صورت خودکار روی مبلغ اعمال می‌شود.'
: 'برای اولین رزرو شما در این کسب‌وکار به‌صورت خودکار اعمال می‌شود.',
);
</script>
@@ -70,26 +70,14 @@
</div>
</section>
<section id="club" class="scroll-mt-24 px-5 pb-6">
<div
class="flex items-center justify-between gap-3 rounded-2xl bg-brand-50 px-4 py-4 dark:bg-brand-500/10"
>
<div class="min-w-0 flex-1 space-y-1">
<p class="text-xs text-landing-muted dark:text-muted">اولین رزرو شما</p>
<p class="text-2xl font-black text-brand-600 dark:text-brand-500">۱۰٪ تخفیف</p>
<p class="text-xs font-medium text-landing-ink dark:text-text">
کد تخفیف:
<span class="font-bold" dir="ltr">NEW10</span>
</p>
</div>
<div
class="flex h-16 w-16 shrink-0 items-center justify-center rounded-2xl bg-white shadow-sm dark:bg-surface dark:shadow-black/20"
aria-hidden="true"
>
<AppIcon name="gift" size="lg" class="text-brand-600 dark:text-brand-500" />
</div>
</div>
</section>
<FirstBookingDiscountBanner
:enabled="discountEnabled"
:title="discountTitle"
:percent="discountPercent"
:code="discountCode"
:mode="discountMode"
compact
/>
<HomeServicesSection
:items="services"
@@ -115,6 +103,7 @@ import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import BusinessImageGallery from '@/components/business/BusinessImageGallery.vue';
import HomeServicesSection from '@/components/home/HomeServicesSection.vue';
import FirstBookingDiscountBanner from '@/components/landing/FirstBookingDiscountBanner.vue';
import AppIcon from '@/components/ui/AppIcon.vue';
import { useTenantStore } from '@/stores/tenant';
import type { AppIconName } from '@/types/icons';
@@ -138,17 +127,40 @@ const businessTitle = computed(() => 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);
-2
View File
@@ -1,7 +1,5 @@
<template>
<footer class="border-t border-landing-line bg-landing-surface dark:border-border dark:bg-surface-2">
<div id="club" class="sr-only" aria-hidden="true" />
<div class="mx-auto max-w-[1200px] px-6 py-12 lg:px-8">
<div class="grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4">
<section>