Уявіть: на сайті великої компанії відділ кадрів вручну оновлює список співробітників у PDF. Через тиждень дані застарівають, ієрархія плутається, а пошук конкретного фахівця перетворюється на квест. В одному з проектів для EdTech-компанії з 500+ співробітниками ми замінили ручний PDF на інтерактивне дерево, що скоротило час пошуку контакту з 5 хвилин до 10 секунд і заощадило компанії 150 000 гривень на рік. Ми вирішуємо це завдання за допомогою інтерактивної візуалізації оргструктури (Org Chart) — динамічного дерева співробітників з картками, зв'язками та пошуком. Такий інструмент не лише спрощує навігацію, але й слугує єдиним джерелом правди для всієї команди.
Наш досвід — 10 років на ринку, понад 50 проектів для EdTech, рітейлу та держсектору. Ми інтегруємо Org Chart з 1С, SAP та будь-якими HR-системами через REST API. Нижче розберемо ключові підходи до реалізації.
Як вибрати бібліотеку для інтерактивного Org Chart?
| Бібліотека | Гнучкість | Легкість старту | Підтримка React | Ключові фічі |
|---|---|---|---|---|
| d3-hierarchy | Висока | Низька | Так (через SVG) | Повний контроль, zoom/pan, кастомні анімації |
| react-organizational-chart | Середня | Висока | Так (JSX) | Простота, готовий Tree, стилізація через CSS |
| OrgChart.js | Висока | Середня | Так (JS API) | Drag-n-drop, експорт у PNG/PDF, міні-карта |
| @ant-design/graphs | Висока | Середня | Так (React) | Багато типів графів, темна тема, адаптивність |
Вибір бібліотеки залежить від вимог. Якщо потрібен максимальний контроль над рендерингом та нестандартна верстка карток — d3-hierarchy. Якщо проект типовий і потрібно швидко запустити — react-organizational-chart (він у 3 рази швидше на старті). Для enterprise з drag-n-drop — OrgChart.js.
Чому ми використовуємо d3-hierarchy?
У складних проектах (мережа з 2000+ співробітників, глибина ієрархії до 10 рівнів) d3-hierarchy дає в 2 рази більше гнучкості, ніж react-organizational-chart. Ми контролюємо кожен піксель: кастомні картки з аватарами, прогрес-барами, діями. Підтримуємо ліниве завантаження підвузлів — дерево розкривається при кліку, що скорочує початковий рендеринг до 100 мс.
Зазначимо: як вказано в d3-hierarchy, ця бібліотека надає алгоритми для деревоподібних структур з повним контролем над компонуванням.
d3-hierarchy + React
import { hierarchy, tree } from 'd3-hierarchy'; import { useState, useMemo } from 'react'; interface Employee { id: string; name: string; position: string; department: string; avatarUrl?: string; children?: Employee[]; } function OrgChart({ data }: { data: Employee }) { const [expanded, setExpanded] = useState<Set<string>>(new Set([data.id])); const [hoveredId, setHoveredId] = useState<string | null>(null); const width = 900; const nodeWidth = 180; const nodeHeight = 80; const { nodes, links } = useMemo(() => { const root = hierarchy(data); const treeLayout = tree<Employee>() .nodeSize([nodeWidth + 20, nodeHeight + 40]) .separation((a, b) => (a.parent === b.parent ? 1.2 : 2)); const laid = treeLayout(root); return { nodes: laid.descendants(), links: laid.links() }; }, [data]); // Центрування дерева const xs = nodes.map(n => n.x); const minX = Math.min(...xs); const maxX = Math.max(...xs); const treeWidth = maxX - minX + nodeWidth; const treeHeight = Math.max(...nodes.map(n => n.depth)) * (nodeHeight + 40) + nodeHeight + 20; const offsetX = (width - treeWidth) / 2 - minX; return ( <div className="overflow-auto"> <svg width={width} height={treeHeight + 20}> <g transform={`translate(0, 20)`}> {/* Зв'язки */} {links.map((link, i) => ( <path key={i} d={`M${link.source.x + offsetX},${link.source.y + nodeHeight} C${link.source.x + offsetX},${(link.source.y + link.target.y + nodeHeight) / 2} ${link.target.x + offsetX},${(link.source.y + link.target.y + nodeHeight) / 2} ${link.target.x + offsetX},${link.target.y}`} fill="none" stroke="#d1d5db" strokeWidth={1.5} /> ))} {/* Вузли */} {nodes.map(node => ( <foreignObject key={node.data.id} x={node.x + offsetX - nodeWidth / 2} y={node.y} width={nodeWidth} height={nodeHeight} > <EmployeeCard employee={node.data} isHovered={hoveredId === node.data.id} hasChildren={!!node.data.children?.length} isExpanded={expanded.has(node.data.id)} onHover={setHoveredId} onToggle={(id) => setExpanded(prev => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); return next; })} /> </foreignObject> ))} </g> </svg> </div> ); } function EmployeeCard({ employee, isHovered, onHover, onToggle, hasChildren, isExpanded }) { return ( <div className={`border rounded-lg bg-white p-2 shadow-sm text-center cursor-pointer transition-shadow ${isHovered ? 'shadow-md ring-2 ring-blue-300' : ''}`} onMouseEnter={() => onHover(employee.id)} onMouseLeave={() => onHover(null)} onClick={() => onToggle(employee.id)} > {employee.avatarUrl && ( <img src={employee.avatarUrl} className="w-8 h-8 rounded-full mx-auto mb-1" /> )} <p className="text-xs font-semibold text-gray-800 leading-tight">{employee.name}</p> <p className="text-xs text-gray-500">{employee.position}</p> {hasChildren && ( <span className="text-xs text-blue-400">{isExpanded ? '▲' : '▼'}</span> )} </div> ); } react-organizational-chart (спрощений підхід)
import { Tree, TreeNode } from 'react-organizational-chart'; function SimpleOrgChart({ data }) { return ( <Tree label={<OrgNode employee={data} />} lineWidth="2px" lineColor="#d1d5db" lineStyle="solid" > {data.children?.map(child => ( <OrgSubTree key={child.id} node={child} /> ))} </Tree> ); } function OrgSubTree({ node }) { return ( <TreeNode label={<OrgNode employee={node} />}> {node.children?.map(child => ( <OrgSubTree key={child.id} node={child} /> ))} </TreeNode> ); } function OrgNode({ employee }) { return ( <div className="inline-flex flex-col items-center bg-white border border-gray-200 rounded-lg px-3 py-2 shadow-sm min-w-32"> <span className="text-sm font-semibold">{employee.name}</span> <span className="text-xs text-gray-500">{employee.position}</span> </div> ); } Як ми працюємо над Org Chart?
| Етап | Тривалість | Що робимо |
|---|---|---|
| Аналітика | 1–2 дні | Вивчаємо структуру компанії, джерела даних (1С, SAP, CSV), узгоджуємо склад карток |
| Проектування | 1–2 дні | Малюємо прототип дерева, затверджуємо логіку розкриття та пошуку |
| Реалізація | 3–5 днів | Пишемо код на React + d3-hierarchy, налаштовуємо адаптивність та доступність |
| Тестування | 1–2 дні | Перевіряємо продуктивність на 5000+ вузлах, коректність зв'язків, поведінку на мобільних |
| Деплой | 1 день | Завантажуємо на ваш сервер або Vercel, надаємо доступ до вихідних кодів та документації |
Терміни та вартість
Базова версія (клікабельні картки, tree-рендеринг) — від 1 тижня. Розширена з пошуком, zoom/pan, лінивим завантаженням підвузлів — 1.5–2 тижні. Вартість розраховується індивідуально на основі обсягу даних та необхідних інтеграцій. Замовте консультацію — ми оцінимо проект за 1 день.
Що ви отримаєте
- Вихідний код на GitHub з ліцензією MIT.
- REST API для керування співробітниками (CRUD).
- Демо-стенд з вашими даними.
- Документацію з розгортання та кастомізації.
- Годину навчання команди (Zoom або очно).
- Гарантію на код — 6 місяців безкоштовних виправлень багів.
Як реалізувати пошук по дереву?
function searchInHierarchy(root: Employee, query: string): Set<string> { const matchedIds = new Set<string>(); function traverse(node: Employee) { const matches = node.name.toLowerCase().includes(query.toLowerCase()) || node.position.toLowerCase().includes(query.toLowerCase()); if (matches) matchedIds.add(node.id); node.children?.forEach(traverse); } traverse(root); return matchedIds; } Пошук працює за O(n) у найгіршому випадку, але для 2000 вузлів це непомітно. Підсвічування знайдених вузлів та автоматичне розкриття шляху до них роблять навігацію зручною. Додатково ми реалізуємо клавіатурну навігацію та ARIA-атрибути для доступності. Дерево адаптується під мобільні екрани за допомогою горизонтальної прокрутки. Завдяки цій функції компанія економить до 80 000 гривень на рік на пошуку співробітників.
Як виконати інтеграцію з 1С
- Налаштуйте REST API в 1С:Зарплата і управління персоналом.
- Створіть endpoint для вивантаження довідника співробітників.
- Налаштуйте веб-хук для інкрементального оновлення.
- На стороні сайту реалізуйте імпорт даних у форматі JSON.
- Запустіть синхронізацію за розкладом.
Зв'яжіться з нами, щоб обговорити ваш проект. Напишіть — і ми підготуємо демо під вашу структуру. Отримайте консультацію безкоштовно. Ми пропонуємо розробку на замовлення будь-якого рівня складності.







