netops/netops-todo-node/public/index.html
Jon Vanvik bdeb6254b9 Dagsplan: flytt/navn/deling + plan-badge, kategorier, feltmodus
Dagsplan kan navngis og flyttes til annen dato (slår sammen uten datatap).
Delbar som hovedregel (shared-flagg default true) med toggle for privat;
andres delte planer kan ses skrivebeskyttet via "Plan for"-velger.
getPlanFor håndhever tilgang. Per-oppgave plan-badge (dag + person) via
planIndex filtrert per viewer.

Egendefinerte kategorier (categories.json) i tillegg til de 12 innebygde;
dynamisk validering, deles via WS, addes fra kategori-nedtrekket.

Feltmodus (body.field-mode): større berøringsmål for mobil, auto på <640px.

plans.json migreres array -> {name,items,shared} ved oppstart (bakoverkompat).
26 integrasjonstester (6 nye), alle grønne. README + CLAUDE.md oppdatert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-15 13:59:19 +02:00

333 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="no">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NetOps To-Do · Network Engineer</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1"></script>
</head>
<body>
<div class="container">
<header>
<div>
<h1><span id="conn-dot" title="Tilkoblingsstatus"></span> NetOps To-Do</h1>
<div class="sub" id="conn-text">kobler til …</div>
</div>
<div class="header-actions">
<span class="online-row" id="online-row" title="Pålogget nå"></span>
<span class="user-chip" id="user-chip" title="Pålogget bruker"></span>
<button id="fieldBtn" title="Feltmodus — større knapper for mobil (f)">📱 Felt</button>
<button id="activityBtn" title="Aktivitetslogg (a)">☰ Aktivitet</button>
<button id="exportBtn" title="Eksporter JSON"></button>
<button id="importBtn" title="Importer JSON (erstatter alt)"></button>
<input type="file" id="importFile" accept=".json" style="display:none">
<button class="primary" id="newTaskBtn" title="Ny oppgave (n)">+ Ny oppgave</button>
</div>
</header>
<div class="anon-banner" id="anon-banner">
<span>Du er pålogget som <b>anon</b>. Velg et navn så kollegene ser hvem som gjør hva.</span>
<button class="primary" id="setNameBtn">Sett navn</button>
</div>
<div class="quickadd">
<div class="quickadd-row">
<input type="text" id="quickadd-input" autocomplete="off"
placeholder='Hurtig-registrering: f.eks. "Bytt SFP i core-sw01 #l1 p2 @me !imorgen ~1h loc:DC-OSL-1"'>
<button class="primary" id="quickadd-btn">Legg til</button>
</div>
<div class="quickadd-preview" id="quickadd-preview"></div>
<div class="quickadd-hint">#kategori-eller-tag · @eier (@me) · p1p4 · !frist (2026-06-20, +3d, idag, imorgen) · ~timer · loc:sted · resten blir tittel</div>
</div>
<section class="kpi-row">
<div class="kpi">
<div class="kpi-label">Åpne oppgaver</div>
<div class="kpi-value" id="kpi-open">0</div>
<div class="kpi-sub" id="kpi-open-sub">av 0 totalt</div>
</div>
<div class="kpi kpi-me">
<div class="kpi-label">Mine åpne</div>
<div class="kpi-value" id="kpi-mine">0</div>
<div class="kpi-sub" id="kpi-mine-sub">tildelt meg</div>
</div>
<div class="kpi kpi-p1">
<div class="kpi-label">P1 Kritisk</div>
<div class="kpi-value" id="kpi-p1">0</div>
<div class="kpi-sub">umiddelbar handling</div>
</div>
<div class="kpi kpi-warn">
<div class="kpi-label">Est. tid igjen</div>
<div class="kpi-value" id="kpi-hours">0t</div>
<div class="kpi-sub" id="kpi-hours-sub">arbeidstimer</div>
</div>
<div class="kpi">
<div class="kpi-label">Forfalte</div>
<div class="kpi-value" id="kpi-overdue">0</div>
<div class="kpi-sub">over frist</div>
</div>
<div class="kpi kpi-ok">
<div class="kpi-label">Ferdig (7d)</div>
<div class="kpi-value" id="kpi-done">0</div>
<div class="kpi-sub">siste 7 dager</div>
</div>
</section>
<section class="chart-row" id="chart-row">
<div class="panel">
<h3>Åpne oppgaver per kategori <span id="cat-total" style="color:var(--text-faint);font-family:var(--mono);text-transform:none;letter-spacing:0"></span></h3>
<div class="chart-wrap"><canvas id="catChart"></canvas></div>
</div>
<div class="panel">
<h3>Fordeling etter prioritet</h3>
<div class="chart-wrap"><canvas id="prioChart"></canvas></div>
</div>
<div class="panel">
<h3>Åpne oppgaver per eier</h3>
<div class="chart-wrap"><canvas id="ownerChart"></canvas></div>
</div>
</section>
<div class="filters">
<div class="view-toggle">
<button id="view-board" title="Tavle (v)">▦ Tavle</button>
<button id="view-table" title="Tabell (v)">☰ Tabell</button>
<button id="view-plan" title="Dagsplan (p)">📅 Dagsplan</button>
</div>
<div class="filter-group" id="fg-status">
<label>Status</label>
<select id="f-status">
<option value="open">Åpne</option>
<option value="all">Alle</option>
<option value="todo">Å gjøre</option>
<option value="progress">Pågår</option>
<option value="blocked">Blokkert</option>
<option value="done">Ferdig</option>
</select>
</div>
<div class="filter-group">
<label>Kategori</label>
<select id="f-category"><option value="all">Alle</option></select>
</div>
<div class="filter-group">
<label>Prioritet</label>
<select id="f-priority">
<option value="all">Alle</option>
<option value="P1">P1 Kritisk</option>
<option value="P2">P2 Høy</option>
<option value="P3">P3 Middels</option>
<option value="P4">P4 Lav</option>
</select>
</div>
<div class="filter-group">
<label>Eier</label>
<select id="f-owner">
<option value="all">Alle</option>
<option value="__me__">Meg</option>
<option value="__unassigned__">Utildelt</option>
</select>
</div>
<input class="search-input" type="text" id="f-search" placeholder="Søk tittel, beskrivelse, lokasjon, tag … (/)">
<button id="clearFilters">Nullstill</button>
</div>
<section class="board" id="board" style="display:none"></section>
<section class="planner" id="planner" style="display:none">
<div class="plan-col" id="plan-available-col">
<div class="plan-col-head">
<h3>Tilgjengelige oppgaver <span class="col-meta" id="plan-avail-count"></span></h3>
<span class="plan-col-hint">klikk + for å planlegge</span>
</div>
<div class="plan-col-body" id="plan-available-body"></div>
</div>
<div class="plan-col plan-today">
<div class="plan-today-head">
<div class="plan-date-row">
<span class="plan-cal">📅</span>
<input type="date" id="plan-date" title="Velg dag å planlegge">
<input type="text" id="plan-name" class="plan-name-input" maxlength="80" autocomplete="off"
placeholder="Navngi planen (valgfritt), f.eks. «Vedlikeholdsvindu DC-OSL» …">
</div>
<div class="plan-controls">
<div class="plan-owner-row">
<label for="plan-view-user">Plan for</label>
<select id="plan-view-user" title="Se en annens dagsplan (skrivebeskyttet)"></select>
<label class="plan-share" title="Når på: andre kan se denne dagsplanen"><input type="checkbox" id="plan-shared"> Synlig for alle</label>
</div>
<span class="col-meta" id="plan-capacity"></span>
<div class="plan-move" id="plan-move">
<label for="plan-move-date">Flytt til</label>
<input type="date" id="plan-move-date" title="Måldato for hele planen">
<button id="plan-move-btn" title="Flytt hele planen til valgt dato">⇄ Flytt</button>
</div>
</div>
</div>
<div class="plan-readonly" id="plan-readonly"></div>
<div class="plan-col-body" id="plan-today-body"></div>
</div>
</section>
<div class="table-panel" id="table-panel" style="display:none">
<div class="table-head">
<h3>Oppgaveliste <span class="count" id="tbl-count">0</span></h3>
<div class="sub" style="color:var(--text-faint); font-family:var(--mono); font-size:11px">klikk kolonne for å sortere</div>
</div>
<div class="table-scroll">
<table id="tbl">
<thead>
<tr>
<th data-sort="priority" style="width:70px">Prio</th>
<th data-sort="title">Oppgave</th>
<th data-sort="category" style="width:110px">Kategori</th>
<th data-sort="status" style="width:100px">Status</th>
<th data-sort="owner" style="width:130px">Eier</th>
<th data-sort="location" style="width:120px">Lokasjon</th>
<th data-sort="estHours" style="width:70px;text-align:right">Est (t)</th>
<th data-sort="deadline" style="width:110px">Frist</th>
<th style="width:120px;text-align:right">Handling</th>
</tr>
</thead>
<tbody id="tbl-body"></tbody>
</table>
</div>
</div>
<div id="empty" class="empty-state" style="display:none">
Ingen oppgaver matcher filtrene.
<div id="empty-seed" style="display:none;margin-top:14px">
Tom database. <button id="seedBtn" class="primary">Last inn eksempel-data</button>
</div>
</div>
<footer>
Sanntidssynk via WebSocket · snarveier:
<span class="kbd">n</span> ny ·
<span class="kbd">/</span> søk ·
<span class="kbd">v</span> bytt visning ·
<span class="kbd">p</span> dagsplan ·
<span class="kbd">a</span> aktivitet ·
<span class="kbd">esc</span> lukk
</footer>
</div>
<aside class="drawer" id="drawer">
<div class="drawer-head">
<h2>Aktivitet</h2>
<button class="icon-btn" id="drawer-close" style="font-size:18px">×</button>
</div>
<div class="drawer-body" id="drawer-body"></div>
</aside>
<div class="modal-overlay" id="modal">
<div class="modal">
<div class="modal-head">
<h2 id="modal-title">Ny oppgave</h2>
<button class="icon-btn" id="modal-close" style="font-size:18px">×</button>
</div>
<div class="modal-warning" id="modal-warning"></div>
<div class="modal-body">
<div class="form-field full">
<label>Tittel *</label>
<input type="text" id="f-title" placeholder="F.eks. Konfigurer BGP-peer mot AS2119">
</div>
<div class="form-field full">
<label>Beskrivelse / notater</label>
<textarea id="f-desc" placeholder="Kommandoer, konfigurasjon, referanser …"></textarea>
</div>
<div class="form-field">
<label>Kategori *</label>
<select id="f-cat"></select>
</div>
<div class="form-field">
<label>Prioritet</label>
<select id="f-prio">
<option value="P1">P1 Kritisk</option>
<option value="P2" selected>P2 Høy</option>
<option value="P3">P3 Middels</option>
<option value="P4">P4 Lav</option>
</select>
</div>
<div class="form-field">
<label>Status</label>
<select id="f-stat">
<option value="todo" selected>Å gjøre</option>
<option value="progress">Pågår</option>
<option value="blocked">Blokkert</option>
<option value="done">Ferdig</option>
</select>
</div>
<div class="form-field">
<label>Est. tid (timer)</label>
<input type="number" id="f-est" min="0" step="0.5" placeholder="2">
</div>
<div class="form-field full">
<label>Eier / tildelt</label>
<div class="owner-input-row">
<input type="text" id="f-owner-input" placeholder="brukernavn, eller blank for utildelt">
<select id="f-owner-select" title="Velg eksisterende bruker">
<option value="">— velg —</option>
</select>
</div>
</div>
<div class="form-field">
<label>Lokasjon / site</label>
<input type="text" id="f-loc" placeholder="DC-OSL-1, Bergen POP, remote …">
</div>
<div class="form-field">
<label>Frist</label>
<input type="date" id="f-dl">
</div>
<div class="form-field full">
<label>Tags (komma-separert)</label>
<input type="text" id="f-tags" placeholder="vedlikeholdsvindu, kunde-X, audit">
</div>
<div class="form-field full status-note-field" id="f-statusnote-field" style="display:none">
<label id="f-statusnote-label">Avslutningskommentar (valgfritt)</label>
<input type="text" id="f-statusnote" placeholder="Legges som kommentar når du lagrer">
</div>
<div class="comments-section" id="comments-section" style="display:none">
<h4>Kommentarer <span id="comment-count" style="font-family:var(--mono)"></span></h4>
<div id="comments-list"></div>
<div class="comment-input-row">
<input type="text" id="comment-input" placeholder="Skriv en kommentar … (enter for å sende)">
<button id="comment-send">Send</button>
</div>
</div>
</div>
<div class="modal-foot">
<button class="danger" id="modal-delete" style="display:none">Slett</button>
<span id="modal-audit" style="flex:1;font-size:11px;color:var(--text-faint);font-family:var(--mono)"></span>
<button id="modal-cancel">Avbryt</button>
<button class="primary" id="modal-save">Lagre</button>
</div>
</div>
</div>
<div class="modal-overlay" id="status-modal">
<div class="modal" style="max-width:460px">
<div class="modal-head">
<h2 id="status-modal-title">Kommentar</h2>
<button class="icon-btn" id="status-modal-close" style="font-size:18px">×</button>
</div>
<div class="modal-body" style="grid-template-columns:1fr">
<div class="form-field full">
<label id="status-modal-label">Legg til en kommentar (valgfritt)</label>
<textarea id="status-comment" placeholder="F.eks. hva som ble gjort, eller hva som blokkerer …"></textarea>
</div>
</div>
<div class="modal-foot">
<span style="flex:1;font-size:11px;color:var(--text-faint)">⏎ for å lagre · Esc for å avbryte</span>
<button id="status-skip">Hopp over</button>
<button class="primary" id="status-save">Lagre</button>
</div>
</div>
</div>
<div class="toast-stack" id="toast-stack"></div>
<script src="app.js"></script>
</body>
</html>