Connect public lead forms to CRM Leads on submit.

This commit is contained in:
2026-09-28 13:39:44 +03:30
parent 32e27d45f7
commit f20206b045
10 changed files with 971 additions and 91 deletions
+59 -3
View File
@@ -10,14 +10,47 @@ const saving = ref(false);
const error = ref('');
const done = ref(false);
const thankYou = ref('');
const started = ref(false);
const form = ref<PublicLeadFormDto | null>(null);
const values = reactive<Record<string, string>>({});
const honeypot = ref('');
const token = String(route.params.token || '');
const coverUrl = computed(() =>
form.value?.coverPath || (form.value?.hasCover ? `/api/public/forms/${token}/cover` : ''),
);
function readUtm() {
const q = route.query;
const pick = (key: string) => {
const v = q[key];
return typeof v === 'string' ? v : Array.isArray(v) ? String(v[0] || '') : '';
};
return {
source: pick('utm_source'),
medium: pick('utm_medium'),
campaign: pick('utm_campaign'),
term: pick('utm_term'),
content: pick('utm_content'),
landingPage: typeof window !== 'undefined' ? window.location.href : '',
referrer: typeof document !== 'undefined' ? document.referrer : '',
};
}
async function postEvent(type: 'view' | 'start') {
try {
await axios.post(`/api/public/forms/${token}/event`, { type });
} catch {
/* ignore analytics errors */
}
}
function onFieldFocus() {
if (started.value) return;
started.value = true;
void postEvent('start');
}
async function load() {
loading.value = true;
error.value = '';
@@ -28,6 +61,7 @@ async function load() {
if (field.fieldType === 'heading') continue;
values[field.key] = values[field.key] ?? '';
}
void postEvent('view');
} catch {
error.value = 'این فرم پیدا نشد یا غیرفعال است.';
form.value = null;
@@ -52,11 +86,21 @@ async function submit() {
saving.value = true;
error.value = '';
try {
const utm = readUtm();
const { data } = await axios.post<SubmitLeadFormResponse>(
`/api/public/forms/${token}/submit`,
{ values: { ...values } },
{
values: { ...values },
utm,
pageUrl: window.location.href,
referrer: document.referrer || undefined,
website: honeypot.value,
},
);
thankYou.value = data.thankYouMessage || 'ثبت شد. متشکریم.';
thankYou.value =
data.message ||
data.thankYouMessage ||
'ثبت شد. متشکریم.';
done.value = true;
if (data.redirectUrl) window.location.assign(data.redirectUrl);
} catch (e) {
@@ -95,9 +139,14 @@ onMounted(load);
</div>
<form v-else-if="form" class="fields" @submit.prevent="submit">
<!-- honeypot -->
<label class="hp" aria-hidden="true">
وب‌سایت
<input v-model="honeypot" type="text" tabindex="-1" autocomplete="off" />
</label>
<template v-for="field in form.fields" :key="field.key">
<p v-if="field.fieldType === 'heading'" class="heading">{{ field.label }}</p>
<label v-else>
<label v-else @focusin="onFieldFocus">
{{ field.label }}
<span v-if="field.required" class="req">*</span>
<textarea
@@ -200,4 +249,11 @@ input, textarea, select {
.stars button { border: 0; background: none; font-size: 1.3rem; color: #cbd5e1; cursor: pointer; }
.stars button.on { color: #eab308; }
.heading { margin: 0; font-weight: 700; color: #0f172a; }
.hp {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
</style>