Barton0708 commited on
Commit
c546001
·
verified ·
1 Parent(s): a7330ff

Помоги мне создать приложение в котором будет вся база данных сотрудников фирмы, в котором можно работать с данными сотрудников, которая имела разные, но определенного формата, интерефейся в зависимости от выбранного варианта вида документа, интерфейс можно и WEB главное что бы не тупил и был современным и красочным, работая область каждого документа, должна соотвествовать отпеделенному файлу EXCEL учитывая форматs, затем выгрузка каждого варианта документа в Excel такого же формата. создание БД путем загрузки файлов EXCEL, так же как и изначальных видов документов. по необходимости, путем отпредееного файла Excel, путем сравнения обновлялсь данные в БД. Рабочии области видов документов с теми же возможностями что и Excel 2016. при входе отображалося красивый Дашборд с данными по сотрудткам: Общее количество, На площадке, Даленн количсетво по Участкам, была информация сколько людей прилетает и улетает на ближ три дня. все даннын дерутся из документов и данных внесенных в порграмму. Могу загрузить Excel файлы

Browse files
Files changed (6) hide show
  1. README.md +8 -5
  2. components/navbar.js +60 -0
  3. components/sidebar.js +72 -0
  4. index.html +469 -19
  5. script.js +557 -0
  6. style.css +304 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Employeehub Pro
