From e3891463e9dbb099bed3e64e6eb776154608c87d Mon Sep 17 00:00:00 2001 From: Jukka Lampikoski Date: Wed, 11 Mar 2026 13:35:17 +0200 Subject: [PATCH] =?UTF-8?q?Teht=C3=A4viin=20Tyyppi-kentt=C3=A4=20(tekniikk?= =?UTF-8?q?a,=20laskutus,=20myynti,=20asennus,=20muu)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uusi category-sarake todosiin. Näkyy listassa badgena, lomakkeessa dropdownina ja lukunäkymässä. Tyypillä voi myös suodattaa listaa. Värikoodatut badget kullekin tyypille. Co-Authored-By: Claude Opus 4.6 --- api.php | 1 + db.php | 11 +++++++---- index.html | 22 +++++++++++++++++++++- script.js | 8 ++++++++ style.css | 6 ++++++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/api.php b/api.php index e5060d8..5dc1e21 100644 --- a/api.php +++ b/api.php @@ -2254,6 +2254,7 @@ switch ($action) { 'description' => $input['description'] ?? '', 'status' => $input['status'] ?? ($type === 'task' ? 'avoin' : 'ehdotettu'), 'priority' => $input['priority'] ?? 'normaali', + 'category' => $input['category'] ?? '', 'assigned_to' => $input['assigned_to'] ?? '', 'created_by' => $isNew ? currentUser() : ($input['created_by'] ?? currentUser()), 'deadline' => $input['deadline'] ?? null, diff --git a/db.php b/db.php index 89b9032..4f65fbb 100644 --- a/db.php +++ b/db.php @@ -454,6 +454,7 @@ function initDatabase(): void { description TEXT, status VARCHAR(30) NOT NULL DEFAULT 'avoin', priority VARCHAR(20) DEFAULT 'normaali', + category VARCHAR(30) DEFAULT '', assigned_to VARCHAR(100) DEFAULT '', created_by VARCHAR(100) NOT NULL DEFAULT '', deadline DATE DEFAULT NULL, @@ -518,6 +519,7 @@ function initDatabase(): void { "ALTER TABLE mailboxes ADD COLUMN auto_reply_enabled BOOLEAN DEFAULT FALSE AFTER aktiivinen", "ALTER TABLE mailboxes ADD COLUMN auto_reply_body TEXT AFTER auto_reply_enabled", "ALTER TABLE companies ADD COLUMN allowed_ips TEXT DEFAULT '' AFTER enabled_modules", + "ALTER TABLE todos ADD COLUMN category VARCHAR(30) DEFAULT '' AFTER priority", ]; foreach ($alters as $sql) { try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa / jo ajettu */ } @@ -1524,13 +1526,13 @@ function dbLoadTodo(string $todoId): ?array { function dbSaveTodo(string $companyId, array $todo): void { _dbExecute(" - INSERT INTO todos (id, company_id, type, title, description, status, priority, assigned_to, created_by, deadline, luotu, muokattu, muokkaaja) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + INSERT INTO todos (id, company_id, type, title, description, status, priority, category, assigned_to, created_by, deadline, luotu, muokattu, muokkaaja) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE title = VALUES(title), description = VALUES(description), status = VALUES(status), priority = VALUES(priority), - assigned_to = VALUES(assigned_to), deadline = VALUES(deadline), - muokattu = VALUES(muokattu), muokkaaja = VALUES(muokkaaja) + category = VALUES(category), assigned_to = VALUES(assigned_to), + deadline = VALUES(deadline), muokattu = VALUES(muokattu), muokkaaja = VALUES(muokkaaja) ", [ $todo['id'], $companyId, $todo['type'] ?? 'task', @@ -1538,6 +1540,7 @@ function dbSaveTodo(string $companyId, array $todo): void { $todo['description'] ?? '', $todo['status'] ?? 'avoin', $todo['priority'] ?? 'normaali', + $todo['category'] ?? '', $todo['assigned_to'] ?? '', $todo['created_by'] ?? '', !empty($todo['deadline']) ? $todo['deadline'] : null, diff --git a/index.html b/index.html index 25ba2d2..616d20e 100644 --- a/index.html +++ b/index.html @@ -456,6 +456,14 @@ + @@ -468,6 +476,7 @@ Status Prioriteetti + Tyyppi Tehtävä Vastuuhenkilö Deadline @@ -539,7 +548,7 @@ -
+
+
+ + +
diff --git a/script.js b/script.js index d5adc67..4426120 100644 --- a/script.js +++ b/script.js @@ -3898,6 +3898,7 @@ let currentTodoSubTab = 'tasks'; const todoStatusLabels = { avoin:'Avoin', kaynnissa:'Käynnissä', odottaa:'Odottaa', valmis:'Valmis', ehdotettu:'Ehdotettu', harkinnassa:'Harkinnassa', toteutettu:'Toteutettu', hylatty:'Hylätty' }; const todoPriorityLabels = { normaali:'Normaali', tarkea:'Tärkeä', kiireellinen:'Kiireellinen' }; +const todoCategoryLabels = { tekniikka:'Tekniikka', laskutus:'Laskutus', myynti:'Myynti', asennus:'Asennus', muu:'Muu' }; function switchTodoSubTab(target) { currentTodoSubTab = target; @@ -3939,10 +3940,12 @@ function renderTasksList() { const query = (document.getElementById('todo-search-input')?.value || '').toLowerCase().trim(); const statusF = document.getElementById('todo-status-filter')?.value || ''; const assignF = document.getElementById('todo-assigned-filter')?.value || ''; + const catF = document.getElementById('todo-category-filter')?.value || ''; let tasks = todosData.filter(t => t.type === 'task'); if (query) tasks = tasks.filter(t => (t.title||'').toLowerCase().includes(query) || (t.description||'').toLowerCase().includes(query) || (t.assigned_to||'').toLowerCase().includes(query)); if (statusF) tasks = tasks.filter(t => t.status === statusF); if (assignF) tasks = tasks.filter(t => t.assigned_to === assignF); + if (catF) tasks = tasks.filter(t => t.category === catF); // Lajittelu: deadline lähimmät ensin (null-deadlinet loppuun), sitten prioriteetti const today = new Date().toISOString().slice(0,10); @@ -3976,6 +3979,7 @@ function renderTasksList() { return ` ${todoStatusLabels[t.status]||t.status} ${todoPriorityLabels[t.priority]||t.priority} + ${t.category ? `${todoCategoryLabels[t.category]||t.category}` : ''} ${esc(t.title)} ${t.assigned_to ? esc(t.assigned_to) : ''} ${t.deadline ? `${t.deadline}` : ''} @@ -4009,6 +4013,7 @@ async function openTaskRead(id) { ` : esc(t.assigned_to || '—')}
Prioriteetti
${todoPriorityLabels[t.priority]||t.priority}
+
Tyyppi
${t.category ? (todoCategoryLabels[t.category]||t.category) : '—'}
Deadline
${t.deadline || '—'}
`; // Populoi vastuuhenkilö-dropdown if (isAdmin) { @@ -4058,6 +4063,7 @@ async function openTaskEdit(id) { document.getElementById('task-form-title').value = t?.title || ''; document.getElementById('task-form-priority').value = t?.priority || 'normaali'; document.getElementById('task-form-status').value = t?.status || 'avoin'; + document.getElementById('task-form-category').value = t?.category || ''; document.getElementById('task-form-deadline').value = t?.deadline || ''; document.getElementById('task-form-desc').value = t?.description || ''; document.getElementById('task-edit-title').textContent = t ? 'Muokkaa tehtävää' : 'Uusi tehtävä'; @@ -4088,6 +4094,7 @@ document.getElementById('task-form')?.addEventListener('submit', async (e) => { title: document.getElementById('task-form-title').value.trim(), description: document.getElementById('task-form-desc').value.trim(), priority: document.getElementById('task-form-priority').value, + category: document.getElementById('task-form-category').value, status: document.getElementById('task-form-status').value, deadline: document.getElementById('task-form-deadline').value || null, assigned_to: document.getElementById('task-form-assigned').value, @@ -4275,6 +4282,7 @@ async function deleteTimeEntry(entryId, todoId) { document.getElementById('todo-search-input')?.addEventListener('input', () => renderTasksList()); document.getElementById('todo-status-filter')?.addEventListener('change', () => renderTasksList()); document.getElementById('todo-assigned-filter')?.addEventListener('change', () => renderTasksList()); +document.getElementById('todo-category-filter')?.addEventListener('change', () => renderTasksList()); document.getElementById('feature-search-input')?.addEventListener('input', () => renderFeaturesList()); document.getElementById('feature-status-filter')?.addEventListener('change', () => renderFeaturesList()); document.getElementById('btn-add-task')?.addEventListener('click', () => openTaskEdit(null)); diff --git a/style.css b/style.css index d3897d3..c3f4424 100644 --- a/style.css +++ b/style.css @@ -1139,6 +1139,12 @@ span.empty { .priority-normaali { background:#e8ebf0; color:#555; } .priority-tarkea { background:#fff3cd; color:#856404; } .priority-kiireellinen { background:#fde8e8; color:#dc2626; } +.todo-category { display:inline-block; padding:2px 8px; border-radius:10px; font-size:0.72rem; font-weight:600; } +.cat-tekniikka { background:#e0f2fe; color:#0369a1; } +.cat-laskutus { background:#fef3c7; color:#92400e; } +.cat-myynti { background:#d1fae5; color:#065f46; } +.cat-asennus { background:#ede9fe; color:#5b21b6; } +.cat-muu { background:#f3f4f6; color:#6b7280; } .todo-status { display:inline-block; padding:2px 8px; border-radius:10px; font-size:0.72rem; font-weight:600; } .status-avoin { background:#e3f2fd; color:#1565c0; } .status-kaynnissa { background:#e8f5e9; color:#2e7d32; }