1
0

Добавление проектов

Добавили возможность создавания разных проектов.
This commit is contained in:
2026-01-14 10:46:38 +07:00
parent 04e88cb7fa
commit 719aecd09e
22 changed files with 996 additions and 233 deletions

86
backend/api/project.php Normal file
View File

@@ -0,0 +1,86 @@
<?php
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'POST') {
$data = RestApi::getInput();
$action = $data['action'] ?? null;
$project = new Project();
// Получение данных проекта (проект + колонки + отделы)
if ($action === 'get_project_data') {
$project_id = $data['id_project'] ?? null;
$result = Project::getProjectData($project_id);
if ($result) {
RestApi::response(['success' => true, 'data' => $result]);
} else {
RestApi::response(['success' => false, 'errors' => ['project' => 'Проект не найден']], 404);
}
}
// Установка колонки "Готово" для проекта
if ($action === 'set_ready_column') {
$project_id = $data['id_project'] ?? null;
$column_id = $data['column_id'] ?? null;
$result = Project::setReadyColumn($project_id, $column_id);
RestApi::response($result);
}
// Создание проекта
if ($action === 'create') {
$project->name = $data['name'] ?? '';
$project->id_ready = $data['id_ready'] ?? null;
$result = $project->create();
RestApi::response($result);
}
// Обновление проекта
if ($action === 'update') {
$project->id = $data['id'] ?? null;
$project->name = $data['name'] ?? '';
$project->id_ready = $data['id_ready'] ?? null;
$project->id_order = $data['id_order'] ?? null;
$result = $project->update();
RestApi::response($result);
}
// Удаление проекта
if ($action === 'delete') {
$id = $data['id'] ?? null;
$result = Project::delete($id);
RestApi::response($result);
}
// Метод не указан
if (!$action) {
RestApi::response(['success' => false, 'error' => 'Укажите метод'], 400);
}
}
if ($method === 'GET') {
// Получение всех проектов
// ?active=ID — дополнительно вернуть данные активного проекта
$project = new Project();
$projects = $project->getAll();
$active_id = $_GET['active'] ?? null;
if ($active_id) {
// Возвращаем список проектов + данные активного
$activeData = Project::getProjectData((int)$active_id);
RestApi::response([
'success' => true,
'data' => [
'projects' => $projects,
'active' => $activeData
]
]);
} else {
// Только список проектов
RestApi::response(['success' => true, 'data' => $projects]);
}
}
?>

View File

