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:
2026-03-11 13:14:53 +02:00
parent ec86263c5c
commit 4a1dccb6ff
5 changed files with 958 additions and 2 deletions

View File

@@ -82,6 +82,7 @@
<button class="tab" data-tab="leads">Liidit</button>
<button class="tab" data-tab="tekniikka">Tekniikka</button>
<button class="tab" data-tab="ohjeet">Ohjeet</button>
<button class="tab" data-tab="todo">Tehtävät</button>
<button class="tab" data-tab="archive">Arkisto</button>
<button class="tab" data-tab="changelog">Muutosloki</button>
<button class="tab" data-tab="settings" id="tab-settings" style="display:none">API</button>
@@ -430,6 +431,218 @@
</div>
</div>
<!-- Tab: Tehtävät / TODO -->
<div class="tab-content" id="tab-content-todo">
<div class="main-container">
<!-- Sub-tab bar -->
<div style="display:flex;gap:0.25rem;margin-bottom:1.25rem;border-bottom:2px solid #f0f2f5;padding-bottom:0;">
<button class="sub-tab active" data-todotab="tasks" onclick="switchTodoSubTab('tasks')" style="padding:0.6rem 1.2rem;border:none;background:none;cursor:pointer;font-weight:600;font-size:0.92rem;color:#888;border-bottom:3px solid transparent;margin-bottom:-2px;">Tehtävät</button>
<button class="sub-tab" data-todotab="features" onclick="switchTodoSubTab('features')" style="padding:0.6rem 1.2rem;border:none;background:none;cursor:pointer;font-weight:600;font-size:0.92rem;color:#888;border-bottom:3px solid transparent;margin-bottom:-2px;">Kehitysehdotukset</button>
</div>
<!-- ===== TEHTÄVÄT ===== -->
<div id="todo-subtab-tasks">
<!-- Listanäkymä -->
<div id="tasks-list-view">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.75rem;margin-bottom:1rem;">
<div class="search-bar" style="flex:1;min-width:200px;max-width:400px;">
<input type="text" id="todo-search-input" placeholder="Hae tehtävistä..." style="width:100%;">
</div>
<div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
<select id="todo-status-filter" style="padding:0.4rem 0.6rem;border-radius:6px;border:1px solid #ddd;font-size:0.85rem;">
<option value="">Kaikki statukset</option>
<option value="avoin">Avoin</option>
<option value="kaynnissa">Käynnissä</option>
<option value="odottaa">Odottaa</option>
<option value="valmis">Valmis</option>
</select>
<select id="todo-assigned-filter" style="padding:0.4rem 0.6rem;border-radius:6px;border:1px solid #ddd;font-size:0.85rem;">
<option value="">Kaikki vastuuhenkilöt</option>
</select>
<button class="btn-primary" id="btn-add-task" style="display:none;">+ Uusi tehtävä</button>
</div>
</div>
<div id="tasks-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem;"></div>
<p id="no-tasks" style="display:none;text-align:center;color:#aaa;padding:3rem 0;">Ei tehtäviä.</p>
</div>
<!-- Lukunäkymä -->
<div id="task-read-view" style="display:none;">
<button class="btn-secondary" id="btn-task-back">&larr; Takaisin</button>
<div class="table-card" style="padding:1.5rem 2rem;margin-top:1rem;">
<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:1rem;margin-bottom:1rem;">
<div>
<h2 id="task-read-title" style="color:var(--primary-color);margin:0;"></h2>
<div id="task-read-meta" style="font-size:0.85rem;color:#888;margin-top:0.3rem;"></div>
</div>
<div style="display:flex;gap:0.5rem;align-items:center;" id="task-read-actions"></div>
</div>
<div style="display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:1rem;" id="task-read-badges"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:0.75rem;margin-bottom:1rem;padding:1rem;background:#fafbfc;border-radius:8px;" id="task-read-fields"></div>
<div id="task-read-description" style="margin-bottom:1.5rem;line-height:1.7;white-space:pre-wrap;"></div>
<!-- Aikakirjaukset -->
<div id="task-time-section" style="margin-bottom:1.5rem;">
<h3 style="font-size:1rem;margin-bottom:0.75rem;">&#9201; Aikakirjaukset <span id="task-time-total" style="font-weight:400;color:#888;"></span></h3>
<table class="time-entries-table" id="task-time-table">
<thead><tr><th>Päivämäärä</th><th>Käyttäjä</th><th>Tunnit</th><th>Kuvaus</th><th></th></tr></thead>
<tbody id="task-time-tbody"></tbody>
</table>
<div id="task-time-form-wrap" style="margin-top:0.75rem;">
<button class="btn-secondary" id="btn-add-time" style="font-size:0.85rem;">+ Kirjaa tunteja</button>
<div id="task-time-form" style="display:none;margin-top:0.5rem;display:none;gap:0.5rem;align-items:flex-end;flex-wrap:wrap;">
<div class="form-group" style="margin:0;"><label style="font-size:0.78rem;">Päivämäärä</label><input type="date" id="time-form-date" style="font-size:0.85rem;"></div>
<div class="form-group" style="margin:0;"><label style="font-size:0.78rem;">Tunnit</label><input type="number" id="time-form-hours" step="0.25" min="0.25" placeholder="1.5" style="width:80px;font-size:0.85rem;"></div>
<div class="form-group" style="margin:0;flex:1;min-width:150px;"><label style="font-size:0.78rem;">Kuvaus</label><input type="text" id="time-form-desc" placeholder="Mitä teit" style="font-size:0.85rem;"></div>
<button class="btn-primary" id="btn-time-save" style="font-size:0.85rem;padding:0.4rem 1rem;">Tallenna</button>
<button class="btn-secondary" id="btn-time-cancel" style="font-size:0.85rem;">Peru</button>
</div>
</div>
</div>
<!-- Kommentit -->
<div id="task-comments-section">
<h3 style="font-size:1rem;margin-bottom:0.75rem;">&#128172; Kommentit <span id="task-comment-count" style="font-weight:400;color:#888;"></span></h3>
<div id="task-comments-list"></div>
<div style="margin-top:0.75rem;display:flex;gap:0.5rem;">
<textarea id="task-comment-input" rows="2" placeholder="Kirjoita kommentti..." style="flex:1;font-size:0.88rem;resize:vertical;min-height:50px;"></textarea>
<button class="btn-primary" id="btn-task-comment-send" style="align-self:flex-end;font-size:0.85rem;">Lähetä</button>
</div>
</div>
</div>
</div>
<!-- Muokkausnäkymä -->
<div id="task-edit-view" style="display:none;">
<button class="btn-secondary" id="btn-task-edit-cancel">&larr; Peruuta</button>
<div class="table-card" style="padding:1.5rem 2rem;margin-top:1rem;">
<h2 id="task-edit-title" style="color:var(--primary-color);margin-bottom:1rem;">Uusi tehtävä</h2>
<form id="task-form">
<input type="hidden" id="task-form-id">
<input type="hidden" id="task-form-type" value="task">
<div class="form-group" style="margin-bottom:1rem;">
<label for="task-form-title">Otsikko *</label>
<input type="text" id="task-form-title" required placeholder="Tehtävän otsikko">
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;margin-bottom:1rem;">
<div class="form-group">
<label for="task-form-priority">Prioriteetti</label>
<select id="task-form-priority">
<option value="normaali">Normaali</option>
<option value="tarkea">Tärkeä</option>
<option value="kiireellinen">Kiireellinen</option>
</select>
</div>
<div class="form-group">
<label for="task-form-status">Status</label>
<select id="task-form-status">
<option value="avoin">Avoin</option>
<option value="kaynnissa">Käynnissä</option>
<option value="odottaa">Odottaa</option>
<option value="valmis">Valmis</option>
</select>
</div>
<div class="form-group">
<label for="task-form-deadline">Deadline</label>
<input type="date" id="task-form-deadline">
</div>
</div>
<div class="form-group" style="margin-bottom:1rem;">
<label for="task-form-assigned">Vastuuhenkilö</label>
<select id="task-form-assigned">
<option value="">— Ei vastuuhenkilöä —</option>
</select>
</div>
<div class="form-group" style="margin-bottom:1rem;">
<label for="task-form-desc">Kuvaus</label>
<textarea id="task-form-desc" rows="6" style="resize:vertical;" placeholder="Kuvaa tehtävä tarkemmin..."></textarea>
</div>
<div style="padding:1rem 0 0 0;border-top:1px solid #eee;display:flex;gap:0.5rem;">
<button type="submit" class="btn-primary">Tallenna</button>
<button type="button" class="btn-secondary" id="task-form-cancel">Peruuta</button>
</div>
</form>
</div>
</div>
</div>
<!-- ===== KEHITYSEHDOTUKSET ===== -->
<div id="todo-subtab-features" style="display:none;">
<!-- Listanäkymä -->
<div id="features-list-view">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.75rem;margin-bottom:1rem;">
<div class="search-bar" style="flex:1;min-width:200px;max-width:400px;">
<input type="text" id="feature-search-input" placeholder="Hae ehdotuksista..." style="width:100%;">
</div>
<div style="display:flex;gap:0.5rem;align-items:center;">
<select id="feature-status-filter" style="padding:0.4rem 0.6rem;border-radius:6px;border:1px solid #ddd;font-size:0.85rem;">
<option value="">Kaikki statukset</option>
<option value="ehdotettu">Ehdotettu</option>
<option value="harkinnassa">Harkinnassa</option>
<option value="toteutettu">Toteutettu</option>
<option value="hylatty">Hylätty</option>
</select>
<button class="btn-primary" id="btn-add-feature">+ Uusi ehdotus</button>
</div>
</div>
<div id="features-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem;"></div>
<p id="no-features" style="display:none;text-align:center;color:#aaa;padding:3rem 0;">Ei kehitysehdotuksia.</p>
</div>
<!-- Lukunäkymä -->
<div id="feature-read-view" style="display:none;">
<button class="btn-secondary" id="btn-feature-back">&larr; Takaisin</button>
<div class="table-card" style="padding:1.5rem 2rem;margin-top:1rem;">
<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:1rem;margin-bottom:1rem;">
<div>
<h2 id="feature-read-title" style="color:var(--primary-color);margin:0;"></h2>
<div id="feature-read-meta" style="font-size:0.85rem;color:#888;margin-top:0.3rem;"></div>
</div>
<div style="display:flex;gap:0.5rem;align-items:center;" id="feature-read-actions"></div>
</div>
<div style="margin-bottom:1rem;" id="feature-read-badges"></div>
<div id="feature-read-description" style="margin-bottom:1.5rem;line-height:1.7;white-space:pre-wrap;"></div>
<!-- Kommentit -->
<div id="feature-comments-section">
<h3 style="font-size:1rem;margin-bottom:0.75rem;">&#128172; Kommentit <span id="feature-comment-count" style="font-weight:400;color:#888;"></span></h3>
<div id="feature-comments-list"></div>
<div style="margin-top:0.75rem;display:flex;gap:0.5rem;">
<textarea id="feature-comment-input" rows="2" placeholder="Kirjoita kommentti..." style="flex:1;font-size:0.88rem;resize:vertical;min-height:50px;"></textarea>
<button class="btn-primary" id="btn-feature-comment-send" style="align-self:flex-end;font-size:0.85rem;">Lähetä</button>
</div>
</div>
</div>
</div>
<!-- Muokkausnäkymä -->
<div id="feature-edit-view" style="display:none;">
<button class="btn-secondary" id="btn-feature-edit-cancel">&larr; Peruuta</button>
<div class="table-card" style="padding:1.5rem 2rem;margin-top:1rem;">
<h2 id="feature-edit-title" style="color:var(--primary-color);margin-bottom:1rem;">Uusi kehitysehdotus</h2>
<form id="feature-form">
<input type="hidden" id="feature-form-id">
<input type="hidden" id="feature-form-type" value="feature_request">
<div class="form-group" style="margin-bottom:1rem;">
<label for="feature-form-title">Otsikko *</label>
<input type="text" id="feature-form-title" required placeholder="Parannusehdotuksen otsikko">
</div>
<div class="form-group" style="margin-bottom:1rem;">
<label for="feature-form-desc">Kuvaus</label>
<textarea id="feature-form-desc" rows="8" style="resize:vertical;" placeholder="Kuvaa ehdotuksesi tarkemmin: miksi tämä olisi hyödyllinen, miten sen pitäisi toimia..."></textarea>
</div>
<div style="padding:1rem 0 0 0;border-top:1px solid #eee;display:flex;gap:0.5rem;">
<button type="submit" class="btn-primary">Lähetä ehdotus</button>
<button type="button" class="btn-secondary" id="feature-form-cancel">Peruuta</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Tab: Arkisto -->
<div class="tab-content" id="tab-content-archive">
<div class="main-container">
@@ -887,6 +1100,9 @@
<label style="display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;cursor:pointer;">
<input type="checkbox" data-module="ohjeet"> Ohjeet
</label>
<label style="display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;cursor:pointer;">
<input type="checkbox" data-module="todo"> Tehtävät
</label>
<label style="display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;cursor:pointer;">
<input type="checkbox" data-module="settings" checked> Asetukset / API
</label>