Spaces:
Running
Running
Помоги мне создать приложение в котором будет вся база данных сотрудников фирмы, в котором можно работать с данными сотрудников, которая имела разные, но определенного формата, интерефейся в зависимости от выбранного варианта вида документа, интерфейс можно и WEB главное что бы не тупил и был современным и красочным, работая область каждого документа, должна соотвествовать отпеделенному файлу EXCEL учитывая форматs, затем выгрузка каждого варианта документа в Excel такого же формата. создание БД путем загрузки файлов EXCEL, так же как и изначальных видов документов. по необходимости, путем отпредееного файла Excel, путем сравнения обновлялсь данные в БД. Рабочии области видов документов с теми же возможностями что и Excel 2016. при входе отображалося красивый Дашборд с данными по сотрудткам: Общее количество, На площадке, Даленн количсетво по Участкам, была информация сколько людей прилетает и улетает на ближ три дня. все даннын дерутся из документов и данных внесенных в порграмму. Могу загрузить Excel файлы
c546001
verified
| // EmployeeHub Pro Main JavaScript | |
| let employees = []; | |
| let currentDocument = null; | |
| let uploadedData = null; | |
| // Initialize application | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeApp(); | |
| loadDashboardCharts(); | |
| loadDefaultEmployees(); | |
| }); | |
| function initializeApp() { | |
| // Initialize with sample data | |
| employees = [ | |
| { id: 1, name: 'Иванов Иван Иванович', department: 'IT', position: 'Разработчик', status: 'onsite', phone: '+7 (900) 123-45-67' }, | |
| { id: 2, name: 'Петров Петр Петрович', department: 'HR', position: 'HR менеджер', status: 'onsite', phone: '+7 (900) 234-56-78' }, | |
| { id: 3, name: 'Сидорова Анна Владимировна', department: 'Бухгалтерия', position: 'Бухгалтер', status: 'remote', phone: '+7 (900) 345-67-89' }, | |
| { id: 4, name: 'Козлов Дмитрий Сергеевич', department: 'Маркетинг', position: 'Маркетолог', status: 'vacation', phone: '+7 (900) 456-78-90' }, | |
| { id: 5, name: 'Михайлова Елена Игоревна', department: 'IT', position: 'Дизайнер', status: 'remote', phone: '+7 (900) 567-89-01' } | |
| ]; | |
| } | |
| // Navigation functions | |
| function showDashboard() { | |
| hideAllSections(); | |
| document.getElementById('dashboard').classList.remove('hidden'); | |
| updateDashboardStats(); | |
| loadDashboardCharts(); | |
| } | |
| function showEmployees() { | |
| hideAllSections(); | |
| document.getElementById('employees').classList.remove('hidden'); | |
| displayEmployees(employees); | |
| } | |
| function showDocuments() { | |
| hideAllSections(); | |
| document.getElementById('documents').classList.remove('hidden'); | |
| } | |
| function showUpload() { | |
| hideAllSections(); | |
| document.getElementById('upload').classList.remove('hidden'); | |
| } | |
| function showAnalytics() { | |
| hideAllSections(); | |
| document.getElementById('analytics').classList.remove('hidden'); | |
| loadAnalyticsCharts(); | |
| } | |
| function hideAllSections() { | |
| const sections = ['dashboard', 'employees', 'documents', 'upload', 'analytics']; | |
| sections.forEach(section => { | |
| document.getElementById(section).classList.add('hidden'); | |
| }); | |
| } | |
| // Dashboard functions | |
| function updateDashboardStats() { | |
| const totalEmployees = employees.length; | |
| const onsiteEmployees = employees.filter(e => e.status === 'onsite').length; | |
| const remoteEmployees = employees.filter(e => e.status === 'remote').length; | |
| const vacationEmployees = employees.filter(e => e.status === 'vacation').length; | |
| // Update stat cards (in real app, would update DOM) | |
| console.log('Stats:', { totalEmployees, onsiteEmployees, remoteEmployees, vacationEmployees }); | |
| } | |
| function loadDashboardCharts() { | |
| // Department distribution chart | |
| const ctx1 = document.getElementById('departmentChart'); | |
| if (ctx1) { | |
| const departments = {}; | |
| employees.forEach(emp => { | |
| departments[emp.department] = (departments[emp.department] || 0) + 1; | |
| }); | |
| new Chart(ctx1, { | |
| type: 'doughnut', | |
| data: { | |
| labels: Object.keys(departments), | |
| datasets: [{ | |
| data: Object.values(departments), | |
| backgroundColor: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444'] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| position: 'bottom' | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| } | |
| // Employees functions | |
| function displayEmployees(employeeList) { | |
| const tableBody = document.getElementById('employeesTable'); | |
| tableBody.innerHTML = ''; | |
| employeeList.forEach(employee => { | |
| const row = document.createElement('tr'); | |
| row.className = 'table-row-hover'; | |
| row.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <div class="flex items-center"> | |
| <img src="https://static.photos/people/40x40/${employee.id}" alt="${employee.name}" class="w-10 h-10 rounded-full mr-3"> | |
| <div class="text-sm font-medium text-gray-900">${employee.name}</div> | |
| </div> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${employee.department}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${employee.position}</td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <span class="status-badge status-${employee.status}">${getStatusText(employee.status)}</span> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${employee.phone}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium"> | |
| <button onclick="editEmployee(${employee.id})" class="text-indigo-600 hover:text-indigo-900 mr-3">Редактировать</button> | |
| <button onclick="deleteEmployee(${employee.id})" class="text-red-600 hover:text-red-900">Удалить</button> | |
| </td> | |
| `; | |
| tableBody.appendChild(row); | |
| }); | |
| } | |
| function getStatusText(status) { | |
| const statuses = { | |
| 'onsite': 'На площадке', | |
| 'remote': 'Удаленно', | |
| 'vacation': 'Отпуск', | |
| 'leave': 'Уход' | |
| }; | |
| return statuses[status] || status; | |
| } | |
| function searchEmployees(query) { | |
| const filtered = employees.filter(emp => | |
| emp.name.toLowerCase().includes(query.toLowerCase()) || | |
| emp.department.toLowerCase().includes(query.toLowerCase()) || | |
| emp.position.toLowerCase().includes(query.toLowerCase()) | |
| ); | |
| displayEmployees(filtered); | |
| } | |
| function filterEmployees(status) { | |
| // Update active tab | |
| const tabs = document.querySelectorAll('.tab-active'); | |
| tabs.forEach(tab => tab.classList.remove('tab-active')); | |
| event.target.classList.add('tab-active'); | |
| let filtered = employees; | |
| if (status !== 'all') { | |
| filtered = employees.filter(emp => emp.status === status); | |
| } | |
| displayEmployees(filtered); | |
| } | |
| function addEmployee() { | |
| showModal('addEmployee'); | |
| } | |
| function editEmployee(id) { | |
| const employee = employees.find(emp => emp.id === id); | |
| if (employee) { | |
| showModal('editEmployee', employee); | |
| } | |
| } | |
| function deleteEmployee(id) { | |
| if (confirm('Вы уверены, что хотите удалить этого сотрудника?')) { | |
| employees = employees.filter(emp => emp.id !== id); | |
| displayEmployees(employees); | |
| showToast('Сотрудник удален', 'success'); | |
| } | |
| } | |
| // Documents functions | |
| function openDocumentTemplate(type) { | |
| currentDocument = type; | |
| const editor = document.getElementById('documentEditor'); | |
| editor.classList.remove('hidden'); | |
| const templates = { | |
| timesheet: { | |
| headers: ['Дата', 'ФИО', 'Табельный номер', 'Приход', 'Уход', 'Часы'], | |
| rows: 30 | |
| }, | |
| vacation: { | |
| headers: ['ФИО', 'Отдел', 'Дата начала', 'Дата окончания', 'Дней'], | |
| rows: 20 | |
| }, | |
| business: { | |
| headers: ['ФИО', 'Место', 'Дата выезда', 'Дата возврата', 'Цель'], | |
| rows: 15 | |
| } | |
| }; | |
| const template = templates[type]; | |
| const table = document.getElementById('documentTable'); | |
| table.innerHTML = ''; | |
| // Create header row | |
| const headerRow = document.createElement('tr'); | |
| template.headers.forEach(header => { | |
| const th = document.createElement('th'); | |
| th.className = 'excel-header'; | |
| th.textContent = header; | |
| headerRow.appendChild(th); | |
| }); | |
| table.appendChild(headerRow); | |
| // Create data rows | |
| for (let i = 0; i < template.rows; i++) { | |
| const row = document.createElement('tr'); | |
| template.headers.forEach((header, colIndex) => { | |
| const cell = document.createElement('td'); | |
| cell.className = 'excel-cell'; | |
| cell.contentEditable = true; | |
| cell.onclick = () => makeCellEditable(cell); | |
| row.appendChild(cell); | |
| }); | |
| table.appendChild(row); | |
| } | |
| } | |
| function makeCellEditable(cell) { | |
| cell.classList.add('editing'); | |
| cell.addEventListener('blur', function() { | |
| this.classList.remove('editing'); | |
| }); | |
| } | |
| function saveDocument() { | |
| // Save document logic | |
| showToast('Документ сохранен', 'success'); | |
| } | |
| function exportToExcel() { | |
| const table = document.getElementById('documentTable'); | |
| const wb = XLSX.utils.table_to_book(table, {sheet: "Документ"}); | |
| XLSX.writeFile(wb, `document_${Date.now()}.xlsx`); | |
| showToast('Документ экспортирован в Excel', 'success'); | |
| } | |
| // Upload functions | |
| function handleFileUpload(event) { | |
| const file = event.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| try { | |
| const data = new Uint8Array(e.target.result); | |
| const workbook = XLSX.read(data, {type: 'array'}); | |
| const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; | |
| const jsonData = XLSX.utils.sheet_to_json(firstSheet); | |
| uploadedData = jsonData; | |
| showUploadPreview(jsonData); | |
| } catch (error) { | |
| showToast('Ошибка при чтении файла', 'error'); | |
| } | |
| }; | |
| reader.readAsArrayBuffer(file); | |
| } | |
| } | |
| function showUploadPreview(data) { | |
| const preview = document.getElementById('uploadPreview'); | |
| preview.classList.remove('hidden'); | |
| const table = document.getElementById('previewTable'); | |
| table.innerHTML = ''; | |
| if (data.length > 0) { | |
| // Create header | |
| const headerRow = document.createElement('tr'); | |
| headerRow.className = 'bg-gray-50'; | |
| Object.keys(data[0]).forEach(key => { | |
| const th = document.createElement('th'); | |
| th.className = 'px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase'; | |
| th.textContent = key; | |
| headerRow.appendChild(th); | |
| }); | |
| table.appendChild(headerRow); | |
| // Show first 5 rows | |
| data.slice(0, 5).forEach(row => { | |
| const tr = document.createElement('tr'); | |
| tr.className = 'border-t'; | |
| Object.values(row).forEach(value => { | |
| const td = document.createElement('td'); | |
| td.className = 'px-4 py-2 text-sm text-gray-900'; | |
| td.textContent = value; | |
| tr.appendChild(td); | |
| }); | |
| table.appendChild(tr); | |
| }); | |
| if (data.length > 5) { | |
| const moreRow = document.createElement('tr'); | |
| moreRow.className = 'border-t text-center'; | |
| const td = document.createElement('td'); | |
| td.colSpan = Object.keys(data[0]).length; | |
| td.className = 'px-4 py-2 text-sm text-gray-500'; | |
| td.textContent = `... и еще ${data.length - 5} записей`; | |
| moreRow.appendChild(td); | |
| table.appendChild(moreRow); | |
| } | |
| } | |
| } | |
| function confirmUpload() { | |
| if (uploadedData) { | |
| // Convert and add to employees array | |
| const newEmployees = uploadedData.map((row, index) => ({ | |
| id: employees.length + index + 1, | |
| name: row['ФИО'] || row['Name'] || '', | |
| department: row['Отдел'] || row['Department'] || '', | |
| position: row['Должность'] || row['Position'] || '', | |
| status: 'onsite', | |
| phone: row['Телефон'] || row['Phone'] || '' | |
| })).filter(emp => emp.name); | |
| employees = [...employees, ...newEmployees]; | |
| showToast(`Загружено ${newEmployees.length} сотрудников`, 'success'); | |
| cancelUpload(); | |
| } | |
| } | |
| function cancelUpload() { | |
| document.getElementById('uploadPreview').classList.add('hidden'); | |
| document.getElementById('fileInput').value = ''; | |
| uploadedData = null; | |
| } | |
| // Analytics functions | |
| function loadAnalyticsCharts() { | |
| // Staff dynamics chart | |
| const ctx1 = document.getElementById('staffDynamicsChart'); | |
| if (ctx1) { | |
| new Chart(ctx1, { | |
| type: 'line', | |
| data: { | |
| labels: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн'], | |
| datasets: [{ | |
| label: 'Количество сотрудников', | |
| data: [1120, 1150, 1180, 1200, 1220, 1248], | |
| borderColor: '#6366f1', | |
| backgroundColor: 'rgba(99, 102, 241, 0.1)', | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Turnover chart | |
| const ctx2 = document.getElementById('turnoverChart'); | |
| if (ctx2) { | |
| new Chart(ctx2, { | |
| type: 'bar', | |
| data: { | |
| labels: ['Q1', 'Q2', 'Q3', 'Q4'], | |
| datasets: [{ | |
| label: 'Уволилось', | |
| data: [12, 19, 15, 8], | |
| backgroundColor: '#ef4444' | |
| }, { | |
| label: 'Принято', | |
| data: [18, 22, 20, 25], | |
| backgroundColor: '#10b981' | |
| }] | |
| }, | |
| options: { | |
| responsive: true | |
| } | |
| }); | |
| } | |
| // Age structure chart | |
| const ctx3 = document.getElementById('ageChart'); | |
| if (ctx3) { | |
| new Chart(ctx3, { | |
| type: 'pie', | |
| data: { | |
| labels: ['18-25', '26-35', '36-45', '46-55', '56+'], | |
| datasets: [{ | |
| data: [180, 420, 350, 230, 68], | |
| backgroundColor: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444'] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| position: 'bottom' | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Workload chart | |
| const ctx4 = document.getElementById('workloadChart'); | |
| if (ctx4) { | |
| new Chart(ctx4, { | |
| type: 'radar', | |
| data: { | |
| labels: ['IT', 'HR', 'Бухгалтерия', 'Маркетинг', 'Продажи', 'Логистика'], | |
| datasets: [{ | |
| label: 'Текущая загрузка', | |
| data: [85, 70, 75, 80, 90, 65], | |
| borderColor: '#6366f1', | |
| backgroundColor: 'rgba(99, 102, 241, 0.2)' | |
| }, { | |
| label: 'Оптимальная', | |
| data: [80, 80, 80, 80, 80, 80], | |
| borderColor: '#10b981', | |
| backgroundColor: 'rgba(16, 185, 129, 0.1)' | |
| }] | |
| }, | |
| options: { | |
| responsive: true | |
| } | |
| }); | |
| } | |
| } | |
| function generateReport() { | |
| showToast('Отчет успешно сформирован', 'success'); | |
| } | |
| // Modal functions | |
| function showModal(type, data = null) { | |
| const modal = document.getElementById('modal'); | |
| const modalTitle = document.getElementById('modalTitle'); | |
| const modalContent = document.getElementById('modalContent'); | |
| if (type === 'addEmployee' || type === 'editEmployee') { | |
| modalTitle.textContent = type === 'addEmployee' ? 'Добавить сотрудника' : 'Редактировать сотрудника'; | |
| modalContent.innerHTML = ` | |
| <form onsubmit="saveEmployee(event)"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">ФИО</label> | |
| <input type="text" id="empName" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg" value="${data ? data.name : ''}"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Отдел</label> | |
| <select id="empDepartment" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg"> | |
| <option value="IT" ${data && data.department === 'IT' ? 'selected' : ''}>IT</option> | |
| <option value="HR" ${data && data.department === 'HR' ? 'selected' : ''}>HR</option> | |
| <option value="Бухгалтерия" ${data && data.department === 'Бухгалтерия' ? 'selected' : ''}>Бухгалтерия</option> | |
| <option value="Маркетинг" ${data && data.department === 'Маркетинг' ? 'selected' : ''}>Маркетинг</option> | |
| <option value="Продажи" ${data && data.department === 'Продажи' ? 'selected' : ''}>Продажи</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Должность</label> | |
| <input type="text" id="empPosition" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg" value="${data ? data.position : ''}"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Статус</label> | |
| <select id="empStatus" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg"> | |
| <option value="onsite" ${data && data.status === 'onsite' ? 'selected' : ''}>На площадке</option> | |
| <option value="remote" ${data && data.status === 'remote' ? 'selected' : ''}>Удаленно</option> | |
| <option value="vacation" ${data && data.status === 'vacation' ? 'selected' : ''}>Отпуск</option> | |
| </select> | |
| </div> | |
| <div class="md:col-span-2"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Телефон</label> | |
| <input type="tel" id="empPhone" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg" value="${data ? data.phone : ''}"> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex justify-end space-x-3"> | |
| <button type="button" onclick="closeModal()" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50">Отмена</button> | |
| <button type="submit" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700">Сохранить</button> | |
| </div> | |
| </form> | |
| `; | |
| } | |
| modal.classList.remove('hidden'); | |
| } | |
| function closeModal() { | |
| document.getElementById('modal').classList.add('hidden'); | |
| } | |
| function saveEmployee(event) { | |
| event.preventDefault(); | |
| const employee = { | |
| id: employees.length + 1, | |
| name: document.getElementById('empName').value, | |
| department: document.getElementById('empDepartment').value, | |
| position: document.getElementById('empPosition').value, | |
| status: document.getElementById('empStatus').value, | |
| phone: document.getElementById('empPhone').value | |
| }; | |
| employees.push(employee); | |
| displayEmployees(employees); | |
| closeModal(); | |
| showToast('Сотрудник добавлен', 'success'); | |
| } | |
| // Toast notification | |
| function showToast(message, type = 'success') { | |
| const toast = document.createElement('div'); | |
| toast.className = `toast toast-${type}`; | |
| toast.textContent = message; | |
| document.body.appendChild(toast); | |
| setTimeout(() => { | |
| toast.remove(); | |
| }, 3000); | |
| } | |
| // Export data function | |
| function exportData() { | |
| const wb = XLSX.utils.book_new(); | |
| const ws = XLSX.utils.json_to_sheet(employees.map(emp => ({ | |
| 'ФИО': emp.name, | |
| 'Отдел': emp.department, | |
| 'Должность': emp.position, | |
| 'Статус': getStatusText(emp.status), | |
| 'Телефон': emp.phone | |
| }))); | |
| XLSX.utils.book_append_sheet(wb, ws, 'Сотрудники'); | |
| XLSX.writeFile(wb, `employees_${new Date().toISOString().split('T')[0]}.xlsx`); | |
| showToast('Данные успешно экспортированы', 'success'); | |
| } | |
| // Show notifications | |
| function showNotifications() { | |
| showToast('У вас 3 новых уведомления', 'success'); | |
| } | |
| // Load default employees | |
| function loadDefaultEmployees() { | |
| console.log('Application initialized with', employees.length, 'employees'); | |
| } |