requireAdmin(); $pdo = DB::connection(); $stmt = $pdo->query(" SELECT id, name, code, description, is_active, show_on_home, created_at FROM boards ORDER BY id DESC "); $boards = $stmt->fetchAll(); View::render('admin/boards/index', [ 'boards' => $boards, 'user' => Auth::user(), ]); } public function create(): void { $this->requireAdmin(); View::render('admin/boards/create', [ 'user' => Auth::user(), ]); } public function store(): void { $this->requireAdmin(); $name = trim($_POST['name'] ?? ''); $code = trim($_POST['code'] ?? ''); $description = trim($_POST['description'] ?? ''); $isActive = isset($_POST['is_active']) ? 1 : 0; if ($name === '' || $code === '') { $_SESSION['error'] = 'Заполните название и код доски'; header('Location: /admin/boards/create'); exit; } $pdo = DB::connection(); $stmt = $pdo->prepare(" INSERT INTO boards (name, code, description, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW()) "); $stmt->execute([ $name, $code, $description !== '' ? $description : null, $isActive, ]); $showOnHome = isset($_POST['show_on_home']) ? 1 : 0; header('Location: /admin/boards'); exit; } public function showModal(): void { $this->requireAdmin(); $boardId = (int)($_GET['id'] ?? 0); if ($boardId <= 0) { http_response_code(400); echo 'Board ID is required'; return; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT b.*, COUNT(t.id) AS task_count, SUM(CASE WHEN t.status = 'NEW' THEN 1 ELSE 0 END) AS new_count, SUM(CASE WHEN t.status = 'IN_PROGRESS' THEN 1 ELSE 0 END) AS in_progress_count, SUM(CASE WHEN t.status = 'REVIEW' THEN 1 ELSE 0 END) AS review_count, SUM(CASE WHEN t.status = 'DONE' THEN 1 ELSE 0 END) AS done_count, SUM(CASE WHEN t.status = 'CANCELED' THEN 1 ELSE 0 END) AS canceled_count, SUM(CASE WHEN t.status = 'OVERDUE' THEN 1 ELSE 0 END) AS overdue_count FROM boards b LEFT JOIN tasks t ON t.board_id = b.id WHERE b.id = ? GROUP BY b.id LIMIT 1 "); $stmt->execute([$boardId]); $board = $stmt->fetch(); if (!$board) { http_response_code(404); echo 'Board not found'; return; } View::render('admin/boards/modal', [ 'board' => $board, 'user' => Auth::user(), ], null); } public function access(): void { $this->requireAdmin(); $boardId = (int)($_GET['id'] ?? 0); if ($boardId <= 0) { http_response_code(400); echo 'Board ID is required'; return; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT id, name, code, description FROM boards WHERE id = ? LIMIT 1 "); $stmt->execute([$boardId]); $board = $stmt->fetch(); if (!$board) { http_response_code(404); echo 'Доска не найдена'; return; } $groups = $pdo->query(" SELECT id, name FROM ad_groups ORDER BY name ASC ")->fetchAll(); $stmt = $pdo->prepare(" SELECT group_id FROM board_access_groups WHERE board_id = ? "); $stmt->execute([$boardId]); $selectedGroupIds = array_map('intval', array_column($stmt->fetchAll(), 'group_id')); View::render('admin/boards/access', [ 'board' => $board, 'groups' => $groups, 'selectedGroupIds' => $selectedGroupIds, 'user' => Auth::user(), ]); } public function saveAccess(): void { $this->requireAdmin(); $boardId = (int)($_POST['board_id'] ?? 0); $groupIds = $_POST['group_ids'] ?? []; if ($boardId <= 0) { $_SESSION['error'] = 'Некорректная доска'; header('Location: /admin/boards'); exit; } $pdo = DB::connection(); $pdo->beginTransaction(); try { $stmt = $pdo->prepare("DELETE FROM board_access_groups WHERE board_id = ?"); $stmt->execute([$boardId]); if (is_array($groupIds)) { $stmt = $pdo->prepare(" INSERT INTO board_access_groups (board_id, group_id, created_at) VALUES (?, ?, NOW()) "); foreach ($groupIds as $groupId) { $groupId = (int)$groupId; if ($groupId > 0) { $stmt->execute([$boardId, $groupId]); } } } $pdo->commit(); $_SESSION['success'] = 'Права доступа к доске сохранены'; } catch (\Throwable $e) { $pdo->rollBack(); $_SESSION['error'] = 'Ошибка сохранения прав: ' . $e->getMessage(); } header('Location: /admin/boards/access?id=' . $boardId); exit; } public function statuses(): void { $this->requireAdmin(); $boardId = (int)($_GET['id'] ?? 0); if ($boardId <= 0) { http_response_code(400); echo 'Board ID is required'; return; } $pdo = DB::connection(); $stmt = $pdo->prepare("SELECT id, name, code FROM boards WHERE id = ? LIMIT 1"); $stmt->execute([$boardId]); $board = $stmt->fetch(); if (!$board) { http_response_code(404); echo 'Доска не найдена'; return; } $stmt = $pdo->prepare(" SELECT * FROM board_statuses WHERE board_id = ? ORDER BY sort_order ASC, id ASC "); $stmt->execute([$boardId]); $statuses = $stmt->fetchAll(); View::render('admin/boards/statuses/index', [ 'board' => $board, 'statuses' => $statuses, 'user' => Auth::user(), ]); } public function createStatus(): void { $this->requireAdmin(); $boardId = (int)($_GET['board_id'] ?? 0); $pdo = DB::connection(); $stmt = $pdo->prepare("SELECT id, name, code FROM boards WHERE id = ? LIMIT 1"); $stmt->execute([$boardId]); $board = $stmt->fetch(); if (!$board) { http_response_code(404); echo 'Доска не найдена'; return; } View::render('admin/boards/statuses/create', [ 'board' => $board, 'user' => Auth::user(), ]); } public function storeStatus(): void { $this->requireAdmin(); $boardId = (int)($_POST['board_id'] ?? 0); $code = trim($_POST['code'] ?? ''); $name = trim($_POST['name'] ?? ''); $color = trim($_POST['color'] ?? 'secondary'); $sortOrder = (int)($_POST['sort_order'] ?? 100); $isDone = isset($_POST['is_done']) ? 1 : 0; $isActive = isset($_POST['is_active']) ? 1 : 0; if ($boardId <= 0 || $code === '' || $name === '') { $_SESSION['error'] = 'Заполните обязательные поля статуса'; header('Location: /admin/boards/statuses/create?board_id=' . $boardId); exit; } $pdo = DB::connection(); $stmt = $pdo->prepare(" INSERT INTO board_statuses ( board_id, code, name, color, sort_order, is_done, is_active, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW()) "); $stmt->execute([ $boardId, $code, $name, $color, $sortOrder, $isDone, $isActive, ]); header('Location: /admin/boards/statuses?id=' . $boardId); exit; } public function editStatus(): void { $this->requireAdmin(); $statusId = (int)($_GET['id'] ?? 0); $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT bs.*, b.name AS board_name, b.code AS board_code FROM board_statuses bs INNER JOIN boards b ON b.id = bs.board_id WHERE bs.id = ? LIMIT 1 "); $stmt->execute([$statusId]); $status = $stmt->fetch(); if (!$status) { http_response_code(404); echo 'Статус не найден'; return; } View::render('admin/boards/statuses/edit', [ 'status' => $status, 'user' => Auth::user(), ]); } public function updateStatus(): void { $this->requireAdmin(); $statusId = (int)($_POST['id'] ?? 0); $code = trim($_POST['code'] ?? ''); $name = trim($_POST['name'] ?? ''); $color = trim($_POST['color'] ?? 'secondary'); $sortOrder = (int)($_POST['sort_order'] ?? 100); $isDone = isset($_POST['is_done']) ? 1 : 0; $isActive = isset($_POST['is_active']) ? 1 : 0; if ($statusId <= 0 || $code === '' || $name === '') { $_SESSION['error'] = 'Заполните обязательные поля'; header('Location: /admin'); exit; } $pdo = DB::connection(); $stmt = $pdo->prepare("SELECT board_id FROM board_statuses WHERE id = ? LIMIT 1"); $stmt->execute([$statusId]); $boardId = (int)$stmt->fetchColumn(); $stmt = $pdo->prepare(" UPDATE board_statuses SET code = ?, name = ?, color = ?, sort_order = ?, is_done = ?, is_active = ?, updated_at = NOW() WHERE id = ? "); $stmt->execute([ $code, $name, $color, $sortOrder, $isDone, $isActive, $statusId, ]); header('Location: /admin/boards/statuses?id=' . $boardId); exit; } public function delete(): void { $this->requireAdmin(); $boardId = (int)($_POST['id'] ?? 0); if ($boardId <= 0) { $_SESSION['error'] = 'Доска не найдена'; header('Location: /admin/boards'); exit; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT id, name, code FROM boards WHERE id = ? LIMIT 1 "); $stmt->execute([$boardId]); $board = $stmt->fetch(); if (!$board) { $_SESSION['error'] = 'Доска не найдена'; header('Location: /admin/boards'); exit; } $stmt = $pdo->prepare("DELETE FROM boards WHERE id = ?"); $stmt->execute([$boardId]); $_SESSION['success'] = 'Доска "' . ($board['name'] ?? '') . '" удалена вместе со связанными данными'; header('Location: /admin/boards'); exit; } public function edit(): void { $this->requireAdmin(); $boardId = (int)($_GET['id'] ?? 0); if ($boardId <= 0) { http_response_code(400); echo 'Board ID is required'; return; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT id, name, code, description, is_active, show_on_home FROM boards WHERE id = ? LIMIT 1 "); $stmt->execute([$boardId]); $board = $stmt->fetch(); if (!$board) { http_response_code(404); echo 'Доска не найдена'; return; } View::render('admin/boards/edit', [ 'board' => $board, 'user' => Auth::user(), ]); } public function update(): void { $this->requireAdmin(); $boardId = (int)($_POST['id'] ?? 0); $name = trim((string)($_POST['name'] ?? '')); $code = trim((string)($_POST['code'] ?? '')); $description = trim((string)($_POST['description'] ?? '')); $isActive = isset($_POST['is_active']) ? 1 : 0; if ($boardId <= 0 || $name === '' || $code === '') { $_SESSION['error'] = 'Заполните название и код доски'; header('Location: /admin/boards/edit?id=' . $boardId); exit; } $pdo = DB::connection(); try { $stmt = $pdo->prepare(" UPDATE boards SET name = ?, code = ?, description = ?, is_active = ?, updated_at = NOW() WHERE id = ? "); $stmt->execute([ $name, $code, $description !== '' ? $description : null, $isActive, $boardId, ]); $_SESSION['success'] = 'Доска обновлена'; } catch (\PDOException $e) { $_SESSION['error'] = 'Не удалось обновить доску: ' . $e->getMessage(); header('Location: /admin/boards/edit?id=' . $boardId); exit; } $showOnHome = isset($_POST['show_on_home']) ? 1 : 0; header('Location: /admin/boards'); exit; } public function saveAll(): void { $pdo = DB::connection(); $boardId = (int)$_POST['board_id']; // === ОБНОВЛЕНИЕ ДОСКИ === $stmt = $pdo->prepare(" UPDATE boards SET name = ?, code = ?, description = ?, is_active = ?, show_on_home = ? WHERE id = ? "); $stmt->execute([ $_POST['name'], $_POST['code'], $_POST['description'] ?? null, isset($_POST['is_active']) ? 1 : 0, isset($_POST['show_on_home']) ? 1 : 0, $boardId ]); // === КАСТОМНЫЕ ПОЛЯ === if (!empty($_POST['fields'])) { foreach ($_POST['fields'] as $field) { if (!empty($field['delete'])) { $pdo->prepare("DELETE FROM board_fields WHERE id = ?") ->execute([$field['id']]); continue; } $pdo->prepare(" UPDATE board_fields SET name = ?, field_type = ? WHERE id = ? ")->execute([ $field['name'], $field['type'], $field['id'] ]); } } // === НОВЫЕ ПОЛЯ === if (!empty($_POST['new_fields'])) { foreach ($_POST['new_fields'] as $f) { if (empty($f['name'])) continue; $pdo->prepare(" INSERT INTO board_fields (board_id, code, name) VALUES (?, ?, ?) ")->execute([ $boardId, strtolower(preg_replace('/[^a-z0-9]/', '_', $f['name'])), $f['name'] ]); } } header('Location: ' . $_SERVER['HTTP_REFERER']); } }