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; }
+}