import React, { useState, useRef } from 'react'; import { DownloadIcon, UploadIcon } from '../utils/svgs'; const ImportExport = ({ token, onRefreshData, onLogout }) => { const [dragActive, setDragActive] = useState(false); const [status, setStatus] = useState({ type: '', message: '' }); const fileInputRef = useRef(null); const onButtonClick = () => { fileInputRef.current.click(); }; const handleDrag = (e) => { e.preventDefault(); e.stopPropagation(); if (e.type === "dragenter" || e.type === "dragover") { setDragActive(true); } else if (e.type === "dragleave") { setDragActive(false); } }; const handleDrop = (e) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFile(e.dataTransfer.files[0]); } }; const handleFileInputChange = (e) => { if (e.target.files && e.target.files[0]) { handleFile(e.target.files[0]); } }; const handleFile = (file) => { const reader = new FileReader(); const fileType = file.name.split('.').pop().toLowerCase(); reader.onload = async (e) => { const text = e.target.result; try { let payload = null; if (fileType === 'json') { payload = JSON.parse(text); if (!payload.employees || !payload.departments || !payload.companies) { throw new Error('Некорректный формат JSON. Ожидались массивы employees, departments и companies.'); } } else if (fileType === 'csv') { const rows = text.split('\n').map(row => row.trim()).filter(Boolean); if (rows.length < 2) { throw new Error('CSV файл пуст или содержит недостаточно строк.'); } // Expected Headers: ФИО;Компания;Отдел;Должность;Телефон;Email;Здание;Этаж;Внутренний телефон // Parse semicolon separated columns const employees = []; const departmentsSet = new Set(); const companiesSet = new Set(); for (let i = 1; i < rows.length; i++) { // Split by semicolon and clean quotes const values = rows[i].split(';').map(v => v.trim().replace(/^"|"$/g, '')); const emp = { name: values[0] || '', company_name: values[1] || '', department_name: values[2] || '', job_title: values[3] || '', phone: values[4] || '', email: values[5] || '', building: values[6] || '', floor: values[7] || '', internal_phone: values[8] || '' }; if (emp.name && emp.job_title) { employees.push(emp); if (emp.department_name) { departmentsSet.add(emp.department_name); } if (emp.company_name) { companiesSet.add(emp.company_name); } } } const departments = Array.from(departmentsSet).map((name, index) => ({ id: index + 1, name })); const companies = Array.from(companiesSet).map((name, index) => ({ id: index + 1, name })); const mappedEmployees = employees.map(emp => { const dept = departments.find(d => d.name === emp.department_name); const comp = companies.find(c => c.name === emp.company_name); return { name: emp.name, job_title: emp.job_title, phone: emp.phone, email: emp.email, building: emp.building, floor: emp.floor, internal_phone: emp.internal_phone, department_id: dept ? dept.id : null, company_id: comp ? comp.id : null }; }); payload = { employees: mappedEmployees, departments, companies }; } else { throw new Error('Поддерживаются только файлы .json и .csv'); } setStatus({ type: 'info', message: 'Импортирование данных...' }); const res = await fetch('/api/admin/import', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(payload) }); if (res.status === 401 || res.status === 403) { onLogout('Сессия истекла, войдите заново'); return; } const data = await res.json(); if (res.ok) { setStatus({ type: 'success', message: `Импорт завершен! Загружено компаний: ${payload.companies.length}, отделов: ${payload.departments.length}, сотрудников: ${payload.employees.length}` }); onRefreshData(); } else { setStatus({ type: 'error', message: data.error || 'Ошибка при импорте на сервер.' }); } } catch (err) { setStatus({ type: 'error', message: `Ошибка чтения файла: ${err.message}` }); } }; // Explicitly read file in UTF-8 reader.readAsText(file, "UTF-8"); }; const handleExportJSON = async () => { try { const res = await fetch('/api/admin/export', { headers: { 'Authorization': `Bearer ${token}` } }); if (res.status === 401 || res.status === 403) { onLogout('Сессия истекла, войдите заново'); return; } if (!res.ok) throw new Error('Ошибка экспорта данных с сервера'); const data = await res.json(); const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.setAttribute('download', `addressbook_backup_${new Date().toISOString().slice(0, 10)}.json`); document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } catch (err) { setStatus({ type: 'error', message: err.message }); } }; const handleExportCSV = async () => { try { const res = await fetch('/api/admin/export', { headers: { 'Authorization': `Bearer ${token}` } }); if (res.status === 401 || res.status === 403) { onLogout('Сессия истекла, войдите заново'); return; } if (!res.ok) throw new Error('Ошибка экспорта данных с сервера'); const data = await res.json(); const headers = ['ФИО', 'Компания', 'Отдел', 'Должность', 'Телефон', 'Email', 'Здание', 'Этаж', 'Внутренний телефон']; // Semicolon separator const csvRows = [headers.join(';')]; for (const emp of data.employees) { const dept = data.departments.find(d => d.id === emp.department_id); const comp = data.companies.find(c => c.id === emp.company_id); const row = [ `"${emp.name.replace(/"/g, '""')}"`, `"${(comp ? comp.name : '').replace(/"/g, '""')}"`, `"${(dept ? dept.name : '').replace(/"/g, '""')}"`, `"${(emp.job_title || '').replace(/"/g, '""')}"`, `"${(emp.phone || '').replace(/"/g, '""')}"`, `"${(emp.email || '').replace(/"/g, '""')}"`, `"${(emp.building || '').replace(/"/g, '""')}"`, `"${(emp.floor || '').replace(/"/g, '""')}"`, `"${(emp.internal_phone || '').replace(/"/g, '""')}"` ]; csvRows.push(row.join(';')); } const blob = new Blob(['\uFEFF' + csvRows.join('\r\n')], { type: 'text/csv;charset=utf-8;' }); // Excel BOM const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.setAttribute('download', `addressbook_roster_${new Date().toISOString().slice(0, 10)}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } catch (err) { setStatus({ type: 'error', message: err.message }); } }; return (

Импорт данных (Бэкап / CSV)

{status.message && (
{status.message}
)}
e.preventDefault()}>

Перетащите сюда файл резервной копии .json или список сотрудников .csv

* При импорте текущая база будет очищена и перезаписана! Структура колонок CSV (разделитель - точка с запятой ";"): ФИО;Компания;Отдел;Должность;Телефон;Email;Здание;Этаж;Внутренний телефон. Кодировка UTF-8.

Экспорт данных (Бэкап)

Экспортируйте базу данных в любой момент. Скачайте структурированный бэкап в формате JSON для восстановления на другом компьютере, или скачайте плоский список сотрудников в формате CSV для редактирования в Excel.

); }; export default ImportExport;