import React, { useEffect, useState, useMemo } from 'react'; import { backendApi } from '../../services/apiClient'; import { SECTION_LABELS, SECTION_SUBS, RESPONSIBILITY_GROUPS } from '../../constants/permissions'; import { Loader2, Users, Save, ChevronDown, ChevronRight } from 'lucide-react'; type Assignment = { employeeId: string; section: string; subId: string }; const SECTIONS_WITH_SUBS = (Object.keys(SECTION_SUBS) as string[]).filter( (s) => SECTION_SUBS[s] && SECTION_SUBS[s].length > 0 && s !== 'dashboard' ); export const ResponsibilityZonesSection: React.FC = () => { const [assignments, setAssignments] = useState([]); const [employees, setEmployees] = useState>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [saving, setSaving] = useState(null); const [expandedSections, setExpandedSections] = useState>(new Set(['hr', 'finance', 'requests'])); const byZone = useMemo(() => { const map: Record = {}; for (const a of assignments) { const key = `${a.section}_${a.subId}`; if (!map[key]) map[key] = []; map[key].push(a.employeeId); } return map; }, [assignments]); const load = async () => { setLoading(true); setError(null); try { const [resp, emp] = await Promise.all([ backendApi.getResponsibility(), backendApi.getEmployeesList(), ]); setAssignments(resp.assignments || []); setEmployees(emp || []); } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Ошибка загрузки'); } finally { setLoading(false); } }; useEffect(() => { load(); }, []); const toggleSection = (section: string) => { setExpandedSections((prev) => { const next = new Set(prev); if (next.has(section)) next.delete(section); else next.add(section); return next; }); }; const getSelectedForZone = (section: string, subId: string): string[] => { return byZone[`${section}_${subId}`] || []; }; const setSelectedForZone = (section: string, subId: string, employeeIds: string[]) => { setAssignments((prev) => { const key = `${section}_${subId}`; const rest = prev.filter((a) => a.section !== section || a.subId !== subId); const next = [...rest, ...employeeIds.map((employeeId) => ({ employeeId, section, subId }))]; return next; }); }; const saveZone = async (section: string, subId: string) => { const key = `${section}_${subId}`; setSaving(key); try { const employeeIds = byZone[key] || []; await backendApi.putResponsibility({ section, subId, employeeIds }); } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Ошибка сохранения'); } finally { setSaving(null); } }; if (loading) { return (
); } if (error) { return (
{error}
); } return (

Назначьте сотрудников ответственными за подразделы модулей. Это используется для уведомлений (например, новая вакансия → ответственные за «Кадры → Вакансии») и для учёта эффективности по зонам.

Группы зон (например, «ЗП и обучение» vs «Найм и адаптация» в Кадрах) заданы в настройках для наглядности; привязка выполняется по каждому подразделу.

{SECTIONS_WITH_SUBS.map((section) => { const subs = SECTION_SUBS[section]; const sectionLabel = SECTION_LABELS[section] || section; const isExpanded = expandedSections.has(section); const groups = RESPONSIBILITY_GROUPS[section]; return (
{isExpanded && (
{groups && groups.length > 0 && (
Группы: {groups.map((g) => g.name).join(' · ')}
)} {subs.map((sub) => { const zoneKey = `${section}_${sub.id}`; const selected = getSelectedForZone(section, sub.id); const savingThis = saving === zoneKey; return (
{sub.label}
); })}
)}
); })}
); };