Files
CRM-IKZ/app/Controllers/AdminBoardController.php
T
2026-07-14 08:19:56 +04:00

591 lines
16 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Controllers;
use App\Core\Auth;
use App\Core\DB;
use App\Core\View;
class AdminBoardController
{
private function requireAdmin(): void
{
if (!Auth::check()) {
header('Location: /login');
exit;
}
$user = Auth::user();
if ((int)($user['is_admin'] ?? 0) !== 1) {
http_response_code(403);
echo 'Доступ запрещен';
exit;
}
}
public function index(): void
{
$this->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']);
}
}