Uusi TODO-moduuli: Tehtävät + Kehitysehdotukset + Ajanseuranta
Talon sisäinen tehtävienhallinta kahdella alatabilla: Tehtävät (admin luo): - Prioriteetti (normaali/tärkeä/kiireellinen), status, deadline - Vastuuhenkilö-osoitus, inline-muokkaus lukunäkymässä - Aikakirjaukset: pvm, tunnit, kuvaus - kaikki voivat kirjata - Myöhästyneet = punainen reunus, lähestyvät = keltainen - Kommentointi kaikille käyttäjille Kehitysehdotukset (kaikki voivat luoda): - Status: ehdotettu → harkinnassa → toteutettu/hylätty (admin muuttaa) - Kommentointi kaikille - Ehdottaja voi muokata omia Tietokanta: 3 taulua (todos, todo_comments, todo_time_entries) API: 10 endpointtia oikeustarkistuksineen Frontend: Sub-tab navigointi, kortti-grid, 3-näkymämalli per alatabi Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
200
api.php
200
api.php
@@ -2208,6 +2208,206 @@ switch ($action) {
|
||||
readfile($path);
|
||||
exit;
|
||||
|
||||
// ---------- TEHTÄVÄT (TODOS) ----------
|
||||
case 'todos':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
echo json_encode(dbLoadTodos($companyId));
|
||||
break;
|
||||
|
||||
case 'todo_detail':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
$id = $_GET['id'] ?? '';
|
||||
$todo = dbLoadTodo($id);
|
||||
if (!$todo || $todo['company_id'] !== $companyId) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Tehtävää ei löydy']);
|
||||
break;
|
||||
}
|
||||
unset($todo['company_id']);
|
||||
echo json_encode($todo);
|
||||
break;
|
||||
|
||||
case 'todo_save':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$type = $input['type'] ?? 'task';
|
||||
$isNew = empty($input['id']);
|
||||
// Task: vain admin. Feature request: kaikki voivat luoda, mutta muokata vain omia (tai admin)
|
||||
if ($type === 'task') {
|
||||
requireAdmin();
|
||||
} elseif (!$isNew) {
|
||||
$existing = dbLoadTodo($input['id']);
|
||||
if ($existing && $existing['created_by'] !== currentUser() && !isAdmin()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Voit muokata vain omia ehdotuksiasi']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$todo = [
|
||||
'id' => $input['id'] ?? generateId(),
|
||||
'type' => $type,
|
||||
'title' => trim($input['title'] ?? ''),
|
||||
'description' => $input['description'] ?? '',
|
||||
'status' => $input['status'] ?? ($type === 'task' ? 'avoin' : 'ehdotettu'),
|
||||
'priority' => $input['priority'] ?? 'normaali',
|
||||
'assigned_to' => $input['assigned_to'] ?? '',
|
||||
'created_by' => $isNew ? currentUser() : ($input['created_by'] ?? currentUser()),
|
||||
'deadline' => $input['deadline'] ?? null,
|
||||
'luotu' => $isNew ? date('Y-m-d H:i:s') : ($input['luotu'] ?? date('Y-m-d H:i:s')),
|
||||
'muokkaaja' => currentUser(),
|
||||
];
|
||||
if (empty($todo['title'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Otsikko on pakollinen']);
|
||||
break;
|
||||
}
|
||||
dbSaveTodo($companyId, $todo);
|
||||
dbAddLog($companyId, currentUser(), $isNew ? 'todo_create' : 'todo_update', $todo['id'], $todo['title'], '');
|
||||
echo json_encode($todo);
|
||||
break;
|
||||
|
||||
case 'todo_delete':
|
||||
requireAuth();
|
||||
requireAdmin();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$id = $input['id'] ?? '';
|
||||
$todo = dbLoadTodo($id);
|
||||
if ($todo) {
|
||||
dbDeleteTodo($id);
|
||||
dbAddLog($companyId, currentUser(), 'todo_delete', $id, $todo['title'] ?? '', '');
|
||||
}
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
|
||||
case 'todo_status':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$id = $input['id'] ?? '';
|
||||
$status = $input['status'] ?? '';
|
||||
$todo = dbLoadTodo($id);
|
||||
if (!$todo || $todo['company_id'] !== $companyId) {
|
||||
http_response_code(404); break;
|
||||
}
|
||||
// Feature request status: vain admin
|
||||
if ($todo['type'] === 'feature_request' && !isAdmin()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Vain admin voi muuttaa ehdotuksen statusta']);
|
||||
break;
|
||||
}
|
||||
// Task status: vain admin
|
||||
if ($todo['type'] === 'task' && !isAdmin()) {
|
||||
http_response_code(403); break;
|
||||
}
|
||||
$todo['status'] = $status;
|
||||
$todo['muokkaaja'] = currentUser();
|
||||
dbSaveTodo($companyId, $todo);
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
|
||||
case 'todo_assign':
|
||||
requireAuth();
|
||||
requireAdmin();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$id = $input['id'] ?? '';
|
||||
$todo = dbLoadTodo($id);
|
||||
if (!$todo || $todo['company_id'] !== $companyId) {
|
||||
http_response_code(404); break;
|
||||
}
|
||||
$todo['assigned_to'] = $input['assigned_to'] ?? '';
|
||||
$todo['muokkaaja'] = currentUser();
|
||||
dbSaveTodo($companyId, $todo);
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
|
||||
case 'todo_comment':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$todoId = $input['todo_id'] ?? '';
|
||||
$body = trim($input['body'] ?? '');
|
||||
if (empty($body)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Kommentti ei voi olla tyhjä']);
|
||||
break;
|
||||
}
|
||||
$comment = [
|
||||
'id' => generateId(),
|
||||
'author' => currentUser(),
|
||||
'body' => $body,
|
||||
'luotu' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
dbAddTodoComment($todoId, $comment);
|
||||
echo json_encode($comment);
|
||||
break;
|
||||
|
||||
case 'todo_comment_delete':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$commentId = $input['id'] ?? '';
|
||||
// Tarkista onko oma kommentti tai admin
|
||||
$rows = _dbFetchAll("SELECT author FROM todo_comments WHERE id = ?", [$commentId]);
|
||||
if (!empty($rows) && ($rows[0]['author'] === currentUser() || isAdmin())) {
|
||||
dbDeleteTodoComment($commentId);
|
||||
echo json_encode(['success' => true]);
|
||||
} else {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Ei oikeutta']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'todo_time_add':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$todoId = $input['todo_id'] ?? '';
|
||||
$hours = floatval($input['hours'] ?? 0);
|
||||
if ($hours <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Tunnit pitää olla > 0']);
|
||||
break;
|
||||
}
|
||||
$entry = [
|
||||
'id' => generateId(),
|
||||
'user' => currentUser(),
|
||||
'hours' => $hours,
|
||||
'description' => trim($input['description'] ?? ''),
|
||||
'work_date' => $input['work_date'] ?? date('Y-m-d'),
|
||||
'luotu' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
dbAddTodoTimeEntry($todoId, $entry);
|
||||
echo json_encode($entry);
|
||||
break;
|
||||
|
||||
case 'todo_time_delete':
|
||||
requireAuth();
|
||||
$companyId = requireCompany();
|
||||
if ($method !== 'POST') break;
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$entryId = $input['id'] ?? '';
|
||||
$rows = _dbFetchAll("SELECT user FROM todo_time_entries WHERE id = ?", [$entryId]);
|
||||
if (!empty($rows) && ($rows[0]['user'] === currentUser() || isAdmin())) {
|
||||
dbDeleteTodoTimeEntry($entryId);
|
||||
echo json_encode(['success' => true]);
|
||||
} else {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Ei oikeutta']);
|
||||
}
|
||||
break;
|
||||
|
||||
// ---------- ARCHIVE ----------
|
||||
case 'archived_customers':
|
||||
requireAuth();
|
||||
|
||||
Reference in New Issue
Block a user