/** * همان الگوریتم جستجوی سرور (apps/api/src/common/search.ts) برای فیلترهای سمت کاربر: * کلمه‌به‌کلمه، بخشی از کلمه کافی است، حروف عربی/فارسی و ارقام فارسی/لاتین یکسان‌اند. */ function toAsciiDigits(s: string): string { return s.replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d))).replace( /[٠-٩]/g, (d) => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d)), ); } export function normalizeSearchText(raw: unknown): string { return toAsciiDigits(String(raw ?? '')) .toLowerCase() .replace(/[يىئ]/g, 'ی') .replace(/ك/g, 'ک') .replace(/ة/g, 'ه') .replace(/[أإٱ]/g, 'ا') .replace(/ؤ/g, 'و') .replace(/[\u064B-\u065F\u0670]/g, '') .replace(/[\u200c\u200f\u200e]/g, ' ') .replace(/\s+/g, ' ') .trim(); } function phoneCore(token: string): string | null { if (!/^\+?[0-9]+$/.test(token)) return null; const core = token.replace(/^\+/, '').replace(/^(0098|98|0)/, ''); return core.length >= 3 ? core : null; } /** آیا همه کلمات عبارت جستجو در یکی از فیلدها پیدا می‌شوند؟ عبارت خالی = true */ export function matchesSearch(query: string, fields: unknown[]): boolean { const q = normalizeSearchText(query); if (!q) return true; const hay = normalizeSearchText(fields.filter((f) => f != null && f !== '').join(' \u0001 ')); return q.split(' ').every((token) => { if (hay.includes(token)) return true; const core = phoneCore(token); return core ? hay.includes(core) : false; }); }