3
- emoji: 🏆
4
- colorFrom: green
5
- colorTo: pink
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: EmployeeHub Pro 🚀
3
+ colorFrom: blue
4
+ colorTo: blue
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/navbar.js ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ }
9
+ .navbar {
10
+ background: white;
11
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
12
+ padding: 1rem 2rem;
13
+ display: flex;
14
+ justify-content: space-between;
15
+ align-items: center;
16
+ }
17
+ .logo {
18
+ font-size: 1.5rem;
19
+ font-weight: bold;
20
+ color: #6366f1;
21
+ display: flex;
22
+ align-items: center;
23
+ }
24
+ .nav-links {
25
+ display: flex;
26
+ gap: 2rem;
27
+ align-items: center;
28
+ }
29
+ .user-profile {
30
+ display: flex;
31
+ align-items: center;
32
+ gap: 1rem;
33
+ }
34
+ .user-avatar {
35
+ width: 40px;
36
+ height: 40px;
37
+ border-radius: 50%;
38
+ object-fit: cover;
39
+ }
40
+ </style>
41
+ <nav class="navbar">
42
+ <div class="logo">
43
+ <i data-feather="users" class="w-8 h-8 mr-2"></i>
44
+ EmployeeHub Pro
45
+ </div>
46
+ <div class="nav-links">
47
+ <div class="user-profile">
48
+ <img src="https://static.photos/people/40x40/1" alt="User" class="user-avatar">
49
+ <div>
50
+ <div class="font-medium">Администратор</div>
51
+ <div class="text-sm text-gray-500">HR Manager</div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </nav>
56
+ `;
57
+ }
58
+ }
59
+
60
+ customElements.define('custom-navbar', CustomNavbar);
components/sidebar.js ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomSidebar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ width: 256px;
9
+ height: 100vh;
10
+ background: white;
11
+ box-shadow: 2px 0 4px rgba(0,0,0,0.1);
12
+ position: fixed;
13
+ left: 0;
14
+ top: 0;
15
+ z-index: 40;
16
+ }
17
+ .sidebar-content {
18
+ padding: 4rem 0 2rem;
19
+ }
20
+ .nav-item {
21
+ display: flex;
22
+ align-items: center;
23
+ padding: 1rem 1.5rem;
24
+ color: #374151;
25
+ text-decoration: none;
26
+ transition: all 0.3s ease;
27
+ cursor: pointer;
28
+ border: none;
29
+ background: none;
30
+ width: 100%;
31
+ text-align: left;
32
+ font-size: 0.95rem;
33
+ }
34
+ .nav-item:hover {
35
+ background: linear-gradient(90deg, #6366f1 0%, #8b5cf6 100%);
36
+ color: white;
37
+ }
38
+ .nav-item i {
39
+ margin-right: 0.75rem;
40
+ }
41
+ .nav-item.active {
42
+ background: linear-gradient(90deg, #6366f1 0%, #8b5cf6 100%);
43
+ color: white;
44
+ }
45
+ </style>
46
+ <div class="sidebar-content">
47
+ <button class="nav-item" onclick="window.showDashboard()">
48
+ <i data-feather="home" class="w-5 h-5"></i>
49
+ Дашборд
50
+ </button>
51
+ <button class="nav-item" onclick="window.showEmployees()">
52
+ <i data-feather="users" class="w-5 h-5"></i>
53
+ Сотрудники
54
+ </button>
55
+ <button class="nav-item" onclick="window.showDocuments()">
56
+ <i data-feather="file-text" class="w-5 h-5"></i>
57
+ Документы
58
+ </button>
59
+ <button class="nav-item" onclick="window.showUpload()">
60
+ <i data-feather="upload" class="w-5 h-5"></i>
61
+ Загрузка Excel
62
+ </button>
63
+ <button class="nav-item" onclick="window.showAnalytics()">
64
+ <i data-feather="bar-chart-2" class="w-5 h-5"></i>
65
+ Аналитика
66
+ </button>
67
+ </div>
68
+ `;
69
+ }
70
+ }
71
+
72
+ customElements.define('custom-sidebar', CustomSidebar);
index.html CHANGED
@@ -1,19 +1,469 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ru">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>EmployeeHub Pro - Система управления персоналом</title>
7
+ <link rel="icon" type="image/x-icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>👥</text></svg>">
8
+ <link rel="stylesheet" href="style.css">
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
13
+ <style>
14
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
15
+ body { font-family: 'Inter', sans-serif; }
16
+ .gradient-bg {
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ }
19
+ .card-shadow {
20
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
21
+ }
22
+ .excel-table {
23
+ border-collapse: collapse;
24
+ }
25
+ .excel-table td, .excel-table th {
26
+ border: 1px solid #e5e7eb;
27
+ padding: 8px;
28
+ }
29
+ .excel-table th {
30
+ background-color: #f3f4f6;
31
+ font-weight: 600;
32
+ }
33
+ .tab-active {
34
+ border-bottom: 3px solid #6366f1;
35
+ color: #6366f1;
36
+ }
37
+ .cell-editing {
38
+ background-color: #fef3c7 !important;
39
+ }
40
+ .sidebar-item:hover {
41
+ background: linear-gradient(90deg, #6366f1 0%, #8b5cf6 100%);
42
+ color: white;
43
+ }
44
+ .stat-card {
45
+ transition: all 0.3s ease;
46
+ }
47
+ .stat-card:hover {
48
+ transform: translateY(-5px);
49
+ box-shadow: 0 20px 40px rgba(0,0,0,0.15);
50
+ }
51
+ </style>
52
+ </head>
53
+ <body class="bg-gray-50">
54
+ <!-- Header -->
55
+ <header class="bg-white shadow-sm border-b border-gray-200 fixed top-0 w-full z-50">
56
+ <div class="px-4 sm:px-6 lg:px-8">
57
+ <div class="flex justify-between items-center h-16">
58
+ <div class="flex items-center">
59
+ <i data-feather="users" class="w-8 h-8 text-indigo-600 mr-3"></i>
60
+ <h1 class="text-xl font-bold text-gray-900">EmployeeHub Pro</h1>
61
+ </div>
62
+ <div class="flex items-center space-x-4">
63
+ <button onclick="showNotifications()" class="relative p-2 rounded-lg hover:bg-gray-100 transition">
64
+ <i data-feather="bell" class="w-5 h-5 text-gray-600"></i>
65
+ <span class="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full"></span>
66
+ </button>
67
+ <div class="flex items-center space-x-3">
68
+ <img src="https://static.photos/people/40x40/1" alt="User" class="w-10 h-10 rounded-full">
69
+ <div>
70
+ <p class="text-sm font-medium text-gray-900">Администратор</p>
71
+ <p class="text-xs text-gray-500">HR Manager</p>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </header>
78
+
79
+ <!-- Main Container -->
80
+ <div class="flex pt-16">
81
+ <!-- Sidebar -->
82
+ <aside class="w-64 bg-white h-screen fixed left-0 shadow-lg">
83
+ <nav class="mt-8">
84
+ <a href="#" onclick="showDashboard()" class="sidebar-item flex items-center px-6 py-3 text-gray-700 hover:text-white transition">
85
+ <i data-feather="home" class="w-5 h-5 mr-3"></i>
86
+ <span>Дашборд</span>
87
+ </a>
88
+ <a href="#" onclick="showEmployees()" class="sidebar-item flex items-center px-6 py-3 text-gray-700 hover:text-white transition">
89
+ <i data-feather="users" class="w-5 h-5 mr-3"></i>
90
+ <span>Сотрудники</span>
91
+ </a>
92
+ <a href="#" onclick="showDocuments()" class="sidebar-item flex items-center px-6 py-3 text-gray-700 hover:text-white transition">
93
+ <i data-feather="file-text" class="w-5 h-5 mr-3"></i>
94
+ <span>Документы</span>
95
+ </a>
96
+ <a href="#" onclick="showUpload()" class="sidebar-item flex items-center px-6 py-3 text-gray-700 hover:text-white transition">
97
+ <i data-feather="upload" class="w-5 h-5 mr-3"></i>
98
+ <span>Загрузка Excel</span>
99
+ </a>
100
+ <a href="#" onclick="showAnalytics()" class="sidebar-item flex items-center px-6 py-3 text-gray-700 hover:text-white transition">
101
+ <i data-feather="bar-chart-2" class="w-5 h-5 mr-3"></i>
102
+ <span>Аналитика</span>
103
+ </a>
104
+ </nav>
105
+ </aside>
106
+
107
+ <!-- Main Content -->
108
+ <main class="ml-64 flex-1 p-8">
109
+ <!-- Dashboard Section -->
110
+ <section id="dashboard" class="space-y-6">
111
+ <div class="flex justify-between items-center mb-8">
112
+ <h2 class="text-2xl font-bold text-gray-900">Панель управления</h2>
113
+ <button onclick="exportData()" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition flex items-center">
114
+ <i data-feather="download" class="w-4 h-4 mr-2"></i>
115
+ Экспорт данных
116
+ </button>
117
+ </div>
118
+
119
+ <!-- Stats Cards -->
120
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
121
+ <div class="stat-card bg-gradient-to-r from-blue-500 to-blue-600 p-6 rounded-xl text-white">
122
+ <div class="flex items-center justify-between">
123
+ <div>
124
+ <p class="text-blue-100">Общее количество</p>
125
+ <p class="text-3xl font-bold mt-2">1,248</p>
126
+ </div>
127
+ <i data-feather="users" class="w-10 h-10 text-blue-200"></i>
128
+ </div>
129
+ </div>
130
+ <div class="stat-card bg-gradient-to-r from-green-500 to-green-600 p-6 rounded-xl text-white">
131
+ <div class="flex items-center justify-between">
132
+ <div>
133
+ <p class="text-green-100">На площадке</p>
134
+ <p class="text-3xl font-bold mt-2">892</p>
135
+ </div>
136
+ <i data-feather="check-circle" class="w-10 h-10 text-green-200"></i>
137
+ </div>
138
+ </div>
139
+ <div class="stat-card bg-gradient-to-r from-purple-500 to-purple-600 p-6 rounded-xl text-white">
140
+ <div class="flex items-center justify-between">
141
+ <div>
142
+ <p class="text-purple-100">Удаленно</p>
143
+ <p class="text-3xl font-bold mt-2">356</p>
144
+ </div>
145
+ <i data-feather="wifi" class="w-10 h-10 text-purple-200"></i>
146
+ </div>
147
+ </div>
148
+ <div class="stat-card bg-gradient-to-r from-orange-500 to-orange-600 p-6 rounded-xl text-white">
149
+ <div class="flex items-center justify-between">
150
+ <div>
151
+ <p class="text-orange-100">В отпуске</p>
152
+ <p class="text-3xl font-bold mt-2">128</p>
153
+ </div>
154
+ <i data-feather="calendar" class="w-10 h-10 text-orange-200"></i>
155
+ </div>
156
+ </div>
157
+ </div>
158
+
159
+ <!-- Charts Section -->
160
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
161
+ <div class="bg-white p-6 rounded-xl card-shadow">
162
+ <h3 class="text-lg font-semibold mb-4">Распределение по отделам</h3>
163
+ <canvas id="departmentChart"></canvas>
164
+ </div>
165
+ <div class="bg-white p-6 rounded-xl card-shadow">
166
+ <h3 class="text-lg font-semibold mb-4">Ближайшие 3 дня</h3>
167
+ <div class="space-y-3">
168
+ <div class="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
169
+ <div class="flex items-center">
170
+ <i data-feather="trending-up" class="w-5 h-5 text-blue-600 mr-2"></i>
171
+ <span class="font-medium">Завтра</span>
172
+ </div>
173
+ <div class="text-right">
174
+ <p class="text-sm text-gray-600">Прилетают</p>
175
+ <p class="font-bold text-blue-600">24</p>
176
+ </div>
177
+ </div>
178
+ <div class="flex items-center justify-between p-3 bg-green-50 rounded-lg">
179
+ <div class="flex items-center">
180
+ <i data-feather="trending-down" class="w-5 h-5 text-green-600 mr-2"></i>
181
+ <span class="font-medium">Завтра</span>
182
+ </div>
183
+ <div class="text-right">
184
+ <p class="text-sm text-gray-600">Улетают</p>
185
+ <p class="font-bold text-green-600">18</p>
186
+ </div>
187
+ </div>
188
+ <div class="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
189
+ <div class="flex items-center">
190
+ <i data-feather="calendar" class="w-5 h-5 text-purple-600 mr-2"></i>
191
+ <span class="font-medium">Послезавтра</span>
192
+ </div>
193
+ <div class="text-right">
194
+ <p class="text-sm text-gray-600">Смены</p>
195
+ <p class="font-bold text-purple-600">12</p>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ <!-- Recent Activities -->
203
+ <div class="bg-white p-6 rounded-xl card-shadow">
204
+ <h3 class="text-lg font-semibold mb-4">Последние активности</h3>
205
+ <div class="space-y-3">
206
+ <div class="flex items-center justify-between p-3 hover:bg-gray-50 rounded-lg transition">
207
+ <div class="flex items-center">
208
+ <div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center mr-3">
209
+ <i data-feather="user-plus" class="w-5 h-5 text-green-600"></i>
210
+ </div>
211
+ <div>
212
+ <p class="font-medium">Новый сотрудник</p>
213
+ <p class="text-sm text-gray-500">Иван Петров - IT отдел</p>
214
+ </div>
215
+ </div>
216
+ <span class="text-sm text-gray-500">2 мин назад</span>
217
+ </div>
218
+ <div class="flex items-center justify-between p-3 hover:bg-gray-50 rounded-lg transition">
219
+ <div class="flex items-center">
220
+ <div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mr-3">
221
+ <i data-feather="file-text" class="w-5 h-5 text-blue-600"></i>
222
+ </div>
223
+ <div>
224
+ <p class="font-medium">Обновлен документ</p>
225
+ <p class="text-sm text-gray-500">Табель учета времени</p>
226
+ </div>
227
+ </div>
228
+ <span class="text-sm text-gray-500">15 мин назад</span>
229
+ </div>
230
+ <div class="flex items-center justify-between p-3 hover:bg-gray-50 rounded-lg transition">
231
+ <div class="flex items-center">
232
+ <div class="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center mr-3">
233
+ <i data-feather="download" class="w-5 h-5 text-purple-600"></i>
234
+ </div>
235
+ <div>
236
+ <p class="font-medium">Выгружен отчет</p>
237
+ <p class="text-sm text-gray-500">Отчет за декабрь</p>
238
+ </div>
239
+ </div>
240
+ <span class="text-sm text-gray-500">1 час назад</span>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </section>
245
+
246
+ <!-- Employees Section -->
247
+ <section id="employees" class="hidden">
248
+ <div class="flex justify-between items-center mb-8">
249
+ <h2 class="text-2xl font-bold text-gray-900">Сотрудники</h2>
250
+ <div class="flex space-x-3">
251
+ <input type="text" placeholder="Поиск..." class="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" onkeyup="searchEmployees(this.value)">
252
+ <button onclick="addEmployee()" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition flex items-center">
253
+ <i data-feather="plus" class="w-4 h-4 mr-2"></i>
254
+ Добавить
255
+ </button>
256
+ </div>
257
+ </div>
258
+
259
+ <!-- Filter Tabs -->
260
+ <div class="bg-white p-4 rounded-lg mb-6 flex space-x-6 border-b">
261
+ <button class="tab-active pb-2 font-medium" onclick="filterEmployees('all')">Все сотрудники</button>
262
+ <button class="pb-2 font-medium text-gray-600 hover:text-gray-900" onclick="filterEmployees('onsite')">На площадке</button>
263
+ <button class="pb-2 font-medium text-gray-600 hover:text-gray-900" onclick="filterEmployees('remote')">Удаленно</button>
264
+ <button class="pb-2 font-medium text-gray-600 hover:text-gray-900" onclick="filterEmployees('vacation')">В отпуске</button>
265
+ </div>
266
+
267
+ <!-- Employees Table -->
268
+ <div class="bg-white rounded-xl overflow-hidden">
269
+ <table class="w-full">
270
+ <thead class="bg-gray-50">
271
+ <tr>
272
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ФИО</th>
273
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Отдел</th>
274
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Должность</th>
275
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
276
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Телефон</th>
277
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
278
+ </tr>
279
+ </thead>
280
+ <tbody id="employeesTable" class="bg-white divide-y divide-gray-200">
281
+ <!-- Employees will be inserted here -->
282
+ </tbody>
283
+ </table>
284
+ </div>
285
+ </section>
286
+
287
+ <!-- Documents Section -->
288
+ <section id="documents" class="hidden">
289
+ <div class="flex justify-between items-center mb-8">
290
+ <h2 class="text-2xl font-bold text-gray-900">Документы</h2>
291
+ <button onclick="addDocument()" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition flex items-center">
292
+ <i data-feather="plus" class="w-4 h-4 mr-2"></i>
293
+ Новый документ
294
+ </button>
295
+ </div>
296
+
297
+ <!-- Document Types -->
298
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
299
+ <div onclick="openDocumentTemplate('timesheet')" class="bg-white p-6 rounded-xl cursor-pointer hover:shadow-lg transition card-shadow">
300
+ <i data-feather="clock" class="w-10 h-10 text-indigo-600 mb-3"></i>
301
+ <h3 class="font-semibold text-lg">Табель учета</h3>
302
+ <p class="text-gray-600 text-sm mt-2">Формат Т-12, Т-13</p>
303
+ </div>
304
+ <div onclick="openDocumentTemplate('vacation')" class="bg-white p-6 rounded-xl cursor-pointer hover:shadow-lg transition card-shadow">
305
+ <i data-feather="umbrella" class="w-10 h-10 text-green-600 mb-3"></i>
306
+ <h3 class="font-semibold text-lg">График отпусков</h3>
307
+ <p class="text-gray-600 text-sm mt-2">Годовой план отпусков</p>
308
+ </div>
309
+ <div onclick="openDocumentTemplate('business')" class="bg-white p-6 rounded-xl cursor-pointer hover:shadow-lg transition card-shadow">
310
+ <i data-feather="briefcase" class="w-10 h-10 text-purple-600 mb-3"></i>
311
+ <h3 class="font-semibold text-lg">Командировки</h3>
312
+ <p class="text-gray-600 text-sm mt-2">Учет командировок</p>
313
+ </div>
314
+ </div>
315
+
316
+ <!-- Document Editor -->
317
+ <div id="documentEditor" class="hidden bg-white p-6 rounded-xl">
318
+ <div class="flex justify-between items-center mb-4">
319
+ <h3 class="text-lg font-semibold">Редактор документа</h3>
320
+ <div class="flex space-x-2">
321
+ <button onclick="saveDocument()" class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition flex items-center">
322
+ <i data-feather="save" class="w-4 h-4 mr-2"></i>
323
+ Сохранить
324
+ </button>
325
+ <button onclick="exportToExcel()" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition flex items-center">
326
+ <i data-feather="download" class="w-4 h-4 mr-2"></i>
327
+ Excel
328
+ </button>
329
+ </div>
330
+ </div>
331
+ <div class="border-2 border-gray-200 rounded-lg p-4 overflow-auto max-h-96">
332
+ <table id="documentTable" class="excel-table w-full">
333
+ <!-- Document content will be inserted here -->
334
+ </table>
335
+ </div>
336
+ </div>
337
+ </section>
338
+
339
+ <!-- Upload Section -->
340
+ <section id="upload" class="hidden">
341
+ <div class="max-w-2xl mx-auto">
342
+ <h2 class="text-2xl font-bold text-gray-900 mb-8">Загрузка данных из Excel</h2>
343
+
344
+ <div class="bg-white p-8 rounded-xl card-shadow">
345
+ <div class="border-2 border-dashed border-gray-300 rounded-lg p-12 text-center hover:border-indigo-500 transition cursor-pointer" onclick="document.getElementById('fileInput').click()">
346
+ <i data-feather="upload-cloud" class="w-16 h-16 text-gray-400 mx-auto mb-4"></i>
347
+ <p class="text-xl font-medium text-gray-700 mb-2">Перетащите файл Excel сюда</p>
348
+ <p class="text-gray-500 mb-4">или нажмите для выбора</p>
349
+ <input type="file" id="fileInput" accept=".xlsx,.xls,.csv" class="hidden" onchange="handleFileUpload(event)">
350
+ <button class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition">
351
+ Выбрать файл
352
+ </button>
353
+ </div>
354
+
355
+ <div id="uploadPreview" class="mt-8 hidden">
356
+ <h3 class="text-lg font-semibold mb-4">Предпросмотр данных</h3>
357
+ <div class="border rounded-lg overflow-auto max-h-96">
358
+ <table id="previewTable" class="w-full">
359
+ <!-- Preview will be inserted here -->
360
+ </table>
361
+ </div>
362
+ <div class="mt-6 flex space-x-3">
363
+ <button onclick="confirmUpload()" class="bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700 transition flex items-center">
364
+ <i data-feather="check" class="w-4 h-4 mr-2"></i>
365
+ Подтвердить загрузку
366
+ </button>
367
+ <button onclick="cancelUpload()" class="bg-gray-600 text-white px-6 py-2 rounded-lg hover:bg-gray-700 transition flex items-center">
368
+ <i data-feather="x" class="w-4 h-4 mr-2"></i>
369
+ Отмена
370
+ </button>
371
+ </div>
372
+ </div>
373
+ </div>
374
+
375
+ <!-- Instructions -->
376
+ <div class="bg-blue-50 p-6 rounded-xl mt-6">
377
+ <h3 class="font-semibold text-blue-900 mb-3">Требования к файлу:</h3>
378
+ <ul class="space-y-2 text-blue-800">
379
+ <li class="flex items-start">
380
+ <i data-feather="check-circle" class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"></i>
381
+ <span>Формат файла: .xlsx, .xls или .csv</span>
382
+ </li>
383
+ <li class="flex items-start">
384
+ <i data-feather="check-circle" class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"></i>
385
+ <span>Обязательные колонки: ФИО, Отдел, Должность, Телефон</span>
386
+ </li>
387
+ <li class="flex items-start">
388
+ <i data-feather="check-circle" class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"></i>
389
+ <span>Первая строка должна содержать заголовки колонок</span>
390
+ </li>
391
+ </ul>
392
+ </div>
393
+ </div>
394
+ </section>
395
+
396
+ <!-- Analytics Section -->
397
+ <section id="analytics" class="hidden">
398
+ <h2 class="text-2xl font-bold text-gray-900 mb-8">Аналитика и отчеты</h2>
399
+
400
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
401
+ <div class="bg-white p-6 rounded-xl card-shadow">
402
+ <h3 class="text-lg font-semibold mb-4">Динамика штата</h3>
403
+ <canvas id="staffDynamicsChart"></canvas>
404
+ </div>
405
+ <div class="bg-white p-6 rounded-xl card-shadow">
406
+ <h3 class="text-lg font-semibold mb-4">Текучесть кадров</h3>
407
+ <canvas id="turnoverChart"></canvas>
408
+ </div>
409
+ <div class="bg-white p-6 rounded-xl card-shadow">
410
+ <h3 class="text-lg font-semibold mb-4">Возрастная структура</h3>
411
+ <canvas id="ageChart"></canvas>
412
+ </div>
413
+ <div class="bg-white p-6 rounded-xl card-shadow">
414
+ <h3 class="text-lg font-semibold mb-4">Загрузка отделов</h3>
415
+ <canvas id="workloadChart"></canvas>
416
+ </div>
417
+ </div>
418
+
419
+ <!-- Report Generator -->
420
+ <div class="bg-white p-6 rounded-xl mt-6 card-shadow">
421
+ <h3 class="text-lg font-semibold mb-4">Генератор отчетов</h3>
422
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
423
+ <div>
424
+ <label class="block text-sm font-medium text-gray-700 mb-2">Тип отчета</label>
425
+ <select class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
426
+ <option>Ежемесячный отчет</option>
427
+ <option>Квартальный отчет</option>
428
+ <option>Годовой отчет</option>
429
+ <option>Отчет по отделу</option>
430
+ </select>
431
+ </div>
432
+ <div>
433
+ <label class="block text-sm font-medium text-gray-700 mb-2">Период</label>
434
+ <input type="date" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
435
+ </div>
436
+ <div class="flex items-end">
437
+ <button onclick="generateReport()" class="w-full bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition flex items-center justify-center">
438
+ <i data-feather="file-text" class="w-4 h-4 mr-2"></i>
439
+ Сформировать
440
+ </button>
441
+ </div>
442
+ </div>
443
+ </div>
444
+ </section>
445
+ </main>
446
+ </div>
447
+
448
+ <!-- Modal -->
449
+ <div id="modal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center">
450
+ <div class="bg-white rounded-xl p-6 max-w-2xl w-full mx-4 max-h-screen overflow-y-auto">
451
+ <div class="flex justify-between items-center mb-4">
452
+ <h3 id="modalTitle" class="text-xl font-semibold">Добавить сотрудника</h3>
453
+ <button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">
454
+ <i data-feather="x" class="w-6 h-6"></i>
455
+ </button>
456
+ </div>
457
+ <div id="modalContent">
458
+ <!-- Modal content will be inserted here -->
459
+ </div>
460
+ </div>
461
+ </div>
462
+
463
+ <script src="components/navbar.js"></script>
464
+ <script src="components/sidebar.js"></script>
465
+ <script src="script.js"></script>
466
+ <script>feather.replace();</script>
467
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
468
+ </body>
469
+ </html>
script.js ADDED
@@ -0,0 +1,557 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // EmployeeHub Pro Main JavaScript
2
+ let employees = [];
3
+ let currentDocument = null;
4
+ let uploadedData = null;
5
+
6
+ // Initialize application
7
+ document.addEventListener('DOMContentLoaded', function() {
8
+ initializeApp();
9
+ loadDashboardCharts();
10
+ loadDefaultEmployees();
11
+ });
12
+
13
+ function initializeApp() {
14
+ // Initialize with sample data
15
+ employees = [
16
+ { id: 1, name: 'Иванов Иван Иванович', department: 'IT', position: 'Разработчик', status: 'onsite', phone: '+7 (900) 123-45-67' },
17
+ { id: 2, name: 'Петров Петр Петрович', department: 'HR', position: 'HR менеджер', status: 'onsite', phone: '+7 (900) 234-56-78' },
18
+ { id: 3, name: 'Сидорова Анна Владимировна', department: 'Бухгалтерия', position: 'Бухгалтер', status: 'remote', phone: '+7 (900) 345-67-89' },
19
+ { id: 4, name: 'Козлов Дмитрий Сергеевич', department: 'Маркетинг', position: 'Маркетолог', status: 'vacation', phone: '+7 (900) 456-78-90' },
20
+ { id: 5, name: 'Михайлова Елена Игоревна', department: 'IT', position: 'Дизайнер', status: 'remote', phone: '+7 (900) 567-89-01' }
21
+ ];
22
+ }
23
+
24
+ // Navigation functions
25
+ function showDashboard() {
26
+ hideAllSections();
27
+ document.getElementById('dashboard').classList.remove('hidden');
28
+ updateDashboardStats();
29
+ loadDashboardCharts();
30
+ }
31
+
32
+ function showEmployees() {
33
+ hideAllSections();
34
+ document.getElementById('employees').classList.remove('hidden');
35
+ displayEmployees(employees);
36
+ }
37
+
38
+ function showDocuments() {
39
+ hideAllSections();
40
+ document.getElementById('documents').classList.remove('hidden');
41
+ }
42
+
43
+ function showUpload() {
44
+ hideAllSections();
45
+ document.getElementById('upload').classList.remove('hidden');
46
+ }
47
+
48
+ function showAnalytics() {
49
+ hideAllSections();
50
+ document.getElementById('analytics').classList.remove('hidden');
51
+ loadAnalyticsCharts();
52
+ }
53
+
54
+ function hideAllSections() {
55
+ const sections = ['dashboard', 'employees', 'documents', 'upload', 'analytics'];
56
+ sections.forEach(section => {
57
+ document.getElementById(section).classList.add('hidden');
58
+ });
59
+ }
60
+
61
+ // Dashboard functions
62
+ function updateDashboardStats() {
63
+ const totalEmployees = employees.length;
64
+ const onsiteEmployees = employees.filter(e => e.status === 'onsite').length;
65
+ const remoteEmployees = employees.filter(e => e.status === 'remote').length;
66
+ const vacationEmployees = employees.filter(e => e.status === 'vacation').length;
67
+
68
+ // Update stat cards (in real app, would update DOM)
69
+ console.log('Stats:', { totalEmployees, onsiteEmployees, remoteEmployees, vacationEmployees });
70
+ }
71
+
72
+ function loadDashboardCharts() {
73
+ // Department distribution chart
74
+ const ctx1 = document.getElementById('departmentChart');
75
+ if (ctx1) {
76
+ const departments = {};
77
+ employees.forEach(emp => {
78
+ departments[emp.department] = (departments[emp.department] || 0) + 1;
79
+ });
80
+
81
+ new Chart(ctx1, {
82
+ type: 'doughnut',
83
+ data: {
84
+ labels: Object.keys(departments),
85
+ datasets: [{
86
+ data: Object.values(departments),
87
+ backgroundColor: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444']
88
+ }]
89
+ },
90
+ options: {
91
+ responsive: true,
92
+ plugins: {
93
+ legend: {
94
+ position: 'bottom'
95
+ }
96
+ }
97
+ }
98
+ });
99
+ }
100
+ }
101
+
102
+ // Employees functions
103
+ function displayEmployees(employeeList) {
104
+ const tableBody = document.getElementById('employeesTable');
105
+ tableBody.innerHTML = '';
106
+
107
+ employeeList.forEach(employee => {
108
+ const row = document.createElement('tr');
109
+ row.className = 'table-row-hover';
110
+ row.innerHTML = `
111
+ <td class="px-6 py-4 whitespace-nowrap">
112
+ <div class="flex items-center">
113
+ <img src="https://static.photos/people/40x40/${employee.id}" alt="${employee.name}" class="w-10 h-10 rounded-full mr-3">
114
+ <div class="text-sm font-medium text-gray-900">${employee.name}</div>
115
+ </div>
116
+ </td>
117
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${employee.department}</td>
118
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${employee.position}</td>
119
+ <td class="px-6 py-4 whitespace-nowrap">
120
+ <span class="status-badge status-${employee.status}">${getStatusText(employee.status)}</span>
121
+ </td>
122
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${employee.phone}</td>
123
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
124
+ <button onclick="editEmployee(${employee.id})" class="text-indigo-600 hover:text-indigo-900 mr-3">Реда��тировать</button>
125
+ <button onclick="deleteEmployee(${employee.id})" class="text-red-600 hover:text-red-900">Удалить</button>
126
+ </td>
127
+ `;
128
+ tableBody.appendChild(row);
129
+ });
130
+ }
131
+
132
+ function getStatusText(status) {
133
+ const statuses = {
134
+ 'onsite': 'На площадке',
135
+ 'remote': 'Удаленно',
136
+ 'vacation': 'Отпуск',
137
+ 'leave': 'Уход'
138
+ };
139
+ return statuses[status] || status;
140
+ }
141
+
142
+ function searchEmployees(query) {
143
+ const filtered = employees.filter(emp =>
144
+ emp.name.toLowerCase().includes(query.toLowerCase()) ||
145
+ emp.department.toLowerCase().includes(query.toLowerCase()) ||
146
+ emp.position.toLowerCase().includes(query.toLowerCase())
147
+ );
148
+ displayEmployees(filtered);
149
+ }
150
+
151
+ function filterEmployees(status) {
152
+ // Update active tab
153
+ const tabs = document.querySelectorAll('.tab-active');
154
+ tabs.forEach(tab => tab.classList.remove('tab-active'));
155
+ event.target.classList.add('tab-active');
156
+
157
+ let filtered = employees;
158
+ if (status !== 'all') {
159
+ filtered = employees.filter(emp => emp.status === status);
160
+ }
161
+ displayEmployees(filtered);
162
+ }
163
+
164
+ function addEmployee() {
165
+ showModal('addEmployee');
166
+ }
167
+
168
+ function editEmployee(id) {
169
+ const employee = employees.find(emp => emp.id === id);
170
+ if (employee) {
171
+ showModal('editEmployee', employee);
172
+ }
173
+ }
174
+
175
+ function deleteEmployee(id) {
176
+ if (confirm('Вы уверены, что хотите удалить этого сотрудника?')) {
177
+ employees = employees.filter(emp => emp.id !== id);
178
+ displayEmployees(employees);
179
+ showToast('Сотрудник удален', 'success');
180
+ }
181
+ }
182
+
183
+ // Documents functions
184
+ function openDocumentTemplate(type) {
185
+ currentDocument = type;
186
+ const editor = document.getElementById('documentEditor');
187
+ editor.classList.remove('hidden');
188
+
189
+ const templates = {
190
+ timesheet: {
191
+ headers: ['Дата', 'ФИО', 'Табельный номер', 'Приход', 'Уход', 'Часы'],
192
+ rows: 30
193
+ },
194
+ vacation: {
195
+ headers: ['ФИО', 'Отдел', 'Дата начала', 'Дата окончания', 'Дней'],
196
+ rows: 20
197
+ },
198
+ business: {
199
+ headers: ['ФИО', 'Место', 'Дата выезда', 'Дата возврата', 'Цель'],
200
+ rows: 15
201
+ }
202
+ };
203
+
204
+ const template = templates[type];
205
+ const table = document.getElementById('documentTable');
206
+ table.innerHTML = '';
207
+
208
+ // Create header row
209
+ const headerRow = document.createElement('tr');
210
+ template.headers.forEach(header => {
211
+ const th = document.createElement('th');
212
+ th.className = 'excel-header';
213
+ th.textContent = header;
214
+ headerRow.appendChild(th);
215
+ });
216
+ table.appendChild(headerRow);
217
+
218
+ // Create data rows
219
+ for (let i = 0; i < template.rows; i++) {
220
+ const row = document.createElement('tr');
221
+ template.headers.forEach((header, colIndex) => {
222
+ const cell = document.createElement('td');
223
+ cell.className = 'excel-cell';
224
+ cell.contentEditable = true;
225
+ cell.onclick = () => makeCellEditable(cell);
226
+ row.appendChild(cell);
227
+ });
228
+ table.appendChild(row);
229
+ }
230
+ }
231
+
232
+ function makeCellEditable(cell) {
233
+ cell.classList.add('editing');
234
+ cell.addEventListener('blur', function() {
235
+ this.classList.remove('editing');
236
+ });
237
+ }
238
+
239
+ function saveDocument() {
240
+ // Save document logic
241
+ showToast('Документ сохранен', 'success');
242
+ }
243
+
244
+ function exportToExcel() {
245
+ const table = document.getElementById('documentTable');
246
+ const wb = XLSX.utils.table_to_book(table, {sheet: "Документ"});
247
+ XLSX.writeFile(wb, `document_${Date.now()}.xlsx`);
248
+ showToast('Документ экспортирован в Excel', 'success');
249
+ }
250
+
251
+ // Upload functions
252
+ function handleFileUpload(event) {
253
+ const file = event.target.files[0];
254
+ if (file) {
255
+ const reader = new FileReader();
256
+ reader.onload = function(e) {
257
+ try {
258
+ const data = new Uint8Array(e.target.result);
259
+ const workbook = XLSX.read(data, {type: 'array'});
260
+ const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
261
+ const jsonData = XLSX.utils.sheet_to_json(firstSheet);
262
+
263
+ uploadedData = jsonData;
264
+ showUploadPreview(jsonData);
265
+ } catch (error) {
266
+ showToast('Ошибка при чтении файла', 'error');
267
+ }
268
+ };
269
+ reader.readAsArrayBuffer(file);
270
+ }
271
+ }
272
+
273
+ function showUploadPreview(data) {
274
+ const preview = document.getElementById('uploadPreview');
275
+ preview.classList.remove('hidden');
276
+
277
+ const table = document.getElementById('previewTable');
278
+ table.innerHTML = '';
279
+
280
+ if (data.length > 0) {
281
+ // Create header
282
+ const headerRow = document.createElement('tr');
283
+ headerRow.className = 'bg-gray-50';
284
+ Object.keys(data[0]).forEach(key => {
285
+ const th = document.createElement('th');
286
+ th.className = 'px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase';
287
+ th.textContent = key;
288
+ headerRow.appendChild(th);
289
+ });
290
+ table.appendChild(headerRow);
291
+
292
+ // Show first 5 rows
293
+ data.slice(0, 5).forEach(row => {
294
+ const tr = document.createElement('tr');
295
+ tr.className = 'border-t';
296
+ Object.values(row).forEach(value => {
297
+ const td = document.createElement('td');
298
+ td.className = 'px-4 py-2 text-sm text-gray-900';
299
+ td.textContent = value;
300
+ tr.appendChild(td);
301
+ });
302
+ table.appendChild(tr);
303
+ });
304
+
305
+ if (data.length > 5) {
306
+ const moreRow = document.createElement('tr');
307
+ moreRow.className = 'border-t text-center';
308
+ const td = document.createElement('td');
309
+ td.colSpan = Object.keys(data[0]).length;
310
+ td.className = 'px-4 py-2 text-sm text-gray-500';
311
+ td.textContent = `... и еще ${data.length - 5} записей`;
312
+ moreRow.appendChild(td);
313
+ table.appendChild(moreRow);
314
+ }
315
+ }
316
+ }
317
+
318
+ function confirmUpload() {
319
+ if (uploadedData) {
320
+ // Convert and add to employees array
321
+ const newEmployees = uploadedData.map((row, index) => ({
322
+ id: employees.length + index + 1,
323
+ name: row['ФИО'] || row['Name'] || '',
324
+ department: row['Отдел'] || row['Department'] || '',
325
+ position: row['Должность'] || row['Position'] || '',
326
+ status: 'onsite',
327
+ phone: row['Телефон'] || row['Phone'] || ''
328
+ })).filter(emp => emp.name);
329
+
330
+ employees = [...employees, ...newEmployees];
331
+ showToast(`Загружено ${newEmployees.length} сотрудников`, 'success');
332
+ cancelUpload();
333
+ }
334
+ }
335
+
336
+ function cancelUpload() {
337
+ document.getElementById('uploadPreview').classList.add('hidden');
338
+ document.getElementById('fileInput').value = '';
339
+ uploadedData = null;
340
+ }
341
+
342
+ // Analytics functions
343
+ function loadAnalyticsCharts() {
344
+ // Staff dynamics chart
345
+ const ctx1 = document.getElementById('staffDynamicsChart');
346
+ if (ctx1) {
347
+ new Chart(ctx1, {
348
+ type: 'line',
349
+ data: {
350
+ labels: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн'],
351
+ datasets: [{
352
+ label: 'Количество сотрудников',
353
+ data: [1120, 1150, 1180, 1200, 1220, 1248],
354
+ borderColor: '#6366f1',
355
+ backgroundColor: 'rgba(99, 102, 241, 0.1)',
356
+ tension: 0.4
357
+ }]
358
+ },
359
+ options: {
360
+ responsive: true,
361
+ plugins: {
362
+ legend: {
363
+ display: false
364
+ }
365
+ }
366
+ }
367
+ });
368
+ }
369
+
370
+ // Turnover chart
371
+ const ctx2 = document.getElementById('turnoverChart');
372
+ if (ctx2) {
373
+ new Chart(ctx2, {
374
+ type: 'bar',
375
+ data: {
376
+ labels: ['Q1', 'Q2', 'Q3', 'Q4'],
377
+ datasets: [{
378
+ label: 'Уволилось',
379
+ data: [12, 19, 15, 8],
380
+ backgroundColor: '#ef4444'
381
+ }, {
382
+ label: 'Принято',
383
+ data: [18, 22, 20, 25],
384
+ backgroundColor: '#10b981'
385
+ }]
386
+ },
387
+ options: {
388
+ responsive: true
389
+ }
390
+ });
391
+ }
392
+
393
+ // Age structure chart
394
+ const ctx3 = document.getElementById('ageChart');
395
+ if (ctx3) {
396
+ new Chart(ctx3, {
397
+ type: 'pie',
398
+ data: {
399
+ labels: ['18-25', '26-35', '36-45', '46-55', '56+'],
400
+ datasets: [{
401
+ data: [180, 420, 350, 230, 68],
402
+ backgroundColor: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444']
403
+ }]
404
+ },
405
+ options: {
406
+ responsive: true,
407
+ plugins: {
408
+ legend: {
409
+ position: 'bottom'
410
+ }
411
+ }
412
+ }
413
+ });
414
+ }
415
+
416
+ // Workload chart
417
+ const ctx4 = document.getElementById('workloadChart');
418
+ if (ctx4) {
419
+ new Chart(ctx4, {
420
+ type: 'radar',
421
+ data: {
422
+ labels: ['IT', 'HR', 'Бухгалтерия', 'Маркетинг', 'Продажи', 'Логистика'],
423
+ datasets: [{
424
+ label: 'Текущая загрузка',
425
+ data: [85, 70, 75, 80, 90, 65],
426
+ borderColor: '#6366f1',
427
+ backgroundColor: 'rgba(99, 102, 241, 0.2)'
428
+ }, {
429
+ label: 'Оптимальная',
430
+ data: [80, 80, 80, 80, 80, 80],
431
+ borderColor: '#10b981',
432
+ backgroundColor: 'rgba(16, 185, 129, 0.1)'
433
+ }]
434
+ },
435
+ options: {
436
+ responsive: true
437
+ }
438
+ });
439
+ }
440
+ }
441
+
442
+ function generateReport() {
443
+ showToast('Отчет успешно сформирован', 'success');
444
+ }
445
+
446
+ // Modal functions
447
+ function showModal(type, data = null) {
448
+ const modal = document.getElementById('modal');
449
+ const modalTitle = document.getElementById('modalTitle');
450
+ const modalContent = document.getElementById('modalContent');
451
+
452
+ if (type === 'addEmployee' || type === 'editEmployee') {
453
+ modalTitle.textContent = type === 'addEmployee' ? 'Добавить сотрудника' : 'Редактировать сотрудника';
454
+
455
+ modalContent.innerHTML = `
456
+ <form onsubmit="saveEmployee(event)">
457
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
458
+ <div>
459
+ <label class="block text-sm font-medium text-gray-700 mb-1">ФИО</label>
460
+ <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 : ''}">
461
+ </div>
462
+ <div>
463
+ <label class="block text-sm font-medium text-gray-700 mb-1">Отдел</label>
464
+ <select id="empDepartment" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg">
465
+ <option value="IT" ${data && data.department === 'IT' ? 'selected' : ''}>IT</option>
466
+ <option value="HR" ${data && data.department === 'HR' ? 'selected' : ''}>HR</option>
467
+ <option value="Бухгалтерия" ${data && data.department === 'Бухгалтерия' ? 'selected' : ''}>Бухгалтерия</option>
468
+ <option value="Маркетинг" ${data && data.department === 'Маркетинг' ? 'selected' : ''}>Маркетинг</option>
469
+ <option value="Продажи" ${data && data.department === 'Продажи' ? 'selected' : ''}>Продажи</option>
470
+ </select>
471
+ </div>
472
+ <div>
473
+ <label class="block text-sm font-medium text-gray-700 mb-1">Должность</label>
474
+ <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 : ''}">
475
+ </div>
476
+ <div>
477
+ <label class="block text-sm font-medium text-gray-700 mb-1">Статус</label>
478
+ <select id="empStatus" required class="form-input w-full px-4 py-2 border border-gray-300 rounded-lg">
479
+ <option value="onsite" ${data && data.status === 'onsite' ? 'selected' : ''}>На площадке</option>
480
+ <option value="remote" ${data && data.status === 'remote' ? 'selected' : ''}>Удаленно</option>
481
+ <option value="vacation" ${data && data.status === 'vacation' ? 'selected' : ''}>Отпуск</option>
482
+ </select>
483
+ </div>
484
+ <div class="md:col-span-2">
485
+ <label class="block text-sm font-medium text-gray-700 mb-1">Телефон</label>
486
+ <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 : ''}">
487
+ </div>
488
+ </div>
489
+ <div class="mt-6 flex justify-end space-x-3">
490
+ <button type="button" onclick="closeModal()" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50">Отмена</button>
491
+ <button type="submit" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700">Сохранить</button>
492
+ </div>
493
+ </form>
494
+ `;
495
+ }
496
+
497
+ modal.classList.remove('hidden');
498
+ }
499
+
500
+ function closeModal() {
501
+ document.getElementById('modal').classList.add('hidden');
502
+ }
503
+
504
+ function saveEmployee(event) {
505
+ event.preventDefault();
506
+
507
+ const employee = {
508
+ id: employees.length + 1,
509
+ name: document.getElementById('empName').value,
510
+ department: document.getElementById('empDepartment').value,
511
+ position: document.getElementById('empPosition').value,
512
+ status: document.getElementById('empStatus').value,
513
+ phone: document.getElementById('empPhone').value
514
+ };
515
+
516
+ employees.push(employee);
517
+ displayEmployees(employees);
518
+ closeModal();
519
+ showToast('Сотрудник добавлен', 'success');
520
+ }
521
+
522
+ // Toast notification
523
+ function showToast(message, type = 'success') {
524
+ const toast = document.createElement('div');
525
+ toast.className = `toast toast-${type}`;
526
+ toast.textContent = message;
527
+ document.body.appendChild(toast);
528
+
529
+ setTimeout(() => {
530
+ toast.remove();
531
+ }, 3000);
532
+ }
533
+
534
+ // Export data function
535
+ function exportData() {
536
+ const wb = XLSX.utils.book_new();
537
+ const ws = XLSX.utils.json_to_sheet(employees.map(emp => ({
538
+ 'ФИО': emp.name,
539
+ 'Отдел': emp.department,
540
+ 'Должность': emp.position,
541
+ 'Статус': getStatusText(emp.status),
542
+ 'Телефон': emp.phone
543
+ })));
544
+ XLSX.utils.book_append_sheet(wb, ws, 'Сотрудники');
545
+ XLSX.writeFile(wb, `employees_${new Date().toISOString().split('T')[0]}.xlsx`);
546
+ showToast('Данные успешно экспортированы', 'success');
547
+ }
548
+
549
+ // Show notifications
550
+ function showNotifications() {
551
+ showToast('У вас 3 новых уведомления', 'success');
552
+ }
553
+
554
+ // Load default employees
555
+ function loadDefaultEmployees() {
556
+ console.log('Application initialized with', employees.length, 'employees');
557
+ }
style.css CHANGED
@@ -1,28 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Custom styles for EmployeeHub Pro */
2
+ :root {
3
+ --primary-color: #6366f1;
4
+ --secondary-color: #8b5cf6;
5
+ --success-color: #10b981;
6
+ --warning-color: #f59e0b;
7
+ --danger-color: #ef4444;
8
+ --dark-color: #1f2937;
9
+ --light-color: #f3f4f6;
10
+ }
11
+
12
  body {
13
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
+ background-color: var(--light-color);
15
+ }
16
+
17
+ /* Scrollbar styles */
18
+ ::-webkit-scrollbar {
19
+ width: 8px;
20
+ height: 8px;
21
+ }
22
+
23
+ ::-webkit-scrollbar-track {
24
+ background: #f1f1f1;
25
+ border-radius: 10px;
26
  }
27
 
28
+ ::-webkit-scrollbar-thumb {
29
+ background: var(--primary-color);
30
+ border-radius: 10px;
31
  }
32
 
33
+ ::-webkit-scrollbar-thumb:hover {
34
+ background: var(--secondary-color);
 
 
 
35
  }
36
 
37
+ /* Animation classes */
38
+ @keyframes slideInUp {
39
+ from {
40
+ transform: translateY(20px);
41
+ opacity: 0;
42
+ }
43
+ to {
44
+ transform: translateY(0);
45
+ opacity: 1;
46
+ }
47
  }
48
 
49
+ .animate-slide-in {
50
+ animation: slideInUp 0.3s ease-out;
51
  }
52
+
53
+ /* Pulse animation for notifications */
54
+ @keyframes pulse {
55
+ 0% {
56
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
57
+ }
58
+ 70% {
59
+ box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
60
+ }
61
+ 100% {
62
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
63
+ }
64
+ }
65
+
66
+ .pulse {
67
+ animation: pulse 2s infinite;
68
+ }
69
+
70
+ /* Hover effects */
71
+ .hover-lift {
72
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
73
+ }
74
+
75
+ .hover-lift:hover {
76
+ transform: translateY(-5px);
77
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
78
+ }
79
+
80
+ /* Gradient text */
81
+ .gradient-text {
82
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
83
+ -webkit-background-clip: text;
84
+ -webkit-text-fill-color: transparent;
85
+ background-clip: text;
86
+ }
87
+
88
+ /* Loading spinner */
89
+ .spinner {
90
+ border: 2px solid #f3f3f3;
91
+ border-top: 2px solid var(--primary-color);
92
+ border-radius: 50%;
93
+ width: 20px;
94
+ height: 20px;
95
+ animation: spin 1s linear infinite;
96
+ }
97
+
98
+ @keyframes spin {
99
+ 0% { transform: rotate(0deg); }
100
+ 100% { transform: rotate(360deg); }
101
+ }
102
+
103
+ /* Excel table styles */
104
+ .excel-cell {
105
+ min-width: 100px;
106
+ position: relative;
107
+ transition: background-color 0.2s ease;
108
+ }
109
+
110
+ .excel-cell:hover {
111
+ background-color: #f9fafb;
112
+ }
113
+
114
+ .excel-cell.editing {
115
+ background-color: #fef3c7 !important;
116
+ outline: 2px solid var(--primary-color);
117
+ }
118
+
119
+ .excel-cell input {
120
+ width: 100%;
121
+ border: none;
122
+ background: transparent;
123
+ padding: 8px;
124
+ font-family: inherit;
125
+ font-size: 14px;
126
+ }
127
+
128
+ .excel-header {
129
+ background: linear-gradient(to bottom, #f8fafc, #f1f5f9);
130
+ font-weight: 600;
131
+ color: #374151;
132
+ position: sticky;
133
+ top: 0;
134
+ z-index: 10;
135
+ }
136
+
137
+ /* Status badges */
138
+ .status-badge {
139
+ padding: 4px 12px;
140
+ border-radius: 12px;
141
+ font-size: 12px;
142
+ font-weight: 500;
143
+ text-transform: uppercase;
144
+ letter-spacing: 0.5px;
145
+ }
146
+
147
+ .status-onsite {
148
+ background-color: #d1fae5;
149
+ color: #065f46;
150
+ }
151
+
152
+ .status-remote {
153
+ background-color: #ddd6fe;
154
+ color: #4c1d95;
155
+ }
156
+
157
+ .status-vacation {
158
+ background-color: #fed7aa;
159
+ color: #92400e;
160
+ }
161
+
162
+ .status-leave {
163
+ background-color: #fee2e2;
164
+ color: #991b1b;
165
+ }
166
+
167
+ /* Upload area styles */
168
+ .upload-area {
169
+ border: 2px dashed #cbd5e1;
170
+ transition: all 0.3s ease;
171
+ }
172
+
173
+ .upload-area:hover {
174
+ border-color: var(--primary-color);
175
+ background-color: #f8fafc;
176
+ }
177
+
178
+ .upload-area.dragover {
179
+ border-color: var(--primary-color);
180
+ background-color: #eef2ff;
181
+ }
182
+
183
+ /* Modal styles */
184
+ .modal-backdrop {
185
+ backdrop-filter: blur(4px);
186
+ }
187
+
188
+ /* Responsive adjustments */
189
+ @media (max-width: 768px) {
190
+ .mobile-hidden {
191
+ display: none;
192
+ }
193
+
194
+ .mobile-full {
195
+ width: 100% !important;
196
+ }
197
+ }
198
+
199
+ /* Form input focus effects */
200
+ .form-input {
201
+ transition: all 0.3s ease;
202
+ }
203
+
204
+ .form-input:focus {
205
+ border-color: var(--primary-color);
206
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
207
+ }
208
+
209
+ /* Table row hover */
210
+ .table-row-hover {
211
+ transition: background-color 0.2s ease;
212
+ }
213
+
214
+ .table-row-hover:hover {
215
+ background-color: #f9fafb;
216
+ }
217
+
218
+ /* Custom checkbox */
219
+ .custom-checkbox {
220
+ width: 20px;
221
+ height: 20px;
222
+ cursor: pointer;
223
+ accent-color: var(--primary-color);
224
+ }
225
+
226
+ /* Floating action button */
227
+ .fab {
228
+ position: fixed;
229
+ bottom: 2rem;
230
+ right: 2rem;
231
+ width: 56px;
232
+ height: 56px;
233
+ border-radius: 50%;
234
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
235
+ color: white;
236
+ display: flex;
237
+ align-items: center;
238
+ justify-content: center;
239
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
240
+ cursor: pointer;
241
+ transition: all 0.3s ease;
242
+ z-index: 40;
243
+ }
244
+
245
+ .fab:hover {
246
+ transform: scale(1.1);
247
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
248
+ }
249
+
250
+ /* Toast notification */
251
+ .toast {
252
+ position: fixed;
253
+ bottom: 2rem;
254
+ left: 50%;
255
+ transform: translateX(-50%);
256
+ padding: 1rem 2rem;
257
+ border-radius: 8px;
258
+ color: white;
259
+ font-weight: 500;
260
+ z-index: 50;
261
+ animation: slideInUp 0.3s ease-out;
262
+ }
263
+
264
+ .toast-success {
265
+ background-color: var(--success-color);
266
+ }
267
+
268
+ .toast-error {
269
+ background-color: var(--danger-color);
270
+ }
271
+
272
+ .toast-warning {
273
+ background-color: var(--warning-color);
274
+ }
275
+
276
+ /* Progress bar */
277
+ .progress-bar {
278
+ height: 8px;
279
+ background-color: #e5e7eb;
280
+ border-radius: 4px;
281
+ overflow: hidden;
282
+ }
283
+
284
+ .progress-fill {
285
+ height: 100%;
286
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
287
+ transition: width 0.3s ease;
288
+ }
289
+
290
+ /* Tooltip */
291
+ .tooltip {
292
+ position: relative;
293
+ }
294
+
295
+ .tooltip::after {
296
+ content: attr(data-tooltip);
297
+ position: absolute;
298
+ bottom: 100%;
299
+ left: 50%;
300
+ transform: translateX(-50%);
301
+ background-color: var(--dark-color);
302
+ color: white;
303
+ padding: 0.5rem 0.75rem;
304
+ border-radius: 6px;
305
+ font-size: 0.875rem;
306
+ white-space: nowrap;
307
+ opacity: 0;
308
+ pointer-events: none;
309
+ transition: opacity 0.3s ease;
310
+ }
311
+
312
+ .tooltip:hover::after {
313
+ opacity: 1;
314
+ }