Karmashek commited on
Commit
be00a0e
·
verified ·
1 Parent(s): 1576d72

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +1583 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Bad
3
- emoji: 🏆
4
- colorFrom: purple
5
- colorTo: purple
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: bad
3
+ emoji: 🐳
4
+ colorFrom: green
5
+ colorTo: blue
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,1583 @@
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
+ ```jsx
2
+ import { useState, useEffect, useRef } from 'react';
3
+
4
+ export default function App() {
5
+ // State for active tab and sidebar
6
+ const [activeTab, setActiveTab] = useState('dashboard');
7
+ const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
8
+ const [scrollY, setScrollY] = useState(0);
9
+
10
+ // Equipment data
11
+ const [equipmentList, setEquipmentList] = useState([
12
+ {
13
+ id: 1,
14
+ name: "5-ти осевой фрезерный центр Nmill 1400 (Niles Simmons)",
15
+ type: "Станок",
16
+ serial: "CNC2000-001",
17
+ inventory: "INV-1001",
18
+ status: "active",
19
+ lastMaintenance: "2023-05-15",
20
+ nextMaintenance: "2023-06-15",
21
+ maintenanceInterval: 30,
22
+ description: "5-ти осевой фрезерный центр."
23
+ },
24
+ {
25
+ id: 2,
26
+ name: "3-х осевой фрезерный центр Mikron HPM 600HD (GF+)",
27
+ type: "Станок",
28
+ serial: "CONV-A12-045",
29
+ inventory: "INV-1042",
30
+ status: "active",
31
+ lastMaintenance: "2023-05-20",
32
+ nextMaintenance: "2023-06-20",
33
+ maintenanceInterval: 30,
34
+ description: "3-х осевой фрезерный центр"
35
+ },
36
+ {
37
+ id: 3,
38
+ name: "5-ти осевой фрезерный центр Versa 645 Linear (Fehlmann)",
39
+ type: "Станок",
40
+ serial: "COMP-V50-112",
41
+ inventory: "INV-1078",
42
+ status: "maintenance",
43
+ lastMaintenance: "2023-04-10",
44
+ nextMaintenance: "2023-06-10",
45
+ maintenanceInterval: 60,
46
+ description: "5-ти осевой фрезерный центр"
47
+ }
48
+ ]);
49
+
50
+ // Users data
51
+ const [users, setUsers] = useState([
52
+ { id: 1, name: "Иван Петров", role: "admin", email: "ivan@example.com" },
53
+ { id: 2, name: "Петр Смирнов", role: "engineer", email: "petr@example.com" },
54
+ { id: 3, name: "Анна Иванова", role: "viewer", email: "anna@example.com" }
55
+ ]);
56
+
57
+ // Maintenance events
58
+ const [maintenanceEvents, setMaintenanceEvents] = useState([
59
+ { equipmentId: 1, date: "2023-06-15", status: "planned", responsible: "Иванов А.П." },
60
+ { equipmentId: 2, date: "2023-06-20", status: "planned", responsible: "Петров И.С." },
61
+ { equipmentId: 3, date: "2023-06-10", status: "overdue", responsible: "Сидоров В.М." }
62
+ ]);
63
+
64
+ // Repair events
65
+ const [repairEvents, setRepairEvents] = useState([
66
+ { equipmentId: 3, startDate: "2023-06-05", endDate: null, status: "in_progress", technician: "Кузнецова Е.В." }
67
+ ]);
68
+
69
+ // New equipment state
70
+ const [newEquipment, setNewEquipment] = useState({
71
+ name: '',
72
+ type: '',
73
+ serial: '',
74
+ inventory: '',
75
+ commissionDate: '',
76
+ maintenanceInterval: 30,
77
+ description: ''
78
+ });
79
+
80
+ const [editEquipment, setEditEquipment] = useState(null);
81
+ const [showAddModal, setShowAddModal] = useState(false);
82
+ const [showEditModal, setShowEditModal] = useState(false);
83
+ const [showMaintenanceModal, setShowMaintenanceModal] = useState(false);
84
+
85
+ // Refs for form validation
86
+ const formRef = useRef(null);
87
+ const editFormRef = useRef(null);
88
+ const maintenanceFormRef = useRef(null);
89
+
90
+ // Format date to DD.MM.YYYY
91
+ const formatDate = (dateString) => {
92
+ if (!dateString) return '';
93
+ const date = new Date(dateString);
94
+ const day = String(date.getDate()).padStart(2, '0');
95
+ const month = String(date.getMonth() + 1).padStart(2, '0');
96
+ const year = date.getFullYear();
97
+ return `${day}.${month}.${year}`;
98
+ };
99
+
100
+ // Calculate next maintenance date
101
+ const calculateNextMaintenanceDate = (commissionDate, intervalDays) => {
102
+ if (!commissionDate) return '';
103
+ const intervalMs = parseInt(intervalDays) * 24 * 60 * 60 * 1000;
104
+ const nextDate = new Date(new Date(commissionDate).getTime() + intervalMs);
105
+ return nextDate.toISOString().split('T')[0];
106
+ };
107
+
108
+ // Handle add equipment modal
109
+ const openAddModal = () => {
110
+ setNewEquipment({
111
+ name: '',
112
+ type: '',
113
+ serial: '',
114
+ inventory: '',
115
+ commissionDate: '',
116
+ maintenanceInterval: 30,
117
+ description: ''
118
+ });
119
+ setShowAddModal(true);
120
+ };
121
+
122
+ const closeAddModal = () => {
123
+ setShowAddModal(false);
124
+ setNewEquipment({
125
+ name: '',
126
+ type: '',
127
+ serial: '',
128
+ inventory: '',
129
+ commissionDate: '',
130
+ maintenanceInterval: 30,
131
+ description: ''
132
+ });
133
+ };
134
+
135
+ // Save new equipment
136
+ const saveEquipment = () => {
137
+ // Validate form
138
+ let isValid = true;
139
+ const inputs = formRef.current.querySelectorAll('[required]');
140
+
141
+ inputs.forEach(input => {
142
+ if (!input.value.trim()) {
143
+ input.classList.add('border-red-500');
144
+ isValid = false;
145
+ } else {
146
+ input.classList.remove('border-red-500');
147
+ }
148
+ });
149
+
150
+ if (!isValid) {
151
+ alert('Пожалуйста, заполните все обязательные поля');
152
+ return;
153
+ }
154
+
155
+ const newEq = {
156
+ ...newEquipment,
157
+ id: equipmentList.length + 1,
158
+ status: 'active',
159
+ lastMaintenance: '',
160
+ nextMaintenance: calculateNextMaintenanceDate(newEquipment.commissionDate, newEquipment.maintenanceInterval)
161
+ };
162
+
163
+ setEquipmentList([...equipmentList, newEq]);
164
+ closeAddModal();
165
+ alert('Оборудование добавлено успешно!');
166
+ };
167
+
168
+ // Open edit modal
169
+ const openEditModal = (id) => {
170
+ const equipment = equipmentList.find(eq => eq.id === id);
171
+ if (equipment) {
172
+ setEditEquipment({...equipment});
173
+ setShowEditModal(true);
174
+ }
175
+ };
176
+
177
+ const closeEditModal = () => {
178
+ setShowEditModal(false);
179
+ setEditEquipment(null);
180
+ };
181
+
182
+ // Update equipment
183
+ const updateEquipment = () => {
184
+ // Validate form
185
+ let isValid = true;
186
+ const inputs = editFormRef.current.querySelectorAll('[required]');
187
+
188
+ inputs.forEach(input => {
189
+ if (!input.value.trim()) {
190
+ input.classList.add('border-red-500');
191
+ isValid = false;
192
+ } else {
193
+ input.classList.remove('border-red-500');
194
+ }
195
+ });
196
+
197
+ if (!isValid) {
198
+ alert('Пожалуйста, заполните все обязательные поля');
199
+ return;
200
+ }
201
+
202
+ setEquipmentList(equipmentList.map(eq =>
203
+ eq.id === editEquipment.id ? {...editEquipment} : eq
204
+ ));
205
+ closeEditModal();
206
+ alert('Изменения сохранены!');
207
+ };
208
+
209
+ // Delete equipment
210
+ const deleteEquipment = (id) => {
211
+ if (window.confirm('Вы уверены, что хотите удалить это оборудование?')) {
212
+ setEquipmentList(equipmentList.filter(eq => eq.id !== id));
213
+ alert('Оборудование удалено!');
214
+ }
215
+ };
216
+
217
+ // Dashboard stats
218
+ const dashboardStats = [
219
+ { title: "Всего оборудования", value: equipmentList.length, icon: "tools", color: "blue" },
220
+ { title: "Выполнено ТО", value: equipmentList.filter(eq => eq.lastMaintenance).length, icon: "check-circle", color: "green" },
221
+ { title: "Просрочено ТО", value: equipmentList.filter(eq =>
222
+ new Date(eq.nextMaintenance) < new Date()
223
+ ).length, icon: "exclamation-triangle", color: "yellow" },
224
+ { title: "Текущие ремонты", value: equipmentList.filter(eq => eq.status === "repair").length, icon: "wrench", color: "red" }
225
+ ];
226
+
227
+ // Render dashboard content
228
+ const renderDashboard = () => (
229
+ <div className="p-6">
230
+ {/* Stats Cards */}
231
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
232
+ {dashboardStats.map((stat, index) => (
233
+ <div key={index} className="bg-white rounded-lg shadow p-6">
234
+ <div className="flex items-center">
235
+ <div className={`p-3 rounded-full bg-${stat.color}-100 text-${stat.color}-600 mr-4`}>
236
+ <i className={`fas fa-${stat.icon} text-xl`}></i>
237
+ </div>
238
+ <div>
239
+ <p className="text-sm text-gray-500">{stat.title}</p>
240
+ <h3 className="text-2xl font-bold">{stat.value}</h3>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ ))}
245
+ </div>
246
+
247
+ {/* Charts Section */}
248
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
249
+ {/* Maintenance Chart */}
250
+ <div className="bg-white rounded-lg shadow overflow-hidden">
251
+ <div className="p-4 border-b flex justify-between items-center">
252
+ <h3 className="text-lg font-semibold">Статистика ТО по месяцам</h3>
253
+ <select className="border rounded-lg px-3 py-1 text-sm">
254
+ <option>2025</option>
255
+ <option>2024</option>
256
+ <option selected>2023</option>
257
+ <option>2022</option>
258
+ <option>2021</option>
259
+ </select>
260
+ </div>
261
+ <div className="p-4">
262
+ <canvas ref={chartRef => {
263
+ if (chartRef && !chartRef.chart) {
264
+ chartRef.chart = new Chart(chartRef.getContext('2d'), {
265
+ type: 'bar',
266
+ data: {
267
+ labels: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
268
+ datasets: [
269
+ {
270
+ label: 'Запланировано',
271
+ data: [5, 7, 6, 8, 10, 12, 8, 7, 6, 9, 7, 5],
272
+ backgroundColor: '#3B82F6'
273
+ },
274
+ {
275
+ label: 'Выполнено',
276
+ data: [5, 6, 5, 7, 9, 8, 6, 5, 4, 7, 5, 4],
277
+ backgroundColor: '#10B981'
278
+ },
279
+ {
280
+ label: 'Просрочено',
281
+ data: [0, 1, 1, 1, 1, 3, 2, 2, 2, 2, 2, 1],
282
+ backgroundColor: '#EF4444'
283
+ }
284
+ ]
285
+ },
286
+ options: {
287
+ responsive: true,
288
+ plugins: {
289
+ legend: {
290
+ position: 'top'
291
+ }
292
+ },
293
+ scales: {
294
+ x: {
295
+ stacked: false
296
+ },
297
+ y: {
298
+ stacked: false,
299
+ beginAtZero: true
300
+ }
301
+ }
302
+ }
303
+ });
304
+ }
305
+ }} height="250"></canvas>
306
+ </div>
307
+ </div>
308
+
309
+ {/* Equipment Status Chart */}
310
+ <div className="bg-white rounded-lg shadow overflow-hidden">
311
+ <div className="p-4 border-b">
312
+ <h3 className="text-lg font-semibold">Статус оборудования</h3>
313
+ </div>
314
+ <div className="p-4">
315
+ <canvas ref={statusChartRef => {
316
+ if (statusChartRef && !statusChartRef.chart) {
317
+ statusChartRef.chart = new Chart(statusChartRef.getContext('2d'), {
318
+ type: 'doughnut',
319
+ data: {
320
+ labels: ['Активно', 'На ТО', 'В ремонте', 'Резерв'],
321
+ datasets: [{
322
+ data: [
323
+ equipmentList.filter(eq => eq.status === 'active').length,
324
+ equipmentList.filter(eq => eq.status === 'maintenance').length,
325
+ equipmentList.filter(eq => eq.status === 'repair').length,
326
+ equipmentList.filter(eq => eq.status === 'standby').length
327
+ ],
328
+ backgroundColor: ['#10B981','#F59E0B','#EF4444','#6B7280'],
329
+ borderWidth: 0
330
+ }]
331
+ },
332
+ options: {
333
+ responsive: true,
334
+ plugins: {
335
+ legend: {
336
+ position: 'right'
337
+ }
338
+ }
339
+ }
340
+ });
341
+ }
342
+ }} height="250"></canvas>
343
+ </div>
344
+ </div>
345
+ </div>
346
+
347
+ {/* Equipment List */}
348
+ <div className="bg-white rounded-lg shadow overflow-hidden">
349
+ <div className="p-4 border-b flex justify-between items-center">
350
+ <h2 className="text-lg font-semibold">Оборудование</h2>
351
+ <button
352
+ onClick={openAddModal}
353
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center"
354
+ >
355
+ <i className="fas fa-plus mr-2"></i> Добавить
356
+ </button>
357
+ </div>
358
+ <div className="overflow-x-auto">
359
+ <table className="min-w-full divide-y divide-gray-200">
360
+ <thead className="bg-gray-50">
361
+ <tr>
362
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
363
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Название</th>
364
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип</th>
365
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
366
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Последнее ТО</th>
367
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
368
+ </tr>
369
+ </thead>
370
+ <tbody className="bg-white divide-y divide-gray-200">
371
+ {equipmentList.map((eq) => (
372
+ <tr key={eq.id}>
373
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{eq.id}</td>
374
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.name}</td>
375
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.type}</td>
376
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
377
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
378
+ eq.status === 'active' ? 'bg-green-100 text-green-800' :
379
+ eq.status === 'maintenance' ? 'bg-yellow-100 text-yellow-800' :
380
+ 'bg-red-100 text-red-800'
381
+ }`}>
382
+ {eq.status === 'active' ? 'Активно' :
383
+ eq.status === 'maintenance' ? 'ТО' : 'Ремонт'}
384
+ </span>
385
+ </td>
386
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
387
+ {formatDate(eq.lastMaintenance)}
388
+ </td>
389
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
390
+ <button
391
+ onClick={() => openEditModal(eq.id)}
392
+ className="text-blue-600 hover:text-blue-900 mr-3"
393
+ >
394
+ <i className="fas fa-edit"></i>
395
+ </button>
396
+ <button
397
+ onClick={() => deleteEquipment(eq.id)}
398
+ className="text-red-600 hover:text-red-900"
399
+ >
400
+ <i className="fas fa-trash"></i>
401
+ </button>
402
+ </td>
403
+ </tr>
404
+ ))}
405
+ </tbody>
406
+ </table>
407
+ </div>
408
+ </div>
409
+ </div>
410
+ );
411
+
412
+ // Render schedule content
413
+ const renderSchedule = () => (
414
+ <div className="p-6">
415
+ <h2 className="text-2xl font-bold mb-6">График ТО</h2>
416
+
417
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
418
+ {/* Calendar */}
419
+ <div className="lg:col-span-2 bg-white rounded-lg shadow overflow-hidden">
420
+ <div className="p-4 border-b">
421
+ <h3 className="text-lg font-semibold">Календарь ТО</h3>
422
+ </div>
423
+ <div className="p-4">
424
+ <div className="flex justify-between items-center mb-4">
425
+ <h4 className="font-medium">Июнь 2023</h4>
426
+ <div className="flex space-x-2">
427
+ <button className="p-2 rounded-full hover:bg-gray-100">
428
+ <i className="fas fa-chevron-left"></i>
429
+ </button>
430
+ <button className="p-2 rounded-full hover:bg-gray-100">
431
+ <i className="fas fa-chevron-right"></i>
432
+ </button>
433
+ </div>
434
+ </div>
435
+
436
+ <div className="grid grid-cols-7 gap-1 mb-2">
437
+ {['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'].map(day => (
438
+ <div key={day} className="text-center text-xs font-medium text-gray-500">
439
+ {day}
440
+ </div>
441
+ ))}
442
+ </div>
443
+
444
+ <div className="grid grid-cols-7 gap-1">
445
+ {[...Array(35)].map((_, i) => {
446
+ const day = i - 4; // Start from day -4 to simulate a real calendar
447
+ const hasEvent = maintenanceEvents.some(event =>
448
+ new Date(event.date).getDate() === day &&
449
+ new Date(event.date).getMonth() === 5 // June is month 5 in JS
450
+ );
451
+
452
+ return (
453
+ <div
454
+ key={i}
455
+ className={`h-10 flex items-center justify-center border rounded cursor-pointer calendar-day ${
456
+ hasEvent ? 'has-event bg-blue-50' : ''
457
+ }`}
458
+ >
459
+ {day > 0 && day <= 30 ? day : ''}
460
+ </div>
461
+ );
462
+ })}
463
+ </div>
464
+ </div>
465
+ </div>
466
+
467
+ {/* Upcoming Maintenance */}
468
+ <div className="bg-white rounded-lg shadow overflow-hidden">
469
+ <div className="p-4 border-b">
470
+ <h3 className="text-lg font-semibold">Предстоящие ТО</h3>
471
+ </div>
472
+ <div className="p-4">
473
+ <ul className="space-y-3">
474
+ {maintenanceEvents
475
+ .filter(event => event.status === 'planned')
476
+ .slice(0, 5)
477
+ .map((event, index) => {
478
+ const equipment = equipmentList.find(eq => eq.id === event.equipmentId);
479
+ const daysLeft = Math.ceil(
480
+ (new Date(event.date) - new Date()) / (1000 * 60 * 60 * 24)
481
+ );
482
+
483
+ return (
484
+ <li key={index} className="p-3 border rounded-lg hover:bg-gray-50 cursor-pointer">
485
+ <div className="flex justify-between items-start">
486
+ <div>
487
+ <h4 className="font-medium">{equipment?.name || 'Неизвестное оборудование'}</h4>
488
+ <p className="text-sm text-gray-500">{equipment?.type || ''}</p>
489
+ </div>
490
+ <span className="text-sm">
491
+ {daysLeft < 0 ? (
492
+ <span className="text-red-500">Просрочено на {Math.abs(daysLeft)} дн.</span>
493
+ ) : daysLeft === 0 ? (
494
+ <span className="text-yellow-600">Сегодня</span>
495
+ ) : (
496
+ `Через ${daysLeft} дн.`
497
+ )}
498
+ </span>
499
+ </div>
500
+ <div className="mt-2 flex justify-between items-center">
501
+ <span className="text-sm">{formatDate(event.date)}</span>
502
+ <span className="text-sm">{event.responsible}</span>
503
+ </div>
504
+ </li>
505
+ );
506
+ })
507
+ }
508
+ {maintenanceEvents.filter(event => event.status === 'planned').length === 0 && (
509
+ <p className="text-gray-500 text-center py-4">Нет предстоящих ТО</p>
510
+ )}
511
+ </ul>
512
+ </div>
513
+ </div>
514
+ </div>
515
+ </div>
516
+ );
517
+
518
+ // Render equipment content
519
+ const renderEquipment = () => (
520
+ <div className="p-6">
521
+ <h2 className="text-2xl font-bold mb-6">Оборудование</h2>
522
+
523
+ <div className="bg-white rounded-lg shadow overflow-hidden">
524
+ <div className="p-4 border-b flex justify-between items-center">
525
+ <h3 className="text-lg font-semibold">Список оборудования</h3>
526
+ <button
527
+ onClick={openAddModal}
528
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center"
529
+ >
530
+ <i className="fas fa-plus mr-2"></i> Добавить
531
+ </button>
532
+ </div>
533
+ <div className="overflow-x-auto">
534
+ <table className="min-w-full divide-y divide-gray-200">
535
+ <thead className="bg-gray-50">
536
+ <tr>
537
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
538
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Название</th>
539
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип</th>
540
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
541
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Последнее ТО</th>
542
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
543
+ </tr>
544
+ </thead>
545
+ <tbody className="bg-white divide-y divide-gray-200">
546
+ {equipmentList.map((eq) => (
547
+ <tr key={eq.id}>
548
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{eq.id}</td>
549
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.name}</td>
550
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{eq.type}</td>
551
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
552
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
553
+ eq.status === 'active' ? 'bg-green-100 text-green-800' :
554
+ eq.status === 'maintenance' ? 'bg-yellow-100 text-yellow-800' :
555
+ 'bg-red-100 text-red-800'
556
+ }`}>
557
+ {eq.status === 'active' ? 'Активно' :
558
+ eq.status === 'maintenance' ? 'ТО' : 'Ремонт'}
559
+ </span>
560
+ </td>
561
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
562
+ {formatDate(eq.lastMaintenance)}
563
+ </td>
564
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
565
+ <button
566
+ onClick={() => openEditModal(eq.id)}
567
+ className="text-blue-600 hover:text-blue-900 mr-3"
568
+ >
569
+ <i className="fas fa-edit"></i>
570
+ </button>
571
+ <button
572
+ onClick={() => deleteEquipment(eq.id)}
573
+ className="text-red-600 hover:text-red-900"
574
+ >
575
+ <i className="fas fa-trash"></i>
576
+ </button>
577
+ </td>
578
+ </tr>
579
+ ))}
580
+ </tbody>
581
+ </table>
582
+ </div>
583
+ </div>
584
+ </div>
585
+ );
586
+
587
+ // Render users content
588
+ const renderUsers = () => (
589
+ <div className="p-6">
590
+ <h2 className="text-2xl font-bold mb-6">Пользователи</h2>
591
+
592
+ <div className="bg-white rounded-lg shadow overflow-hidden">
593
+ <div className="p-4 border-b">
594
+ <h3 className="text-lg font-semibold">Список пользователей</h3>
595
+ </div>
596
+ <div className="overflow-x-auto">
597
+ <table className="min-w-full divide-y divide-gray-200">
598
+ <thead className="bg-gray-50">
599
+ <tr>
600
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Имя</th>
601
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
602
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Роль</th>
603
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
604
+ </tr>
605
+ </thead>
606
+ <tbody className="bg-white divide-y divide-gray-200">
607
+ {users.map((user) => (
608
+ <tr key={user.id}>
609
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{user.name}</td>
610
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.email}</td>
611
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
612
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
613
+ user.role === 'admin' ? 'bg-purple-100 text-purple-800' :
614
+ user.role === 'engineer' ? 'bg-blue-100 text-blue-800' :
615
+ 'bg-gray-100 text-gray-800'
616
+ }`}>
617
+ {user.role === 'admin' ? 'Администратор' :
618
+ user.role === 'engineer' ? 'Инженер' : 'Просмотр'}
619
+ </span>
620
+ </td>
621
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
622
+ <button className="text-blue-600 hover:text-blue-900 mr-3">
623
+ <i className="fas fa-edit"></i>
624
+ </button>
625
+ <button className="text-red-600 hover:text-red-900">
626
+ <i className="fas fa-trash"></i>
627
+ </button>
628
+ </td>
629
+ </tr>
630
+ ))}
631
+ </tbody>
632
+ </table>
633
+ </div>
634
+ </div>
635
+ </div>
636
+ );
637
+
638
+ // Render repair content
639
+ const renderRepairs = () => (
640
+ <div className="p-6">
641
+ <h2 className="text-2xl font-bold mb-6">Ремонтные работы</h2>
642
+
643
+ <div className="bg-white rounded-lg shadow overflow-hidden">
644
+ <div className="p-4 border-b flex justify-between items-center">
645
+ <h3 className="text-lg font-semibold">Текущие ремонты</h3>
646
+ <button
647
+ onClick={() => window.alert('Добавление новых ремонтов в разработке')}
648
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center"
649
+ >
650
+ <i className="fas fa-plus mr-2"></i> Новый ремонт
651
+ </button>
652
+ </div>
653
+ <div className="overflow-x-auto">
654
+ <table className="min-w-full divide-y divide-gray-200">
655
+ <thead className="bg-gray-50">
656
+ <tr>
657
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Оборудование</th>
658
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Дата начала</th>
659
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Плановая дата окончания</th>
660
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
661
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ответственный</th>
662
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
663
+ </tr>
664
+ </thead>
665
+ <tbody className="bg-white divide-y divide-gray-200">
666
+ {repairEvents
667
+ .filter(repair => !repair.endDate || new Date(repair.endDate) > new Date())
668
+ .map((repair, index) => {
669
+ const equipment = equipmentList.find(eq => eq.id === repair.equipmentId);
670
+ const daysLeft = repair.endDate ?
671
+ Math.ceil((new Date(repair.endDate) - new Date()) / (1000 * 60 * 60 * 24)) :
672
+ null;
673
+
674
+ return (
675
+ <tr key={index}>
676
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
677
+ {equipment?.name || 'Неизвестное оборудование'}
678
+ </td>
679
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
680
+ {formatDate(repair.startDate)}
681
+ </td>
682
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
683
+ {formatDate(repair.endDate || repair.nextMaintenance)}
684
+ </td>
685
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
686
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
687
+ repair.status === 'in_progress' ? 'bg-red-100 text-red-800' :
688
+ repair.status === 'completed' ? 'bg-green-100 text-green-800' :
689
+ 'bg-yellow-100 text-yellow-800'
690
+ }`}>
691
+ {repair.status === 'in_progress' ? 'В процессе' :
692
+ repair.status === 'completed' ? 'Завершено' : 'Планируется'}
693
+ </span>
694
+ </td>
695
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
696
+ {repair.technician}
697
+ </td>
698
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
699
+ <button
700
+ onClick={() => window.alert(`Детали ремонта для ${equipment?.name}`)}
701
+ className="text-blue-600 hover:text-blue-900 mr-3"
702
+ >
703
+ <i className="fas fa-eye"></i>
704
+ </button>
705
+ <button
706
+ onClick={() => window.alert(`Обновить статус ремонта для ${equipment?.name}`)}
707
+ className="text-green-600 hover:text-green-900"
708
+ >
709
+ <i className="fas fa-check"></i>
710
+ </button>
711
+ </td>
712
+ </tr>
713
+ );
714
+ })
715
+ }
716
+ {repairEvents.filter(repair => !repair.endDate || new Date(repair.endDate) > new Date()).length === 0 && (
717
+ <tr>
718
+ <td colSpan="6" className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-center">
719
+ Нет текущих ремонтов
720
+ </td>
721
+ </tr>
722
+ )}
723
+ </tbody>
724
+ </table>
725
+ </div>
726
+ </div>
727
+
728
+ <div className="mt-8 bg-white rounded-lg shadow overflow-hidden">
729
+ <div className="p-4 border-b">
730
+ <h3 className="text-lg font-semibold">История ремонтов</h3>
731
+ </div>
732
+ <div className="overflow-x-auto">
733
+ <table className="min-w-full divide-y divide-gray-200">
734
+ <thead className="bg-gray-50">
735
+ <tr>
736
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Оборудование</th>
737
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Дата начала</th>
738
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Дата окончания</th>
739
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Продолжительность</th>
740
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Причина</th>
741
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Детали</th>
742
+ </tr>
743
+ </thead>
744
+ <tbody className="bg-white divide-y divide-gray-200">
745
+ {repairEvents
746
+ .filter(repair => repair.endDate && new Date(repair.endDate) <= new Date())
747
+ .map((repair, index) => {
748
+ const equipment = equipmentList.find(eq => eq.id === repair.equipmentId);
749
+ const duration = repair.endDate ?
750
+ Math.ceil((new Date(repair.endDate) - new Date(repair.startDate)) / (1000 * 60 * 60 * 24)) :
751
+ 0;
752
+
753
+ return (
754
+ <tr key={index}>
755
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
756
+ {equipment?.name || 'Неизвестное оборудование'}
757
+ </td>
758
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
759
+ {formatDate(repair.startDate)}
760
+ </td>
761
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
762
+ {formatDate(repair.endDate)}
763
+ </td>
764
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
765
+ {duration} дней
766
+ </td>
767
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
768
+ Плановое обслуживание
769
+ </td>
770
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
771
+ <button className="text-blue-600 hover:text-blue-900">
772
+ <i className="fas fa-eye"></i>
773
+ </button>
774
+ </td>
775
+ </tr>
776
+ );
777
+ })
778
+ }
779
+ {repairEvents.filter(repair => repair.endDate && new Date(repair.endDate) <= new Date()).length === 0 && (
780
+ <tr>
781
+ <td colSpan="6" className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-center">
782
+ Нет истории ремонтов
783
+ </td>
784
+ </tr>
785
+ )}
786
+ </tbody>
787
+ </table>
788
+ </div>
789
+ </div>
790
+ </div>
791
+ );
792
+
793
+ // Render maintenance content
794
+ const renderMaintenance = () => (
795
+ <div className="p-6">
796
+ <h2 className="text-2xl font-bold mb-6">Техническое обслуживание</h2>
797
+
798
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
799
+ {/* Current Maintenance */}
800
+ <div className="lg:col-span-2 bg-white rounded-lg shadow overflow-hidden">
801
+ <div className="p-4 border-b">
802
+ <h3 className="text-lg font-semibold">Текущее обслуживание</h3>
803
+ </div>
804
+ <div className="p-4">
805
+ <div className="space-y-4">
806
+ {maintenanceEvents
807
+ .filter(event => event.status === 'planned' || event.status === 'overdue')
808
+ .map((event, index) => {
809
+ const equipment = equipmentList.find(eq => eq.id === event.equipmentId);
810
+ const daysLeft = Math.ceil(
811
+ (new Date(event.date) - new Date()) / (1000 * 60 * 60 * 24)
812
+ );
813
+
814
+ return (
815
+ <div key={index} className="border rounded-lg p-4 hover:bg-gray-50 transition-colors">
816
+ <div className="flex justify-between items-start mb-2">
817
+ <h4 className="font-medium">{equipment?.name || 'Неизвестное оборудование'}</h4>
818
+ <span className={`px-2 py-1 rounded-full text-xs font-semibold ${
819
+ daysLeft < 0 ? 'bg-red-100 text-red-800' :
820
+ daysLeft === 0 ? 'bg-yellow-100 text-yellow-800' :
821
+ 'bg-blue-100 text-blue-800'
822
+ }`}>
823
+ {daysLeft < 0 ? `Просрочено на ${Math.abs(daysLeft)} дн.` :
824
+ daysLeft === 0 ? 'Сегодня' : `Через ${daysLeft} дн.`}
825
+ </span>
826
+ </div>
827
+
828
+ <div className="grid grid-cols-2 gap-4 mt-2">
829
+ <div>
830
+ <p className="text-sm text-gray-500">Дата ТО</p>
831
+ <p className="font-medium">{formatDate(event.date)}</p>
832
+ </div>
833
+ <div>
834
+ <p className="text-sm text-gray-500">Ответственный</p>
835
+ <p className="font-medium">{event.responsible}</p>
836
+ </div>
837
+ </div>
838
+
839
+ <div className="mt-4 flex space-x-2">
840
+ <button
841
+ onClick={() => {
842
+ setShowMaintenanceModal(true);
843
+ // Set current editing equipment ID here if needed
844
+ }}
845
+ className="flex-1 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center justify-center"
846
+ >
847
+ <i className="fas fa-edit mr-2"></i> Редактировать
848
+ </button>
849
+ <button
850
+ onClick={() => window.alert('Подтвердить выполнение ТО')}
851
+ className="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg flex items-center"
852
+ >
853
+ <i className="fas fa-check mr-2"></i> Выполнено
854
+ </button>
855
+ </div>
856
+ </div>
857
+ );
858
+ })
859
+ }
860
+ {maintenanceEvents.filter(event => event.status === 'planned' || event.status === 'overdue').length === 0 && (
861
+ <p className="text-gray-500 text-center py-4">Нет запланированного технического обслуживания</p>
862
+ )}
863
+ </div>
864
+ </div>
865
+ </div>
866
+
867
+ {/* Upcoming Maintenance */}
868
+ <div className="bg-white rounded-lg shadow overflow-hidden">
869
+ <div className="p-4 border-b">
870
+ <h3 className="text-lg font-semibold">Предстоящие ТО</h3>
871
+ </div>
872
+ <div className="p-4">
873
+ <ul className="space-y-3">
874
+ {maintenanceEvents
875
+ .filter(event => event.status === 'planned')
876
+ .slice(0, 5)
877
+ .map((event, index) => {
878
+ const equipment = equipmentList.find(eq => eq.id === event.equipmentId);
879
+ const daysLeft = Math.ceil(
880
+ (new Date(event.date) - new Date()) / (1000 * 60 * 60 * 24)
881
+ );
882
+
883
+ return (
884
+ <li key={index} className="p-3 border rounded-lg hover:bg-gray-50 cursor-pointer">
885
+ <div className="flex justify-between items-start">
886
+ <div>
887
+ <h4 className="font-medium">{equipment?.name || 'Неизвестное оборудование'}</h4>
888
+ <p className="text-sm text-gray-500">{equipment?.type || ''}</p>
889
+ </div>
890
+ <span className="text-sm">
891
+ {daysLeft < 0 ? (
892
+ <span className="text-red-500">Просрочено на {Math.abs(daysLeft)} дн.</span>
893
+ ) : daysLeft === 0 ? (
894
+ <span className="text-yellow-600">Сегодня</span>
895
+ ) : (
896
+ `Через ${daysLeft} дн.`
897
+ )}
898
+ </span>
899
+ </div>
900
+ <div className="mt-2 flex justify-between items-center">
901
+ <span className="text-sm">{formatDate(event.date)}</span>
902
+ <span className="text-sm">{event.responsible}</span>
903
+ </div>
904
+ </li>
905
+ );
906
+ })
907
+ }
908
+ {maintenanceEvents.filter(event => event.status === 'planned').length === 0 && (
909
+ <p className="text-gray-500 text-center py-4">Нет предстоящих ТО</p>
910
+ )}
911
+ </ul>
912
+ </div>
913
+ </div>
914
+ </div>
915
+ </div>
916
+ );
917
+
918
+ // Render reports content
919
+ const renderReports = () => (
920
+ <div className="p-6">
921
+ <h2 className="text-2xl font-bold mb-6">Отчеты</h2>
922
+
923
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
924
+ {/* Maintenance Report */}
925
+ <div className="bg-white rounded-lg shadow overflow-hidden">
926
+ <div className="p-4 border-b">
927
+ <h3 className="text-lg font-semibold">Отчет по техническому обслуживанию</h3>
928
+ </div>
929
+ <div className="p-4">
930
+ <div className="mb-4">
931
+ <label className="block text-sm font-medium text-gray-700 mb-2">Период</label>
932
+ <div className="flex space-x-2">
933
+ <input
934
+ type="date"
935
+ className="border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
936
+ />
937
+ <span className="flex items-center">-</span>
938
+ <input
939
+ type="date"
940
+ className="border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
941
+ />
942
+ </div>
943
+ </div>
944
+
945
+ <div className="mb-4">
946
+ <label className="block text-sm font-medium text-gray-700 mb-2">Оборудование</label>
947
+ <select className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
948
+ <option value="">Все оборудование</option>
949
+ {equipmentList.map(eq => (
950
+ <option key={eq.id} value={eq.id}>{eq.name}</option>
951
+ ))}
952
+ </select>
953
+ </div>
954
+
955
+ <button className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg w-full">
956
+ Сформировать отчет
957
+ </button>
958
+ </div>
959
+
960
+ <div className="p-4 border-t">
961
+ <h4 className="font-medium mb-3">Показатели эффективности</h4>
962
+ <div className="space-y-2">
963
+ <div className="flex justify-between">
964
+ <span>Количество выполненных ТО:</span>
965
+ <span className="font-medium">{maintenanceEvents.filter(e => e.status === 'completed').length}</span>
966
+ </div>
967
+ <div className="flex justify-between">
968
+ <span>Количество просроченных ТО:</span>
969
+ <span className="font-medium">{maintenanceEvents.filter(e => e.status === 'overdue').length}</span>
970
+ </div>
971
+ <div className="flex justify-between">
972
+ <span>Среднее время между ТО:</span>
973
+ <span className="font-medium">35 дней</span>
974
+ </div>
975
+ </div>
976
+ </div>
977
+ </div>
978
+
979
+ {/* Equipment Utilization Report */}
980
+ <div className="bg-white rounded-lg shadow overflow-hidden">
981
+ <div className="p-4 border-b">
982
+ <h3 className="text-lg font-semibold">Отчет по использованию оборудования</h3>
983
+ </div>
984
+ <div className="p-4">
985
+ <div className="space-y-4">
986
+ <div>
987
+ <p className="text-sm text-gray-500">Общая доступность оборудования</p>
988
+ <div className="mt-2 relative pt-1">
989
+ <div className="flex items-center justify-between">
990
+ <div>
991
+ <span className="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full bg-blue-200 text-blue-800">
992
+ 92%
993
+ </span>
994
+ </div>
995
+ <div className="text-right">
996
+ <span className="text-xs font-semibold inline-block">
997
+ 8% простои
998
+ </span>
999
+ </div>
1000
+ </div>
1001
+ <div className="overflow-hidden h-2 mb-4 text-xs flex rounded bg-blue-200">
1002
+ <div style={{ width: "92%" }} className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-blue-500"></div>
1003
+ </div>
1004
+ </div>
1005
+ </div>
1006
+
1007
+ <div className="grid grid-cols-2 gap-4">
1008
+ <div className="bg-gray-50 p-3 rounded-lg">
1009
+ <p className="text-sm text-gray-500">Среднее время безотказной работы</p>
1010
+ <p className="text-xl font-bold">142 дня</p>
1011
+ </div>
1012
+ <div className="bg-gray-50 p-3 rounded-lg">
1013
+ <p className="text-sm text-gray-500">Среднее время восстановления</p>
1014
+ <p className="text-xl font-bold">2.3 дня</p>
1015
+ </div>
1016
+ </div>
1017
+
1018
+ <div>
1019
+ <h4 className="font-medium mb-2">Топ 5 проблемных оборудования</h4>
1020
+ <ul className="space-y-2">
1021
+ {equipmentList
1022
+ .sort((a, b) => {
1023
+ const repairsA = repairEvents.filter(r => r.equipmentId === a.id && r.endDate).length;
1024
+ const repairsB = repairEvents.filter(r => r.equipmentId === b.id && r.endDate).length;
1025
+ return repairsB - repairsA;
1026
+ })
1027
+ .slice(0, 5)
1028
+ .map((eq, index) => {
1029
+ const repairCount = repairEvents.filter(r => r.equipmentId === eq.id && r.endDate).length;
1030
+ return (
1031
+ <li key={eq.id} className="flex justify-between items-center">
1032
+ <span>{index + 1}. {eq.name}</span>
1033
+ <span className="bg-red-100 text-red-800 text-xs px-2 py-1 rounded-full">
1034
+ {repairCount} аварии
1035
+ </span>
1036
+ </li>
1037
+ );
1038
+ })
1039
+ }
1040
+ </ul>
1041
+ </div>
1042
+ </div>
1043
+ </div>
1044
+ </div>
1045
+ </div>
1046
+ </div>
1047
+ );
1048
+
1049
+ // Render content based on active tab
1050
+ const renderContent = () => {
1051
+ switch (activeTab) {
1052
+ case 'dashboard':
1053
+ return renderDashboard();
1054
+ case 'schedule':
1055
+ return renderSchedule();
1056
+ case 'equipment':
1057
+ return renderEquipment();
1058
+ case 'repairs':
1059
+ return renderRepairs();
1060
+ case 'maintenance':
1061
+ return renderMaintenance();
1062
+ case 'reports':
1063
+ return renderReports();
1064
+ case 'users':
1065
+ return renderUsers();
1066
+ default:
1067
+ return renderDashboard();
1068
+ }
1069
+ };
1070
+
1071
+ return (
1072
+ <div className="flex h-screen overflow-hidden bg-gray-50">
1073
+ {/* Sidebar */}
1074
+ <div className={`sidebar bg-blue-800 text-white ${
1075
+ isSidebarCollapsed ? 'collapsed w-16' : 'w-64'
1076
+ } transition-all duration-300 flex flex-col`}>
1077
+ <div className="p-4 flex items-center justify-between border-b border-blue-700">
1078
+ <div className="flex items-center">
1079
+ <i className="fas fa-cogs text-2xl mr-3"></i>
1080
+ {!isSidebarCollapsed && <span className="sidebar-text text-xl font-bold">P4 System</span>}
1081
+ </div>
1082
+ <button
1083
+ onClick={() => setIsSidebarCollapsed(!isSidebarCollapsed)}
1084
+ className="text-white hover:text-blue-200"
1085
+ >
1086
+ <i className="fas fa-bars"></i>
1087
+ </button>
1088
+ </div>
1089
+
1090
+ <div className="flex-1 overflow-y-auto">
1091
+ <nav className="p-4">
1092
+ <div className="mb-6">
1093
+ <p className="uppercase text-xs font-semibold text-blue-300 mb-2">Основное</p>
1094
+ <button
1095
+ onClick={() => setActiveTab('dashboard')}
1096
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1097
+ activeTab === 'dashboard'
1098
+ ? 'bg-blue-700 text-white'
1099
+ : 'hover:bg-blue-700 text-white'
1100
+ } mb-2`}
1101
+ >
1102
+ <i className="fas fa-tachometer-alt mr-3"></i>
1103
+ {!isSidebarCollapsed && <span>Главная</span>}
1104
+ </button>
1105
+ <button
1106
+ onClick={() => setActiveTab('schedule')}
1107
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1108
+ activeTab === 'schedule'
1109
+ ? 'bg-blue-700 text-white'
1110
+ : 'hover:bg-blue-700 text-white'
1111
+ } mb-2`}
1112
+ >
1113
+ <i className="fas fa-calendar-alt mr-3"></i>
1114
+ {!isSidebarCollapsed && (
1115
+ <>
1116
+ <span>График ТО</span>
1117
+ <span className="ml-auto bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">3</span>
1118
+ </>
1119
+ )}
1120
+ </button>
1121
+ <button
1122
+ onClick={() => setActiveTab('equipment')}
1123
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1124
+ activeTab === 'equipment'
1125
+ ? 'bg-blue-700 text-white'
1126
+ : 'hover:bg-blue-700 text-white'
1127
+ } mb-2`}
1128
+ >
1129
+ <i className="fas fa-tools mr-3"></i>
1130
+ {!isSidebarCollapsed && <span>Оборудование</span>}
1131
+ </button>
1132
+ </div>
1133
+
1134
+ <div className="mb-6">
1135
+ <p className="uppercase text-xs font-semibold text-blue-300 mb-2">Учет</p>
1136
+ <button
1137
+ onClick={() => setActiveTab('repairs')}
1138
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1139
+ activeTab === 'repairs'
1140
+ ? 'bg-blue-700 text-white'
1141
+ : 'hover:bg-blue-700 text-white'
1142
+ } mb-2`}
1143
+ >
1144
+ <i className="fas fa-wrench mr-3"></i>
1145
+ {!isSidebarCollapsed && <span>Ремонтные работы</span>}
1146
+ </button>
1147
+ <button
1148
+ onClick={() => setActiveTab('maintenance')}
1149
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1150
+ activeTab === 'maintenance'
1151
+ ? 'bg-blue-700 text-white'
1152
+ : 'hover:bg-blue-700 text-white'
1153
+ } mb-2`}
1154
+ >
1155
+ <i className="fas fa-clipboard-check mr-3"></i>
1156
+ {!isSidebarCollapsed && <span>Техническое обслуживание</span>}
1157
+ </button>
1158
+ <button
1159
+ onClick={() => setActiveTab('reports')}
1160
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1161
+ activeTab === 'reports'
1162
+ ? 'bg-blue-700 text-white'
1163
+ : 'hover:bg-blue-700 text-white'
1164
+ } mb-2`}
1165
+ >
1166
+ <i className="fas fa-chart-line mr-3"></i>
1167
+ {!isSidebarCollapsed && <span>Отчеты</span>}
1168
+ </button>
1169
+ </div>
1170
+
1171
+ <div className="mb-6">
1172
+ <p className="uppercase text-xs font-semibold text-blue-300 mb-2">Настройки</p>
1173
+ <button
1174
+ className="flex items-center py-2 px-3 rounded-lg hover:bg-blue-700 text-white mb-2 w-full text-left"
1175
+ >
1176
+ <i className="fas fa-bell mr-3"></i>
1177
+ {!isSidebarCollapsed && <span>Оповещения</span>}
1178
+ </button>
1179
+ <button
1180
+ onClick={() => setActiveTab('users')}
1181
+ className={`flex items-center py-2 px-3 rounded-lg w-full text-left ${
1182
+ activeTab === 'users'
1183
+ ? 'bg-blue-700 text-white'
1184
+ : 'hover:bg-blue-700 text-white'
1185
+ } mb-2`}
1186
+ >
1187
+ <i className="fas fa-users-cog mr-3"></i>
1188
+ {!isSidebarCollapsed && <span>Пользователи</span>}
1189
+ </button>
1190
+ </div>
1191
+ </nav>
1192
+ </div>
1193
+
1194
+ <div className="p-4 border-t border-blue-700">
1195
+ <div className="flex items-center">
1196
+ <img
1197
+ src="https://randomuser.me/api/portraits/men/32.jpg"
1198
+ alt="User"
1199
+ className="w-10 h-10 rounded-full mr-3"
1200
+ />
1201
+ {!isSidebarCollapsed && (
1202
+ <div className="sidebar-text">
1203
+ <p className="font-medium">Иван Петров</p>
1204
+ <p className="text-xs text-blue-300">Администратор</p>
1205
+ </div>
1206
+ )}
1207
+ </div>
1208
+ </div>
1209
+ </div>
1210
+
1211
+ {/* Main Content */}
1212
+ <div className="main-content flex-1 overflow-auto">
1213
+ {/* Header */}
1214
+ <header className="bg-white shadow-sm py-4 px-6 flex items-center justify-between">
1215
+ <h1 className="text-2xl font-bold text-gray-800">
1216
+ {activeTab === 'dashboard' && 'Главная панель'}
1217
+ {activeTab === 'schedule' && 'График ТО'}
1218
+ {activeTab === 'equipment' && 'Оборудование'}
1219
+ {activeTab === 'repairs' && 'Ремонтные работы'}
1220
+ {activeTab === 'maintenance' && 'Техническое обслуживание'}
1221
+ {activeTab === 'reports' && 'Отчеты'}
1222
+ {activeTab === 'users' && 'Пользователи'}
1223
+ </h1>
1224
+
1225
+ <div className="flex items-center space-x-4">
1226
+ <div className="relative">
1227
+ <button className="p-2 rounded-full hover:bg-gray-100 relative">
1228
+ <i className="fas fa-bell text-gray-600"></i>
1229
+ <span className="notification-badge bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center absolute top-0 right-0">5</span>
1230
+ </button>
1231
+ </div>
1232
+
1233
+ <div className="relative">
1234
+ <input
1235
+ type="text"
1236
+ placeholder="Поиск..."
1237
+ className="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
1238
+ />
1239
+ <i className="fas fa-search absolute left-3 top-3 text-gray-400"></i>
1240
+ </div>
1241
+ </div>
1242
+ </header>
1243
+
1244
+ {/* Content */}
1245
+ <div className="p-6">
1246
+ {renderContent()}
1247
+ </div>
1248
+ </div>
1249
+
1250
+ {/* Add Equipment Modal */}
1251
+ {showAddModal && (
1252
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
1253
+ <div className="bg-white rounded-lg shadow-xl w-full max-w-2xl">
1254
+ <div className="p-4 border-b flex justify-between items-center">
1255
+ <h3 className="text-lg font-semibold">Добавить новое оборудование</h3>
1256
+ <button
1257
+ onClick={closeAddModal}
1258
+ className="text-gray-500 hover:text-gray-700"
1259
+ >
1260
+ <i className="fas fa-times"></i>
1261
+ </button>
1262
+ </div>
1263
+
1264
+ <div className="p-6">
1265
+ <form ref={formRef}>
1266
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1267
+ <div>
1268
+ <label className="block text-sm font-medium text-gray-700 mb-1">Название оборудования</label>
1269
+ <input
1270
+ type="text"
1271
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1272
+ required
1273
+ value={newEquipment.name}
1274
+ onChange={(e) => setNewEquipment({...newEquipment, name: e.target.value})}
1275
+ />
1276
+ </div>
1277
+
1278
+ <div>
1279
+ <label className="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
1280
+ <select
1281
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1282
+ required
1283
+ value={newEquipment.type}
1284
+ onChange={(e) => setNewEquipment({...newEquipment, type: e.target.value})}
1285
+ >
1286
+ <option value="">Выберите тип</option>
1287
+ <option>Станок</option>
1288
+ <option>Конвейер</option>
1289
+ <option>Компрессор</option>
1290
+ <option>Генератор</option>
1291
+ <option>Насос</option>
1292
+ </select>
1293
+ </div>
1294
+
1295
+ <div>
1296
+ <label className="block text-sm font-medium text-gray-700 mb-1">Серийный номер</label>
1297
+ <input
1298
+ type="text"
1299
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1300
+ required
1301
+ value={newEquipment.serial}
1302
+ onChange={(e) => setNewEquipment({...newEquipment, serial: e.target.value})}
1303
+ />
1304
+ </div>
1305
+
1306
+ <div>
1307
+ <label className="block text-sm font-medium text-gray-700 mb-1">Инвентарный номер</label>
1308
+ <input
1309
+ type="text"
1310
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1311
+ required
1312
+ value={newEquipment.inventory}
1313
+ onChange={(e) => setNewEquipment({...newEquipment, inventory: e.target.value})}
1314
+ />
1315
+ </div>
1316
+
1317
+ <div>
1318
+ <label className="block text-sm font-medium text-gray-700 mb-1">Дата ввода в эксплуатацию</label>
1319
+ <input
1320
+ type="date"
1321
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1322
+ required
1323
+ value={newEquipment.commissionDate}
1324
+ onChange={(e) => setNewEquipment({...newEquipment, commissionDate: e.target.value})}
1325
+ />
1326
+ </div>
1327
+
1328
+ <div>
1329
+ <label className="block text-sm font-medium text-gray-700 mb-1">Периодичность ТО (дни)</label>
1330
+ <input
1331
+ type="number"
1332
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1333
+ required
1334
+ min="1"
1335
+ value={newEquipment.maintenanceInterval}
1336
+ onChange={(e) => setNewEquipment({...newEquipment, maintenanceInterval: e.target.value})}
1337
+ />
1338
+ </div>
1339
+
1340
+ <div className="md:col-span-2">
1341
+ <label className="block text-sm font-medium text-gray-700 mb-1">Описание</label>
1342
+ <textarea
1343
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1344
+ rows="3"
1345
+ value={newEquipment.description}
1346
+ onChange={(e) => setNewEquipment({...newEquipment, description: e.target.value})}
1347
+ ></textarea>
1348
+ </div>
1349
+ </div>
1350
+ </form>
1351
+ </div>
1352
+
1353
+ <div className="p-4 border-t flex justify-end space-x-3">
1354
+ <button
1355
+ onClick={closeAddModal}
1356
+ className="px-4 py-2 border rounded-lg hover:bg-gray-50"
1357
+ >
1358
+ Отмена
1359
+ </button>
1360
+ <button
1361
+ onClick={saveEquipment}
1362
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1363
+ >
1364
+ Сохранить
1365
+ </button>
1366
+ </div>
1367
+ </div>
1368
+ </div>
1369
+ )}
1370
+
1371
+ {/* Edit Equipment Modal */}
1372
+ {showEditModal && editEquipment && (
1373
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
1374
+ <div className="bg-white rounded-lg shadow-xl w-full max-w-2xl">
1375
+ <div className="p-4 border-b flex justify-between items-center">
1376
+ <h3 className="text-lg font-semibold">Редактировать оборудование</h3>
1377
+ <button
1378
+ onClick={closeEditModal}
1379
+ className="text-gray-500 hover:text-gray-700"
1380
+ >
1381
+ <i className="fas fa-times"></i>
1382
+ </button>
1383
+ </div>
1384
+
1385
+ <div className="p-6">
1386
+ <form ref={editFormRef}>
1387
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1388
+ <div>
1389
+ <label className="block text-sm font-medium text-gray-700 mb-1">Название оборудования</label>
1390
+ <input
1391
+ type="text"
1392
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1393
+ required
1394
+ value={editEquipment.name}
1395
+ onChange={(e) => setEditEquipment({...editEquipment, name: e.target.value})}
1396
+ />
1397
+ </div>
1398
+
1399
+ <div>
1400
+ <label className="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
1401
+ <select
1402
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1403
+ required
1404
+ value={editEquipment.type}
1405
+ onChange={(e) => setEditEquipment({...editEquipment, type: e.target.value})}
1406
+ >
1407
+ <option value="">Выберите тип</option>
1408
+ <option>Станок</option>
1409
+ <option>Конвейер</option>
1410
+ <option>Ко��прессор</option>
1411
+ <option>Генератор</option>
1412
+ <option>Насос</option>
1413
+ </select>
1414
+ </div>
1415
+
1416
+ <div>
1417
+ <label className="block text-sm font-medium text-gray-700 mb-1">Серийный номер</label>
1418
+ <input
1419
+ type="text"
1420
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1421
+ required
1422
+ value={editEquipment.serial}
1423
+ onChange={(e) => setEditEquipment({...editEquipment, serial: e.target.value})}
1424
+ />
1425
+ </div>
1426
+
1427
+ <div>
1428
+ <label className="block text-sm font-medium text-gray-700 mb-1">Инвентарный номер</label>
1429
+ <input
1430
+ type="text"
1431
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1432
+ required
1433
+ value={editEquipment.inventory}
1434
+ onChange={(e) => setEditEquipment({...editEquipment, inventory: e.target.value})}
1435
+ />
1436
+ </div>
1437
+
1438
+ <div>
1439
+ <label className="block text-sm font-medium text-gray-700 mb-1">Дата ввода в эксплуатацию</label>
1440
+ <input
1441
+ type="date"
1442
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1443
+ required
1444
+ value={editEquipment.commissionDate}
1445
+ onChange={(e) => setEditEquipment({...editEquipment, commissionDate: e.target.value})}
1446
+ />
1447
+ </div>
1448
+
1449
+ <div>
1450
+ <label className="block text-sm font-medium text-gray-700 mb-1">Периодичность ТО (дни)</label>
1451
+ <input
1452
+ type="number"
1453
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1454
+ required
1455
+ min="1"
1456
+ value={editEquipment.maintenanceInterval}
1457
+ onChange={(e) => setEditEquipment({...editEquipment, maintenanceInterval: e.target.value})}
1458
+ />
1459
+ </div>
1460
+
1461
+ <div className="md:col-span-2">
1462
+ <label className="block text-sm font-medium text-gray-700 mb-1">Описание</label>
1463
+ <textarea
1464
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1465
+ rows="3"
1466
+ value={editEquipment.description}
1467
+ onChange={(e) => setEditEquipment({...editEquipment, description: e.target.value})}
1468
+ ></textarea>
1469
+ </div>
1470
+ </div>
1471
+ </form>
1472
+ </div>
1473
+
1474
+ <div className="p-4 border-t flex justify-end space-x-3">
1475
+ <button
1476
+ onClick={closeEditModal}
1477
+ className="px-4 py-2 border rounded-lg hover:bg-gray-50"
1478
+ >
1479
+ Отмена
1480
+ </button>
1481
+ <button
1482
+ onClick={updateEquipment}
1483
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1484
+ >
1485
+ Сохранить изменения
1486
+ </button>
1487
+ </div>
1488
+ </div>
1489
+ </div>
1490
+ )}
1491
+
1492
+ {/* Maintenance Details Modal */}
1493
+ {showMaintenanceModal && (
1494
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
1495
+ <div className="bg-white rounded-lg shadow-xl w-full max-w-2xl">
1496
+ <div className="p-4 border-b flex justify-between items-center">
1497
+ <h3 className="text-lg font-semibold">Техническое обслуживание</h3>
1498
+ <button
1499
+ onClick={() => setShowMaintenanceModal(false)}
1500
+ className="text-gray-500 hover:text-gray-700"
1501
+ >
1502
+ <i className="fas fa-times"></i>
1503
+ </button>
1504
+ </div>
1505
+
1506
+ <div className="p-6">
1507
+ <div className="mb-6">
1508
+ <h4 className="font-medium text-lg mb-2" id="maintenanceEquipmentName">
1509
+ {equipmentList.find(eq => eq.id === editEquipment?.id)?.name || 'Выберите оборудование'}
1510
+ </h4>
1511
+ <div className="grid grid-cols-2 gap-4 mb-4">
1512
+ <div>
1513
+ <p className="text-sm text-gray-500">П��следнее ТО</p>
1514
+ <p className="font-medium">{formatDate(editEquipment?.lastMaintenance || '')}</p>
1515
+ </div>
1516
+ <div>
1517
+ <p className="text-sm text-gray-500">Следующее ТО</p>
1518
+ <p className="font-medium">{formatDate(editEquipment?.nextMaintenance || '')}</p>
1519
+ </div>
1520
+ </div>
1521
+ </div>
1522
+
1523
+ <div className="mb-6">
1524
+ <label className="block text-sm font-medium text-gray-700 mb-1">Дата ТО</label>
1525
+ <input
1526
+ type="date"
1527
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1528
+ required
1529
+ />
1530
+ </div>
1531
+
1532
+ <div className="mb-6">
1533
+ <label className="block text-sm font-medium text-gray-700 mb-1">Ответственный</label>
1534
+ <select
1535
+ className="w-full border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
1536
+ required
1537
+ >
1538
+ <option>Иванов А.П.</option>
1539
+ <option>Петров И.С.</option>
1540
+ <option>Сидоров В.М.</option>
1541
+ <option>Кузнецова Е.В.</option>
1542
+ </select>
1543
+ </div>
1544
+
1545
+ <div className="mb-6">
1546
+ <h4 className="font-medium mb-2">Задачи ТО</h4>
1547
+ <div className="space-y-2">
1548
+ <div className="flex items-center">
1549
+ <input type="checkbox" id="task1" className="mr-2" />
1550
+ <label htmlFor="task1">Проверка и регулировка ЧПУ</label>
1551
+ </div>
1552
+ <div className="flex items-center">
1553
+ <input type="checkbox" id="task2" className="mr-2" />
1554
+ <label htmlFor="task2">Смазка направляющих</label>
1555
+ </div>
1556
+ <div className="flex items-center">
1557
+ <input type="checkbox" id="task3" className="mr-2" />
1558
+ <label htmlFor="task3">Замена фильтров</label>
1559
+ </div>
1560
+ </div>
1561
+ </div>
1562
+ </div>
1563
+
1564
+ <div className="p-4 border-t flex justify-end space-x-3">
1565
+ <button
1566
+ onClick={() => setShowMaintenanceModal(false)}
1567
+ className="px-4 py-2 border rounded-lg hover:bg-gray-50"
1568
+ >
1569
+ Отмена
1570
+ </button>
1571
+ <button
1572
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1573
+ >
1574
+ Сохранить изменения
1575
+ </button>
1576
+ </div>
1577
+ </div>
1578
+ </div>
1579
+ )}
1580
+ </div>
1581
+ );
1582
+ }
1583
+ ```
prompts.txt ADDED
File without changes