2026-05-18 20:18:28 +03:30
|
|
|
|
<template>
|
2026-06-12 20:16:14 +03:30
|
|
|
|
<ResponsiveAppLayout>
|
|
|
|
|
|
<h1 class="mb-1 text-xl font-bold text-text">مدیریت کسبوکار</h1>
|
|
|
|
|
|
<p class="mb-6 text-sm text-muted">تنظیمات کسبوکار، خدمات و ساعات کاری</p>
|
2026-05-23 18:13:25 +03:30
|
|
|
|
|
2026-06-03 16:35:09 +03:30
|
|
|
|
<FormPageSkeleton v-if="loading" />
|
2026-05-23 18:13:25 +03:30
|
|
|
|
|
2026-06-12 20:16:14 +03:30
|
|
|
|
<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"
|
2026-05-24 11:34:52 +03:30
|
|
|
|
>
|
2026-06-12 20:16:14 +03:30
|
|
|
|
{{ error }}
|
|
|
|
|
|
</p>
|
2026-05-23 18:13:25 +03:30
|
|
|
|
|
2026-06-12 20:16:14 +03:30
|
|
|
|
<div v-else-if="business" class="mx-auto max-w-3xl space-y-6">
|
|
|
|
|
|
<form class="space-y-4 rounded-2xl border border-border bg-surface-2 p-4" @submit.prevent="saveBusiness">
|
2026-05-24 11:34:52 +03:30
|
|
|
|
<AppInput id="ownerTitle" v-model="form.title" label="نام کسبوکار" required />
|
2026-06-12 20:16:14 +03:30
|
|
|
|
<AppInput id="ownerSubtitle" v-model="form.subtitle" label="زیرعنوان" />
|
|
|
|
|
|
<AppTextarea id="ownerDescription" v-model="form.description" label="توضیحات" />
|
2026-05-24 11:34:52 +03:30
|
|
|
|
<AppInput id="ownerAddress" v-model="form.address" label="آدرس" />
|
2026-06-12 20:16:14 +03:30
|
|
|
|
<AppInput id="ownerPhone" v-model="form.phone" label="تلفن" ltr />
|
|
|
|
|
|
<p v-if="saveMessage" class="text-sm text-brand-700 dark:text-brand-500">{{ saveMessage }}</p>
|
|
|
|
|
|
<AppButton type="submit" :loading="saving">ذخیره اطلاعات</AppButton>
|
2026-05-24 11:34:52 +03:30
|
|
|
|
</form>
|
2026-05-23 18:13:25 +03:30
|
|
|
|
|
2026-05-31 21:38:09 +03:30
|
|
|
|
<ServiceClassManager
|
|
|
|
|
|
:business-id="business.id"
|
2026-06-12 20:16:14 +03:30
|
|
|
|
:enabled="serviceClassesEnabled"
|
|
|
|
|
|
@update:enabled="setServiceClassesEnabled"
|
2026-05-31 21:38:09 +03:30
|
|
|
|
/>
|
2026-06-12 20:16:14 +03:30
|
|
|
|
<BusinessAvailabilityEditor :business-id="business.id" />
|
2026-05-24 11:34:52 +03:30
|
|
|
|
</div>
|
2026-06-12 20:16:14 +03:30
|
|
|
|
</ResponsiveAppLayout>
|
2026-05-18 20:18:28 +03:30
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-06-12 20:16:14 +03:30
|
|
|
|
import { onMounted, ref } from 'vue';
|
2026-05-23 18:13:25 +03:30
|
|
|
|
import { fetchMyBusinesses, updateBusiness } from '@/api/businesses';
|
|
|
|
|
|
import { ApiError } from '@/api/client';
|
2026-06-12 20:16:14 +03:30
|
|
|
|
import type { Business } from '@/api/types';
|
2026-05-25 19:13:28 +03:30
|
|
|
|
import BusinessAvailabilityEditor from '@/components/owner/BusinessAvailabilityEditor.vue';
|
2026-05-31 21:38:09 +03:30
|
|
|
|
import ServiceClassManager from '@/components/owner/ServiceClassManager.vue';
|
2026-06-10 14:16:50 +03:30
|
|
|
|
import AppButton from '@/components/ui/AppButton.vue';
|
2026-05-23 18:13:25 +03:30
|
|
|
|
import AppInput from '@/components/ui/AppInput.vue';
|
2026-06-12 20:16:14 +03:30
|
|
|
|
import AppTextarea from '@/components/ui/AppTextarea.vue';
|
|
|
|
|
|
import FormPageSkeleton from '@/components/ui/FormPageSkeleton.vue';
|
|
|
|
|
|
import ResponsiveAppLayout from '@/layouts/ResponsiveAppLayout.vue';
|
|
|
|
|
|
import { useTenantStore } from '@/stores/tenant';
|
2026-05-23 18:13:25 +03:30
|
|
|
|
|
2026-06-12 20:16:14 +03:30
|
|
|
|
const tenant = useTenantStore();
|
2026-05-23 18:13:25 +03:30
|
|
|
|
const business = ref<Business | null>(null);
|
|
|
|
|
|
const loading = ref(true);
|
|
|
|
|
|
const saving = ref(false);
|
|
|
|
|
|
const error = ref('');
|
2026-06-12 20:16:14 +03:30
|
|
|
|
const saveMessage = ref('');
|
|
|
|
|
|
const serviceClassesEnabled = ref(false);
|
2026-05-23 18:13:25 +03:30
|
|
|
|
const form = ref({
|
|
|
|
|
|
title: '',
|
2026-06-12 20:16:14 +03:30
|
|
|
|
subtitle: '',
|
2026-05-23 18:13:25 +03:30
|
|
|
|
description: '',
|
|
|
|
|
|
address: '',
|
2026-06-12 20:16:14 +03:30
|
|
|
|
phone: '',
|
2026-05-23 18:13:25 +03:30
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-12 20:16:14 +03:30
|
|
|
|
async function loadBusiness() {
|
|
|
|
|
|
loading.value = true;
|
2026-05-31 21:38:09 +03:30
|
|
|
|
error.value = '';
|
2026-06-12 20:16:14 +03:30
|
|
|
|
try {
|
|
|
|
|
|
const items = await fetchMyBusinesses();
|
|
|
|
|
|
business.value = items[0] ?? null;
|
|
|
|
|
|
if (!business.value) {
|
|
|
|
|
|
error.value = 'کسبوکاری برای مدیریت یافت نشد.';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
form.value = {
|
|
|
|
|
|
title: business.value.title,
|
|
|
|
|
|
subtitle: business.value.subtitle ?? '',
|
|
|
|
|
|
description: business.value.description ?? '',
|
|
|
|
|
|
address: business.value.address ?? '',
|
|
|
|
|
|
phone: business.value.phone ?? '',
|
|
|
|
|
|
};
|
|
|
|
|
|
serviceClassesEnabled.value = business.value.serviceClassesEnabled ?? false;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e instanceof ApiError ? e.message : 'بارگذاری کسبوکار ناموفق بود.';
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function setServiceClassesEnabled(value: boolean) {
|
|
|
|
|
|
if (!business.value) return;
|
|
|
|
|
|
const previous = serviceClassesEnabled.value;
|
|
|
|
|
|
serviceClassesEnabled.value = value;
|
2026-05-31 21:38:09 +03:30
|
|
|
|
try {
|
|
|
|
|
|
business.value = await updateBusiness(business.value.id, {
|
|
|
|
|
|
serviceClassesEnabled: value,
|
|
|
|
|
|
});
|
2026-06-12 20:16:14 +03:30
|
|
|
|
tenant.syncSingleBusiness(business.value);
|
2026-05-31 21:38:09 +03:30
|
|
|
|
} catch (e) {
|
2026-06-12 20:16:14 +03:30
|
|
|
|
serviceClassesEnabled.value = previous;
|
|
|
|
|
|
error.value =
|
|
|
|
|
|
e instanceof ApiError ? e.message : 'ذخیره وضعیت کلاسبندی ناموفق بود.';
|
2026-05-31 21:38:09 +03:30
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 20:16:14 +03:30
|
|
|
|
async function saveBusiness() {
|
2026-05-23 18:13:25 +03:30
|
|
|
|
if (!business.value) return;
|
|
|
|
|
|
saving.value = true;
|
2026-06-12 20:16:14 +03:30
|
|
|
|
saveMessage.value = '';
|
2026-05-23 18:13:25 +03:30
|
|
|
|
try {
|
|
|
|
|
|
business.value = await updateBusiness(business.value.id, {
|
|
|
|
|
|
title: form.value.title.trim(),
|
2026-06-12 20:16:14 +03:30
|
|
|
|
subtitle: form.value.subtitle.trim() || null,
|
2026-05-23 18:13:25 +03:30
|
|
|
|
description: form.value.description.trim() || null,
|
|
|
|
|
|
address: form.value.address.trim() || null,
|
2026-06-12 20:16:14 +03:30
|
|
|
|
phone: form.value.phone.trim() || null,
|
2026-05-23 18:13:25 +03:30
|
|
|
|
});
|
2026-06-12 20:16:14 +03:30
|
|
|
|
tenant.syncSingleBusiness(business.value);
|
|
|
|
|
|
saveMessage.value = 'تغییرات ذخیره شد.';
|
2026-05-23 18:13:25 +03:30
|
|
|
|
} catch (e) {
|
2026-06-12 20:16:14 +03:30
|
|
|
|
error.value = e instanceof ApiError ? e.message : 'ذخیره ناموفق بود.';
|
2026-05-23 18:13:25 +03:30
|
|
|
|
} finally {
|
|
|
|
|
|
saving.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-24 11:34:52 +03:30
|
|
|
|
|
2026-06-12 20:16:14 +03:30
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
void loadBusiness();
|
|
|
|
|
|
});
|
2026-05-18 20:18:28 +03:30
|
|
|
|
</script>
|