Files
CRM_Montzar/apps/web/src/lib/lead-form-notes.ts
T

133 lines
4.2 KiB
TypeScript
Raw Normal View History

import { formatJalaliNumeric } from '@/lib/jalali';
export type LeadNoteFile = {
name: string;
mime: string;
dataUrl: string | null;
};
export type LeadNoteRow = {
label: string;
text: string;
file: LeadNoteFile | null;
};
export type LeadNoteView = {
mode: 'prose' | 'answers';
prose: string;
rows: LeadNoteRow[];
};
const UTM_LABELS: Record<string, string> = {
utm_source: 'منبع',
utm_medium: 'رسانه',
utm_campaign: 'کمپین',
utm_term: 'عبارت',
utm_content: 'محتوا',
صفحه: 'صفحه',
ارجاع: 'ارجاع',
};
/** یادداشت لید را اگر جواب فرم باشد به ردیف سؤال/جواب تبدیل می‌کند. */
export function parseLeadFormNotes(raw: string | null | undefined): LeadNoteView {
const text = (raw ?? '').trim();
if (!text) return { mode: 'prose', prose: '', rows: [] };
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
const rows: LeadNoteRow[] = [];
const prose: string[] = [];
for (const line of lines) {
const row = parseAnswerLine(line);
if (row) rows.push(row);
else prose.push(line);
}
const answersAreMain = rows.length >= 2 && rows.length >= prose.length;
if (!answersAreMain) return { mode: 'prose', prose: text, rows: [] };
return { mode: 'answers', prose: prose.join('\n'), rows };
}
export function leadNoteFileIsImage(file: LeadNoteFile): boolean {
return file.mime.startsWith('image/') || (file.dataUrl ?? '').startsWith('data:image/');
}
/** فیلد تاریخ تولد روی خود لید هست؛ جواب فرم نباید دوباره ردیف بسازد. */
export function isBirthDateAnswer(label: string): boolean {
return label.trim() === 'تاریخ تولد';
}
function parseAnswerLine(line: string): LeadNoteRow | null {
const splitAt = line.indexOf(':');
if (splitAt <= 0 || splitAt > 200) return null;
const label = line.slice(0, splitAt).trim();
const value = line.slice(splitAt + 1).trim();
if (!label || !value || looksLikeUrl(label)) return null;
if (label === 'UTM') {
return { label: 'اطلاعات ورود', text: humanizeUtm(value), file: null };
}
const file = readFileValue(value);
if (file) return { label, text: file.name, file };
return { label, text: humanizePlain(value), file: null };
}
function looksLikeUrl(label: string): boolean {
return /^https?:\/\//i.test(label);
}
function humanizePlain(value: string): string {
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
const date = new Date(`${value}T12:00:00`);
if (!Number.isNaN(date.getTime())) return formatJalaliNumeric(date);
}
if (value === 'true') return 'بله';
if (value === 'false') return 'خیر';
return value;
}
function humanizeUtm(value: string): string {
return value
.split('|')
.map((part) => part.trim())
.filter(Boolean)
.map((part) => {
const eq = part.indexOf('=');
if (eq <= 0) return part;
const key = part.slice(0, eq).trim();
const val = part.slice(eq + 1).trim();
return `${UTM_LABELS[key] ?? key}: ${val}`;
})
.join('، ');
}
function readFileValue(value: string): LeadNoteFile | null {
if (!value.startsWith('{') && !value.includes('"dataUrl"') && !value.includes('"name"')) {
return null;
}
try {
return asFile(JSON.parse(value));
} catch {
const name = value.match(/"name"\s*:\s*"([^"]+)"/)?.[1]?.trim();
if (name) return { name, mime: '', dataUrl: null };
if (value.includes('data:')) return { name: 'فایل پیوست', mime: '', dataUrl: null };
return null;
}
}
function asFile(value: unknown): LeadNoteFile | null {
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
const row = value as { name?: unknown; mime?: unknown; dataUrl?: unknown };
const name = typeof row.name === 'string' ? row.name.trim() : '';
const mime = typeof row.mime === 'string' ? row.mime.trim() : '';
const dataUrl = typeof row.dataUrl === 'string' && row.dataUrl.startsWith('data:') ? row.dataUrl : null;
if (!name && !dataUrl) return null;
return {
name: name || 'فایل پیوست',
mime: mime || (dataUrl?.startsWith('data:image/') ? 'image/' : ''),
dataUrl,
};
}