mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-04-11 18:08:16 +00:00
- Streaming AI chat via Vercel AI SDK v6 UI message stream — tool-based
generative UI (pick_branch, list_departments, show_clinic_timings,
show_doctors, show_doctor_slots, suggest_booking). Typing indicator,
markdown suppressed, text parts hidden when widgets are rendered.
- Centralized Preact store (store.tsx) for visitor, leadId, captchaToken,
bookingPrefill, doctors roster, branches, selectedBranch — replaces prop
drilling across chat/book/contact tabs.
- Cloudflare Turnstile captcha gate rendered via light-DOM portal so it
renders correctly inside the shadow DOM (Turnstile CSS doesn't cross
shadow boundaries).
- Lead dedup helper (findOrCreateLeadByPhone, 24h phone window) shared
across chat-start / book / contact so one visitor == one lead. Booking
upgrades existing lead status NEW → APPOINTMENT_SET via updateLeadStatus.
- Pre-chat name+phone form captures the visitor; chat transcript logged
to leadActivity records after each stream.
- Booking wizard gains a branch step 0 (skipped for single-branch
hospitals); departments + doctors filtered by selectedBranch. Chat slot
picks prefill the booking details step and lock the branch.
- Window-level captcha gate, modal maximize mode, header badge showing
selected branch, widget font inherits from host page (fix :host { all:
initial } override).
- 23 FA Pro 7.1 duotone icons bundled — medical departments, nav, actions,
hospital/location-dot for branch context.
- main.ts: resolve public/ from process.cwd() so widget.js serves in both
dev and prod. tsconfig: exclude widget-src/public/data from server tsc.
- captcha.guard: switch from reCAPTCHA v3 to Cloudflare Turnstile verify.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
84 lines
20 KiB
TypeScript
84 lines
20 KiB
TypeScript
// FontAwesome Pro 7.1.0 Duotone SVGs — bundled as inline strings
|
|
// License: https://fontawesome.com/license (Commercial License)
|
|
// Paths use fill="currentColor" so color is inherited from the <svg> element.
|
|
|
|
export const icons = {
|
|
// Navigation / UI
|
|
'message-dots': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M0 128L0 352c0 53 43 96 96 96l32 0 0 72c0 13.3 10.7 24 24 24 5.2 0 10.2-1.7 14.4-4.8l115.2-86.4c4.2-3.1 9.2-4.8 14.4-4.8l120 0c53 0 96-43 96-96l0-224c0-53-43-96-96-96L96 32C43 32 0 75 0 128zM160 240a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zm128 0a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zm128 0a32 32 0 1 1 -64 0 32 32 0 1 1 64 0z"/><path fill="currentColor" d="M96 240a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm128 0a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm160-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>`,
|
|
|
|
calendar: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path opacity=".4" fill="currentColor" d="M0 160l448 0 0 272c0 26.5-21.5 48-48 48L48 480c-26.5 0-48-21.5-48-48L0 160z"/><path fill="currentColor" d="M160 32c0-17.7-14.3-32-32-32S96 14.3 96 32l0 32-48 0C21.5 64 0 85.5 0 112l0 48 448 0 0-48c0-26.5-21.5-48-48-48l-48 0 0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 32-128 0 0-32z"/></svg>`,
|
|
|
|
phone: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M86.8 90l19 47c5 12.3 19 18.2 31.2 13.3s18.2-19 13.3-31.2l-19-47c-5-12.3-19-18.2-31.2-13.3-12.3 5-18.2 19-13.3 31.2zm275 285c-5 12.3 1 26.3 13.3 31.2l47 19c12.3 5 26.3-1 31.2-13.3s-1-26.3-13.3-31.2l-47-19c-12.3-5-26.3 1-31.2 13.3z"/><path fill="currentColor" d="M112.1 1.4c19.7-5.4 40.3 4.7 48.1 23.5l40.5 97.3c6.9 16.5 2.1 35.6-11.8 47l-44.1 36.1c32.5 71.6 89 130 159.3 164.9L342.8 323c11.3-13.9 30.4-18.6 47-11.8L487 351.8c18.8 7.8 28.9 28.4 23.5 48.1l-1.5 5.5C491.4 470.1 428.9 525.3 352.6 509.2 177.6 472.1 39.9 334.4 2.8 159.4-13.3 83.1 41.9 20.6 106.5 2.9l5.5-1.5zM131.3 72c-5-12.3-19-18.2-31.2-13.3S81.8 77.7 86.8 90l19 47c5 12.3 19 18.2 31.2 13.3s18.2-19 13.3-31.2l-19-47zM393 361.7c-12.3-5-26.3 1-31.2 13.3s1 26.3 13.3 31.2l47 19c12.3 5 26.3-1 31.2-13.3s-1-26.3-13.3-31.2l-47-19z"/></svg>`,
|
|
|
|
'paper-plane-top': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" fill="currentColor" d="M32 479c0 18.1 14.7 33 32.8 33 4.7 0 9.4-1 13.7-3L554.2 290c13.3-6.1 21.8-19.4 21.8-34l-448 0-93.2 209.6C33 469.8 32 474.4 32 479z"/><path fill="currentColor" d="M78.5 3L554.2 222c13.3 6.1 21.8 19.4 21.8 34L128 256 34.8 46.4C33 42.2 32 37.6 32 33 32 14.8 46.7 0 64.8 0 69.5 0 74.2 1 78.5 3z"/></svg>`,
|
|
|
|
xmark: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M55.1 73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L147.2 256 9.9 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192.5 301.3 329.9 438.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.8 256 375.1 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192.5 210.7 55.1 73.4z"/></svg>`,
|
|
|
|
'circle-check': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M0 256a256 256 0 1 0 512 0 256 256 0 1 0 -512 0zm135.1 7.1c9.4-9.4 24.6-9.4 33.9 0L221.1 315.2 340.5 151c7.8-10.7 22.8-13.1 33.5-5.3s13.1 22.8 5.3 33.5L243.4 366.1c-4.1 5.7-10.5 9.3-17.5 9.8s-13.9-2-18.8-7l-72-72c-9.4-9.4-9.4-24.6 0-33.9z"/><path fill="currentColor" d="M340.5 151c7.8-10.7 22.8-13.1 33.5-5.3s13.1 22.8 5.3 33.5L243.4 366.1c-4.1 5.7-10.5 9.3-17.5 9.8s-13.9-2-18.8-7l-72-72c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0L221.1 315.2 340.5 151z"/></svg>`,
|
|
|
|
sparkles: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" fill="currentColor" d="M352 448c0 4.8 3 9.1 7.5 10.8L416 480 437.2 536.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L480 480 536.5 458.8c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L480 416 458.8 359.5c-1.7-4.5-6-7.5-10.8-7.5s-9.1 3-10.8 7.5L416 416 359.5 437.2c-4.5 1.7-7.5 6-7.5 10.8zM384 64c0 4.8 3 9.1 7.5 10.8L448 96 469.2 152.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L512 96 568.5 74.8c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L512 32 490.8-24.5c-1.7-4.5-6-7.5-10.8-7.5s-9.1 3-10.8 7.5L448 32 391.5 53.2c-4.5 1.7-7.5 6-7.5 10.8z"/><path fill="currentColor" d="M205.1 73.3c-2.6-5.7-8.3-9.3-14.5-9.3s-11.9 3.6-14.5 9.3L123.4 187.4 9.3 240C3.6 242.6 0 248.3 0 254.6s3.6 11.9 9.3 14.5L123.4 321.8 176 435.8c2.6 5.7 8.3 9.3 14.5 9.3s11.9-3.6 14.5-9.3l52.7-114.1 114.1-52.7c5.7-2.6 9.3-8.3 9.3-14.5s-3.6-11.9-9.3-14.5L257.8 187.4 205.1 73.3z"/></svg>`,
|
|
|
|
'hands-praying': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path opacity=".4" fill="currentColor" d="M352 224l0 119.6c0 57.2 37.9 107.4 92.8 123.1l154.4 44.1c9.7 2.8 20 .8 28.1-5.2S640 490 640 480l0-96c0-13.8-8.8-26-21.9-30.4l-58.1-19.4 0-110.7c0-29-9.3-57.3-26.5-80.7L440.2 16.3C427.1-1.5 402.1-5.3 384.3 7.8s-21.6 38.1-8.5 55.9L464 183.4 464 296c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-72c0-17.7-14.3-32-32-32s-32 14.3-32 32z"/><path fill="currentColor" d="M200 320c13.3 0 24-10.7 24-24l0-72c0-17.7 14.3-32 32-32s32 14.3 32 32l0 119.6c0 57.2-37.9 107.4-92.8 123.1L40.8 510.8c-9.7 2.8-20 .8-28.1-5.2S0 490 0 480l0-96c0-13.8 8.8-26 21.9-30.4L80 334.3 80 223.6c0-29 9.3-57.3 26.5-80.7L199.8 16.3c13.1-17.8 38.1-21.6 55.9-8.5s21.6 38.1 8.5 55.9L176 183.4 176 296c0 13.3 10.7 24 24 24z"/></svg>`,
|
|
|
|
'hand-wave': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" fill="currentColor" d="M73.4 265.4c-12.5 12.5-12.5 32.8 0 45.3 8.8 8.8 55.2 55.2 139.1 139.1l4.9 4.9c22.2 22.2 49.2 36.9 77.6 44.1 58 17 122.8 6.6 173.6-32.7 47.6-36.8 75.5-93.5 75.5-153.7L544 136c0-22.1-17.9-40-40-40s-40 17.9-40 40l0 77.7c0 4.7-6 7-9.4 3.7l-192-192c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L344 197.3c5.2 5.2 5.2 13.6 0 18.7s-13.6 5.2-18.7 0L182.6 73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L280 261.3c5.2 5.2 5.2 13.6 0 18.7s-13.6 5.2-18.7 0L134.6 153.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L216 325.3c5.2 5.2 5.2 13.6 0 18.7s-13.6 5.2-18.7 0c-33.5-33.5-59.8-59.8-78.6-78.6-12.5-12.5-32.8-12.5-45.3 0z"/><path fill="currentColor" d="M392.2 67.4c1.9 13.1 14 22.2 27.2 20.4s22.2-14 20.4-27.2l-1.2-8.5c-5.5-38.7-36-69.1-74.7-74.7l-8.5-1.2c-13.1-1.9-25.3 7.2-27.2 20.4s7.2 25.3 20.4 27.2l8.5 1.2c17.6 2.5 31.4 16.3 33.9 33.9l1.2 8.5zM55.8 380.6c-1.9-13.1-14-22.2-27.2-20.4s-22.2 14-20.4 27.2l1.2 8.5c5.5 38.7 36 69.1 74.7 74.7l8.5 1.2c13.1 1.9 25.3-7.2 27.2-20.4s-7.2-25.3-20.4-27.2L90.9 423c-17.6-2.5-31.4-16.3-33.9-33.9l-1.2-8.5z"/></svg>`,
|
|
|
|
'shield-check': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M16 140c.5 99.2 41.3 280.7 213.6 363.2 16.7 8 36.1 8 52.7 0 172.4-82.5 213.2-263.9 213.7-363.2 .1-26.2-16.3-47.9-38.3-57.2L269.4 2.9C265.3 1 260.7 0 256.1 0s-9.2 1-13.4 2.9L54.3 82.8c-22 9.3-38.4 31-38.3 57.2zM166.8 293.5c-9.2-9.5-9-24.7 .6-33.9 9.5-9.2 24.7-9 33.9 .6 8.8 9.1 17.7 18.3 26.5 27.4 28.5-39.2 57.1-78.5 85.6-117.7 7.8-10.7 22.8-13.1 33.5-5.3s13.1 22.8 5.3 33.5c-34.1 46.9-68.3 93.9-102.4 140.8-4.2 5.7-10.7 9.4-17.8 9.8s-14-2.2-18.9-7.3c-15.5-16-30.9-32-46.4-48z"/><path fill="currentColor" d="M313.4 169.9c7.8-10.7 22.8-13.1 33.5-5.3s13.1 22.8 5.3 33.5L249.8 338.9c-4.2 5.7-10.7 9.4-17.8 9.8s-14-2.2-18.9-7.3l-46.4-48c-9.2-9.5-9-24.7 .6-33.9s24.7-8.9 33.9 .6l26.5 27.4 85.6-117.7z"/></svg>`,
|
|
|
|
'arrow-left': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M77.3 256l32 32 370.7 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-370.7 0-32 32z"/><path fill="currentColor" d="M9.4 278.6c-12.5-12.5-12.5-32.8 0-45.3l160-160c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L77.3 256 214.6 393.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-160-160z"/></svg>`,
|
|
|
|
'arrow-right': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M0 256c0 17.7 14.3 32 32 32l370.7 0 32-32-32-32-370.7 0c-17.7 0-32 14.3-32 32z"/><path fill="currentColor" d="M502.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L434.7 256 297.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg>`,
|
|
|
|
'up-right-and-down-left-from-center': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M0 344L0 488c0 13.3 10.7 24 24 24l144 0c9.7 0 18.5-5.8 22.2-14.8s1.7-19.3-5.2-26.2l-39-39 87-87c9.4-9.4 9.4-24.6 0-33.9l-32-32c-9.4-9.4-24.6-9.4-33.9 0l-87 87-39-39c-6.9-6.9-17.2-8.9-26.2-5.2S0 334.3 0 344z"/><path fill="currentColor" d="M488 0L344 0c-9.7 0-18.5 5.8-22.2 14.8S320.2 34.1 327 41l39 39-87 87c-9.4 9.4-9.4 24.6 0 33.9l32 32c9.4 9.4 24.6 9.4 33.9 0l87-87 39 39c6.9 6.9 17.2 8.9 26.2 5.2S512 177.7 512 168l0-144c0-13.3-10.7-24-24-24z"/></svg>`,
|
|
|
|
'down-left-and-up-right-to-center': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M7.5 439c-9.4 9.4-9.4 24.6 0 33.9l32 32c9.4 9.4 24.6 9.4 33.9 0l87-87 39 39c6.9 6.9 17.2 8.9 26.2 5.2s14.8-12.5 14.8-22.2l0-144c0-13.3-10.7-24-24-24l-144 0c-9.7 0-18.5 5.8-22.2 14.8s-1.7 19.3 5.2 26.2l39 39-87 87z"/><path fill="currentColor" d="M473.5 7c-9.4-9.4-24.6-9.4-33.9 0l-87 87-39-39c-6.9-6.9-17.2-8.9-26.2-5.2S272.5 62.3 272.5 72l0 144c0 13.3 10.7 24 24 24l144 0c9.7 0 18.5-5.8 22.2-14.8s1.7-19.3-5.2-26.2l-39-39 87-87c9.4-9.4 9.4-24.6 0-33.9l-32-32z"/></svg>`,
|
|
|
|
// Branch / location
|
|
hospital: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" fill="currentColor" d="M0 192L0 448c0 35.3 28.7 64 64 64l176 0 0-112c0-17.7 14.3-32 32-32l32 0c17.7 0 32 14.3 32 32l0 112 176 0c35.3 0 64-28.7 64-64l0-256c0-35.3-28.7-64-64-64l-64 0 0-64c0-35.3-28.7-64-64-64L192 0c-35.3 0-64 28.7-64 64l0 64-64 0c-35.3 0-64 28.7-64 64zm64 16c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zm0 128c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM216 152c0-8.8 7.2-16 16-16l32 0 0-32c0-8.8 7.2-16 16-16l16 0c8.8 0 16 7.2 16 16l0 32 32 0c8.8 0 16 7.2 16 16l0 16c0 8.8-7.2 16-16 16l-32 0 0 32c0 8.8-7.2 16-16 16l-16 0c-8.8 0-16-7.2-16-16l0-32-32 0c-8.8 0-16-7.2-16-16l0-16zm232 56c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zm0 128c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32z"/><path fill="currentColor" d="M264 104c0-8.8 7.2-16 16-16l16 0c8.8 0 16 7.2 16 16l0 32 32 0c8.8 0 16 7.2 16 16l0 16c0 8.8-7.2 16-16 16l-32 0 0 32c0 8.8-7.2 16-16 16l-16 0c-8.8 0-16-7.2-16-16l0-32-32 0c-8.8 0-16-7.2-16-16l0-16c0-8.8 7.2-16 16-16l32 0 0-32zM112 256l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16zm16 112c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32zm112 32c0-17.7 14.3-32 32-32l32 0c17.7 0 32 14.3 32 32l0 112-96 0 0-112zm272-32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32zM496 256l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16z"/></svg>`,
|
|
|
|
'location-dot': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path opacity=".4" fill="currentColor" d="M0 188.6c0 119.3 120.2 262.3 170.4 316.8 11.8 12.8 31.5 12.8 43.3 0 50.2-54.5 170.4-197.5 170.4-316.8 0-104.1-86-188.6-192-188.6S0 84.4 0 188.6zM256 192a64 64 0 1 1 -128 0 64 64 0 1 1 128 0z"/><path fill="currentColor" d="M128 192a64 64 0 1 1 128 0 64 64 0 1 1 -64 0z"/></svg>`,
|
|
|
|
// Medical departments
|
|
stethoscope: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" fill="currentColor" d="M160 348.8c10.3 2.1 21 3.2 32 3.2s21.7-1.1 32-3.2l0 19.2c0 61.9 50.1 112 112 112s112-50.1 112-112l0-85.5c10 3.5 20.8 5.5 32 5.5s22-1.9 32-5.5l0 85.5c0 97.2-78.8 176-176 176S160 465.2 160 368l0-19.2z"/><path fill="currentColor" d="M80 0C53.5 0 32 21.5 32 48l0 144c0 88.4 71.6 160 160 160s160-71.6 160-160l0-144c0-26.5-21.5-48-48-48L256 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l32 0 0 128c0 53-43 96-96 96s-96-43-96-96l0-128 32 0c17.7 0 32-14.3 32-32S145.7 0 128 0L80 0zM448 192a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm128 0a96 96 0 1 0 -192 0 96 96 0 1 0 192 0z"/></svg>`,
|
|
|
|
'heart-pulse': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M0 165.1l0 2.6c0 23.6 6.2 48 16.6 72.3l106 0c3.2 0 6.1-1.9 7.4-4.9l31.8-76.3c3.7-8.8 12.3-14.6 21.8-14.8s18.3 5.4 22.2 14.1l51.3 113.9 41.4-82.8c4.1-8.1 12.4-13.3 21.5-13.3s17.4 5.1 21.5 13.3l23.2 46.3c1.4 2.7 4.1 4.4 7.2 4.4l123.6 0c10.5-24.3 16.6-48.7 16.6-72.3l0-2.6C512 91.6 452.4 32 378.9 32 336.2 32 296 52.5 271 87.1l-15 20.7-15-20.7C216 52.5 175.9 32 133.1 32 59.6 32 0 91.6 0 165.1zM42.5 288c47.2 73.8 123 141.7 170.4 177.9 12.4 9.4 27.6 14.1 43.1 14.1s30.8-4.6 43.1-14.1C346.6 429.7 422.4 361.8 469.6 288l-97.8 0c-21.2 0-40.6-12-50.1-31l-1.7-3.4-42.5 85.1c-4.1 8.3-12.7 13.5-22 13.3s-17.6-5.7-21.4-14.1l-49.3-109.5-10.5 25.2c-8.7 20.9-29.1 34.5-51.7 34.5l-80.2 0z"/><path fill="currentColor" d="M42.5 288c-10.1-15.8-18.9-31.9-25.8-48l106 0c3.2 0 6.1-1.9 7.4-4.9l31.8-76.3c3.7-8.8 12.3-14.6 21.8-14.8s18.3 5.4 22.2 14.1l51.3 113.9 41.4-82.8c4.1-8.1 12.4-13.3 21.5-13.3s17.4 5.1 21.5 13.3l23.2 46.3c1.4 2.7 4.1 4.4 7.2 4.4l123.6 0c-6.9 16.1-15.7 32.2-25.8 48l-97.8 0c-21.2 0-40.6-12-50.1-31l-1.7-3.4-42.5 85.1c-4.1 8.3-12.7 13.5-22 13.3s-17.6-5.7-21.4-14.1l-49.3-109.5-10.5 25.2c-8.7 20.9-29.1 34.5-51.7 34.5l-80.2 0z"/></svg>`,
|
|
|
|
bone: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M197.4 160c-3.9 0-7.2-2.8-8.1-6.6-10.2-42.1-48.1-73.4-93.3-73.4-53 0-96 43-96 96 0 29.1 12.9 55.1 33.3 72.7 4.3 3.7 4.3 10.8 0 14.5-20.4 17.6-33.3 43.7-33.3 72.7 0 53 43 96 96 96 45.2 0 83.1-31.3 93.3-73.4 .9-3.8 4.2-6.6 8.1-6.6l245.1 0c3.9 0 7.2 2.8 8.1 6.6 10.2 42.1 48.1 73.4 93.3 73.4 53 0 96-43 96-96 0-29.1-12.9-55.1-33.3-72.7-4.3-3.7-4.3-10.8 0-14.5 20.4-17.6 33.3-43.7 33.3-72.7 0-53-43-96-96-96-45.2 0-83.1 31.3-93.3 73.4-.9 3.8-4.2 6.6-8.1 6.6l-245.1 0z"/></svg>`,
|
|
|
|
'person-pregnant': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path opacity=".4" fill="currentColor" d="M136 24a56 56 0 1 0 112 0 56 56 0 1 0 -112 0z"/><path fill="currentColor" d="M74.6 305.8l29-43.5-30.5 113.5c-2.6 9.6-.6 19.9 5.5 27.8S94 416 104 416l8 0 0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96 32 0 0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-110.8c8.6-4.5 16.8-10 24.3-16.5l4-3.4c22.6-19.4 35.7-47.7 35.7-77.6 0-35.9-18.8-69.1-49.6-87.6l-30.4-18.2 0-1.8c0-46.5-37.7-84.1-84.1-84.1-28.1 0-54.4 14.1-70 37.5L21.4 270.2c-9.8 14.7-5.8 34.6 8.9 44.4s34.6 5.8 44.4-8.9z"/></svg>`,
|
|
|
|
ear: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path opacity=".4" fill="currentColor" d="M0 192L0 384c0 70.7 57.3 128 128 128l9.3 0c52.3 0 99.4-31.9 118.8-80.5l20.1-50.2c5.5-13.7 15.8-24.8 27.8-33.4 48.4-34.9 80-91.7 80-156 0-106-86-192-192-192S0 86 0 192zm64 0c0-70.7 57.3-128 128-128s128 57.3 128 128l0 8c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-8c0-44.2-35.8-80-80-80s-80 35.8-80 80l0 16.4c36 4 64 34.5 64 71.6 0 39.8-32.2 72-72 72l-16 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l16 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-16 0c-13.3 0-24-10.7-24-24l0-40z"/><path fill="currentColor" d="M192 112c-44.2 0-80 35.8-80 80l0 16.4c36 4 64 34.5 64 71.6 0 39.8-32.2 72-72 72l-16 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l16 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-16 0c-13.3 0-24-10.7-24-24l0-40c0-70.7 57.3-128 128-128s128 57.3 128 128l0 8c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-8c0-44.2-35.8-80-80-80z"/></svg>`,
|
|
|
|
baby: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path opacity=".4" fill="currentColor" d="M7.7 144.5c13-17.9 38-21.8 55.9-8.8L99.8 162c26.8 19.5 59.1 30 92.2 30s65.4-10.5 92.2-30l36.2-26.4c17.9-13 42.9-9 55.9 8.8s9 42.9-8.8 55.9l-36.2 26.4c-13.6 9.9-28.1 18.2-43.3 25l0 36.3-192 0 0-36.3c-15.2-6.7-29.7-15.1-43.3-25L16.5 200.3c-17.9-13-21.8-38-8.8-55.9zM47.2 401.1l50.2-71.8c20.2 17.7 40.4 35.3 60.6 53l-26 37.2 24.3 24.3c15.6 15.6 15.6 40.9 0 56.6s-40.9 15.6-56.6 0l-48-48C38 438.6 36.1 417 47.2 401.1zM264 88a72 72 0 1 1 -144 0 72 72 0 1 1 144 0zM226 382.3c20.2-17.7 40.4-35.3 60.6-53l50.2 71.8c11.1 15.9 9.2 37.5-4.5 51.2l-48 48c-15.6 15.6-40.9 15.6-56.6 0s-15.6-40.9 0-56.6l24.3-24.3-26-37.2z"/><path fill="currentColor" d="M160 384l-64-56 0-40 192 0 0 40-64 56-64 0z"/></svg>`,
|
|
|
|
brain: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path opacity=".4" fill="currentColor" d="M8 272c0 26.2 12.6 49.4 32 64-10 13.4-16 30-16 48 0 44.2 35.8 80 80 80 .7 0 1.3 0 2 0 7.1 27.6 32.2 48 62 48l32 0c17.7 0 32-14.3 32-32l0-448c0-17.7-14.3-32-32-32L176 0c-30.9 0-56 25.1-56 56l0 24c-44.2 0-80 35.8-80 80 0 15 4.1 29 11.2 40.9-25.7 13.3-43.2 40.1-43.2 71.1zM280 32l0 448c0 17.7 14.3 32 32 32l32 0c29.8 0 54.9-20.4 62-48 .7 0 1.3 0 2 0 44.2 0 80-35.8 80-80 0-18-6-34.6-16-48 19.4-14.6 32-37.8 32-64 0-30.9-17.6-57.8-43.2-71.1 7.1-12 11.2-26 11.2-40.9 0-44.2-35.8-80-80-80l0-24c0-30.9-25.1-56-56-56L312 0c-17.7 0-32 14.3-32 32z"/><path fill="currentColor" d="M232 32l48 0 0 448-48 0 0-448z"/></svg>`,
|
|
|
|
eye: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path opacity=".4" fill="currentColor" d="M2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6 14.9 35.7 46.2 87.7 93 131.1 47.1 43.7 111.8 80.6 192.6 80.6s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1 3.3-7.9 3.3-16.7 0-24.6-14.9-35.7-46.2-87.7-93-131.1-47.1-43.7-111.8-80.6-192.6-80.6S142.5 68.8 95.4 112.6C48.6 156 17.3 208 2.5 243.7zM432 256a144 144 0 1 1 -288 0 144 144 0 1 1 288 0z"/><path fill="currentColor" d="M288 192c0 35.3-28.7 64-64 64-11.5 0-22.3-3-31.7-8.4-1 10.9-.1 22.1 2.9 33.2 13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-12.2-45.7-55.5-74.8-101.1-70.8 5.3 9.3 8.4 20.1 8.4 31.7z"/></svg>`,
|
|
|
|
tooth: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M145 5.7L224 32 303 5.7C314.3 1.9 326 0 337.9 0 398.7 0 448 49.3 448 110.1l0 68.5c0 29.4-9.5 58.1-27.2 81.6l-1.1 1.5c-12.9 17.2-21.3 37.4-24.3 58.7L373.7 471.9c-3.3 23-23 40.1-46.2 40.1-22.8 0-42.3-16.5-46-39L261.3 351.6c-3-18.2-18.8-31.6-37.3-31.6s-34.2 13.4-37.3 31.6L166.5 473c-3.8 22.5-23.2 39-46 39-23.2 0-42.9-17.1-46.2-40.1L52.6 320.5c-3-21.3-11.4-41.5-24.3-58.7l-1.1-1.5C9.5 236.7 0 208.1 0 178.7l0-68.5C0 49.3 49.3 0 110.1 0 122 0 133.7 1.9 145 5.7z"/></svg>`,
|
|
};
|
|
|
|
export type IconName = keyof typeof icons;
|
|
|
|
// Render an icon as an HTML string with given size and color.
|
|
// Color cascades to paths via fill="currentColor".
|
|
export const icon = (name: IconName, size = 16, color = 'currentColor'): string => {
|
|
return icons[name].replace(
|
|
'<svg',
|
|
`<svg width="${size}" height="${size}" style="vertical-align:middle;color:${color};flex-shrink:0"`,
|
|
);
|
|
};
|
|
|
|
// Map a department name to a medical icon. Keyword-based with stethoscope fallback.
|
|
export const departmentIcon = (department: string): IconName => {
|
|
const key = department.toLowerCase().replace(/_/g, ' ');
|
|
if (key.includes('cardio') || key.includes('heart')) return 'heart-pulse';
|
|
if (key.includes('ortho') || key.includes('bone') || key.includes('spine')) return 'bone';
|
|
if (key.includes('gyn') || key.includes('obstet') || key.includes('maternity') || key.includes('pregnan')) return 'person-pregnant';
|
|
if (key.includes('ent') || key.includes('otolaryn') || key.includes('ear') || key.includes('nose') || key.includes('throat')) return 'ear';
|
|
if (key.includes('pediatric') || key.includes('paediatric') || key.includes('child') || key.includes('neonat')) return 'baby';
|
|
if (key.includes('neuro') || key.includes('psych') || key.includes('mental')) return 'brain';
|
|
if (key.includes('ophthal') || key.includes('eye') || key.includes('vision') || key.includes('retina')) return 'eye';
|
|
if (key.includes('dental') || key.includes('dent') || key.includes('tooth')) return 'tooth';
|
|
return 'stethoscope';
|
|
};
|