@@ -7,24 +7,6 @@ if ($method === 'POST') {
$action = $data['action'] ?? null;
$task = new Task();
// Получение колонок
if ($action === 'get_columns') {
$result = $task->getColumns();
RestApi::response(['success' => true, 'data' => $result]);
}
// Получение департаментов
if ($action === 'get_departments') {
$result = $task->getDepartments();
RestApi::response(['success' => true, 'data' => $result]);
}
// Получение меток
if ($action === 'get_labels') {
$result = $task->getLabels();
RestApi::response(['success' => true, 'data' => $result]);
}
// Загрузка изображения
if ($action === 'upload_image') {
$task_id = $data['task_id'] ?? null;
@@ -73,6 +55,7 @@ if ($method === 'POST') {
// Создание задачи
if ($action === 'create') {
$task->id_project = $data['id_project'] ?? null;
$task->id_department = $data['id_department'] ?? null;
$task->id_label = $data['id_label'] ?? null;
$task->id_account = $data['id_account'] ?? null;
@@ -110,8 +93,15 @@ if ($method === 'POST') {
}
if ($method === 'GET') {
// Получение всех задач
// Получение задач проекта
// ?id_project=1 (обязательный)
// ?archive=0 (неархивные, по умолчанию), ?archive=1 (архивные), ?archive=all (все)
$id_project = $_GET['id_project'] ?? null;
if (!$id_project) {
RestApi::response(['success' => false, 'errors' => ['id_project' => 'Проект не указан']], 400);
}
$archive = $_GET['archive'] ?? 0;
if ($archive === 'all') {
$archive = null;
@@ -120,7 +110,7 @@ if ($method === 'GET') {
}
$task = new Task();
$tasks = $task->getAll($archive);
$tasks = $task->getAll($id_project, $archive);
RestApi::response(['success' => true, 'data' => $tasks]);
}

View File

@@ -0,0 +1,241 @@
<?php
class Project extends BaseEntity {
protected $db_name = 'project';
// Свойства проекта
public $id;
public $id_order;
public $name;
public $id_ready;
// Валидация данных
protected function validate() {
static::$error_message = [];
if (!$this->id) {
$this->addError('id', 'ID проекта не указан');
}
if (!$this->name) {
$this->addError('name', 'Название проекта не может быть пустым');
}
return $this->getErrors();
}
// Валидация данных (для create)
protected function validateCreate() {
static::$error_message = [];
if (!$this->name) {
$this->addError('name', 'Название проекта не может быть пустым');
}
return $this->getErrors();
}
// Создание проекта
public function create() {
// Валидация
if ($errors = $this->validateCreate()) {
return $errors;
}
// Получаем максимальный order
$maxOrder = Database::max($this->db_name, 'id_order') ?? 0;
// Вставляем в базу
Database::insert($this->db_name, [
'name' => $this->name,
'id_order' => $maxOrder + 1,
'id_ready' => $this->id_ready
]);
$this->id = Database::id();
return [
'success' => true,
'id' => $this->id
];
}
// Обновление проекта
public function update() {
// Валидация
if ($errors = $this->validate()) {
return $errors;
}
// Проверка что проект существует
$project = Database::get($this->db_name, ['id'], ['id' => $this->id]);
if (!$project) {
$this->addError('project', 'Проект не найден');
return $this->getErrors();
}
// Формируем данные для обновления
$update_data = [
'name' => $this->name
];
// id_ready обновляем только если передан
if ($this->id_ready !== null) {
$update_data['id_ready'] = $this->id_ready;
}
// id_order обновляем только если передан
if ($this->id_order !== null) {
$update_data['id_order'] = $this->id_order;
}
// Обновляем в БД
Database::update($this->db_name, $update_data, [
'id' => $this->id
]);
return ['success' => true];
}
// Удаление проекта
public static function delete($id) {
// Проверка что проект существует
$project = self::checkProject($id);
if (!$project) {
return ['success' => false, 'errors' => ['project' => 'Проект не найден']];
}
// Удаляем все связанные данные
// Удаляем задачи проекта (и их файлы)
$tasks = Database::select('cards_task', ['id'], ['id_project' => $id]);
foreach ($tasks as $task) {
Task::delete($task['id']);
}
// Удаляем колонки проекта
Database::delete('columns', ['id_project' => $id]);
// Удаляем отделы проекта
Database::delete('departments', ['id_project' => $id]);
// Удаляем сам проект
Database::delete('project', ['id' => $id]);
return ['success' => true];
}
// Получение всех проектов
public function getAll() {
return Database::select($this->db_name, [
'id',
'id_order',
'name',
'id_ready'
], [
'ORDER' => ['id_order' => 'ASC']
]);
}
// Получение одного проекта
public static function get($id) {
return Database::get('project', [
'id',
'id_order',
'name',
'id_ready'
], ['id' => $id]);
}
// Получение id_ready (ID колонки "Готово") по ID проекта
public static function getReadyColumnId($project_id) {
$project = Database::get('project', ['id_ready'], ['id' => $project_id]);
return $project ? (int)$project['id_ready'] : null;
}
// Установка id_ready для проекта
public static function setReadyColumn($project_id, $column_id) {
// Проверка что проект существует
$project = self::checkProject($project_id);
if (!$project) {
return ['success' => false, 'errors' => ['project' => 'Проект не найден']];
}
// Проверка что колонка существует и принадлежит этому проекту
$column = Database::get('columns', ['id', 'id_project'], ['id' => $column_id]);
if (!$column || (int)$column['id_project'] !== (int)$project_id) {
return ['success' => false, 'errors' => ['column' => 'Колонка не найдена или не принадлежит проекту']];
}
// Обновляем id_ready
Database::update('project', [
'id_ready' => $column_id
], [
'id' => $project_id
]);
return ['success' => true, 'id_ready' => $column_id];
}
// Получение колонок проекта
public static function getColumns($project_id) {
return Database::select('columns', [
'id',
'name_columns',
'color',
'id_order'
], [
'id_project' => $project_id,
'ORDER' => ['id_order' => 'ASC']
]);
}
// Получение отделов проекта
public static function getDepartments($project_id) {
return Database::select('departments', [
'id',
'name_departments',
'color'
], [
'id_project' => $project_id
]);
}
// Получение всех данных проекта (проект + колонки + отделы + метки)
public static function getProjectData($project_id) {
$project = self::get($project_id);
if (!$project) {
return null;
}
// Получаем метки (глобальные)
$labels = Database::select('labels', [
'id',
'name_labels',
'icon',
'color'
]);
return [
'project' => $project,
'columns' => self::getColumns($project_id),
'departments' => self::getDepartments($project_id),
'labels' => $labels
];
}
// Проверка и получение проекта
public static function checkProject($project_id) {
return Database::get('project', '*', ['id' => $project_id]);
}
// Проверка проекта с ответом (при ошибке — сразу ответ и exit)
public static function check($project_id) {
$project = self::checkProject($project_id);
if (!$project_id || !$project) {
RestApi::response(['success' => false, 'errors' => ['project' => 'Проект не найден']], 400);
}
return $project;
}
}
?>

View File

@@ -6,6 +6,7 @@ class Task extends BaseEntity {
// Свойства задачи
public $id;
public $id_project;
public $id_department;
public $id_label;
public $order;
@@ -45,6 +46,9 @@ class Task extends BaseEntity {
if (!$this->id_department) {
$this->addError('id_department', 'Департамент не указан');
}
if (!$this->id_project) {
$this->addError('id_project', 'Проект не указан');
}
return $this->getErrors();
}
@@ -58,6 +62,7 @@ class Task extends BaseEntity {
// Вставляем в базу
Database::insert($this->db_name, [
'id_project' => $this->id_project,
'id_department' => $this->id_department,
'id_label' => $this->id_label,
'order' => $this->order ?? 0,
@@ -101,7 +106,7 @@ class Task extends BaseEntity {
}
// Проверка что задача существует и получаем текущие данные
$task = Database::get($this->db_name, ['id', 'column_id', 'order'], ['id' => $this->id]);
$task = Database::get($this->db_name, ['id', 'column_id', 'order', 'id_project'], ['id' => $this->id]);
if (!$task) {
$this->addError('task', 'Задача не найдена');
return $this->getErrors();
@@ -113,6 +118,9 @@ class Task extends BaseEntity {
// Если column_id не передан — оставляем текущий
$new_column_id = $this->column_id !== null ? (int)$this->column_id : $old_column_id;
// Получаем id_ready (колонка "Готово") из проекта
$done_column_id = Project::getReadyColumnId($task['id_project']);
// Формируем данные для обновления
$update_data = [
'id_department' => $this->id_department,
@@ -127,9 +135,9 @@ class Task extends BaseEntity {
];
// Обновляем date_closed при смене колонки
if ($new_column_id === COLUMN_DONE_ID && $old_column_id !== COLUMN_DONE_ID) {
if ($done_column_id && $new_column_id === $done_column_id && $old_column_id !== $done_column_id) {
$update_data['date_closed'] = date('Y-m-d H:i:s');
} elseif ($old_column_id === COLUMN_DONE_ID && $new_column_id !== COLUMN_DONE_ID) {
} elseif ($done_column_id && $old_column_id === $done_column_id && $new_column_id !== $done_column_id) {
$update_data['date_closed'] = null;
}
@@ -173,6 +181,9 @@ class Task extends BaseEntity {
$new_column_id = (int)$column_id;
$archive = (int)$task['archive'];
// Получаем id_ready (колонка "Готово") из проекта
$done_column_id = Project::getReadyColumnId($task['id_project']);
// Получаем все карточки целевой колонки с тем же статусом архивации (кроме перемещаемой)
$cards = Database::select('cards_task', ['id', 'order'], [
'column_id' => $column_id,
@@ -192,13 +203,13 @@ class Task extends BaseEntity {
];
// Только для перемещаемой карточки обновляем date_closed
if ($card['id'] == $id) {
if ($card['id'] == $id && $done_column_id) {
// Перемещаем В колонку "Готово" — устанавливаем дату закрытия
if ($new_column_id === COLUMN_DONE_ID && $old_column_id !== COLUMN_DONE_ID) {
if ($new_column_id === $done_column_id && $old_column_id !== $done_column_id) {
$update_data['date_closed'] = date('Y-m-d H:i:s');
}
// Перемещаем ИЗ колонки "Готово" — обнуляем дату
elseif ($old_column_id === COLUMN_DONE_ID && $new_column_id !== COLUMN_DONE_ID) {
elseif ($old_column_id === $done_column_id && $new_column_id !== $done_column_id) {
$update_data['date_closed'] = null;
}
}
@@ -211,16 +222,20 @@ class Task extends BaseEntity {
return ['success' => true];
}
// Получение всех задач
// Получение всех задач проекта
// $id_project: ID проекта (обязательный)
// $archive: 0 = неархивные, 1 = архивные, null = все
public function getAll($archive = 0) {
$where = [];
public function getAll($id_project, $archive = 0) {
$where = [
'id_project' => $id_project
];
if ($archive !== null) {
$where['archive'] = $archive ? 1 : 0;
}
$tasks = Database::select($this->db_name, [
'id',
'id_project',
'id_department',
'id_label',
'id_account',
@@ -238,7 +253,7 @@ class Task extends BaseEntity {
// Декодируем JSON и получаем avatar_url из accounts
return array_map(function($task) {
$task['file_img'] = json_decode($task['file_img'], true) ?? [];
$task['file_img'] = $task['file_img'] ? json_decode($task['file_img'], true) : [];
// Получаем avatar_url из accounts по id_account
if ($task['id_account']) {
@@ -252,21 +267,27 @@ class Task extends BaseEntity {
}, $tasks);
}
// Получение всех колонок
public function getColumns() {
// Получение колонок проекта
public function getColumns($id_project) {
return Database::select('columns', [
'id',
'name_columns',
'color'
'color',
'id_order'
], [
'id_project' => $id_project,
'ORDER' => ['id_order' => 'ASC']
]);
}
// Получение всех департаментов
public function getDepartments() {
// Получение отделов проекта
public function getDepartments($id_project) {
return Database::select('departments', [
'id',
'name_departments',
'color'
], [
'id_project' => $id_project
]);
}
@@ -294,8 +315,11 @@ class Task extends BaseEntity {
// Проверка что задача существует
$task = self::check_task($id);
// Получаем id_ready (колонка "Готово") из проекта
$done_column_id = Project::getReadyColumnId($task['id_project']);
// Архивировать можно только задачи в колонке "Готово"
if ($archive && (int)$task['column_id'] !== COLUMN_DONE_ID) {
if ($archive && $done_column_id && (int)$task['column_id'] !== $done_column_id) {
RestApi::response([
'success' => false,
'errors' => ['column' => 'Архивировать можно только задачи из колонки "Готово"']
@@ -308,8 +332,8 @@ class Task extends BaseEntity {
];
// При разархивировании — возвращаем в колонку "Готово"
if (!$archive) {
$update_data['column_id'] = COLUMN_DONE_ID;
if (!$archive && $done_column_id) {
$update_data['column_id'] = $done_column_id;
}
// Обновляем в БД

View File

@@ -81,7 +81,7 @@ class TaskImage {
$file_size = strlen($file_data);
// Обновляем file_img в базе
$current_files = json_decode($task['file_img'], true) ?? [];
$current_files = $task['file_img'] ? json_decode($task['file_img'], true) : [];
$current_files[] = [
'url' => $file_url,
'name' => $final_name,
@@ -115,7 +115,7 @@ class TaskImage {
}
// Получаем текущие файлы
$current_files = json_decode($task['file_img'], true) ?? [];
$current_files = $task['file_img'] ? json_decode($task['file_img'], true) : [];
$upload_dir = __DIR__ . '/../../../public/task/' . $task_id;
$deleted = [];

View File

@@ -12,6 +12,7 @@
require_once __DIR__ . '/restAPI/class_restApi.php';
require_once __DIR__ . '/class/enity/class_base.php';
require_once __DIR__ . '/class/enity/class_user.php';
require_once __DIR__ . '/class/enity/class_project.php';
require_once __DIR__ . '/class/enity/class_task.php';
require_once __DIR__ . '/class/enity/class_taskImage.php';
@@ -23,15 +24,13 @@
define('DB_PORT', 3306);
define('DB_CHARSET', 'utf8mb4');
// ID колонки "Готово" (для фиксации date_closed и архивации)
define('COLUMN_DONE_ID', 4);
// Инициализация подключения к БД
Database::init();
$routes = [
'/api/user' => __DIR__ . '/../api/user.php',
'/api/task' => __DIR__ . '/../api/task.php',
'/api/project' => __DIR__ . '/../api/project.php',
];
$publicActions = ['auth_login', 'check_session'];