Mer neon i appen, mobiltilpasning og gruppert søkbar planoversikt
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
3f69cd4248
commit
4f24592dd0
2 changed files with 127 additions and 24 deletions
|
|
@ -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 `<div class="plan-pick-row${cur ? ' current' : ''}" onclick="pickPlan('${escapeHtml(p.user)}','${p.date}')">
|
||||
<span class="pp-date">${planDateLabel(p.date)}</span>
|
||||
<span class="pp-name ${p.name ? '' : 'unnamed'}">${p.name ? escapeHtml(p.name) : '(uten navn)'}</span>
|
||||
<span class="pp-who ${me ? 'me' : ''}">${me ? 'meg' : escapeHtml(p.user)}</span>
|
||||
<span class="pp-count" title="oppgaver">${p.count}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
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 = `<div class="plan-pick-empty">${q ? 'Ingen planer matcher søket.' : 'Ingen planer ennå.'}</div>`;
|
||||
el.innerHTML = `<div class="plan-pick-empty">${q ? 'Ingen planer matcher søket.' : 'Ingen planer ennå — lag en ved å legge oppgaver i dagens plan.'}</div>`;
|
||||
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 `<div class="plan-pick-row" onclick="pickPlan('${escapeHtml(p.user)}','${p.date}')">
|
||||
<span class="pp-date">${d}</span>
|
||||
<span class="pp-name ${p.name ? '' : 'unnamed'}">${p.name ? escapeHtml(p.name) : '(uten navn)'}</span>
|
||||
<span class="pp-who ${me ? 'me' : ''}">${me ? 'meg' : escapeHtml(p.user)}</span>
|
||||
<span class="pp-count">${p.count}</span>
|
||||
</div>`;
|
||||
}).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 ? `<div class="pp-group">${title}</div>` + arr.map(planPickRow).join('') : '';
|
||||
el.innerHTML = section('I dag', groups.today) + section('Kommende', groups.upcoming) + section('Tidligere', groups.past);
|
||||
}
|
||||
|
||||
function togglePlanPicker(open) {
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue