From 4f24592dd0a18c0fb1cbc883e3480cd687a5f2d9 Mon Sep 17 00:00:00 2001 From: Jon Vanvik Date: Mon, 15 Jun 2026 15:09:57 +0200 Subject: [PATCH] =?UTF-8?q?Mer=20neon=20i=20appen,=20mobiltilpasning=20og?= =?UTF-8?q?=20gruppert=20s=C3=B8kbar=20planoversikt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neon-tema trukket videre inn i hoved-GUI: animert topp-stripe, glødende panel-kanter/overskrifter, KPI-glød, fokus-glød på input, kort-hover. - Mobil (≤600px): fjerner horisontal overflyt — planner-header, filtre, hurtig-add, planoversikt-panel, admin-skjema og skuff fyller/wrapper bredden. - Planvelger → «📋 Planoversikt (N)»: gruppert i I dag/Kommende/Tidligere med relative datoer, søk på navn/dato/person, markerer aktiv plan. Co-Authored-By: Claude Fable 5 --- netops-todo-node/public/app.js | 51 +++++++++++---- netops-todo-node/public/style.css | 100 ++++++++++++++++++++++++++---- 2 files changed, 127 insertions(+), 24 deletions(-) diff --git a/netops-todo-node/public/app.js b/netops-todo-node/public/app.js index 409c2ca..c1d319d 100644 --- a/netops-todo-node/public/app.js +++ b/netops-todo-node/public/app.js @@ -795,6 +795,10 @@ function renderPlan() { $('plan-shared').disabled = !own; $('plan-shared').closest('.plan-share').style.display = own ? '' : 'none'; $('plan-move').style.display = own ? '' : 'none'; + const nPlans = derivePlanList().length; + $('plan-picker-btn').textContent = `📋 Planoversikt${nPlans ? ` (${nPlans})` : ''} ▾`; + // Hold panelet oppdatert hvis det er åpent + if ($('plan-picker-panel').classList.contains('open')) renderPlanPicker($('plan-picker-search').value); populatePlanUserSelect(); // Lesemodus-banner når man ser en annens plan @@ -865,27 +869,50 @@ function derivePlanList() { }); } +function planDateLabel(date) { + const today = todayISO(); + if (date === today) return 'I dag'; + if (date === todayISO(1)) return 'I morgen'; + if (date === todayISO(-1)) return 'I går'; + const diff = Math.round((new Date(date) - new Date(today)) / 86400000); + if (diff > 1 && diff <= 7) return `om ${diff} dager`; + if (diff < -1 && diff >= -7) return `${Math.abs(diff)} dager siden`; + return date; +} + +function planPickRow(p) { + const me = p.user === state.user; + const cur = p.user === state.plan.viewUser && p.date === state.plan.date; + return `
+ ${planDateLabel(p.date)} + ${p.name ? escapeHtml(p.name) : '(uten navn)'} + ${me ? 'meg' : escapeHtml(p.user)} + ${p.count} +
`; +} + function renderPlanPicker(filter = '') { const q = filter.trim().toLowerCase(); const list = derivePlanList().filter(p => { if (!q) return true; - return [p.name, p.date, p.user === state.user ? 'meg' : p.user].join(' ').toLowerCase().includes(q); + return [p.name, p.date, planDateLabel(p.date), p.user === state.user ? 'meg' : p.user].join(' ').toLowerCase().includes(q); }); const el = $('plan-picker-list'); if (!list.length) { - el.innerHTML = `
${q ? 'Ingen planer matcher søket.' : 'Ingen planer ennå.'}
`; + el.innerHTML = `
${q ? 'Ingen planer matcher søket.' : 'Ingen planer ennå — lag en ved å legge oppgaver i dagens plan.'}
`; return; } - el.innerHTML = list.map(p => { - const me = p.user === state.user; - const d = p.date.slice(8, 10) + '.' + p.date.slice(5, 7) + '.' + p.date.slice(0, 4); - return `
- ${d} - ${p.name ? escapeHtml(p.name) : '(uten navn)'} - ${me ? 'meg' : escapeHtml(p.user)} - ${p.count} -
`; - }).join(''); + const today = todayISO(); + const groups = { today: [], upcoming: [], past: [] }; + for (const p of list) { + if (p.date === today) groups.today.push(p); + else if (p.date > today) groups.upcoming.push(p); + else groups.past.push(p); + } + groups.upcoming.sort((a, b) => a.date.localeCompare(b.date)); // nærmeste først + groups.past.sort((a, b) => b.date.localeCompare(a.date)); // nyeste fortid først + const section = (title, arr) => arr.length ? `
${title}
` + arr.map(planPickRow).join('') : ''; + el.innerHTML = section('I dag', groups.today) + section('Kommende', groups.upcoming) + section('Tidligere', groups.past); } function togglePlanPicker(open) { diff --git a/netops-todo-node/public/style.css b/netops-todo-node/public/style.css index d2fdd3f..a0b6311 100644 --- a/netops-todo-node/public/style.css +++ b/netops-todo-node/public/style.css @@ -391,11 +391,15 @@ tbody tr.status-done .task-title { text-decoration: line-through; } .plan-picker { position: relative; } .plan-picker > button { padding: 6px 10px; font-size: 12px; } .plan-picker-panel { display: none; position: absolute; z-index: 30; top: calc(100% + 6px); left: 0; - width: 320px; max-width: 80vw; background: var(--card); border: 1px solid var(--border-strong); - border-radius: 8px; box-shadow: 0 16px 40px rgba(0,0,0,0.5); padding: 10px; } + width: 360px; max-width: calc(100vw - 24px); background: var(--card); border: 1px solid var(--border-strong); + border-radius: 8px; box-shadow: 0 16px 40px rgba(0,0,0,0.5), 0 0 40px rgba(79,195,247,0.15); padding: 10px; } .plan-picker-panel.open { display: block; } .plan-picker-panel input { width: 100%; margin-bottom: 8px; } -.plan-picker-list { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; } +.plan-picker-list { max-height: 340px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; } +.pp-group { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 1px; + color: var(--accent); margin: 9px 2px 3px; opacity: 0.85; } +.pp-group:first-child { margin-top: 2px; } +.plan-pick-row.current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 12px rgba(79,195,247,0.3); } .plan-pick-row { display: flex; align-items: center; gap: 8px; padding: 7px 9px; border-radius: 6px; cursor: pointer; background: var(--bg-soft); border: 1px solid var(--border); font-size: 13px; } .plan-pick-row:hover { border-color: var(--accent); background: var(--card-hover); } @@ -502,18 +506,47 @@ body.field-mode .row-actions { gap: 8px; } /* Modaler fyller skjermen bedre på mobil */ body.field-mode .modal { max-width: 100%; } -/* ---------- Neon-aksenter (samme tema som login) ---------- */ -header h1 { text-shadow: 0 0 10px rgba(79,195,247,0.4), 0 0 26px rgba(79,195,247,0.18); } -#conn-dot.live { box-shadow: 0 0 12px var(--ok), 0 0 20px rgba(74,222,128,0.5); } -button.primary { box-shadow: 0 0 16px rgba(79,195,247,0.35); } -button.primary:hover { box-shadow: 0 0 28px rgba(79,195,247,0.6); } -button.active-view { box-shadow: inset 0 0 0 1px rgba(79,195,247,0.4), 0 0 14px rgba(79,195,247,0.22); } -.user-chip { box-shadow: 0 0 14px rgba(79,195,247,0.18); } -.kpi::before { box-shadow: 0 0 10px rgba(79,195,247,0.45); } +/* ---------- Neon-tema (samme uttrykk som login) ---------- */ +/* Tynn neon-stripe øverst på siden */ +body::before { + content: ""; position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 50; pointer-events: none; + background: linear-gradient(90deg, transparent, var(--accent) 20%, #ff5cf4 50%, var(--accent) 80%, transparent); + background-size: 200% 100%; opacity: 0.8; animation: neonbar 8s linear infinite; + box-shadow: 0 0 12px rgba(79,195,247,0.5); +} +@keyframes neonbar { from { background-position: 200% 0; } to { background-position: 0 0; } } + +header { border-bottom-color: rgba(79,195,247,0.25); box-shadow: 0 1px 0 rgba(79,195,247,0.15); } +header h1 { color: #eafcff; text-shadow: 0 0 10px rgba(79,195,247,0.5), 0 0 28px rgba(79,195,247,0.25); } +#conn-dot.live { box-shadow: 0 0 12px var(--ok), 0 0 22px rgba(74,222,128,0.55); } + +/* Paneler får en svak neon-kant */ +.panel, .table-panel, .kpi, .board-col, .plan-col, .filters, .quickadd { + border-color: rgba(79,195,247,0.16); +} +.panel, .table-panel { box-shadow: 0 0 24px rgba(79,195,247,0.06); } +.panel h3, .table-head h3, .drawer-head h2, .board-col-head h3 { color: var(--accent); } +.panel h3 { text-shadow: 0 0 10px rgba(79,195,247,0.25); } + +/* Knapper / chips */ +button.primary { box-shadow: 0 0 16px rgba(79,195,247,0.4); } +button.primary:hover { box-shadow: 0 0 30px rgba(79,195,247,0.7); } +button.active-view { box-shadow: inset 0 0 0 1px rgba(79,195,247,0.45), 0 0 16px rgba(79,195,247,0.3); } +.user-chip { box-shadow: 0 0 14px rgba(79,195,247,0.22); } + +/* Inputs: cyan glød ved fokus */ +select:focus, input:focus, textarea:focus { box-shadow: 0 0 0 1px var(--accent), 0 0 14px rgba(79,195,247,0.35); } + +/* KPI-kanter og kort */ +.kpi::before { box-shadow: 0 0 10px rgba(79,195,247,0.5); } .kpi.kpi-p1::before { box-shadow: 0 0 12px var(--p1); } .kpi.kpi-ok::before { box-shadow: 0 0 12px var(--ok); } .kpi.kpi-me::before { box-shadow: 0 0 12px var(--purple); } -.badge.prio-P1 { box-shadow: 0 0 10px rgba(255,83,112,0.3); } +.kpi.kpi-warn::before { box-shadow: 0 0 12px var(--warn); } +.kpi-value { text-shadow: 0 0 14px rgba(79,195,247,0.25); } +.card:hover { border-color: rgba(79,195,247,0.45); box-shadow: 0 4px 14px rgba(0,0,0,0.35), 0 0 18px rgba(79,195,247,0.18); } +.badge.prio-P1 { box-shadow: 0 0 10px rgba(255,83,112,0.35); } +.cat-badge { box-shadow: 0 0 8px rgba(79,195,247,0.12); } @media (max-width: 1250px) { .board { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 1100px) { .chart-row { grid-template-columns: 1fr 1fr; } } @@ -525,3 +558,46 @@ button.active-view { box-shadow: inset 0 0 0 1px rgba(79,195,247,0.4), 0 0 14px .kpi-value { font-size: 20px; } header h1 { font-size: 18px; } } + +/* Smal mobil: hindre horisontal overflyt, la rader wrappe og fylle bredden */ +@media (max-width: 600px) { + .container { padding: 12px; } + header { gap: 8px; } + header h1 { font-size: 17px; } + .header-actions { gap: 6px; } + .header-actions button { padding: 7px 10px; } + .kpi-row { grid-template-columns: 1fr 1fr; gap: 8px; } + .kpi { padding: 12px 14px; } + .kpi-value { font-size: 19px; } + + .filters { gap: 8px; padding: 10px; } + .view-toggle { width: 100%; } + .view-toggle button { flex: 1; } + .filter-group { flex: 1 1 auto; } + .filter-group select { flex: 1; } + .search-input { min-width: 0; flex: 1 1 100%; } + + .quickadd-row { flex-direction: column; } + .quickadd-row button { width: 100%; } + + /* Planner-header: alt wrapper og fyller bredden */ + .plan-date-row { flex-wrap: wrap; } + .plan-name-input { flex: 1 1 100%; } + .plan-controls { flex-direction: column; align-items: stretch; gap: 8px; } + .plan-owner-row, .plan-move { flex-wrap: wrap; } + .plan-move input[type="date"] { flex: 1; } + .plan-picker { width: 100%; } + .plan-picker > button { width: 100%; } + .plan-picker-panel { left: 0; width: calc(100vw - 24px); } + + /* Konto/admin og skuff */ + .admin-create { flex-direction: column; } + .admin-create input, .admin-create select, .admin-create button { width: 100%; min-width: 0; } + .modal { max-width: 100%; } + .drawer { width: 100%; max-width: 100%; } + + /* Toasts klemmes innenfor skjermen */ + .toast-stack { left: 10px; right: 10px; align-items: stretch; } + .toast { max-width: 100%; } + .toast span { word-break: break-word; } +}