Files
site_aegisone/service/pages/engineer/incidents.php
T
2026-05-17 05:22:06 +03:00

221 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../../inc/auth.php';
require_once __DIR__ . '/../../inc/functions.php';
$session = auth_require('engineer', 'technician');
$pdo = getDB();
$message = '';
$userId = $session['user_id'];
$role = $session['user_role'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = (int)($_POST['id'] ?? 0);
$objectId = (int)($_POST['object_id'] ?? 0);
$title = trim($_POST['title'] ?? '');
$desc = trim($_POST['description'] ?? '');
$severity = $_POST['severity'] ?? 'P3';
$assignedTo = (int)($_POST['assigned_to'] ?? 0);
$csrf = $_POST['csrf_token'] ?? '';
if (!auth_csrf_verify($csrf)) {
$message = 'Ошибка безопасности.';
} elseif (!$objectId || !$title) {
$message = 'Объект и название обязательны.';
} else {
try {
if ($id > 0) {
$stmt = $pdo->prepare("UPDATE incidents SET object_id=?, title=?, description=?, severity=?, assigned_to=? WHERE id=?");
$stmt->execute([$objectId, $title, $desc, $severity, $assignedTo ?: null, $id]);
} else {
$stmt = $pdo->prepare("INSERT INTO incidents (object_id, reported_by, assigned_to, title, description, severity) VALUES (?,?,?,?,?,?)");
$stmt->execute([$objectId, $userId, $assignedTo ?: null, $title, $desc, $severity]);
}
$message = 'Инцидент сохранён.';
auth_log($userId, 'incident_save', "Title: $title");
} catch (\PDOException $e) {
$message = 'Ошибка: ' . $e->getMessage();
}
}
}
// Resolve incident
if (isset($_GET['resolve'])) {
$id = (int)$_GET['resolve'];
$csrf = $_GET['csrf'] ?? '';
if (auth_csrf_verify($csrf)) {
$resolution = trim($_GET['resolution'] ?? '');
$pdo->prepare("UPDATE incidents SET status='resolved', resolution=?, resolved_at=NOW() WHERE id=?")
->execute([$resolution ?: 'Выполнено', $id]);
$message = 'Инцидент закрыт.';
}
}
$editInc = null;
if (isset($_GET['edit'])) {
$stmt = $pdo->prepare("SELECT * FROM incidents WHERE id=?");
$stmt->execute([(int)$_GET['edit']]);
$editInc = $stmt->fetch();
}
$statusFilter = $_GET['status'] ?? '';
$severityFilter = $_GET['severity'] ?? '';
$sql = "SELECT i.*, o.name as object_name, u.full_name as assigned_name, r.full_name as reported_name
FROM incidents i
JOIN objects o ON o.id = i.object_id
LEFT JOIN users u ON u.id = i.assigned_to
LEFT JOIN users r ON r.id = i.reported_by
WHERE 1=1";
$params = [];
if ($role === 'technician') {
$sql .= " AND (i.assigned_to = ? OR i.reported_by = ?)";
$params[] = $userId;
$params[] = $userId;
}
if ($statusFilter) {
$sql .= " AND i.status = ?";
$params[] = $statusFilter;
}
if ($severityFilter) {
$sql .= " AND i.severity = ?";
$params[] = $severityFilter;
}
$sql .= " ORDER BY FIELD(i.severity,'P1','P2','P3'), i.created_at DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$incidents = $stmt->fetchAll();
$objects = $pdo->query("SELECT id, name FROM objects WHERE status='active' ORDER BY name")->fetchAll();
$engineers = $pdo->query("SELECT id, full_name FROM users WHERE is_active=1 ORDER BY full_name")->fetchAll();
$csrf = auth_csrf_token();
require __DIR__ . '/../../inc/header.php';
?>
<div class="main-header">
<div>
<h1>Инциденты</h1>
<div class="breadcrumb"><a href="/service/dashboard.php">Главная</a> / Инциденты</div>
</div>
<button class="btn btn-primary btn-sm" onclick="openModal()">+ Создать</button>
</div>
<?php if ($message): ?>
<div class="alert alert-info"><?= htmlspecialchars($message) ?></div>
<?php endif; ?>
<div class="filter-bar">
<form method="get">
<select name="severity" onchange="this.form.submit()">
<option value="">Все уровни</option>
<option value="P1" <?= $severityFilter === 'P1' ? 'selected' : '' ?>>P1 — Критический</option>
<option value="P2" <?= $severityFilter === 'P2' ? 'selected' : '' ?>>P2 — Частичный</option>
<option value="P3" <?= $severityFilter === 'P3' ? 'selected' : '' ?>>P3 — Незначительный</option>
</select>
<select name="status" onchange="this.form.submit()">
<option value="">Все статусы</option>
<option value="open" <?= $statusFilter === 'open' ? 'selected' : '' ?>>Открытые</option>
<option value="in_progress" <?= $statusFilter === 'in_progress' ? 'selected' : '' ?>>В работе</option>
<option value="resolved" <?= $statusFilter === 'resolved' ? 'selected' : '' ?>>Решённые</option>
</select>
<?php if ($statusFilter || $severityFilter): ?><a href="incidents.php" class="btn btn-secondary btn-sm">Сброс</a><?php endif; ?>
</form>
</div>
<div class="card">
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Уровень</th>
<th>Название</th>
<th>Объект</th>
<th>Назначен</th>
<th>Статус</th>
<th>Создан</th>
<th class="actions">Действия</th>
</tr>
</thead>
<tbody>
<?php if (empty($incidents)): ?>
<tr><td colspan="7" style="text-align:center;color:var(--text-muted);">Нет инцидентов</td></tr>
<?php endif; ?>
<?php foreach ($incidents as $inc): ?>
<tr>
<td><span class="badge badge-<?= strtolower($inc['severity']) ?>"><?= $inc['severity'] ?></span></td>
<td><strong><?= htmlspecialchars($inc['title']) ?></strong></td>
<td><?= htmlspecialchars($inc['object_name']) ?></td>
<td><?= htmlspecialchars($inc['assigned_name'] ?: '—') ?></td>
<td><span class="badge badge-<?= $inc['status'] ?>"><?= $inc['status'] ?></span></td>
<td><?= date('d.m.Y H:i', strtotime($inc['created_at'])) ?></td>
<td class="actions">
<a href="?edit=<?= $inc['id'] ?>" class="btn btn-secondary btn-sm">✏️</a>
<?php if ($inc['status'] !== 'resolved' && $inc['status'] !== 'closed'): ?>
<a href="?resolve=<?= $inc['id'] ?>&csrf=<?= $csrf ?>" class="btn btn-success btn-sm" onclick="return confirm('Закрыть инцидент?')">✓</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="modal-overlay <?= $editInc ? 'open' : '' ?>" id="incModal">
<div class="modal">
<button class="modal-close" onclick="closeModal()">&times;</button>
<h2><?= $editInc ? 'Редактировать' : 'Создать' ?> инцидент</h2>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= $csrf ?>">
<input type="hidden" name="id" value="<?= $editInc['id'] ?? 0 ?>">
<div class="form-group">
<label for="title">Название *</label>
<input type="text" id="title" name="title" value="<?= htmlspecialchars($editInc['title'] ?? '') ?>" required>
</div>
<div class="form-row">
<div class="form-group">
<label for="object_id">Объект *</label>
<select id="object_id" name="object_id" required>
<option value="">— выберите —</option>
<?php foreach ($objects as $o): ?>
<option value="<?= $o['id'] ?>" <?= ($editInc['object_id'] ?? 0) === $o['id'] ? 'selected' : '' ?>><?= htmlspecialchars($o['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="severity">Уровень</label>
<select id="severity" name="severity">
<option value="P1" <?= ($editInc['severity'] ?? '') === 'P1' ? 'selected' : '' ?>>P1 — Критический</option>
<option value="P2" <?= ($editInc['severity'] ?? '') === 'P2' ? 'selected' : '' ?>>P2 — Частичный</option>
<option value="P3" <?= ($editInc['severity'] ?? '') === 'P3' ? 'selected' : '' ?>>P3 — Незначительный</option>
</select>
</div>
</div>
<div class="form-group">
<label for="assigned_to">Назначить</label>
<select id="assigned_to" name="assigned_to">
<option value="">— не назначено —</option>
<?php foreach ($engineers as $e): ?>
<option value="<?= $e['id'] ?>" <?= ($editInc['assigned_to'] ?? 0) === $e['id'] ? 'selected' : '' ?>><?= htmlspecialchars($e['full_name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="description">Описание</label>
<textarea id="description" name="description"><?= htmlspecialchars($editInc['description'] ?? '') ?></textarea>
</div>
<button type="submit" class="btn btn-primary">Сохранить</button>
<button type="button" class="btn btn-secondary" onclick="closeModal()">Отмена</button>
</form>
</div>
</div>
<script>
function openModal() { document.getElementById('incModal').classList.add('open'); }
function closeModal() { document.getElementById('incModal').classList.remove('open'); window.location.href = 'incidents.php'; }
</script>
<?php require __DIR__ . '/../../inc/footer.php'; ?>