TODO-listaus tauluriveinä korttien sijaan, deadline-lajittelu
Tehtävät ja kehitysehdotukset näytetään nyt taulukkoriveinä (kuten tukitiketit) kortti-gridin sijaan. Tehtävät lajitellaan deadlinen mukaan (lähimmät ensin), valmiit loppuun. Myöhästyneet rivit punaisella ja pian erääntyvät keltaisella taustalla. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
92
script.js
92
script.js
@@ -3943,28 +3943,45 @@ function renderTasksList() {
|
||||
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);
|
||||
const grid = document.getElementById('tasks-grid');
|
||||
const noEl = document.getElementById('no-tasks');
|
||||
if (!grid) return;
|
||||
if (!tasks.length) { grid.innerHTML = ''; if (noEl) noEl.style.display = ''; return; }
|
||||
if (noEl) noEl.style.display = 'none';
|
||||
|
||||
// Lajittelu: deadline lähimmät ensin (null-deadlinet loppuun), sitten prioriteetti
|
||||
const today = new Date().toISOString().slice(0,10);
|
||||
grid.innerHTML = tasks.map(t => {
|
||||
const prioOrder = { kiireellinen: 0, tarkea: 1, normaali: 2 };
|
||||
const statusOrder = { avoin: 0, kaynnissa: 1, odottaa: 2, valmis: 3 };
|
||||
tasks.sort((a, b) => {
|
||||
// Valmiit aina loppuun
|
||||
if ((a.status === 'valmis') !== (b.status === 'valmis')) return a.status === 'valmis' ? 1 : -1;
|
||||
// Deadline: lähimmät ensin, null loppuun
|
||||
const da = a.deadline || '9999-99-99';
|
||||
const db = b.deadline || '9999-99-99';
|
||||
if (da !== db) return da.localeCompare(db);
|
||||
// Prioriteetti
|
||||
const pa = prioOrder[a.priority] ?? 2;
|
||||
const pb = prioOrder[b.priority] ?? 2;
|
||||
if (pa !== pb) return pa - pb;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const tbody = document.getElementById('tasks-tbody');
|
||||
const table = document.getElementById('tasks-table');
|
||||
const noEl = document.getElementById('no-tasks');
|
||||
if (!tbody) return;
|
||||
if (!tasks.length) { tbody.innerHTML = ''; table.style.display = 'none'; if (noEl) noEl.style.display = ''; return; }
|
||||
if (noEl) noEl.style.display = 'none';
|
||||
table.style.display = 'table';
|
||||
tbody.innerHTML = tasks.map(t => {
|
||||
const overdue = t.deadline && t.status !== 'valmis' && t.deadline < today;
|
||||
const soon = t.deadline && t.status !== 'valmis' && !overdue && t.deadline <= new Date(Date.now()+3*86400000).toISOString().slice(0,10);
|
||||
return `<div class="todo-card${overdue ? ' overdue' : ''}${soon ? ' deadline-soon' : ''}" onclick="openTaskRead('${t.id}')" style="cursor:pointer;">
|
||||
<div style="display:flex;gap:0.4rem;margin-bottom:0.5rem;flex-wrap:wrap;">
|
||||
<span class="priority-badge priority-${t.priority}">${todoPriorityLabels[t.priority]||t.priority}</span>
|
||||
<span class="todo-status status-${t.status}">${todoStatusLabels[t.status]||t.status}</span>
|
||||
</div>
|
||||
<div style="font-weight:600;font-size:0.95rem;margin-bottom:0.4rem;">${esc(t.title)}</div>
|
||||
<div style="font-size:0.82rem;color:#888;display:flex;gap:0.75rem;flex-wrap:wrap;margin-top:auto;">
|
||||
${t.assigned_to ? `<span>👤 ${esc(t.assigned_to)}</span>` : ''}
|
||||
${t.deadline ? `<span${overdue ? ' style="color:#e74c3c;font-weight:600;"' : ''}>📅 ${t.deadline}</span>` : ''}
|
||||
${t.total_hours > 0 ? `<span>⏱ ${t.total_hours}h</span>` : ''}
|
||||
${t.comment_count > 0 ? `<span>💬 ${t.comment_count}</span>` : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
const rowClass = overdue ? 'todo-row-overdue' : (soon ? 'todo-row-soon' : (t.status === 'valmis' ? 'todo-row-done' : ''));
|
||||
return `<tr class="${rowClass}" onclick="openTaskRead('${t.id}')" style="cursor:pointer;">
|
||||
<td><span class="todo-status status-${t.status}">${todoStatusLabels[t.status]||t.status}</span></td>
|
||||
<td><span class="priority-badge priority-${t.priority}">${todoPriorityLabels[t.priority]||t.priority}</span></td>
|
||||
<td><strong>${esc(t.title)}</strong></td>
|
||||
<td>${t.assigned_to ? esc(t.assigned_to) : '<span style="color:#ccc;">—</span>'}</td>
|
||||
<td class="nowrap">${t.deadline ? `<span${overdue ? ' style="color:#e74c3c;font-weight:600;"' : (soon ? ' style="color:#f39c12;font-weight:600;"' : '')}>${t.deadline}</span>` : '<span style="color:#ccc;">—</span>'}</td>
|
||||
<td style="text-align:center;">${t.total_hours > 0 ? t.total_hours + 'h' : '<span style="color:#ccc;">—</span>'}</td>
|
||||
<td style="text-align:center;">${t.comment_count > 0 ? t.comment_count : ''}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
@@ -4093,20 +4110,33 @@ function renderFeaturesList() {
|
||||
let features = todosData.filter(t => t.type === 'feature_request');
|
||||
if (query) features = features.filter(t => (t.title||'').toLowerCase().includes(query) || (t.description||'').toLowerCase().includes(query));
|
||||
if (statusF) features = features.filter(t => t.status === statusF);
|
||||
const grid = document.getElementById('features-grid');
|
||||
|
||||
// Lajittelu: uusimmat ensin, toteutetut/hylätyt loppuun
|
||||
features.sort((a, b) => {
|
||||
const doneA = (a.status === 'toteutettu' || a.status === 'hylatty') ? 1 : 0;
|
||||
const doneB = (b.status === 'toteutettu' || b.status === 'hylatty') ? 1 : 0;
|
||||
if (doneA !== doneB) return doneA - doneB;
|
||||
return (b.luotu || '').localeCompare(a.luotu || '');
|
||||
});
|
||||
|
||||
const tbody = document.getElementById('features-tbody');
|
||||
const table = document.getElementById('features-table');
|
||||
const noEl = document.getElementById('no-features');
|
||||
if (!grid) return;
|
||||
if (!features.length) { grid.innerHTML = ''; if (noEl) noEl.style.display = ''; return; }
|
||||
if (!tbody) return;
|
||||
if (!features.length) { tbody.innerHTML = ''; table.style.display = 'none'; if (noEl) noEl.style.display = ''; return; }
|
||||
if (noEl) noEl.style.display = 'none';
|
||||
grid.innerHTML = features.map(t => `<div class="todo-card" onclick="openFeatureRead('${t.id}')" style="cursor:pointer;">
|
||||
<div style="margin-bottom:0.5rem;"><span class="todo-status status-${t.status}">${todoStatusLabels[t.status]||t.status}</span></div>
|
||||
<div style="font-weight:600;font-size:0.95rem;margin-bottom:0.4rem;">${esc(t.title)}</div>
|
||||
<div style="font-size:0.82rem;color:#888;display:flex;gap:0.75rem;flex-wrap:wrap;margin-top:auto;">
|
||||
<span>👤 ${esc(t.created_by)}</span>
|
||||
<span>${(t.luotu||'').slice(0,10)}</span>
|
||||
${t.comment_count > 0 ? `<span>💬 ${t.comment_count}</span>` : ''}
|
||||
</div>
|
||||
</div>`).join('');
|
||||
table.style.display = 'table';
|
||||
tbody.innerHTML = features.map(t => {
|
||||
const done = t.status === 'toteutettu' || t.status === 'hylatty';
|
||||
const rowClass = done ? 'todo-row-done' : '';
|
||||
return `<tr class="${rowClass}" onclick="openFeatureRead('${t.id}')" style="cursor:pointer;">
|
||||
<td><span class="todo-status status-${t.status}">${todoStatusLabels[t.status]||t.status}</span></td>
|
||||
<td><strong>${esc(t.title)}</strong></td>
|
||||
<td>${esc(t.created_by)}</td>
|
||||
<td class="nowrap">${(t.luotu||'').slice(0,10)}</td>
|
||||
<td style="text-align:center;">${t.comment_count > 0 ? t.comment_count : ''}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function showFeatureListView() {
|
||||
|
||||
Reference in New Issue
Block a user