Add optional slug field to business registration form
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -50,6 +50,7 @@ export type BusinessRegistrationRequest = {
|
|||||||
export type CreateBusinessRegistrationPayload = {
|
export type CreateBusinessRegistrationPayload = {
|
||||||
title: string;
|
title: string;
|
||||||
categoryId: string;
|
categoryId: string;
|
||||||
|
slug?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
contactPhone?: string;
|
contactPhone?: string;
|
||||||
address?: string;
|
address?: string;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/** Simple slug for API (latin, hyphen-separated). */
|
||||||
|
export function slugify(value: string): string {
|
||||||
|
return value
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^a-z0-9-]/g, '')
|
||||||
|
.replace(/-+/g, '-')
|
||||||
|
.replace(/^-|-$/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
||||||
|
|
||||||
|
export function isValidSlug(slug: string): boolean {
|
||||||
|
return SLUG_PATTERN.test(slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optionalSlug(value: string): string | undefined {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
return trimmed || undefined;
|
||||||
|
}
|
||||||
@@ -37,7 +37,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form v-if="showForm" class="space-y-4" @submit.prevent="onSubmit">
|
<form v-if="showForm" class="space-y-4" @submit.prevent="onSubmit">
|
||||||
<AppInput id="bizTitle" v-model="form.title" label="نام کسبوکار" required />
|
<AppInput id="bizTitle" v-model="form.title" label="نام کسبوکار" required @blur="maybeSlug" />
|
||||||
|
|
||||||
|
<AppInput
|
||||||
|
id="bizSlug"
|
||||||
|
v-model="form.slug"
|
||||||
|
label="اسلاگ (انگلیسی)"
|
||||||
|
ltr
|
||||||
|
placeholder="اختیاری — از عنوان ساخته میشود"
|
||||||
|
@input="slugTouched = true"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="space-y-1.5">
|
<div class="space-y-1.5">
|
||||||
<label for="bizCategory" class="block text-sm font-medium text-muted">دسته</label>
|
<label for="bizCategory" class="block text-sm font-medium text-muted">دسته</label>
|
||||||
@@ -93,6 +102,7 @@ import AppButton from '@/components/ui/AppButton.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';
|
||||||
|
import { isValidSlug, optionalSlug, slugify } from '@/utils/slug';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
@@ -100,11 +110,13 @@ const auth = useAuthStore();
|
|||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const error = ref('');
|
const error = ref('');
|
||||||
|
const slugTouched = ref(false);
|
||||||
const categories = ref<BusinessCategory[]>([]);
|
const categories = ref<BusinessCategory[]>([]);
|
||||||
const requests = ref<BusinessRegistrationRequest[]>([]);
|
const requests = ref<BusinessRegistrationRequest[]>([]);
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
title: '',
|
title: '',
|
||||||
|
slug: '',
|
||||||
categoryId: '',
|
categoryId: '',
|
||||||
description: '',
|
description: '',
|
||||||
contactPhone: '',
|
contactPhone: '',
|
||||||
@@ -148,17 +160,29 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function maybeSlug() {
|
||||||
|
if (!slugTouched.value && !form.value.slug.trim()) {
|
||||||
|
form.value.slug = slugify(form.value.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
if (!form.value.title.trim() || !form.value.categoryId) {
|
if (!form.value.title.trim() || !form.value.categoryId) {
|
||||||
error.value = 'نام و دسته را وارد کنید.';
|
error.value = 'نام و دسته را وارد کنید.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const slug = optionalSlug(form.value.slug);
|
||||||
|
if (slug && !isValidSlug(slug)) {
|
||||||
|
error.value = 'اسلاگ فقط حروف کوچک انگلیسی و خط تیره باشد.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
error.value = '';
|
error.value = '';
|
||||||
try {
|
try {
|
||||||
await submitBusinessRegistration({
|
await submitBusinessRegistration({
|
||||||
title: form.value.title.trim(),
|
title: form.value.title.trim(),
|
||||||
categoryId: form.value.categoryId,
|
categoryId: form.value.categoryId,
|
||||||
|
...(slug ? { slug } : {}),
|
||||||
description: form.value.description.trim() || undefined,
|
description: form.value.description.trim() || undefined,
|
||||||
contactPhone: form.value.contactPhone.trim() || undefined,
|
contactPhone: form.value.contactPhone.trim() || undefined,
|
||||||
address: form.value.address.trim() || undefined,
|
address: form.value.address.trim() || undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user