add skeletin
This commit is contained in:
@@ -37,7 +37,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="loadingSlots" class="text-sm text-muted">در حال بارگذاری ساعات…</div>
|
<div v-if="loadingSlots" class="py-1">
|
||||||
|
<SlotGridSkeleton />
|
||||||
|
</div>
|
||||||
<p v-else-if="slotsError" class="text-sm text-red-600">{{ slotsError }}</p>
|
<p v-else-if="slotsError" class="text-sm text-red-600">{{ slotsError }}</p>
|
||||||
<p
|
<p
|
||||||
v-else-if="!slots.length"
|
v-else-if="!slots.length"
|
||||||
@@ -75,6 +77,7 @@
|
|||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import { fetchAvailableSlots } from '@/api/bookings';
|
import { fetchAvailableSlots } from '@/api/bookings';
|
||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
|
import SlotGridSkeleton from '@/components/ui/SlotGridSkeleton.vue';
|
||||||
import {
|
import {
|
||||||
clampJalaliDay,
|
clampJalaliDay,
|
||||||
daysInJalaliMonth,
|
daysInJalaliMonth,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
{{ message }}
|
{{ message }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="loading" class="py-6 text-center text-sm text-muted">در حال بارگذاری…</div>
|
<ListCardSkeleton v-if="loading" :count="2" list-class="space-y-2 py-2" />
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="flex rounded-xl bg-surface-3 p-1">
|
<div class="flex rounded-xl bg-surface-3 p-1">
|
||||||
@@ -162,6 +162,7 @@ import {
|
|||||||
type AvailabilitySlot,
|
type AvailabilitySlot,
|
||||||
} from '@/api/availability';
|
} from '@/api/availability';
|
||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
|
import ListCardSkeleton from '@/components/ui/ListCardSkeleton.vue';
|
||||||
import AppButton from '@/components/ui/AppButton.vue';
|
import AppButton from '@/components/ui/AppButton.vue';
|
||||||
import AppIcon from '@/components/ui/AppIcon.vue';
|
import AppIcon from '@/components/ui/AppIcon.vue';
|
||||||
import TimeRangeList from '@/components/owner/TimeRangeList.vue';
|
import TimeRangeList from '@/components/owner/TimeRangeList.vue';
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
{{ error }}
|
{{ error }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="loading" class="text-sm text-muted">در حال بارگذاری…</div>
|
<ListCardSkeleton v-if="loading" :count="2" />
|
||||||
|
|
||||||
<ul v-else-if="classes.length" class="space-y-2">
|
<ul v-else-if="classes.length" class="space-y-2">
|
||||||
<li
|
<li
|
||||||
@@ -125,6 +125,7 @@ import {
|
|||||||
} from '@/api/business-service-classes';
|
} from '@/api/business-service-classes';
|
||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
import type { BusinessServiceClass } from '@/api/types';
|
import type { BusinessServiceClass } from '@/api/types';
|
||||||
|
import ListCardSkeleton from '@/components/ui/ListCardSkeleton.vue';
|
||||||
import AppButton from '@/components/ui/AppButton.vue';
|
import AppButton from '@/components/ui/AppButton.vue';
|
||||||
import AppIcon from '@/components/ui/AppIcon.vue';
|
import AppIcon from '@/components/ui/AppIcon.vue';
|
||||||
import AppIconButton from '@/components/ui/AppIconButton.vue';
|
import AppIconButton from '@/components/ui/AppIconButton.vue';
|
||||||
@@ -153,7 +154,7 @@ const editForm = ref({ title: '', description: '', isActive: true });
|
|||||||
|
|
||||||
const collapsedSummary = computed(() => {
|
const collapsedSummary = computed(() => {
|
||||||
if (!props.enabled) return 'غیرفعال — هر خدمت یک قیمت ثابت';
|
if (!props.enabled) return 'غیرفعال — هر خدمت یک قیمت ثابت';
|
||||||
if (loading.value) return 'فعال — در حال بارگذاری…';
|
if (loading.value) return '…';
|
||||||
const count = classes.value.length;
|
const count = classes.value.length;
|
||||||
if (count === 0) return 'فعال — هنوز کلاسی تعریف نشده';
|
if (count === 0) return 'فعال — هنوز کلاسی تعریف نشده';
|
||||||
const activeCount = classes.value.filter((c) => c.isActive).length;
|
const activeCount = classes.value.filter((c) => c.isActive).length;
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="animate-pulse rounded-lg bg-surface-3"
|
||||||
|
:class="klass"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { class: klass } = defineProps<{
|
||||||
|
class?: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<ul :class="listClass" aria-hidden="true">
|
||||||
|
<li
|
||||||
|
v-for="i in count"
|
||||||
|
:key="i"
|
||||||
|
class="flex items-center gap-3 rounded-xl border border-border bg-surface-2 p-2.5"
|
||||||
|
>
|
||||||
|
<div class="min-w-0 flex-1 space-y-2">
|
||||||
|
<div class="flex items-start justify-between gap-2">
|
||||||
|
<AppSkeleton class="h-4 w-32" />
|
||||||
|
<AppSkeleton class="h-4 w-14 shrink-0 rounded-lg" />
|
||||||
|
</div>
|
||||||
|
<AppSkeleton class="h-3 w-full max-w-[80%]" />
|
||||||
|
<AppSkeleton class="h-3 w-24" />
|
||||||
|
</div>
|
||||||
|
<AppSkeleton :class="imageClass" class="shrink-0 rounded-lg" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
count?: number;
|
||||||
|
listClass?: string;
|
||||||
|
size?: 'md' | 'lg';
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
count: 4,
|
||||||
|
listClass: 'space-y-2',
|
||||||
|
size: 'md',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const imageClass = computed(() =>
|
||||||
|
props.size === 'lg' ? 'h-20 w-20' : 'h-14 w-14 sm:h-16 sm:w-16',
|
||||||
|
);
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-6" aria-hidden="true">
|
||||||
|
<AppSkeleton class="aspect-[16/10] w-full rounded-2xl" />
|
||||||
|
<AppSkeleton class="h-5 w-20 rounded-lg" />
|
||||||
|
<AppSkeleton class="h-8 w-3/5 max-w-xs" />
|
||||||
|
<AppSkeleton class="h-4 w-full max-w-md" />
|
||||||
|
<div class="space-y-3 rounded-2xl border border-border bg-surface-2 p-4">
|
||||||
|
<div class="flex items-center justify-between gap-4">
|
||||||
|
<AppSkeleton class="h-4 w-12" />
|
||||||
|
<AppSkeleton class="h-4 w-28" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-start justify-between gap-4">
|
||||||
|
<AppSkeleton class="h-4 w-10" />
|
||||||
|
<AppSkeleton class="h-4 w-40 max-w-[75%]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<AppSkeleton class="mb-3 h-5 w-16" />
|
||||||
|
<ServiceGridSkeleton :count="4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
import ServiceGridSkeleton from '@/components/ui/ServiceGridSkeleton.vue';
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-4" aria-hidden="true">
|
||||||
|
<AppSkeleton class="h-6 w-40" />
|
||||||
|
<AppSkeleton class="h-24 w-full rounded-2xl" />
|
||||||
|
<AppSkeleton class="h-12 w-full rounded-xl" />
|
||||||
|
<AppSkeleton class="h-24 w-full rounded-xl" />
|
||||||
|
<AppSkeleton class="h-12 w-full rounded-xl" />
|
||||||
|
<AppSkeleton class="h-12 w-full rounded-xl" />
|
||||||
|
<AppSkeleton class="h-11 w-full rounded-xl" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-3 rounded-xl border border-border bg-surface-3 p-3" aria-hidden="true">
|
||||||
|
<AppSkeleton class="h-4 w-24" />
|
||||||
|
<AppSkeleton class="h-5 w-40" />
|
||||||
|
<AppSkeleton class="h-4 w-32" />
|
||||||
|
<AppSkeleton class="h-6 w-16 rounded-lg" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<ul :class="listClass" aria-hidden="true">
|
||||||
|
<li
|
||||||
|
v-for="i in count"
|
||||||
|
:key="i"
|
||||||
|
class="rounded-2xl border border-border bg-surface-2 p-4"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between gap-4">
|
||||||
|
<AppSkeleton class="h-5 w-36 max-w-[45%]" />
|
||||||
|
<AppSkeleton class="h-4 w-24 shrink-0" />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 flex items-center justify-between gap-2">
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<AppSkeleton class="h-6 w-14 rounded-lg" />
|
||||||
|
<AppSkeleton class="h-6 w-12 rounded-lg" />
|
||||||
|
<AppSkeleton class="h-6 w-16 rounded-lg" />
|
||||||
|
</div>
|
||||||
|
<AppSkeleton class="h-9 w-9 shrink-0 rounded-full" />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
count?: number;
|
||||||
|
listClass?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
count: 4,
|
||||||
|
listClass: 'space-y-2',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<ul class="grid grid-cols-2 gap-3" aria-hidden="true">
|
||||||
|
<li
|
||||||
|
v-for="i in count"
|
||||||
|
:key="i"
|
||||||
|
class="flex min-h-[7rem] flex-col rounded-xl border border-border bg-surface-2 p-3"
|
||||||
|
>
|
||||||
|
<AppSkeleton class="mx-auto h-4 w-3/4" />
|
||||||
|
<AppSkeleton class="mx-auto mt-2 h-3 w-1/2" />
|
||||||
|
<AppSkeleton class="mx-auto mt-auto h-3 w-16 pt-4" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
|
||||||
|
withDefaults(defineProps<{ count?: number }>(), { count: 4 });
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap gap-2" aria-hidden="true">
|
||||||
|
<AppSkeleton
|
||||||
|
v-for="i in count"
|
||||||
|
:key="i"
|
||||||
|
class="h-10 w-[4.5rem] rounded-xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
|
||||||
|
withDefaults(defineProps<{ count?: number }>(), { count: 8 });
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div class="grid grid-cols-2 gap-3 lg:grid-cols-3" aria-hidden="true">
|
||||||
|
<AppSkeleton
|
||||||
|
v-for="i in count"
|
||||||
|
:key="i"
|
||||||
|
class="h-24 rounded-2xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AppSkeleton from '@/components/ui/AppSkeleton.vue';
|
||||||
|
|
||||||
|
withDefaults(defineProps<{ count?: number }>(), { count: 5 });
|
||||||
|
</script>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
{{ error }}
|
{{ error }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="loading" class="py-8 text-center text-sm text-muted">در حال بارگذاری…</div>
|
<ListCardSkeleton v-if="loading" list-class="space-y-3" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="!bookings.length"
|
v-else-if="!bookings.length"
|
||||||
@@ -83,6 +83,7 @@ import { cancelBooking, confirmBooking, fetchBookings } from '@/api/bookings';
|
|||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
import type { Booking } from '@/api/types';
|
import type { Booking } from '@/api/types';
|
||||||
import BookingItem from '@/components/booking/BookingItem.vue';
|
import BookingItem from '@/components/booking/BookingItem.vue';
|
||||||
|
import ListCardSkeleton from '@/components/ui/ListCardSkeleton.vue';
|
||||||
import WebAppLayout from '@/layouts/WebAppLayout.vue';
|
import WebAppLayout from '@/layouts/WebAppLayout.vue';
|
||||||
import { useAuthStore } from '@/stores/auth';
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<WebAppLayout>
|
<WebAppLayout>
|
||||||
<div v-if="loading" class="py-12 text-center text-sm text-muted">در حال بارگذاری…</div>
|
<DetailPageSkeleton v-if="loading" />
|
||||||
<p
|
<p
|
||||||
v-else-if="error"
|
v-else-if="error"
|
||||||
class="rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600 dark:text-red-400"
|
class="rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600 dark:text-red-400"
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 class="mb-3 font-semibold text-text">خدمات</h2>
|
<h2 class="mb-3 font-semibold text-text">خدمات</h2>
|
||||||
<div v-if="loadingServices" class="text-sm text-muted">در حال بارگذاری…</div>
|
<ServiceGridSkeleton v-if="loadingServices" :count="4" />
|
||||||
<p
|
<p
|
||||||
v-else-if="servicesError"
|
v-else-if="servicesError"
|
||||||
class="rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600 dark:text-red-400"
|
class="rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600 dark:text-red-400"
|
||||||
@@ -291,7 +291,9 @@ import { fetchBusiness, fetchBusinesses } from '@/api/businesses';
|
|||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
import type { Business, BusinessService, BusinessServiceClass } from '@/api/types';
|
import type { Business, BusinessService, BusinessServiceClass } from '@/api/types';
|
||||||
import AppButton from '@/components/ui/AppButton.vue';
|
import AppButton from '@/components/ui/AppButton.vue';
|
||||||
|
import DetailPageSkeleton from '@/components/ui/DetailPageSkeleton.vue';
|
||||||
import AppIcon from '@/components/ui/AppIcon.vue';
|
import AppIcon from '@/components/ui/AppIcon.vue';
|
||||||
|
import ServiceGridSkeleton from '@/components/ui/ServiceGridSkeleton.vue';
|
||||||
import BusinessImageGallery from '@/components/business/BusinessImageGallery.vue';
|
import BusinessImageGallery from '@/components/business/BusinessImageGallery.vue';
|
||||||
import BookingSlotPicker from '@/components/booking/BookingSlotPicker.vue';
|
import BookingSlotPicker from '@/components/booking/BookingSlotPicker.vue';
|
||||||
import AppModal from '@/components/ui/AppModal.vue';
|
import AppModal from '@/components/ui/AppModal.vue';
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<WebAppLayout>
|
<WebAppLayout>
|
||||||
<div v-if="bootstrapping" class="py-12 text-center text-sm text-muted">
|
<BusinessCardSkeleton v-if="bootstrapping" class="py-2" />
|
||||||
در حال بارگذاری…
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section v-else class="space-y-5">
|
<section v-else class="space-y-5">
|
||||||
<div>
|
<div>
|
||||||
@@ -33,9 +31,7 @@
|
|||||||
@select="onCategorySelect"
|
@select="onCategorySelect"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="loadingBusinesses" class="py-8 text-center text-sm text-muted">
|
<BusinessCardSkeleton v-if="loadingBusinesses" class="py-2" />
|
||||||
در حال بارگذاری…
|
|
||||||
</div>
|
|
||||||
<p
|
<p
|
||||||
v-else-if="error"
|
v-else-if="error"
|
||||||
class="rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600 dark:text-red-400"
|
class="rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-600 dark:text-red-400"
|
||||||
@@ -65,6 +61,7 @@ import { fetchBusinesses } from '@/api/businesses';
|
|||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
import type { Business, BusinessCategory } from '@/api/types';
|
import type { Business, BusinessCategory } from '@/api/types';
|
||||||
import BusinessCard from '@/components/business/BusinessCard.vue';
|
import BusinessCard from '@/components/business/BusinessCard.vue';
|
||||||
|
import BusinessCardSkeleton from '@/components/ui/BusinessCardSkeleton.vue';
|
||||||
import AppIcon from '@/components/ui/AppIcon.vue';
|
import AppIcon from '@/components/ui/AppIcon.vue';
|
||||||
import CategoryFilter from '@/components/business/CategoryFilter.vue';
|
import CategoryFilter from '@/components/business/CategoryFilter.vue';
|
||||||
import WebAppLayout from '@/layouts/WebAppLayout.vue';
|
import WebAppLayout from '@/layouts/WebAppLayout.vue';
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
{{ message }}
|
{{ message }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="loading" class="text-sm text-muted">در حال بارگذاری…</div>
|
<FormPageSkeleton v-if="loading" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="!business"
|
v-else-if="!business"
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
خدماتی که اینجا ثبت میکنید در صفحه کسبوکار برای مشتریان و مقایسه نمایش داده میشود.
|
خدماتی که اینجا ثبت میکنید در صفحه کسبوکار برای مشتریان و مقایسه نمایش داده میشود.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="loadingServices" class="text-sm text-muted">در حال بارگذاری خدمات…</div>
|
<ListCardSkeleton v-if="loadingServices" :count="3" />
|
||||||
|
|
||||||
<ul v-else-if="services.length" class="space-y-2">
|
<ul v-else-if="services.length" class="space-y-2">
|
||||||
<li
|
<li
|
||||||
@@ -461,7 +461,8 @@ import type {
|
|||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
import BusinessAvailabilityEditor from '@/components/owner/BusinessAvailabilityEditor.vue';
|
import BusinessAvailabilityEditor from '@/components/owner/BusinessAvailabilityEditor.vue';
|
||||||
import ServiceClassManager from '@/components/owner/ServiceClassManager.vue';
|
import ServiceClassManager from '@/components/owner/ServiceClassManager.vue';
|
||||||
import AppButton from '@/components/ui/AppButton.vue';
|
import FormPageSkeleton from '@/components/ui/FormPageSkeleton.vue';
|
||||||
|
import ListCardSkeleton from '@/components/ui/ListCardSkeleton.vue';
|
||||||
import AppIcon from '@/components/ui/AppIcon.vue';
|
import AppIcon from '@/components/ui/AppIcon.vue';
|
||||||
import AppIconButton from '@/components/ui/AppIconButton.vue';
|
import AppIconButton from '@/components/ui/AppIconButton.vue';
|
||||||
import AppInput from '@/components/ui/AppInput.vue';
|
import AppInput from '@/components/ui/AppInput.vue';
|
||||||
@@ -542,7 +543,7 @@ const hasServiceClasses = computed(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const servicesSummary = computed(() => {
|
const servicesSummary = computed(() => {
|
||||||
if (loadingServices.value) return 'در حال بارگذاری…';
|
if (loadingServices.value) return '…';
|
||||||
const count = services.value.length;
|
const count = services.value.length;
|
||||||
if (count === 0) return 'هنوز خدمتی ثبت نشده';
|
if (count === 0) return 'هنوز خدمتی ثبت نشده';
|
||||||
const activeCount = services.value.filter((s) => s.isActive).length;
|
const activeCount = services.value.filter((s) => s.isActive).length;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
{{ error }}
|
{{ error }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="loading" class="text-sm text-muted">در حال بارگذاری…</div>
|
<FormPageSkeleton v-if="loading" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="pendingRequest"
|
v-else-if="pendingRequest"
|
||||||
@@ -99,6 +99,7 @@ import {
|
|||||||
import type { BusinessCategory, BusinessRegistrationRequest } from '@/api/types';
|
import type { BusinessCategory, BusinessRegistrationRequest } from '@/api/types';
|
||||||
import { ApiError } from '@/api/client';
|
import { ApiError } from '@/api/client';
|
||||||
import AppButton from '@/components/ui/AppButton.vue';
|
import AppButton from '@/components/ui/AppButton.vue';
|
||||||
|
import FormPageSkeleton from '@/components/ui/FormPageSkeleton.vue';
|
||||||
import AppInput from '@/components/ui/AppInput.vue';
|
import AppInput from '@/components/ui/AppInput.vue';
|
||||||
import WebAppLayout from '@/layouts/WebAppLayout.vue';
|
import WebAppLayout from '@/layouts/WebAppLayout.vue';
|
||||||
import { useAuthStore } from '@/stores/auth';
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|||||||
Reference in New Issue
Block a user