Karmashek commited on
Commit
45d0b5a
·
verified ·
1 Parent(s): 7f540e6

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +1285 -18
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: P4 General
3
- emoji: 📚
4
- colorFrom: indigo
5
- colorTo: green
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: p4-general
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1286 @@
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>P4 System - Учет ремонтов и ТО</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
11
+ <style>
12
+ .sidebar {
13
+ transition: all 0.3s ease;
14
+ }
15
+ .sidebar.collapsed {
16
+ width: 70px;
17
+ }
18
+ .sidebar.collapsed .sidebar-text {
19
+ display: none;
20
+ }
21
+ .main-content {
22
+ transition: all 0.3s ease;
23
+ }
24
+ .equipment-card:hover {
25
+ transform: translateY(-5px);
26
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
27
+ }
28
+ .notification-badge {
29
+ position: absolute;
30
+ top: -5px;
31
+ right: -5px;
32
+ }
33
+ .calendar-day.has-event {
34
+ background-color: #EFF6FF;
35
+ }
36
+ .calendar-day.event-due {
37
+ background-color: #FEE2E2;
38
+ }
39
+ .calendar-day.event-completed {
40
+ background-color: #D1FAE5;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body class="bg-gray-50">
45
+ <div class="flex h-screen overflow-hidden">
46
+ <!-- Sidebar -->
47
+ <div class="sidebar bg-blue-800 text-white w-64 flex flex-col">
48
+ <div class="p-4 flex items-center justify-between border-b border-blue-700">
49
+ <div class="flex items-center">
50
+ <i class="fas fa-cogs text-2xl mr-3"></i>
51
+ <span class="sidebar-text text-xl font-bold">P4 System</span>
52
+ </div>
53
+ <button id="toggleSidebar" class="text-white hover:text-blue-200">
54
+ <i class="fas fa-bars"></i>
55
+ </button>
56
+ </div>
57
+ <div class="flex-1 overflow-y-auto">
58
+ <nav class="p-4">
59
+ <div class="mb-6">
60
+ <p class="sidebar-text uppercase text-xs font-semibold text-blue-300 mb-2">Основное</p>
61
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg bg-blue-700 text-white mb-2">
62
+ <i class="fas fa-tachometer-alt mr-3"></i>
63
+ <span class="sidebar-text">Главная</span>
64
+ </a>
65
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
66
+ <i class="fas fa-calendar-alt mr-3"></i>
67
+ <span class="sidebar-text">График ТО</span>
68
+ <span class="ml-auto bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">3</span>
69
+ </a>
70
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
71
+ <i class="fas fa-tools mr-3"></i>
72
+ <span class="sidebar-text">Оборудование</span>
73
+ </a>
74
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
75
+ <i class="fas fa-eye"></i>
76
+ <span class="sidebar-text">Ежедневный осмотр</span>
77
+ </a>
78
+ </div>
79
+ <div class="mb-6">
80
+ <p class="sidebar-text uppercase text-xs font-semibold text-blue-300 mb-2">Учет</p>
81
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
82
+ <i class="fas fa-wrench mr-3"></i>
83
+ <span class="sidebar-text">Ремонтные работы</span>
84
+ </a>
85
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
86
+ <i class="fas fa-clipboard-check mr-3"></i>
87
+ <span class="sidebar-text">Техническое обслуживание</span>
88
+ </a>
89
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
90
+ <i class="fas fa-chart-line mr-3"></i>
91
+ <span class="sidebar-text">Отчеты</span>
92
+ </a>
93
+ </div>
94
+ <div class="mb-6">
95
+ <p class="sidebar-text uppercase text-xs font-semibold text-blue-300 mb-2">Настройки</p>
96
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
97
+ <i class="fas fa-bell mr-3"></i>
98
+ <span class="sidebar-text">Оповещения</span>
99
+ </a>
100
+ <a href="#" class="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2">
101
+ <i class="fas fa-users-cog mr-3"></i>
102
+ <span class="sidebar-text">Пользователи</span>
103
+ </a>
104
+ </div>
105
+ </nav>
106
+ </div>
107
+ <div class="p-4 border-t border-blue-700">
108
+ <div class="flex items-center">
109
+ <img src="https://academy.starline.ru/upload/main/3d7/3d781c35a80756120dc550c6318572da.jpg" alt="User" class="w-10 h-10 rounded-full mr-3">
110
+ <div class="sidebar-text">
111
+ <p class="font-medium">Никита</p>
112
+ <p class="text-xs text-blue-300">Администратор</p>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ <!-- Main Content -->
119
+ <div class="main-content flex-1 overflow-auto">
120
+ <!-- Header -->
121
+ <header class="bg-white shadow-sm py-4 px-6 flex items-center justify-between">
122
+ <h1 class="text-2xl font-bold text-gray-800">Главная панель</h1>
123
+ <div class="flex items-center space-x-4">
124
+ <div class="relative">
125
+ <button class="p-2 rounded-full hover:bg-gray-100 relative">
126
+ <i class="fas fa-bell text-gray-600"></i>
127
+ <span class="notification-badge bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">5</span>
128
+ </button>
129
+ </div>
130
+ <div class="relative">
131
+ <input type="text" placeholder="Поиск..." class="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
132
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
133
+ </div>
134
+ </div>
135
+ </header>
136
+
137
+ <!-- Dashboard Content -->
138
+ <div class="p-6">
139
+ <!-- Stats Cards -->
140
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
141
+ <div class="bg-white rounded-lg shadow p-6">
142
+ <div class="flex items-center">
143
+ <div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
144
+ <i class="fas fa-tools text-xl"></i>
145
+ </div>
146
+ <div>
147
+ <p class="text-sm text-gray-500">Всего оборудования</p>
148
+ <h3 class="text-2xl font-bold">42</h3>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ <div class="bg-white rounded-lg shadow p-6">
153
+ <div class="flex items-center">
154
+ <div class="p-3 rounded-full bg-green-100 text-green-600 mr-4">
155
+ <i class="fas fa-check-circle text-xl"></i>
156
+ </div>
157
+ <div>
158
+ <p class="text-sm text-gray-500">Выполнено ТО</p>
159
+ <h3 class="text-2xl font-bold">28</h3>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ <div class="bg-white rounded-lg shadow p-6">
164
+ <div class="flex items-center">
165
+ <div class="p-3 rounded-full bg-yellow-100 text-yellow-600 mr-4">
166
+ <i class="fas fa-exclamation-triangle text-xl"></i>
167
+ </div>
168
+ <div>
169
+ <p class="text-sm text-gray-500">Просрочено ТО</p>
170
+ <h3 class="text-2xl font-bold">3</h3>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ <div class="bg-white rounded-lg shadow p-6">
175
+ <div class="flex items-center">
176
+ <div class="p-3 rounded-full bg-red-100 text-red-600 mr-4">
177
+ <i class="fas fa-wrench text-xl"></i>
178
+ </div>
179
+ <div>
180
+ <p class="text-sm text-gray-500">Текущие ремонты</p>
181
+ <h3 class="text-2xl font-bold">5</h3>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <!-- Equipment and Calendar -->
188
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
189
+ <!-- Equipment List -->
190
+ <div class="lg:col-span-2 bg-white rounded-lg shadow overflow-hidden">
191
+ <div class="p-4 border-b flex justify-between items-center">
192
+ <h2 class="text-lg font-semibold">Оборудование</h2>
193
+ <button onclick="openAddEquipmentModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center">
194
+ <i class="fas fa-plus mr-2"></i> Добавить
195
+ </button>
196
+ </div>
197
+ <div class="overflow-x-auto">
198
+ <table class="min-w-full divide-y divide-gray-200">
199
+ <thead class="bg-gray-50">
200
+ <tr>
201
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
202
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Название</th>
203
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип</th>
204
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
205
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Последнее ТО</th>
206
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
207
+ </tr>
208
+ </thead>
209
+ <tbody class="bg-white divide-y divide-gray-200" id="equipmentTableBody">
210
+ <!-- Equipment rows will be added here by JavaScript -->
211
+ </tbody>
212
+ </table>
213
+ </div>
214
+ </div>
215
+
216
+ <!-- Maintenance Calendar -->
217
+ <div class="bg-white rounded-lg shadow overflow-hidden">
218
+ <div class="p-4 border-b">
219
+ <h2 class="text-lg font-semibold">График ТО</h2>
220
+ </div>
221
+ <div class="p-4">
222
+ <div class="flex justify-between items-center mb-4">
223
+ <h3 class="font-medium">Июнь 2025</h3>
224
+ <div class="flex space-x-2">
225
+ <button class="p-2 rounded-full hover:bg-gray-100">
226
+ <i class="fas fa-chevron-left"></i>
227
+ </button>
228
+ <button class="p-2 rounded-full hover:bg-gray-100">
229
+ <i class="fas fa-chevron-right"></i>
230
+ </button>
231
+ </div>
232
+ </div>
233
+ <div class="grid grid-cols-7 gap-1 mb-2">
234
+ <div class="text-center text-xs font-medium text-gray-500">Пн</div>
235
+ <div class="text-center text-xs font-medium text-gray-500">Вт</div>
236
+ <div class="text-center text-xs font-medium text-gray-500">Ср</div>
237
+ <div class="text-center text-xs font-medium text-gray-500">Чт</div>
238
+ <div class="text-center text-xs font-medium text-gray-500">Пт</div>
239
+ <div class="text-center text-xs font-medium text-gray-500">Сб</div>
240
+ <div class="text-center text-xs font-medium text-gray-500">Вс</div>
241
+ </div>
242
+ <div class="grid grid-cols-7 gap-1" id="calendarDays">
243
+ <!-- Calendar days will be added here by JavaScript -->
244
+ </div>
245
+ </div>
246
+ <div class="p-4 border-t">
247
+ <h3 class="font-medium mb-2">Предстоящие ТО</h3>
248
+ <div class="space-y-3" id="upcomingMaintenance">
249
+ <!-- Upcoming maintenance items will be added here by JavaScript -->
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+
255
+ <!-- Charts -->
256
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
257
+ <div class="bg-white rounded-lg shadow p-6">
258
+ <div class="flex justify-between items-center mb-4">
259
+ <h2 class="text-lg font-semibold">Статистика ТО по месяцам</h2>
260
+ <select class="border rounded-lg px-3 py-1 text-sm">
261
+ <option>2025</option>
262
+ <option>2024</option>
263
+ <option>2023</option>
264
+ <option>2022</option>
265
+ <option>2021</option>
266
+ </select>
267
+ </div>
268
+ <canvas id="maintenanceChart" height="250"></canvas>
269
+ </div>
270
+ <div class="bg-white rounded-lg shadow p-6">
271
+ <div class="flex justify-between items-center mb-4">
272
+ <h2 class="text-lg font-semibold">Статус оборудования</h2>
273
+ </div>
274
+ <canvas id="equipmentStatusChart" height="250"></canvas>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+
281
+ <!-- Add Equipment Modal -->
282
+ <div id="addEquipmentModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
283
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl">
284
+ <div class="p-4 border-b flex justify-between items-center">
285
+ <h3 class="text-lg font-semibold">Добавить новое оборудование</h3>
286
+ <button onclick="closeAddEquipmentModal()" class="text-gray-500 hover:text-gray-700">
287
+ <i class="fas fa-times"></i>
288
+ </button>
289
+ </div>
290
+ <div class="p-6">
291
+ <form id="equipmentForm">
292
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
293
+ <div>
294
+ <label class="block text-sm font-medium text-gray-700 mb-1">Название оборудования</label>
295
+ <input type="text" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
296
+ </div>
297
+ <div>
298
+ <label class="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
299
+ <select class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
300
+ <option value="">Выберите тип</option>
301
+ <option>Станок</option>
302
+ <option>Конвейер</option>
303
+ <option>Компрессор</option>
304
+ <option>Генератор</option>
305
+ <option>Насос</option>
306
+ </select>
307
+ </div>
308
+ <div>
309
+ <label class="block text-sm font-medium text-gray-700 mb-1">Серийный номер</label>
310
+ <input type="text" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
311
+ </div>
312
+ <div>
313
+ <label class="block text-sm font-medium text-gray-700 mb-1">Инвентарный номер</label>
314
+ <input type="text" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
315
+ </div>
316
+ <div>
317
+ <label class="block text-sm font-medium text-gray-700 mb-1">Дата ввода в эксплуатацию</label>
318
+ <input type="date" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
319
+ </div>
320
+ <div>
321
+ <label class="block text-sm font-medium text-gray-700 mb-1">Периодичность ТО (дни)</label>
322
+ <input type="number" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" value="30" required>
323
+ </div>
324
+ <div class="md:col-span-2">
325
+ <label class="block text-sm font-medium text-gray-700 mb-1">Описание</label>
326
+ <textarea class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" rows="3"></textarea>
327
+ </div>
328
+ </div>
329
+ </form>
330
+ </div>
331
+ <div class="p-4 border-t flex justify-end space-x-3">
332
+ <button onclick="closeAddEquipmentModal()" class="px-4 py-2 border rounded-lg hover:bg-gray-50">Отмена</button>
333
+ <button onclick="saveEquipment()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Сохранить</button>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <!-- Edit Equipment Modal -->
339
+ <div id="editEquipmentModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
340
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl">
341
+ <div class="p-4 border-b flex justify-between items-center">
342
+ <h3 class="text-lg font-semibold">Редактировать оборудование</h3>
343
+ <button onclick="closeEditEquipmentModal()" class="text-gray-500 hover:text-gray-700">
344
+ <i class="fas fa-times"></i>
345
+ </button>
346
+ </div>
347
+ <div class="p-6">
348
+ <form id="editEquipmentForm">
349
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
350
+ <div>
351
+ <label class="block text-sm font-medium text-gray-700 mb-1">Название оборудования</label>
352
+ <input type="text" id="editName" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
353
+ </div>
354
+ <div>
355
+ <label class="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
356
+ <select id="editType" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
357
+ <option value="">Выберите тип</option>
358
+ <option>Станок</option>
359
+ <option>Конвейер</option>
360
+ <option>Компрессор</option>
361
+ <option>Генератор</option>
362
+ <option>Насос</option>
363
+ </select>
364
+ </div>
365
+ <div>
366
+ <label class="block text-sm font-medium text-gray-700 mb-1">Серийный номер</label>
367
+ <input type="text" id="editSerial" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
368
+ </div>
369
+ <div>
370
+ <label class="block text-sm font-medium text-gray-700 mb-1">Инвентарный номер</label>
371
+ <input type="text" id="editInventory" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
372
+ </div>
373
+ <div>
374
+ <label class="block text-sm font-medium text-gray-700 mb-1">Дата ввода в эксплуатацию</label>
375
+ <input type="date" id="editCommissionDate" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
376
+ </div>
377
+ <div>
378
+ <label class="block text-sm font-medium text-gray-700 mb-1">Периодичность ТО (дни)</label>
379
+ <input type="number" id="editMaintenanceInterval" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required>
380
+ </div>
381
+ <div class="md:col-span-2">
382
+ <label class="block text-sm font-medium text-gray-700 mb-1">Описание</label>
383
+ <textarea id="editDescription" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" rows="3"></textarea>
384
+ </div>
385
+ </div>
386
+ </form>
387
+ </div>
388
+ <div class="p-4 border-t flex justify-end space-x-3">
389
+ <button onclick="closeEditEquipmentModal()" class="px-4 py-2 border rounded-lg hover:bg-gray-50">Отмена</button>
390
+ <button onclick="updateEquipment()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Сохранить изменения</button>
391
+ </div>
392
+ </div>
393
+ </div>
394
+
395
+ <!-- Maintenance Details Modal -->
396
+ <div id="maintenanceModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
397
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl">
398
+ <div class="p-4 border-b flex justify-between items-center">
399
+ <h3 class="text-lg font-semibold">Техническое обслуживание</h3>
400
+ <button onclick="closeMaintenanceModal()" class="text-gray-500 hover:text-gray-700">
401
+ <i class="fas fa-times"></i>
402
+ </button>
403
+ </div>
404
+ <div class="p-6">
405
+ <div class="mb-6">
406
+ <h4 class="font-medium text-lg mb-2" id="maintenanceEquipmentName">Токарный станок CNC-2000</h4>
407
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
408
+ <div>
409
+ <p class="text-sm text-gray-500">Дата последнего ТО</p>
410
+ <p class="font-medium" id="lastMaintenanceDate">15.05.2023</p>
411
+ </div>
412
+ <div>
413
+ <p class="text-sm text-gray-500">Следующее ТО</p>
414
+ <p class="font-medium" id="nextMaintenanceDate">15.06.2023</p>
415
+ </div>
416
+ <div>
417
+ <p class="text-sm text-gray-500">Статус</p>
418
+ <p class="font-medium">
419
+ <span id="maintenanceStatus" class="px-2 py-1 rounded-full text-xs font-semibold bg-yellow-100 text-yellow-800">Запланировано</span>
420
+ </p>
421
+ </div>
422
+ <div>
423
+ <p class="text-sm text-gray-500">Ответственный</p>
424
+ <p class="font-medium" id="maintenanceResponsible">Иванов А.П.</p>
425
+ </div>
426
+ </div>
427
+ </div>
428
+ <div class="mb-6">
429
+ <h4 class="font-medium mb-3">Запланировать ТО</h4>
430
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
431
+ <div>
432
+ <label class="block text-sm font-medium text-gray-700 mb-1">Дата ТО</label>
433
+ <input type="date" id="maintenanceDate" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
434
+ </div>
435
+ <div>
436
+ <label class="block text-sm font-medium text-gray-700 mb-1">Ответственный</label>
437
+ <select id="maintenanceResponsibleSelect" class="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
438
+ <option>Иванов А.П.</option>
439
+ <option>Петров И.С.</option>
440
+ <option>Сидоров В.М.</option>
441
+ <option>Кузнецова Е.В.</option>
442
+ </select>
443
+ </div>
444
+ </div>
445
+ </div>
446
+ <div>
447
+ <h4 class="font-medium mb-3">Выполненные работы</h4>
448
+ <div class="space-y-4" id="maintenanceTasks">
449
+ <!-- Maintenance tasks will be added here by JavaScript -->
450
+ </div>
451
+ <button onclick="addMaintenanceTask()" class="mt-4 flex items-center text-blue-600 hover:text-blue-800">
452
+ <i class="fas fa-plus mr-2"></i> Добавить работу
453
+ </button>
454
+ </div>
455
+ </div>
456
+ <div class="p-4 border-t flex justify-between">
457
+ <button onclick="markMaintenanceAsCompleted()" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700">
458
+ <i class="fas fa-check mr-2"></i> Завершить ТО
459
+ </button>
460
+ <div class="flex space-x-3">
461
+ <button onclick="closeMaintenanceModal()" class="px-4 py-2 border rounded-lg hover:bg-gray-50">Отмена</button>
462
+ <button onclick="saveMaintenance()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Сохранить</button>
463
+ </div>
464
+ </div>
465
+ </div>
466
+ </div>
467
+
468
+ <script>
469
+ // Sample data for equipment
470
+ const equipmentData = [
471
+ {
472
+ id: 1,
473
+ name: "Токарный станок CNC-2000",
474
+ type: "Станок",
475
+ serial: "CNC2000-001",
476
+ inventory: "INV-1001",
477
+ status: "active",
478
+ lastMaintenance: "2023-05-15",
479
+ nextMaintenance: "2023-06-15",
480
+ maintenanceInterval: 30,
481
+ description: "Токарный станок с ЧПУ, производитель XYZ Corp."
482
+ },
483
+ {
484
+ id: 2,
485
+ name: "Конвейерная линия A-12",
486
+ type: "Конвейер",
487
+ serial: "CONV-A12-045",
488
+ inventory: "INV-1042",
489
+ status: "active",
490
+ lastMaintenance: "2023-05-20",
491
+ nextMaintenance: "2023-06-20",
492
+ maintenanceInterval: 30,
493
+ description: "Конвейерная линия для сборки продукции"
494
+ },
495
+ {
496
+ id: 3,
497
+ name: "Воздушный компрессор V-50",
498
+ type: "Компрессор",
499
+ serial: "COMP-V50-112",
500
+ inventory: "INV-1078",
501
+ status: "maintenance",
502
+ lastMaintenance: "2023-04-10",
503
+ nextMaintenance: "2023-06-10",
504
+ maintenanceInterval: 60,
505
+ description: "Промышленный воздушный компрессор"
506
+ },
507
+ {
508
+ id: 4,
509
+ name: "Генератор DG-5000",
510
+ type: "Генератор",
511
+ serial: "GEN-DG5000-008",
512
+ inventory: "INV-1123",
513
+ status: "active",
514
+ lastMaintenance: "2023-05-01",
515
+ nextMaintenance: "2023-08-01",
516
+ maintenanceInterval: 90,
517
+ description: "Дизельный генератор резервного питания"
518
+ },
519
+ {
520
+ id: 5,
521
+ name: "Насосный агрегат NP-200",
522
+ type: "Насос",
523
+ serial: "PUMP-NP200-156",
524
+ inventory: "INV-1156",
525
+ status: "repair",
526
+ lastMaintenance: "2023-03-15",
527
+ nextMaintenance: "2023-06-15",
528
+ maintenanceInterval: 90,
529
+ description: "Центробежный насос для перекачки жидкостей"
530
+ }
531
+ ];
532
+
533
+ // Sample data for maintenance events
534
+ const maintenanceEvents = [
535
+ { equipmentId: 1, date: "2023-06-15", status: "planned", responsible: "Иванов А.П." },
536
+ { equipmentId: 2, date: "2023-06-20", status: "planned", responsible: "Петров И.С." },
537
+ { equipmentId: 3, date: "2023-06-10", status: "overdue", responsible: "Сидоров В.М." },
538
+ { equipmentId: 4, date: "2023-08-01", status: "planned", responsible: "Кузнецова Е.В." },
539
+ { equipmentId: 5, date: "2023-06-15", status: "planned", responsible: "Иванов А.П." },
540
+ { equipmentId: 1, date: "2023-05-15", status: "completed", responsible: "Иванов А.П." },
541
+ { equipmentId: 2, date: "2023-05-20", status: "completed", responsible: "Петров И.С." },
542
+ { equipmentId: 3, date: "2023-04-10", status: "completed", responsible: "Сидоров В.М." },
543
+ { equipmentId: 4, date: "2023-05-01", status: "completed", responsible: "Кузнецова Е.В." },
544
+ { equipmentId: 5, date: "2023-03-15", status: "completed", responsible: "Иванов А.П." }
545
+ ];
546
+
547
+ // Sample data for maintenance tasks
548
+ const maintenanceTasks = {
549
+ 1: [
550
+ { id: 1, description: "Проверка и регулировка ЧПУ", completed: true },
551
+ { id: 2, description: "Смазка направляющих", completed: true },
552
+ { id: 3, description: "Замена фильтров", completed: true }
553
+ ],
554
+ 2: [
555
+ { id: 1, description: "Проверка натяжения ленты", completed: true },
556
+ { id: 2, description: "Смазка роликов", completed: true }
557
+ ],
558
+ 3: [
559
+ { id: 1, description: "Замена масла", completed: true },
560
+ { id: 2, description: "Проверка давления", completed: false }
561
+ ]
562
+ };
563
+
564
+ // Current month and year for calendar
565
+ const currentDate = new Date();
566
+ const currentMonth = currentDate.getMonth();
567
+ const currentYear = currentDate.getFullYear();
568
+
569
+ // DOM elements
570
+ const equipmentTableBody = document.getElementById('equipmentTableBody');
571
+ const calendarDays = document.getElementById('calendarDays');
572
+ const upcomingMaintenance = document.getElementById('upcomingMaintenance');
573
+ let currentEditingEquipmentId = null;
574
+
575
+ // Initialize the application
576
+ function init() {
577
+ renderEquipmentTable();
578
+ renderCalendar();
579
+ renderUpcomingMaintenance();
580
+ initCharts();
581
+ setupEventListeners();
582
+ }
583
+
584
+ // Render equipment table
585
+ function renderEquipmentTable() {
586
+ equipmentTableBody.innerHTML = '';
587
+
588
+ equipmentData.forEach(equipment => {
589
+ const row = document.createElement('tr');
590
+
591
+ // Determine status badge color
592
+ let statusBadge = '';
593
+ if (equipment.status === 'active') {
594
+ statusBadge = '<span class="px-2 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">Активно</span>';
595
+ } else if (equipment.status === 'maintenance') {
596
+ statusBadge = '<span class="px-2 py-1 rounded-full text-xs font-semibold bg-yellow-100 text-yellow-800">ТО</span>';
597
+ } else if (equipment.status === 'repair') {
598
+ statusBadge = '<span class="px-2 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">Ремонт</span>';
599
+ }
600
+
601
+ // Format dates
602
+ const lastMaintenanceDate = formatDate(equipment.lastMaintenance);
603
+ const nextMaintenanceDate = formatDate(equipment.nextMaintenance);
604
+
605
+ row.innerHTML = `
606
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${equipment.id}</td>
607
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${equipment.name}</td>
608
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${equipment.type}</td>
609
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${statusBadge}</td>
610
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${lastMaintenanceDate}</td>
611
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
612
+ <button onclick="openMaintenanceModal(${equipment.id})" class="text-blue-600 hover:text-blue-900 mr-3">
613
+ <i class="fas fa-calendar-alt"></i>
614
+ </button>
615
+ <button onclick="openEditEquipmentModal(${equipment.id})" class="text-yellow-600 hover:text-yellow-900 mr-3">
616
+ <i class="fas fa-edit"></i>
617
+ </button>
618
+ <button onclick="deleteEquipment(${equipment.id})" class="text-red-600 hover:text-red-900">
619
+ <i class="fas fa-trash"></i>
620
+ </button>
621
+ </td>
622
+ `;
623
+
624
+ equipmentTableBody.appendChild(row);
625
+ });
626
+ }
627
+
628
+ // Render calendar
629
+ function renderCalendar() {
630
+ calendarDays.innerHTML = '';
631
+
632
+ // Get first day of month and total days in month
633
+ const firstDay = new Date(currentYear, currentMonth, 1).getDay();
634
+ const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
635
+
636
+ // Adjust for Monday as first day (0 = Sunday, 1 = Monday, etc.)
637
+ const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1;
638
+
639
+ // Add empty cells for days before the first day of the month
640
+ for (let i = 0; i < adjustedFirstDay; i++) {
641
+ const emptyCell = document.createElement('div');
642
+ emptyCell.className = 'h-10';
643
+ calendarDays.appendChild(emptyCell);
644
+ }
645
+
646
+ // Add cells for each day of the month
647
+ for (let day = 1; day <= daysInMonth; day++) {
648
+ const dateStr = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
649
+ const cell = document.createElement('div');
650
+ cell.className = 'h-10 flex items-center justify-center border rounded cursor-pointer calendar-day';
651
+
652
+ // Check if this day has any maintenance events
653
+ const events = maintenanceEvents.filter(event => event.date === dateStr);
654
+
655
+ if (events.length > 0) {
656
+ const statuses = events.map(event => event.status);
657
+
658
+ if (statuses.includes('overdue')) {
659
+ cell.classList.add('event-due');
660
+ } else if (statuses.includes('completed')) {
661
+ cell.classList.add('event-completed');
662
+ } else {
663
+ cell.classList.add('has-event');
664
+ }
665
+
666
+ // Add tooltip with equipment names
667
+ const equipmentNames = events.map(event => {
668
+ const equipment = equipmentData.find(e => e.id === event.equipmentId);
669
+ return equipment ? equipment.name : '';
670
+ }).filter(name => name !== '');
671
+
672
+ cell.setAttribute('title', equipmentNames.join('\n'));
673
+ }
674
+
675
+ cell.textContent = day;
676
+ cell.onclick = () => showDayEvents(day);
677
+ calendarDays.appendChild(cell);
678
+ }
679
+ }
680
+
681
+ // Render upcoming maintenance
682
+ function renderUpcomingMaintenance() {
683
+ upcomingMaintenance.innerHTML = '';
684
+
685
+ // Get today's date in YYYY-MM-DD format
686
+ const today = new Date().toISOString().split('T')[0];
687
+
688
+ // Filter maintenance events that are upcoming or overdue
689
+ const upcomingEvents = maintenanceEvents.filter(event => {
690
+ return event.status !== 'completed' && (event.date >= today || event.status === 'overdue');
691
+ }).sort((a, b) => new Date(a.date) - new Date(b.date));
692
+
693
+ // Limit to 5 events
694
+ const eventsToShow = upcomingEvents.slice(0, 5);
695
+
696
+ eventsToShow.forEach(event => {
697
+ const equipment = equipmentData.find(e => e.id === event.equipmentId);
698
+ if (!equipment) return;
699
+
700
+ const eventDate = new Date(event.date);
701
+ const today = new Date();
702
+ const diffTime = eventDate - today;
703
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
704
+
705
+ let daysText = '';
706
+ if (diffDays < 0) {
707
+ daysText = `<span class="text-red-500">Просрочено на ${Math.abs(diffDays)} дн.</span>`;
708
+ } else if (diffDays === 0) {
709
+ daysText = '<span class="text-yellow-600">Сегодня</span>';
710
+ } else {
711
+ daysText = `Через ${diffDays} дн.`;
712
+ }
713
+
714
+ const eventElement = document.createElement('div');
715
+ eventElement.className = 'p-3 border rounded-lg hover:bg-gray-50 cursor-pointer';
716
+ eventElement.onclick = () => openMaintenanceModal(event.equipmentId);
717
+
718
+ eventElement.innerHTML = `
719
+ <div class="flex justify-between items-start">
720
+ <div>
721
+ <h4 class="font-medium">${equipment.name}</h4>
722
+ <p class="text-sm text-gray-500">${equipment.type}</p>
723
+ </div>
724
+ <span class="text-sm">${daysText}</span>
725
+ </div>
726
+ <div class="mt-2 flex justify-between items-center">
727
+ <span class="text-sm">${formatDate(event.date)}</span>
728
+ <span class="text-sm">${event.responsible}</span>
729
+ </div>
730
+ `;
731
+
732
+ upcomingMaintenance.appendChild(eventElement);
733
+ });
734
+
735
+ if (eventsToShow.length === 0) {
736
+ upcomingMaintenance.innerHTML = '<p class="text-gray-500 text-center py-4">Нет предстоящих ТО</p>';
737
+ }
738
+ }
739
+
740
+ // Initialize charts
741
+ function initCharts() {
742
+ // Maintenance by month chart
743
+ const maintenanceCtx = document.getElementById('maintenanceChart').getContext('2d');
744
+ new Chart(maintenanceCtx, {
745
+ type: 'bar',
746
+ data: {
747
+ labels: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
748
+ datasets: [
749
+ {
750
+ label: 'Запланировано',
751
+ data: [5, 7, 6, 8, 10, 12, 8, 7, 6, 9, 7, 5],
752
+ backgroundColor: '#3B82F6',
753
+ },
754
+ {
755
+ label: 'Выполнено',
756
+ data: [5, 6, 5, 7, 9, 8, 6, 5, 4, 7, 5, 4],
757
+ backgroundColor: '#10B981',
758
+ },
759
+ {
760
+ label: 'Просрочено',
761
+ data: [0, 1, 1, 1, 1, 3, 2, 2, 2, 2, 2, 1],
762
+ backgroundColor: '#EF4444',
763
+ }
764
+ ]
765
+ },
766
+ options: {
767
+ responsive: true,
768
+ plugins: {
769
+ legend: {
770
+ position: 'top',
771
+ },
772
+ },
773
+ scales: {
774
+ x: {
775
+ stacked: false,
776
+ },
777
+ y: {
778
+ stacked: false,
779
+ beginAtZero: true
780
+ }
781
+ }
782
+ }
783
+ });
784
+
785
+ // Equipment status chart
786
+ const statusCtx = document.getElementById('equipmentStatusChart').getContext('2d');
787
+ new Chart(statusCtx, {
788
+ type: 'doughnut',
789
+ data: {
790
+ labels: ['Активно', 'На ТО', 'В ремонте', 'Резерв'],
791
+ datasets: [{
792
+ data: [32, 5, 3, 2],
793
+ backgroundColor: [
794
+ '#10B981',
795
+ '#F59E0B',
796
+ '#EF4444',
797
+ '#6B7280'
798
+ ],
799
+ borderWidth: 0
800
+ }]
801
+ },
802
+ options: {
803
+ responsive: true,
804
+ plugins: {
805
+ legend: {
806
+ position: 'right',
807
+ }
808
+ }
809
+ }
810
+ });
811
+ }
812
+
813
+ // Setup event listeners
814
+ function setupEventListeners() {
815
+ // Toggle sidebar
816
+ document.getElementById('toggleSidebar').addEventListener('click', () => {
817
+ document.querySelector('.sidebar').classList.toggle('collapsed');
818
+ document.querySelector('.main-content').classList.toggle('ml-16');
819
+ document.querySelector('.main-content').classList.toggle('ml-64');
820
+ });
821
+ }
822
+
823
+ // Format date to DD.MM.YYYY
824
+ function formatDate(dateString) {
825
+ if (!dateString) return '';
826
+ const date = new Date(dateString);
827
+ const day = String(date.getDate()).padStart(2, '0');
828
+ const month = String(date.getMonth() + 1).padStart(2, '0');
829
+ const year = date.getFullYear();
830
+ return `${day}.${month}.${year}`;
831
+ }
832
+
833
+ // Show events for a specific day
834
+ function showDayEvents(day) {
835
+ const dateStr = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
836
+ const events = maintenanceEvents.filter(event => event.date === dateStr);
837
+
838
+ if (events.length === 0) {
839
+ Swal.fire({
840
+ title: `${day} ${getMonthName(currentMonth)} ${currentYear}`,
841
+ text: 'На этот день не запланировано ТО',
842
+ icon: 'info'
843
+ });
844
+ return;
845
+ }
846
+
847
+ let html = `<h3 class="font-bold mb-2">${day} ${getMonthName(currentMonth)} ${currentYear}</h3>`;
848
+ html += '<div class="space-y-2">';
849
+
850
+ events.forEach(event => {
851
+ const equipment = equipmentData.find(e => e.id === event.equipmentId);
852
+ if (!equipment) return;
853
+
854
+ let statusBadge = '';
855
+ if (event.status === 'planned') {
856
+ statusBadge = '<span class="px-2 py-1 rounded-full text-xs font-semibold bg-blue-100 text-blue-800">Запланировано</span>';
857
+ } else if (event.status === 'overdue') {
858
+ statusBadge = '<span class="px-2 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">Просрочено</span>';
859
+ } else if (event.status === 'completed') {
860
+ statusBadge = '<span class="px-2 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">Выполнено</span>';
861
+ }
862
+
863
+ html += `
864
+ <div class="p-3 border rounded-lg">
865
+ <div class="flex justify-between items-center mb-1">
866
+ <h4 class="font-medium">${equipment.name}</h4>
867
+ ${statusBadge}
868
+ </div>
869
+ <p class="text-sm text-gray-600">${equipment.type}</p>
870
+ <p class="text-sm mt-1">Ответственный: ${event.responsible}</p>
871
+ <button onclick="openMaintenanceModal(${equipment.id})" class="mt-2 text-sm text-blue-600 hover:text-blue-800">
872
+ Подробнее <i class="fas fa-chevron-right ml-1"></i>
873
+ </button>
874
+ </div>
875
+ `;
876
+ });
877
+
878
+ html += '</div>';
879
+
880
+ Swal.fire({
881
+ html: html,
882
+ showConfirmButton: false,
883
+ width: '500px'
884
+ });
885
+ }
886
+
887
+ // Get month name
888
+ function getMonthName(monthIndex) {
889
+ const months = [
890
+ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
891
+ 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'
892
+ ];
893
+ return months[monthIndex];
894
+ }
895
+
896
+ // Open add equipment modal
897
+ function openAddEquipmentModal() {
898
+ document.getElementById('addEquipmentModal').classList.remove('hidden');
899
+ }
900
+
901
+ // Close add equipment modal
902
+ function closeAddEquipmentModal() {
903
+ document.getElementById('addEquipmentModal').classList.add('hidden');
904
+ document.getElementById('equipmentForm').reset();
905
+ }
906
+
907
+ // Save new equipment
908
+ function saveEquipment() {
909
+ const form = document.getElementById('equipmentForm');
910
+ const formData = new FormData(form);
911
+
912
+ // Validate form
913
+ let isValid = true;
914
+ form.querySelectorAll('[required]').forEach(input => {
915
+ if (!input.value) {
916
+ input.classList.add('border-red-500');
917
+ isValid = false;
918
+ } else {
919
+ input.classList.remove('border-red-500');
920
+ }
921
+ });
922
+
923
+ if (!isValid) {
924
+ Swal.fire({
925
+ title: 'Ошибка',
926
+ text: 'Пожалуйста, заполните все обязательные поля',
927
+ icon: 'error'
928
+ });
929
+ return;
930
+ }
931
+
932
+ // Create new equipment object
933
+ const newEquipment = {
934
+ id: equipmentData.length + 1,
935
+ name: formData.get('name'),
936
+ type: formData.get('type'),
937
+ serial: formData.get('serial'),
938
+ inventory: formData.get('inventory'),
939
+ status: 'active',
940
+ lastMaintenance: '',
941
+ nextMaintenance: calculateNextMaintenanceDate(formData.get('commissionDate'), formData.get('maintenanceInterval')),
942
+ maintenanceInterval: parseInt(formData.get('maintenanceInterval')),
943
+ description: formData.get('description')
944
+ };
945
+
946
+ // Add to equipment data
947
+ equipmentData.push(newEquipment);
948
+
949
+ // Close modal and refresh table
950
+ closeAddEquipmentModal();
951
+ renderEquipmentTable();
952
+
953
+ Swal.fire({
954
+ title: 'Успешно',
955
+ text: 'Оборудование добавлено',
956
+ icon: 'success'
957
+ });
958
+ }
959
+
960
+ // Calculate next maintenance date
961
+ function calculateNextMaintenanceDate(commissionDate, intervalDays) {
962
+ if (!commissionDate) return '';
963
+
964
+ const intervalMs = parseInt(intervalDays) * 24 * 60 * 60 * 1000;
965
+ const nextDate = new Date(new Date(commissionDate).getTime() + intervalMs);
966
+ return nextDate.toISOString().split('T')[0];
967
+ }
968
+
969
+ // Open edit equipment modal
970
+ function openEditEquipmentModal(id) {
971
+ const equipment = equipmentData.find(e => e.id === id);
972
+ if (!equipment) return;
973
+
974
+ currentEditingEquipmentId = id;
975
+
976
+ document.getElementById('editName').value = equipment.name;
977
+ document.getElementById('editType').value = equipment.type;
978
+ document.getElementById('editSerial').value = equipment.serial;
979
+ document.getElementById('editInventory').value = equipment.inventory;
980
+ document.getElementById('editCommissionDate').value = equipment.commissionDate || '';
981
+ document.getElementById('editMaintenanceInterval').value = equipment.maintenanceInterval;
982
+ document.getElementById('editDescription').value = equipment.description || '';
983
+
984
+ document.getElementById('editEquipmentModal').classList.remove('hidden');
985
+ }
986
+
987
+ // Close edit equipment modal
988
+ function closeEditEquipmentModal() {
989
+ document.getElementById('editEquipmentModal').classList.add('hidden');
990
+ currentEditingEquipmentId = null;
991
+ }
992
+
993
+ // Update equipment
994
+ function updateEquipment() {
995
+ const equipment = equipmentData.find(e => e.id === currentEditingEquipmentId);
996
+ if (!equipment) return;
997
+
998
+ equipment.name = document.getElementById('editName').value;
999
+ equipment.type = document.getElementById('editType').value;
1000
+ equipment.serial = document.getElementById('editSerial').value;
1001
+ equipment.inventory = document.getElementById('editInventory').value;
1002
+ equipment.commissionDate = document.getElementById('editCommissionDate').value;
1003
+ equipment.maintenanceInterval = parseInt(document.getElementById('editMaintenanceInterval').value);
1004
+ equipment.description = document.getElementById('editDescription').value;
1005
+
1006
+ // Close modal and refresh table
1007
+ closeEditEquipmentModal();
1008
+ renderEquipmentTable();
1009
+
1010
+ Swal.fire({
1011
+ title: 'Успешно',
1012
+ text: 'Изменения сохранены',
1013
+ icon: 'success'
1014
+ });
1015
+ }
1016
+
1017
+ // Delete equipment
1018
+ function deleteEquipment(id) {
1019
+ Swal.fire({
1020
+ title: 'Удалить оборудование?',
1021
+ text: 'Вы уверены, что хотите удалить это оборудование? Это действие нельзя отменить.',
1022
+ icon: 'warning',
1023
+ showCancelButton: true,
1024
+ confirmButtonText: 'Да, удалить',
1025
+ cancelButtonText: 'Отмена'
1026
+ }).then((result) => {
1027
+ if (result.isConfirmed) {
1028
+ const index = equipmentData.findIndex(e => e.id === id);
1029
+ if (index !== -1) {
1030
+ equipmentData.splice(index, 1);
1031
+ renderEquipmentTable();
1032
+
1033
+ Swal.fire(
1034
+ 'Удалено!',
1035
+ 'Оборудование было удалено.',
1036
+ 'success'
1037
+ );
1038
+ }
1039
+ }
1040
+ });
1041
+ }
1042
+
1043
+ // Open maintenance modal
1044
+ function openMaintenanceModal(equipmentId) {
1045
+ const equipment = equipmentData.find(e => e.id === equipmentId);
1046
+ if (!equipment) return;
1047
+
1048
+ // Find current maintenance event
1049
+ const maintenanceEvent = maintenanceEvents.find(event =>
1050
+ event.equipmentId === equipmentId &&
1051
+ (event.status === 'planned' || event.status === 'overdue')
1052
+ );
1053
+
1054
+ // Set equipment info
1055
+ document.getElementById('maintenanceEquipmentName').textContent = equipment.name;
1056
+ document.getElementById('lastMaintenanceDate').textContent = formatDate(equipment.lastMaintenance);
1057
+ document.getElementById('nextMaintenanceDate').textContent = formatDate(equipment.nextMaintenance);
1058
+
1059
+ // Set status
1060
+ const statusElement = document.getElementById('maintenanceStatus');
1061
+ statusElement.textContent = maintenanceEvent ?
1062
+ (maintenanceEvent.status === 'overdue' ? 'Просрочено' : 'Запланировано') :
1063
+ 'Не запланировано';
1064
+
1065
+ statusElement.className = 'px-2 py-1 rounded-full text-xs font-semibold ' +
1066
+ (maintenanceEvent ?
1067
+ (maintenanceEvent.status === 'overdue' ? 'bg-red-100 text-red-800' : 'bg-blue-100 text-blue-800') :
1068
+ 'bg-gray-100 text-gray-800');
1069
+
1070
+ // Set responsible
1071
+ document.getElementById('maintenanceResponsible').textContent =
1072
+ maintenanceEvent ? maintenanceEvent.responsible : 'Не назначен';
1073
+
1074
+ if (maintenanceEvent) {
1075
+ document.getElementById('maintenanceDate').value = maintenanceEvent.date;
1076
+ document.getElementById('maintenanceResponsibleSelect').value = maintenanceEvent.responsible;
1077
+ } else {
1078
+ document.getElementById('maintenanceDate').value = equipment.nextMaintenance;
1079
+ document.getElementById('maintenanceResponsibleSelect').value = 'Иванов А.П.';
1080
+ }
1081
+
1082
+ // Render tasks
1083
+ renderMaintenanceTasks(equipmentId);
1084
+
1085
+ document.getElementById('maintenanceModal').classList.remove('hidden');
1086
+ }
1087
+
1088
+ // Close maintenance modal
1089
+ function closeMaintenanceModal() {
1090
+ document.getElementById('maintenanceModal').classList.add('hidden');
1091
+ }
1092
+
1093
+ // Render maintenance tasks
1094
+ function renderMaintenanceTasks(equipmentId) {
1095
+ const tasksContainer = document.getElementById('maintenanceTasks');
1096
+ tasksContainer.innerHTML = '';
1097
+
1098
+ const tasks = maintenanceTasks[equipmentId] || [];
1099
+
1100
+ tasks.forEach((task, index) => {
1101
+ const taskElement = document.createElement('div');
1102
+ taskElement.className = 'flex items-start';
1103
+
1104
+ taskElement.innerHTML = `
1105
+ <input type="checkbox" ${task.completed ? 'checked' : ''}
1106
+ onchange="toggleTaskCompletion(${equipmentId}, ${task.id})"
1107
+ class="mt-1 mr-3 h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500">
1108
+ <div class="flex-1">
1109
+ <p class="${task.completed ? 'line-through text-gray-500' : ''}">${task.description}</p>
1110
+ <button onclick="removeTask(${equipmentId}, ${task.id})" class="mt-1 text-sm text-red-600 hover:text-red-800">
1111
+ <i class="fas fa-trash mr-1"></i> Удалить
1112
+ </button>
1113
+ </div>
1114
+ `;
1115
+
1116
+ tasksContainer.appendChild(taskElement);
1117
+ });
1118
+
1119
+ if (tasks.length === 0) {
1120
+ tasksContainer.innerHTML = '<p class="text-gray-500 text-center py-4">Нет добавленных работ</p>';
1121
+ }
1122
+ }
1123
+
1124
+ // Add maintenance task
1125
+ function addMaintenanceTask() {
1126
+ const equipmentId = currentEditingEquipmentId;
1127
+ if (!equipmentId) return;
1128
+
1129
+ Swal.fire({
1130
+ title: 'Добавить работу',
1131
+ input: 'text',
1132
+ inputLabel: 'Описание работы',
1133
+ inputPlaceholder: 'Введите описание работы...',
1134
+ showCancelButton: true,
1135
+ confirmButtonText: 'Добавить',
1136
+ cancelButtonText: 'Отмена',
1137
+ inputValidator: (value) => {
1138
+ if (!value) {
1139
+ return 'Пожалуйста, введите описание работы';
1140
+ }
1141
+ }
1142
+ }).then((result) => {
1143
+ if (result.isConfirmed) {
1144
+ if (!maintenanceTasks[equipmentId]) {
1145
+ maintenanceTasks[equipmentId] = [];
1146
+ }
1147
+
1148
+ const newTask = {
1149
+ id: maintenanceTasks[equipmentId].length + 1,
1150
+ description: result.value,
1151
+ completed: false
1152
+ };
1153
+
1154
+ maintenanceTasks[equipmentId].push(newTask);
1155
+ renderMaintenanceTasks(equipmentId);
1156
+ }
1157
+ });
1158
+ }
1159
+
1160
+ // Remove task
1161
+ function removeTask(equipmentId, taskId) {
1162
+ if (!maintenanceTasks[equipmentId]) return;
1163
+
1164
+ const index = maintenanceTasks[equipmentId].findIndex(t => t.id === taskId);
1165
+ if (index !== -1) {
1166
+ maintenanceTasks[equipmentId].splice(index, 1);
1167
+ renderMaintenanceTasks(equipmentId);
1168
+ }
1169
+ }
1170
+
1171
+ // Toggle task completion
1172
+ function toggleTaskCompletion(equipmentId, taskId) {
1173
+ if (!maintenanceTasks[equipmentId]) return;
1174
+
1175
+ const task = maintenanceTasks[equipmentId].find(t => t.id === taskId);
1176
+ if (task) {
1177
+ task.completed = !task.completed;
1178
+ renderMaintenanceTasks(equipmentId);
1179
+ }
1180
+ }
1181
+
1182
+ // Save maintenance
1183
+ function saveMaintenance() {
1184
+ const equipmentId = currentEditingEquipmentId;
1185
+ if (!equipmentId) return;
1186
+
1187
+ const equipment = equipmentData.find(e => e.id === equipmentId);
1188
+ if (!equipment) return;
1189
+
1190
+ const date = document.getElementById('maintenanceDate').value;
1191
+ const responsible = document.getElementById('maintenanceResponsibleSelect').value;
1192
+
1193
+ if (!date) {
1194
+ Swal.fire({
1195
+ title: 'Ошибка',
1196
+ text: 'Пожалуйста, укажите дату ТО',
1197
+ icon: 'error'
1198
+ });
1199
+ return;
1200
+ }
1201
+
1202
+ // Find or create maintenance event
1203
+ let maintenanceEvent = maintenanceEvents.find(event =>
1204
+ event.equipmentId === equipmentId &&
1205
+ (event.status === 'planned' || event.status === 'overdue')
1206
+ );
1207
+
1208
+ if (maintenanceEvent) {
1209
+ maintenanceEvent.date = date;
1210
+ maintenanceEvent.responsible = responsible;
1211
+ } else {
1212
+ maintenanceEvent = {
1213
+ equipmentId,
1214
+ date,
1215
+ status: 'planned',
1216
+ responsible
1217
+ };
1218
+ maintenanceEvents.push(maintenanceEvent);
1219
+ }
1220
+
1221
+ // Update equipment's next maintenance date
1222
+ equipment.nextMaintenance = date;
1223
+
1224
+ closeMaintenanceModal();
1225
+ renderEquipmentTable();
1226
+ renderCalendar();
1227
+ renderUpcomingMaintenance();
1228
+
1229
+ Swal.fire({
1230
+ title: 'Успешно',
1231
+ text: 'График ТО обновлен',
1232
+ icon: 'success'
1233
+ });
1234
+ }
1235
+
1236
+ // Mark maintenance as completed
1237
+ function markMaintenanceAsCompleted() {
1238
+ const equipmentId = currentEditingEquipmentId;
1239
+ if (!equipmentId) return;
1240
+
1241
+ const equipment = equipmentData.find(e => e.id === equipmentId);
1242
+ if (!equipment) return;
1243
+
1244
+ // Find current maintenance event
1245
+ const maintenanceEvent = maintenanceEvents.find(event =>
1246
+ event.equipmentId === equipmentId &&
1247
+ (event.status === 'planned' || event.status === 'overdue')
1248
+ );
1249
+
1250
+ if (maintenanceEvent) {
1251
+ maintenanceEvent.status = 'completed';
1252
+
1253
+ // Update equipment's last maintenance date
1254
+ equipment.lastMaintenance = maintenanceEvent.date;
1255
+
1256
+ // Calculate next maintenance date
1257
+ const nextDate = new Date(new Date(maintenanceEvent.date).getTime() +
1258
+ equipment.maintenanceInterval * 24 * 60 * 60 * 1000);
1259
+ equipment.nextMaintenance = nextDate.toISOString().split('T')[0];
1260
+
1261
+ // Add new planned maintenance event
1262
+ maintenanceEvents.push({
1263
+ equipmentId,
1264
+ date: equipment.nextMaintenance,
1265
+ status: 'planned',
1266
+ responsible: maintenanceEvent.responsible
1267
+ });
1268
+
1269
+ closeMaintenanceModal();
1270
+ renderEquipmentTable();
1271
+ renderCalendar();
1272
+ renderUpcomingMaintenance();
1273
+
1274
+ Swal.fire({
1275
+ title: 'Успешно',
1276
+ text: 'ТО отмечено как выполненное',
1277
+ icon: 'success'
1278
+ });
1279
+ }
1280
+ }
1281
+
1282
+ // Initialize the app when DOM is loaded
1283
+ document.addEventListener('DOMContentLoaded', init);
1284
+ </script>
1285
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Karmashek/p4-general" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1286
  </html>
prompts.txt ADDED
File without changes