Представьте: на сайте крупной компании отдел кадров вручную обновляет список сотрудников в 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.
- Запустите синхронизацию по расписанию.
Свяжитесь с нами, чтобы обсудить ваш проект. Напишите — и мы подготовим демо под вашу структуру. Получите консультацию бесплатно. Мы предлагаем разработку на заказ любого уровня сложности.







