Files
webMontime/src/views/app/AppHomeView.vue
T

132 lines
3.9 KiB
Vue
Raw Normal View History

2026-05-18 20:18:28 +03:30
<template>
<WebAppLayout>
<section class="space-y-5">
<div>
<h1 class="text-xl font-bold text-text">کشف خدمات</h1>
<p class="mt-1 text-sm text-muted">کسب‌وکارهای محلی نزدیک شما</p>
</div>
<div>
<label class="sr-only" for="search">جستجو</label>
<input
id="search"
v-model="searchQuery"
type="search"
placeholder="جستجو در نام یا آدرس…"
class="w-full rounded-xl border border-border bg-surface-2 px-4 py-3 text-sm text-text outline-none placeholder:text-muted focus:border-brand-500 focus:ring-2 focus:ring-brand-500/30"
/>
</div>
<div v-if="loadingCategories" class="text-center text-sm text-muted">
در حال بارگذاری دسته‌ها…
</div>
<CategoryFilter
v-else-if="categories.length"
:categories="activeCategories"
:selected-id="selectedCategoryId"
@select="onCategorySelect"
/>
<div v-if="loadingBusinesses" class="py-8 text-center text-sm text-muted">
در حال بارگذاری…
</div>
<p
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"
>
{{ error }}
2026-05-18 20:18:28 +03:30
</p>
<div
v-else-if="!filteredBusinesses.length"
class="rounded-2xl border border-dashed border-border bg-surface-2 p-8 text-center text-sm text-muted"
2026-05-18 20:18:28 +03:30
>
کسب‌وکاری یافت نشد.
2026-05-18 20:18:28 +03:30
</div>
<ul v-else class="space-y-2">
<li v-for="b in filteredBusinesses" :key="b.id">
<BusinessCard :business="b" />
</li>
</ul>
2026-05-18 20:18:28 +03:30
</section>
</WebAppLayout>
</template>
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { fetchBusinessCategories } from '@/api/business-categories';
import { fetchBusinesses } from '@/api/businesses';
import { ApiError } from '@/api/client';
import type { Business, BusinessCategory } from '@/api/types';
import BusinessCard from '@/components/business/BusinessCard.vue';
import CategoryFilter from '@/components/business/CategoryFilter.vue';
2026-05-18 20:18:28 +03:30
import WebAppLayout from '@/layouts/WebAppLayout.vue';
const categories = ref<BusinessCategory[]>([]);
const businesses = ref<Business[]>([]);
const selectedCategoryId = ref<string | null>(null);
const searchQuery = ref('');
const loadingCategories = ref(true);
const loadingBusinesses = ref(true);
const error = ref('');
const activeCategories = computed(() =>
categories.value.filter((c) => c.isActive),
);
const filteredBusinesses = computed(() => {
const q = searchQuery.value.trim().toLowerCase();
if (!q) {
return businesses.value;
}
return businesses.value.filter((b) => {
const haystack = [b.title, b.description, b.address]
.filter(Boolean)
.join(' ')
.toLowerCase();
return haystack.includes(q);
});
});
async function loadCategories() {
loadingCategories.value = true;
try {
const all = await fetchBusinessCategories();
categories.value = all;
} catch (e) {
error.value =
e instanceof ApiError ? e.message : 'بارگذاری دسته‌ها ناموفق بود.';
} finally {
loadingCategories.value = false;
}
}
async function loadBusinesses() {
loadingBusinesses.value = true;
error.value = '';
try {
businesses.value = await fetchBusinesses({
categoryId: selectedCategoryId.value ?? undefined,
isActive: true,
});
} catch (e) {
error.value =
e instanceof ApiError ? e.message : 'بارگذاری کسب‌وکارها ناموفق بود.';
} finally {
loadingBusinesses.value = false;
}
}
function onCategorySelect(id: string | null) {
selectedCategoryId.value = id;
}
watch(selectedCategoryId, () => {
loadBusinesses();
});
onMounted(async () => {
await loadCategories();
await loadBusinesses();
});
2026-05-18 20:18:28 +03:30
</script>