Osatehtävät (subtaskit) TODO-tehtäviin

Uusi todo_subtasks-taulu + 3 API-endpointtia (add/toggle/delete).
Tehtävän lukunäkymässä checkbox-lista osatehtäville, lisäys
Enter-näppäimellä tai Lisää-napilla. Valmiit yliviivataan.
Tehtävälistassa näkyy edistyminen (esim. ☑ 2/5).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 14:45:47 +02:00
parent ad4c5605f6
commit 093f40ac09
5 changed files with 171 additions and 2 deletions

44
db.php
View File

@@ -490,6 +490,18 @@ function initDatabase(): void {
INDEX idx_todo (todo_id),
INDEX idx_user (user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
"CREATE TABLE IF NOT EXISTS todo_subtasks (
id VARCHAR(20) PRIMARY KEY,
todo_id VARCHAR(20) NOT NULL,
title VARCHAR(500) NOT NULL,
completed TINYINT(1) DEFAULT 0,
sort_order INT DEFAULT 0,
created_by VARCHAR(100) DEFAULT '',
luotu DATETIME,
FOREIGN KEY (todo_id) REFERENCES todos(id) ON DELETE CASCADE,
INDEX idx_todo (todo_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
];
foreach ($tables as $i => $sql) {
@@ -1495,7 +1507,9 @@ function dbLoadTodos(string $companyId): array {
$rows = _dbFetchAll("
SELECT t.*,
COALESCE((SELECT SUM(te.hours) FROM todo_time_entries te WHERE te.todo_id = t.id), 0) AS total_hours,
(SELECT COUNT(*) FROM todo_comments tc WHERE tc.todo_id = t.id) AS comment_count
(SELECT COUNT(*) FROM todo_comments tc WHERE tc.todo_id = t.id) AS comment_count,
(SELECT COUNT(*) FROM todo_subtasks ts WHERE ts.todo_id = t.id) AS subtask_count,
(SELECT COUNT(*) FROM todo_subtasks ts2 WHERE ts2.todo_id = t.id AND ts2.completed = 1) AS subtask_done
FROM todos t
WHERE t.company_id = ?
ORDER BY
@@ -1506,6 +1520,8 @@ function dbLoadTodos(string $companyId): array {
foreach ($rows as &$r) {
$r['total_hours'] = floatval($r['total_hours']);
$r['comment_count'] = intval($r['comment_count']);
$r['subtask_count'] = intval($r['subtask_count']);
$r['subtask_done'] = intval($r['subtask_done']);
unset($r['company_id']);
}
return $rows;
@@ -1517,9 +1533,13 @@ function dbLoadTodo(string $todoId): ?array {
$todo = $row[0];
$todo['comments'] = _dbFetchAll("SELECT * FROM todo_comments WHERE todo_id = ? ORDER BY luotu", [$todoId]);
$todo['time_entries'] = _dbFetchAll("SELECT * FROM todo_time_entries WHERE todo_id = ? ORDER BY work_date DESC, luotu DESC", [$todoId]);
$todo['subtasks'] = _dbFetchAll("SELECT * FROM todo_subtasks WHERE todo_id = ? ORDER BY sort_order, luotu", [$todoId]);
foreach ($todo['time_entries'] as &$te) {
$te['hours'] = floatval($te['hours']);
}
foreach ($todo['subtasks'] as &$st) {
$st['completed'] = (bool)$st['completed'];
}
$todo['total_hours'] = array_sum(array_column($todo['time_entries'], 'hours'));
return $todo;
}
@@ -1583,3 +1603,25 @@ function dbAddTodoTimeEntry(string $todoId, array $entry): void {
function dbDeleteTodoTimeEntry(string $entryId): void {
_dbExecute("DELETE FROM todo_time_entries WHERE id = ?", [$entryId]);
}
function dbAddTodoSubtask(string $todoId, array $subtask): void {
$maxOrder = _dbFetchAll("SELECT COALESCE(MAX(sort_order), 0) + 1 AS next_order FROM todo_subtasks WHERE todo_id = ?", [$todoId]);
_dbExecute("INSERT INTO todo_subtasks (id, todo_id, title, completed, sort_order, created_by, luotu) VALUES (?, ?, ?, 0, ?, ?, ?)", [
$subtask['id'] ?? generateId(),
$todoId,
$subtask['title'] ?? '',
$maxOrder[0]['next_order'] ?? 0,
$subtask['created_by'] ?? '',
date('Y-m-d H:i:s')
]);
}
function dbToggleTodoSubtask(string $subtaskId): bool {
_dbExecute("UPDATE todo_subtasks SET completed = NOT completed WHERE id = ?", [$subtaskId]);
$row = _dbFetchAll("SELECT completed FROM todo_subtasks WHERE id = ?", [$subtaskId]);
return !empty($row) && $row[0]['completed'];
}
function dbDeleteTodoSubtask(string $subtaskId): void {
_dbExecute("DELETE FROM todo_subtasks WHERE id = ?", [$subtaskId]);
}