/** * قاعدهٔ مشترک نمایش تخصص‌های پزشک — منبع واحد کارت و پوستر. * * پزشک می‌تواند چند تخصص داشته باشد و معمولاً هم دارد: ذخیرهٔ یک زیرتخصص در * بک‌اند والدهایش را هم می‌نشاند، پس «جراح گوارش» عملاً یعنی «جراحی عمومی» + * زیرشاخه. چاپ همهٔ نام‌ها پشت‌سرهم، در موبایل ارتفاع کارت را باد می‌کند و در * پوستر از کادر ثابت بیرون می‌زند. * * نمایش در دو جا متفاوت است — کارت `+N` کلیک‌شدنی دارد و پوستر ندارد — اما * «کدام تخصص اصلی است» یک قاعده بیشتر نیست و اینجا می‌ماند. */ const SEPARATOR = " · "; /** * تخصص «اصلی» و بقیه. * * ریشه (بدون `parent_id`) اصلی است: عنوانی است که بیمار می‌شناسد و چون هنگام * ذخیره خودکار اضافه می‌شود تقریباً همیشه وجود دارد. اگر پزشکی فقط زیرتخصص داشت، * اولین آیتم آرایه. * * @param {Array<{name?: string, parent_id?: string|number|null}>} list * @returns {{primary: object|null, rest: Array}} */ export function splitSpecialties(list) { const items = (list ?? []).filter((s) => s?.name); if (items.length === 0) return { primary: null, rest: [] }; const primary = items.find((s) => s.parent_id == null) ?? items[0]; return { primary, rest: items.filter((s) => s !== primary) }; } /** * زیرتخصص‌ها به‌شکل یک خط متنی، بریده روی مرز کلمه با بودجهٔ کاراکتر. * * پوستر کادر ثابت ۱۰۸۰×۱۳۵۰ با `overflow-hidden` دارد؛ هر ردیف اضافه بخش‌های * پایین را بیرون می‌اندازد. برش بر اساس طول متن است نه تعداد ثابت، چون * «جراحی لاپاراسکوپی» و «قلب» یک‌اندازه جا نمی‌گیرند. * * @param {Array<{name?: string}>} rest * @param {number} budget حداکثر کاراکترِ خط * @returns {{text: string, hidden: number}} */ export function posterSpecialtyLine(rest, budget = 90) { const names = (rest ?? []).filter((s) => s?.name).map((s) => s.name); const shown = []; let used = 0; for (const name of names) { const cost = name.length + (shown.length ? SEPARATOR.length : 0); if (shown.length && used + cost > budget) break; shown.push(name); used += cost; } return { text: shown.join(SEPARATOR), hidden: names.length - shown.length }; }