Karmashek commited on
Commit
41e5b0b
·
verified ·
1 Parent(s): bf607ed

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +929 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Ver1
3
- emoji: 🦀
4
- colorFrom: green
5
- colorTo: gray
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: ver1
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: green
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,929 @@
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
+ import { useState, useEffect, useRef } from 'react';
2
+
3
+ export default function App() {
4
+ // State for active tab and sidebar
5
+ const [activeTab, setActiveTab] = useState('dashboard');
6
+ const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
7
+ const [equipmentList, setEquipmentList] = useState([
8
+ {
9
+ id: 1,
10
+ name: "Токарный станок CNC-2000",
11
+ type: "Станок",
12
+ serial: "CNC2000-001",
13
+ inventory: "INV-1001",
14
+ status: "active",
15
+ lastMaintenance: "2023-05-15",
16
+ nextMaintenance: "2023-06-15",
17
+ maintenanceInterval: 30,
18
+ description: "Токарный станок с ЧПУ, производитель XYZ Corp."
19
+ },
20
+ {
21
+ id: 2,
22
+ name: "Конвейерная линия A-12",
23
+ type: "Конвейер",
24
+ serial: "CONV-A12-045",
25
+ inventory: "INV-1042",
26
+ status: "active",
27
+ lastMaintenance: "2023-05-20",
28
+ nextMaintenance: "2023-06-20",
29
+ maintenanceInterval: 30,
30
+ description: "Конвейерная линия для сборки продукции"
31
+ },
32
+ {
33
+ id: 3,
34
+ name: "Воздушный компрессор V-50",
35
+ type: "Компрессор",
36
+ serial: "COMP-V50-112",
37
+ inventory: "INV-1078",
38
+ status: "maintenance",
39
+ lastMaintenance: "2023-04-10",
40
+ nextMaintenance: "2023-06-10",
41
+ maintenanceInterval: 60,
42
+ description: "Промышленный воздушный компрессор"
43
+ }
44
+ ]);
45
+
46
+ const [users, setUsers] = useState([
47
+ { id: 1, name: "Иван Петров", role: "admin", email: "ivan@example.com" },
48
+ { id: 2, name: "Петр Смирнов", role: "engineer", email: "petr@example.com" },
49
+ { id: 3, name: "Анна Иванова", role: "viewer", email: "anna@example.com" }
50
+ ]);
51
+
52
+ const [maintenanceEvents, setMaintenanceEvents] = useState([
53
+ { equipmentId: 1, date: "2023-06-15", status: "planned", responsible: "Иван Петров" },
54
+ { equipmentId: 2, date: "2023-06-20", status: "planned", responsible: "Петр Смирнов" },
55
+ { equipmentId: 3, date: "2023-06-10", status: "overdue", responsible: "Анна Иванова" }
56
+ ]);
57
+
58
+ const [newEquipment, setNewEquipment] = useState({
59
+ name: '',
60
+ type: '',
61
+ serial: '',
62
+ inventory: '',
63
+ commissionDate: '',
64
+ maintenanceInterval: 30,
65
+ description: ''
66
+ });
67
+
68
+ const [editEquipment, setEditEquipment] = useState(null);
69
+ const [showAddModal, setShowAddModal] = useState(false);
70
+ const [showEditModal, setShowEditModal] = useState(false);
71
+
72
+ // Refs for form validation
73
+ const formRef = useRef(null);
74
+ const editFormRef = useRef(null);
75
+
76
+ // Format date to DD.MM.YYYY
77
+ const formatDate = (dateString) => {
78
+ if (!dateString) return '';
79
+ const date = new Date(dateString);
80
+ const day = String(date.getDate()).padStart(2, '0');
81
+ const month = String(date.getMonth() + 1).padStart(2, '0');
82
+ const year = date.getFullYear();
83
+ return `${day}.${month}.${year}`;
84
+ };
85
+
86
+ // Calculate next maintenance date
87
+ const calculateNextMaintenanceDate = (commissionDate, intervalDays) => {
88
+ if (!commissionDate) return '';
89
+ const intervalMs = parseInt(intervalDays) * 24 * 60 * 60 * 1000;
90
+ const nextDate = new Date(new Date(commissionDate).getTime() + intervalMs);
91
+ return nextDate.toISOString().split('T')[0];
92
+ };
93
+
94
+ // Handle add equipment modal
95
+ const openAddModal = () => {
96
+ setNewEquipment({
97
+ name: '',
98
+ type: '',
99
+ serial: '',
100
+ inventory: '',
101
+ commissionDate: '',
102
+ maintenanceInterval: 30,
103
+ description: ''
104
+ });
105
+ setShowAddModal(true);
106
+ };
107
+
108
+ const closeAddModal = () => {
109
+ setShowAddModal(false);
110
+ setNewEquipment({
111
+ name: '',
112
+ type: '',
113
+ serial: '',
114
+ inventory: '',
115
+ commissionDate: '',
116
+ maintenanceInterval: 30,
117
+ description: ''
118
+ });
119
+ };
120
+
121
+ // Save new equipment
122
+ const saveEquipment = () => {
123
+ // Validate form
124
+ let isValid = true;
125
+ const inputs = formRef.current.querySelectorAll('[required]');
126
+
127
+ inputs.forEach(input => {
128
+ if (!input.value.trim()) {
129
+ input.classList.add('border-red-500');
130
+ isValid = false;
131
+ } else {
132
+ input.classList.remove('border-red-500');
133
+ }
134
+ });
135
+
136
+ if (!isValid) {
137
+ alert('Пожалуйста, заполните все обязательные по��я');
138
+ return;
139
+ }
140
+
141
+ const newEq = {
142
+ ...newEquipment,
143
+ id: equipmentList.length + 1,
144
+ status: 'active'
145
+ };
146
+
147
+ setEquipmentList([...equipmentList, newEq]);
148
+ closeAddModal();
149
+ alert('Оборудование добавлено успешно!');
150
+ };
151
+
152
+ // Open edit modal
153
+ const openEditModal = (id) => {
154
+ const equipment = equipmentList.find(eq => eq.id === id);
155
+ if (equipment) {
156
+ setEditEquipment({...equipment});
157
+ setShowEditModal(true);
158
+ }
159
+ };
160
+
161
+ const closeEditModal = () => {
162
+ setShowEditModal(false);
163
+ setEditEquipment(null);
164
+ };
165
+
166
+ // Update equipment
167
+ const updateEquipment = () => {
168
+ // Validate form
169
+ let isValid = true;
170
+ const inputs = editFormRef.current.querySelectorAll('[required]');
171
+
172
+ inputs.forEach(input => {
173
+ if (!input.value.trim()) {
174
+ input.classList.add('border-red-500');
175
+ isValid = false;
176
+ } else {
177
+ input.classList.remove('border-red-500');
178
+ }
179
+ });
180
+
181
+ if (!isValid) {
182
+ alert('Пожалуйста, заполните все обязательные поля');
183
+ return;
184
+ }
185
+
186
+ setEquipmentList(equipmentList.map(eq =>
187
+ eq.id === editEquipment.id ? {...editEquipment} : eq
188
+ ));
189
+ closeEditModal();
190
+ alert('Изменения сохранены!');
191
+ };
192
+
193
+ // Delete equipment
194
+ const deleteEquipment = (id) => {
195
+ if (window.confirm('Вы уверены, что хотите удалить это оборудование?')) {
196
+ setEquipmentList(equipmentList.filter(eq => eq.id !== id));
197
+ alert('Оборудование удалено!');
198
+ }
199
+ };
200
+
201
+ // Dashboard stats
202
+ const dashboardStats = [
203
+ { title: "Всего оборудования", value: equipmentList.length, icon: "tools", color: "blue" },
204
+ { title: "Выполнено ТО", value: equipmentList.filter(eq => eq.lastMaintenance).length, icon: "check-circle", color: "green" },
205
+ { title: "Просрочено ТО", value: equipmentList.filter(eq =>
206
+ new Date(eq.nextMaintenance) < new Date()
207
+ ).length, icon: "exclamation-triangle", color: "yellow" },
208
+ { title: "Текущие ремонты", value: equipmentList.filter(eq => eq.status === "repair").length, icon: "wrench", color: "red" }
209
+ ];
210
+
211
+ // Render dashboard content
212
+ const renderDashboard = () => (
213
+ <div className="p-6">
214
+ {/* Stats Cards */}
215
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
216
+ {dashboardStats.map((stat, index) => (
217
+ <div key={index} className="bg-white rounded-lg shadow p-6">
218
+ <div className="flex items-center">
219
+ <div className={`p-3 rounded-full bg-${stat.color}-100 text-${stat.color}-600 mr-4`}>
220
+ <i className={`fas fa-${stat.icon} text-xl`}></i>
221
+ </div>
222
+ <div>
223
+ <p className="text-sm text-gray-500">{stat.title}</p>
224
+ <h3 className="text-2xl font-bold">{stat.value}</h3>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ ))}
229
+ </div>
230
+
231
+ {/* Equipment List */}
232
+ <div className="bg-white rounded-lg shadow overflow-hidden">
233
+ <div className="p-4 border-b flex justify-between items-center">
234
+ <h2 className="text-lg font-semibold">Оборудование</h2>
235
+ <button
236
+ onClick={openAddModal}
237
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center"
238
+ >
239
+ <i className="fas fa-plus mr-2"></i> Добавить
240
+ </button>
241
+ </div>
242
+ <div className="overflow-x-auto">
243
+ <table className="min-w-full divide-y divide-gray-200">
244
+ <thead className="bg-gray-50">
245
+ <tr>
246
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
247
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Название</th>
248
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип</th>
249
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
250
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Последнее ТО</th>
251
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
252
+ </tr>
253
+ </thead>
254
+ <tbody className="bg-white divide-y divide-gray-200">
255
+ {equipmentList.map((eq) => (
256
+ <tr key={eq.id}>
257
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{eq.id}</td>
258
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.name}</td>
259
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.type}</td>
260
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
261
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
262
+ eq.status === 'active' ? 'bg-green-100 text-green-800' :
263
+ eq.status === 'maintenance' ? 'bg-yellow-100 text-yellow-800' :
264
+ 'bg-red-100 text-red-800'
265
+ }`}>
266
+ {eq.status === 'active' ? 'Активно' :
267
+ eq.status === 'maintenance' ? 'ТО' : 'Ремонт'}
268
+ </span>
269
+ </td>
270
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
271
+ {formatDate(eq.lastMaintenance)}
272
+ </td>
273
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
274
+ <button
275
+ onClick={() => openEditModal(eq.id)}
276
+ className="text-blue-600 hover:text-blue-900 mr-3"
277
+ >
278
+ <i className="fas fa-edit"></i>
279
+ </button>
280
+ <button
281
+ onClick={() => deleteEquipment(eq.id)}
282
+ className="text-red-600 hover:text-red-900"
283
+ >
284
+ <i className="fas fa-trash"></i>
285
+ </button>
286
+ </td>
287
+ </tr>
288
+ ))}
289
+ </tbody>
290
+ </table>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ );
295
+
296
+ // Render schedule content
297
+ const renderSchedule = () => (
298
+ <div className="p-6">
299
+ <h2 className="text-2xl font-bold mb-6">График ТО</h2>
300
+
301
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
302
+ {/* Calendar */}
303
+ <div className="lg:col-span-2 bg-white rounded-lg shadow overflow-hidden">
304
+ <div className="p-4 border-b">
305
+ <h3 className="text-lg font-semibold">Календарь ТО</h3>
306
+ </div>
307
+ <div className="p-4">
308
+ <div className="flex justify-between items-center mb-4">
309
+ <h4 className="font-medium">Июнь 2023</h4>
310
+ <div className="flex space-x-2">
311
+ <button className="p-2 rounded-full hover:bg-gray-100">
312
+ <i className="fas fa-chevron-left"></i>
313
+ </button>
314
+ <button className="p-2 rounded-full hover:bg-gray-100">
315
+ <i className="fas fa-chevron-right"></i>
316
+ </button>
317
+ </div>
318
+ </div>
319
+
320
+ <div className="grid grid-cols-7 gap-1 mb-2">
321
+ {['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'].map(day => (
322
+ <div key={day} className="text-center text-xs font-medium text-gray-500">
323
+ {day}
324
+ </div>
325
+ ))}
326
+ </div>
327
+
328
+ <div className="grid grid-cols-7 gap-1">
329
+ {[...Array(35)].map((_, i) => {
330
+ const day = i - 4; // Start from day -4 to simulate a real calendar
331
+ const hasEvent = maintenanceEvents.some(event =>
332
+ new Date(event.date).getDate() === day &&
333
+ new Date(event.date).getMonth() === 5 // June is month 5 in JS
334
+ );
335
+
336
+ return (
337
+ <div
338
+ key={i}
339
+ className={`h-10 flex items-center justify-center border rounded cursor-pointer calendar-day ${
340
+ hasEvent ? 'has-event bg-blue-50' : ''
341
+ }`}
342
+ >
343
+ {day > 0 && day <= 30 ? day : ''}
344
+ </div>
345
+ );
346
+ })}
347
+ </div>
348
+ </div>
349
+ </div>
350
+
351
+ {/* Upcoming Maintenance */}
352
+ <div className="bg-white rounded-lg shadow overflow-hidden">
353
+ <div className="p-4 border-b">
354
+ <h3 className="text-lg font-semibold">Предстоящие ТО</h3>
355
+ </div>
356
+ <div className="p-4">
357
+ <ul className="space-y-3">
358
+ {maintenanceEvents
359
+ .filter(event => event.status === 'planned')
360
+ .slice(0, 5)
361
+ .map((event, index) => {
362
+ const equipment = equipmentList.find(eq => eq.id === event.equipmentId);
363
+ const daysLeft = Math.ceil(
364
+ (new Date(event.date) - new Date()) / (1000 * 60 * 60 * 24)
365
+ );
366
+
367
+ return (
368
+ <li key={index} className="p-3 border rounded-lg hover:bg-gray-50 cursor-pointer">
369
+ <div className="flex justify-between items-start">
370
+ <div>
371
+ <h4 className="font-medium">{equipment?.name || 'Неизвестное оборудование'}</h4>
372
+ <p className="text-sm text-gray-500">{equipment?.type || ''}</p>
373
+ </div>
374
+ <span className="text-sm">
375
+ {daysLeft < 0 ? (
376
+ <span className="text-red-500">Просрочено на {Math.abs(daysLeft)} дн.</span>
377
+ ) : daysLeft === 0 ? (
378
+ <span className="text-yellow-600">Сегодня</span>
379
+ ) : (
380
+ `Через ${daysLeft} дн.`
381
+ )}
382
+ </span>
383
+ </div>
384
+ <div className="mt-2 flex justify-between items-center">
385
+ <span className="text-sm">{formatDate(event.date)}</span>
386
+ <span className="text-sm">{event.responsible}</span>
387
+ </div>
388
+ </li>
389
+ );
390
+ })
391
+ }
392
+ {maintenanceEvents.filter(event => event.status === 'planned').length === 0 && (
393
+ <p className="text-gray-500 text-center py-4">Нет предстоящих ТО</p>
394
+ )}
395
+ </ul>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ </div>
400
+ );
401
+
402
+ // Render equipment content
403
+ const renderEquipment = () => (
404
+ <div className="p-6">
405
+ <h2 className="text-2xl font-bold mb-6">Оборудование</h2>
406
+
407
+ <div className="bg-white rounded-lg shadow overflow-hidden">
408
+ <div className="p-4 border-b flex justify-between items-center">
409
+ <h3 className="text-lg font-semibold">Список оборудования</h3>
410
+ <button
411
+ onClick={openAddModal}
412
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center"
413
+ >
414
+ <i className="fas fa-plus mr-2"></i> Добавить
415
+ </button>
416
+ </div>
417
+ <div className="overflow-x-auto">
418
+ <table className="min-w-full divide-y divide-gray-200">
419
+ <thead className="bg-gray-50">
420
+ <tr>
421
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
422
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Название</th>
423
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип</th>
424
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
425
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Последнее ТО</th>
426
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
427
+ </tr>
428
+ </thead>
429
+ <tbody className="bg-white divide-y divide-gray-200">
430
+ {equipmentList.map((eq) => (
431
+ <tr key={eq.id}>
432
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{eq.id}</td>
433
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.name}</td>
434
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.type}</td>
435
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
436
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
437
+ eq.status === 'active' ? 'bg-green-100 text-green-800' :
438
+ eq.status === 'maintenance' ? 'bg-yellow-100 text-yellow-800' :
439
+ 'bg-red-100 text-red-800'
440
+ }`}>
441
+ {eq.status === 'active' ? 'Активно' :
442
+ eq.status === 'maintenance' ? 'ТО' : 'Ремонт'}
443
+ </span>
444
+ </td>
445
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
446
+ {formatDate(eq.lastMaintenance)}
447
+ </td>
448
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
449
+ <button
450
+ onClick={() => openEditModal(eq.id)}
451
+ className="text-blue-600 hover:text-blue-900 mr-3"
452
+ >
453
+ <i className="fas fa-edit"></i>
454
+ </button>
455
+ <button
456
+ onClick={() => deleteEquipment(eq.id)}
457
+ className="text-red-600 hover:text-red-900"
458
+ >
459
+ <i className="fas fa-trash"></i>
460
+ </button>
461
+ </td>
462
+ </tr>
463
+ ))}
464
+ </tbody>
465
+ </table>
466
+ </div>
467
+ </div>
468
+ </div>
469
+ );
470
+
471
+ // Render users content
472
+ const renderUsers = () => (
473
+ <div className="p-6">
474
+ <h2 className="text-2xl font-bold mb-6">Пользов��тели</h2>
475
+
476
+ <div className="bg-white rounded-lg shadow overflow-hidden">
477
+ <div className="p-4 border-b">
478
+ <h3 className="text-lg font-semibold">Список пользователей</h3>
479
+ </div>
480
+ <div className="overflow-x-auto">
481
+ <table className="min-w-full divide-y divide-gray-200">
482
+ <thead className="bg-gray-50">
483
+ <tr>
484
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Имя</th>
485
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
486
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Роль</th>
487
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
488
+ </tr>
489
+ </thead>
490
+ <tbody className="bg-white divide-y divide-gray-200">
491
+ {users.map((user) => (
492
+ <tr key={user.id}>
493
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{user.name}</td>
494
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.email}</td>
495
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
496
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
497
+ user.role === 'admin' ? 'bg-purple-100 text-purple-800' :
498
+ user.role === 'engineer' ? 'bg-blue-100 text-blue-800' :
499
+ 'bg-gray-100 text-gray-800'
500
+ }`}>
501
+ {user.role === 'admin' ? 'Администратор' :
502
+ user.role === 'engineer' ? 'Инженер' : 'Просмотр'}
503
+ </span>
504
+ </td>
505
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
506
+ <button className="text-blue-600 hover:text-blue-900 mr-3">
507
+ <i className="fas fa-edit"></i>
508
+ </button>
509
+ <button className="text-red-600 hover:text-red-900">
510
+ <i className="fas fa-trash"></i>
511
+ </button>
512
+ </td>
513
+ </tr>
514
+ ))}
515
+ </tbody>
516
+ </table>
517
+ </div>
518
+ </div>
519
+ </div>
520
+ );
521
+
522
+ return (
523
+ <div className="flex h-screen overflow-hidden bg-gray-50">
524
+ {/* Sidebar */}
525
+ <div className={`sidebar bg-blue-800 text-white ${
526
+ isSidebarCollapsed ? 'collapsed w-16' : 'w-64'
527
+ } transition-all duration-300 flex flex-col`}>
528
+ <div className="p-4 flex items-center justify-between border-b border-blue-700">
529
+ <div className="flex items-center">
530
+ <i className="fas fa-cogs text-2xl mr-3"></i>
531
+ {!isSidebarCollapsed && <span className="sidebar-text text-xl font-bold">MES System</span>}
532
+ </div>
533
+ <button
534
+ onClick={() => setIsSidebarCollapsed(!isSidebarCollapsed)}
535
+ className="text-white hover:text-blue-200"
536
+ >
537
+ <i className="fas fa-bars"></i>
538
+ </button>
539
+ </div>
540
+
541
+ <div className="flex-1 overflow-y-auto">
542
+ <nav className="p-4">
543
+ <div className="mb-6">
544
+ <p className="sidebar-text uppercase text-xs font-semibold text-blue-300 mb-2">Основное</p>
545
+ <button
546
+ onClick={() => setActiveTab('dashboard')}
547
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
548
+ activeTab === 'dashboard'
549
+ ? 'bg-blue-700 text-white'
550
+ : 'hover:bg-blue-700 text-white'
551
+ } mb-2`}
552
+ >
553
+ <i className="fas fa-tachometer-alt mr-3"></i>
554
+ {!isSidebarCollapsed && <span>Главная</span>}
555
+ </button>
556
+ <button
557
+ onClick={() => setActiveTab('schedule')}
558
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
559
+ activeTab === 'schedule'
560
+ ? 'bg-blue-700 text-white'
561
+ : 'hover:bg-blue-700 text-white'
562
+ } mb-2`}
563
+ >
564
+ <i className="fas fa-calendar-alt mr-3"></i>
565
+ {!isSidebarCollapsed && (
566
+ <>
567
+ <span>График ТО</span>
568
+ <span className="ml-auto bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">3</span>
569
+ </>
570
+ )}
571
+ </button>
572
+ <button
573
+ onClick={() => setActiveTab('equipment')}
574
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
575
+ activeTab === 'equipment'
576
+ ? 'bg-blue-700 text-white'
577
+ : 'hover:bg-blue-700 text-white'
578
+ } mb-2`}
579
+ >
580
+ <i className="fas fa-tools mr-3"></i>
581
+ {!isSidebarCollapsed && <span>Оборудование</span>}
582
+ </button>
583
+ </div>
584
+
585
+ <div className="mb-6">
586
+ <p className="sidebar-text uppercase text-xs font-semibold text-blue-300 mb-2">Учет</p>
587
+ <button
588
+ className="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2 w-full text-left"
589
+ >
590
+ <i className="fas fa-wrench mr-3"></i>
591
+ {!isSidebarCollapsed && <span>Ремонтные работы</span>}
592
+ </button>
593
+ <button
594
+ className="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2 w-full text-left"
595
+ >
596
+ <i className="fas fa-clipboard-check mr-3"></i>
597
+ {!isSidebarCollapsed && <span>Техническое обслуживание</span>}
598
+ </button>
599
+ <button
600
+ className="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2 w-full text-left"
601
+ >
602
+ <i className="fas fa-chart-line mr-3"></i>
603
+ {!isSidebarCollapsed && <span>Отчеты</span>}
604
+ </button>
605
+ </div>
606
+
607
+ <div className="mb-6">
608
+ <p className="sidebar-text uppercase text-xs font-semibold text-blue-300 mb-2">Настройки</p>
609
+ <button
610
+ className="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2 w-full text-left"
611
+ >
612
+ <i className="fas fa-bell mr-3"></i>
613
+ {!isSidebarCollapsed && <span>Оповещения</span>}
614
+ </button>
615
+ <button
616
+ onClick={() => setActiveTab('users')}
617
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
618
+ activeTab === 'users'
619
+ ? 'bg-blue-700 text-white'
620
+ : 'hover:bg-blue-700 text-white'
621
+ } mb-2`}
622
+ >
623
+ <i className="fas fa-users-cog mr-3"></i>
624
+ {!isSidebarCollapsed && <span>Пользователи</span>}
625
+ </button>
626
+ </div>
627
+ </nav>
628
+ </div>
629
+
630
+ <div className="p-4 border-t border-blue-700">
631
+ <div className="flex items-center">
632
+ <img
633
+ src="https://randomuser.me/api/portraits/men/32.jpg "
634
+ alt="User"
635
+ className="w-10 h-10 rounded-full mr-3"
636
+ />
637
+ {!isSidebarCollapsed && (
638
+ <div className="sidebar-text">
639
+ <p className="font-medium">Иван Петров</p>
640
+ <p className="text-xs text-blue-300">Администратор</p>
641
+ </div>
642
+ )}
643
+ </div>
644
+ </div>
645
+ </div>
646
+
647
+ {/* Main Content */}
648
+ <div className="main-content flex-1 overflow-auto">
649
+ {/* Header */}
650
+ <header className="bg-white shadow-sm py-4 px-6 flex items-center justify-between">
651
+ <h1 className="text-2xl font-bold text-gray-800">
652
+ {activeTab === 'dashboard' && 'Главная панель'}
653
+ {activeTab === 'schedule' && 'График ТО'}
654
+ {activeTab === 'equipment' && 'Оборудование'}
655
+ {activeTab === 'users' && 'Пользователи'}
656
+ </h1>
657
+
658
+ <div className="flex items-center space-x-4">
659
+ <div className="relative">
660
+ <button className="p-2 rounded-full hover:bg-gray-100 relative">
661
+ <i className="fas fa-bell text-gray-600"></i>
662
+ <span className="notification-badge bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">5</span>
663
+ </button>
664
+ </div>
665
+
666
+ <div className="relative">
667
+ <input
668
+ type="text"
669
+ placeholder="Поиск..."
670
+ className="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
671
+ />
672
+ <i className="fas fa-search absolute left-3 top-3 text-gray-400"></i>
673
+ </div>
674
+ </div>
675
+ </header>
676
+
677
+ {/* Content based on active tab */}
678
+ <div className="p-6">
679
+ {activeTab === 'dashboard' && renderDashboard()}
680
+ {activeTab === 'schedule' && renderSchedule()}
681
+ {activeTab === 'equipment' && renderEquipment()}
682
+ {activeTab === 'users' && renderUsers()}
683
+ </div>
684
+ </div>
685
+
686
+ {/* Add Equipment Modal */}
687
+ {showAddModal && (
688
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
689
+ <div className="bg-white rounded-lg shadow-xl w-full max-w-2xl">
690
+ <div className="p-4 border-b flex justify-between items-center">
691
+ <h3 className="text-lg font-semibold">Добавить новое оборудование</h3>
692
+ <button
693
+ onClick={closeAddModal}
694
+ className="text-gray-500 hover:text-gray-700"
695
+ >
696
+ <i className="fas fa-times"></i>
697
+ </button>
698
+ </div>
699
+
700
+ <div className="p-6">
701
+ <form ref={formRef}>
702
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
703
+ <div>
704
+ <label className="block text-sm font-medium text-gray-700 mb-1">Название оборудования</label>
705
+ <input
706
+ type="text"
707
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
708
+ required
709
+ value={newEquipment.name}
710
+ onChange={(e) => setNewEquipment({...newEquipment, name: e.target.value})}
711
+ />
712
+ </div>
713
+
714
+ <div>
715
+ <label className="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
716
+ <select
717
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
718
+ required
719
+ value={newEquipment.type}
720
+ onChange={(e) => setNewEquipment({...newEquipment, type: e.target.value})}
721
+ >
722
+ <option value="">Выберите тип</option>
723
+ <option>Станок</option>
724
+ <option>Конвейер</option>
725
+ <option>Компрессор</option>
726
+ <option>Генератор</option>
727
+ <option>Насос</option>
728
+ </select>
729
+ </div>
730
+
731
+ <div>
732
+ <label className="block text-sm font-medium text-gray-700 mb-1">Серийный номер</label>
733
+ <input
734
+ type="text"
735
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
736
+ required
737
+ value={newEquipment.serial}
738
+ onChange={(e) => setNewEquipment({...newEquipment, serial: e.target.value})}
739
+ />
740
+ </div>
741
+
742
+ <div>
743
+ <label className="block text-sm font-medium text-gray-700 mb-1">Инвентарный номер</label>
744
+ <input
745
+ type="text"
746
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
747
+ required
748
+ value={newEquipment.inventory}
749
+ onChange={(e) => setNewEquipment({...newEquipment, inventory: e.target.value})}
750
+ />
751
+ </div>
752
+
753
+ <div>
754
+ <label className="block text-sm font-medium text-gray-700 mb-1">Дата ввода в эксплуатацию</label>
755
+ <input
756
+ type="date"
757
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
758
+ required
759
+ value={newEquipment.commissionDate}
760
+ onChange={(e) => setNewEquipment({...newEquipment, commissionDate: e.target.value})}
761
+ />
762
+ </div>
763
+
764
+ <div>
765
+ <label className="block text-sm font-medium text-gray-700 mb-1">Периодичность ТО (дни)</label>
766
+ <input
767
+ type="number"
768
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
769
+ required
770
+ min="1"
771
+ value={newEquipment.maintenanceInterval}
772
+ onChange={(e) => setNewEquipment({...newEquipment, maintenanceInterval: e.target.value})}
773
+ />
774
+ </div>
775
+
776
+ <div className="md:col-span-2">
777
+ <label className="block text-sm font-medium text-gray-700 mb-1">Описание</label>
778
+ <textarea
779
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
780
+ rows="3"
781
+ value={newEquipment.description}
782
+ onChange={(e) => setNewEquipment({...newEquipment, description: e.target.value})}
783
+ ></textarea>
784
+ </div>
785
+ </div>
786
+ </form>
787
+ </div>
788
+
789
+ <div className="p-4 border-t flex justify-end space-x-3">
790
+ <button
791
+ onClick={closeAddModal}
792
+ className="px-4 py-2 border rounded-lg hover:bg-gray-50"
793
+ >
794
+ Отмена
795
+ </button>
796
+ <button
797
+ onClick={saveEquipment}
798
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
799
+ >
800
+ Сохранить
801
+ </button>
802
+ </div>
803
+ </div>
804
+ </div>
805
+ )}
806
+
807
+ {/* Edit Equipment Modal */}
808
+ {showEditModal && editEquipment && (
809
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
810
+ <div className="bg-white rounded-lg shadow-xl w-full max-w-2xl">
811
+ <div className="p-4 border-b flex justify-between items-center">
812
+ <h3 className="text-lg font-semibold">Редактировать оборудование</h3>
813
+ <button
814
+ onClick={closeEditModal}
815
+ className="text-gray-500 hover:text-gray-700"
816
+ >
817
+ <i className="fas fa-times"></i>
818
+ </button>
819
+ </div>
820
+
821
+ <div className="p-6">
822
+ <form ref={editFormRef}>
823
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
824
+ <div>
825
+ <label className="block text-sm font-medium text-gray-700 mb-1">Название оборудования</label>
826
+ <input
827
+ type="text"
828
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
829
+ required
830
+ value={editEquipment.name}
831
+ onChange={(e) => setEditEquipment({...editEquipment, name: e.target.value})}
832
+ />
833
+ </div>
834
+
835
+ <div>
836
+ <label className="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
837
+ <select
838
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
839
+ required
840
+ value={editEquipment.type}
841
+ onChange={(e) => setEditEquipment({...editEquipment, type: e.target.value})}
842
+ >
843
+ <option value="">Выберите тип</option>
844
+ <option>Станок</option>
845
+ <option>Конвейер</option>
846
+ <option>Компрессор</option>
847
+ <option>Генератор</option>
848
+ <option>Насос</option>
849
+ </select>
850
+ </div>
851
+
852
+ <div>
853
+ <label className="block text-sm font-medium text-gray-700 mb-1">Серийный номер</label>
854
+ <input
855
+ type="text"
856
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
857
+ required
858
+ value={editEquipment.serial}
859
+ onChange={(e) => setEditEquipment({...editEquipment, serial: e.target.value})}
860
+ />
861
+ </div>
862
+
863
+ <div>
864
+ <label className="block text-sm font-medium text-gray-700 mb-1">Инвентарный номер</label>
865
+ <input
866
+ type="text"
867
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
868
+ required
869
+ value={editEquipment.inventory}
870
+ onChange={(e) => setEditEquipment({...editEquipment, inventory: e.target.value})}
871
+ />
872
+ </div>
873
+
874
+ <div>
875
+ <label className="block text-sm font-medium text-gray-700 mb-1">Дата ввода в эксплуатацию</label>
876
+ <input
877
+ type="date"
878
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
879
+ required
880
+ value={editEquipment.commissionDate}
881
+ onChange={(e) => setEditEquipment({...editEquipment, commissionDate: e.target.value})}
882
+ />
883
+ </div>
884
+
885
+ <div>
886
+ <label className="block text-sm font-medium text-gray-700 mb-1">Периодичность ТО (дни)</label>
887
+ <input
888
+ type="number"
889
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
890
+ required
891
+ min="1"
892
+ value={editEquipment.maintenanceInterval}
893
+ onChange={(e) => setEditEquipment({...editEquipment, maintenanceInterval: e.target.value})}
894
+ />
895
+ </div>
896
+
897
+ <div className="md:col-span-2">
898
+ <label className="block text-sm font-medium text-gray-700 mb-1">Описание</label>
899
+ <textarea
900
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
901
+ rows="3"
902
+ value={editEquipment.description}
903
+ onChange={(e) => setEditEquipment({...editEquipment, description: e.target.value})}
904
+ ></textarea>
905
+ </div>
906
+ </div>
907
+ </form>
908
+ </div>
909
+
910
+ <div className="p-4 border-t flex justify-end space-x-3">
911
+ <button
912
+ onClick={closeEditModal}
913
+ className="px-4 py-2 border rounded-lg hover:bg-gray-50"
914
+ >
915
+ Отмена
916
+ </button>
917
+ <button
918
+ onClick={updateEquipment}
919
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
920
+ >
921
+ Сохранить изменения
922
+ </button>
923
+ </div>
924
+ </div>
925
+ </div>
926
+ )}
927
+ </div>
928
+ );
929
+ }
prompts.txt ADDED
File without changes