Skip to content

Commit 0adf33a

Browse files
authored
Update index.html
1 parent ca47398 commit 0adf33a

1 file changed

Lines changed: 237 additions & 23 deletions

File tree

index.html

Lines changed: 237 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>dev_dashboard</title>
77
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Syne:wght@400;600;800&display=swap" rel="stylesheet" />
8+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
89
<style>
910
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
1011
:root{
@@ -234,6 +235,32 @@
234235
@keyframes blink{0%,80%,100%{opacity:.2;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}
235236
@keyframes slide{from{transform:translateX(-100%)}to{transform:translateX(350%)}}
236237
@media(max-width:900px){.grid-2,.grid-4,.kanban-board,.suggestion-grid{grid-template-columns:1fr}}
238+
/* ── Markdown styles ── */
239+
.md-content h1,.md-content h2,.md-content h3{color:var(--green);font-family:var(--font-display);font-weight:600;margin:10px 0 6px;line-height:1.3}
240+
.md-content h1{font-size:16px}.md-content h2{font-size:14px}.md-content h3{font-size:13px}
241+
.md-content p{margin-bottom:8px;line-height:1.7;font-size:12px;color:var(--text)}
242+
.md-content ul,.md-content ol{margin:6px 0 8px 16px;font-size:12px;color:var(--text);line-height:1.7}
243+
.md-content li{margin-bottom:3px}
244+
.md-content code{background:var(--surface2);border:1px solid var(--border);padding:1px 5px;font-family:var(--font-mono);font-size:11px;color:var(--green)}
245+
.md-content pre{background:var(--surface2);border:1px solid var(--border);padding:10px;margin:8px 0;overflow-x:auto}
246+
.md-content pre code{background:none;border:none;padding:0;color:var(--green);font-size:11px}
247+
.md-content blockquote{border-left:2px solid var(--green);padding-left:10px;color:var(--text-muted);font-style:italic;margin:8px 0}
248+
.md-content strong{color:var(--text);font-weight:700}
249+
.md-content em{color:var(--text-muted)}
250+
.md-content a{color:var(--blue);text-decoration:none}
251+
.md-content a:hover{text-decoration:underline}
252+
.md-content hr{border:none;border-top:1px solid var(--border);margin:10px 0}
253+
.md-content table{width:100%;border-collapse:collapse;font-size:11px;margin:8px 0}
254+
.md-content th{background:var(--surface2);border:1px solid var(--border);padding:6px 10px;text-align:left;color:var(--green);font-size:10px;text-transform:uppercase;letter-spacing:.5px}
255+
.md-content td{border:1px solid var(--border);padding:6px 10px;color:var(--text)}
256+
/* ── MD editor ── */
257+
.md-editor-wrap{position:relative;margin-bottom:10px}
258+
.md-tabs{display:flex;gap:0;margin-bottom:0;border-bottom:1px solid var(--border)}
259+
.md-tab{background:transparent;border:none;border-bottom:2px solid transparent;color:var(--text-muted);font-family:var(--font-mono);font-size:10px;padding:6px 14px;cursor:pointer;letter-spacing:.5px;text-transform:uppercase;margin-bottom:-1px;transition:all .2s}
260+
.md-tab.active{color:var(--green);border-bottom-color:var(--green)}
261+
.md-textarea{width:100%;min-height:120px;background:var(--surface2);border:1px solid var(--border);border-top:none;color:var(--text);font-family:var(--font-mono);font-size:12px;padding:10px 12px;outline:none;resize:vertical;transition:border-color .2s;line-height:1.6}
262+
.md-textarea:focus{border-color:var(--green)}
263+
.md-preview{min-height:120px;background:var(--surface2);border:1px solid var(--border);border-top:none;padding:10px 12px;display:none}
237264
</style>
238265
</head>
239266
<body>
@@ -321,7 +348,6 @@
321348

322349
<div class="nav-tabs">
323350
<button class="nav-tab active" onclick="switchTab('overview')">overview</button>
324-
<button class="nav-tab" onclick="switchTab('todos')">todos</button>
325351
<button class="nav-tab" onclick="switchTab('plans')">plans</button>
326352
<button class="nav-tab" onclick="switchTab('ai')">ai</button>
327353
<button class="nav-tab" onclick="switchTab('settings')">settings</button>
@@ -362,10 +388,20 @@
362388
</div>
363389
</div>
364390

365-
<!-- Todos tab -->
366-
<div id="tab-todos" class="tab-panel">
367-
<div class="card">
368-
<div class="card-title"><div class="card-title-left"><span class="card-icon"></span>todos</div></div>
391+
<!-- Plans tab (includes todos) -->
392+
<div id="tab-plans" class="tab-panel">
393+
394+
<!-- Quick todos section -->
395+
<div class="card" style="margin-bottom:16px">
396+
<div class="card-title">
397+
<div class="card-title-left"><span class="card-icon"></span>todos</div>
398+
<div style="display:flex;gap:6px">
399+
<button class="filter-btn active" onclick="filterTodos('all',this)">all</button>
400+
<button class="filter-btn" onclick="filterTodos('todo',this)">todo</button>
401+
<button class="filter-btn" onclick="filterTodos('wip',this)">wip</button>
402+
<button class="filter-btn" onclick="filterTodos('done',this)">done</button>
403+
</div>
404+
</div>
369405
<div class="todo-form">
370406
<input class="todo-input" id="todo-title-input" placeholder="add a new todo..." />
371407
<select class="todo-select" id="todo-priority-input">
@@ -376,23 +412,15 @@
376412
<input class="todo-input" id="todo-due-input" type="date" style="flex:0 0 140px;color:var(--text-muted)" title="due date (optional)" />
377413
<button class="btn-primary" onclick="addTodo()">[ add ]</button>
378414
</div>
379-
<div class="todo-filters">
380-
<button class="filter-btn active" onclick="filterTodos('all',this)">all</button>
381-
<button class="filter-btn" onclick="filterTodos('todo',this)">todo</button>
382-
<button class="filter-btn" onclick="filterTodos('wip',this)">wip</button>
383-
<button class="filter-btn" onclick="filterTodos('done',this)">done</button>
384-
</div>
385415
<div id="todo-list"></div>
386416
</div>
387-
</div>
388417

389-
<!-- Plans tab -->
390-
<div id="tab-plans" class="tab-panel">
418+
<!-- Kanban plans section -->
391419
<div class="card">
392420
<div class="card-title"><div class="card-title-left"><span class="card-icon"></span>plans</div></div>
393421
<div class="plan-form">
394422
<input class="plan-input" id="plan-title-input" placeholder="plan title..." />
395-
<input class="plan-input" id="plan-desc-input" placeholder="description (optional)" style="flex:2" />
423+
396424
<input class="todo-input" id="plan-due-input" type="date" style="flex:0 0 140px;color:var(--text-muted);background:var(--surface2);border:1px solid var(--border);padding:8px 10px;font-family:var(--font-mono);font-size:11px;outline:none" title="due date (optional)" />
397425
<select class="todo-select" id="plan-col-input">
398426
<option value="backlog">backlog</option>
@@ -401,22 +429,33 @@
401429
</select>
402430
<button class="btn-primary" onclick="addPlan()">[ add ]</button>
403431
</div>
404-
<div class="kanban-board">
405-
<div class="kanban-col" id="col-backlog">
432+
<div class="kanban-board" id="kanban-board"
433+
ondragover="event.preventDefault()"
434+
ondrop="event.preventDefault()">
435+
<div class="kanban-col" id="col-backlog"
436+
ondragover="event.preventDefault();this.style.borderColor='var(--green)'"
437+
ondragleave="this.style.borderColor='var(--border)'"
438+
ondrop="dropPlan(event,'backlog');this.style.borderColor='var(--border)'">
406439
<div class="kanban-col-header">
407440
<div class="kanban-col-title"><div class="col-dot backlog"></div>backlog</div>
408441
<span class="col-count" id="count-backlog">0</span>
409442
</div>
410443
<div id="cards-backlog"></div>
411444
</div>
412-
<div class="kanban-col" id="col-inprogress">
445+
<div class="kanban-col" id="col-inprogress"
446+
ondragover="event.preventDefault();this.style.borderColor='var(--amber)'"
447+
ondragleave="this.style.borderColor='var(--border)'"
448+
ondrop="dropPlan(event,'inprogress');this.style.borderColor='var(--border)'">
413449
<div class="kanban-col-header">
414450
<div class="kanban-col-title"><div class="col-dot inprogress"></div>in progress</div>
415451
<span class="col-count" id="count-inprogress">0</span>
416452
</div>
417453
<div id="cards-inprogress"></div>
418454
</div>
419-
<div class="kanban-col" id="col-done">
455+
<div class="kanban-col" id="col-done"
456+
ondragover="event.preventDefault();this.style.borderColor='var(--green)'"
457+
ondragleave="this.style.borderColor='var(--border)'"
458+
ondrop="dropPlan(event,'done');this.style.borderColor='var(--border)'">
420459
<div class="kanban-col-header">
421460
<div class="kanban-col-title"><div class="col-dot done"></div>done</div>
422461
<span class="col-count" id="count-done">0</span>
@@ -480,6 +519,51 @@
480519
</div>
481520
</div>
482521

522+
<!-- ── Plan detail panel ──────────────────────────────────── -->
523+
<div class="modal-overlay" id="plan-detail-overlay" onclick="closePlanDetail()">
524+
<div class="modal" style="width:520px;max-height:80vh;overflow-y:auto" onclick="event.stopPropagation()">
525+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px">
526+
<div class="modal-title" id="pd-title">Plan Detail</div>
527+
<button class="btn-ghost" onclick="closePlanDetail()">[ close ]</button>
528+
</div>
529+
<div style="margin-bottom:12px">
530+
<div style="font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted);margin-bottom:4px">status</div>
531+
<div id="pd-status" style="font-size:12px;color:var(--green)"></div>
532+
</div>
533+
<div style="margin-bottom:12px">
534+
<div style="font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted);margin-bottom:4px">description</div>
535+
<div id="pd-desc" class="md-content" style="font-size:12px;line-height:1.7"></div>
536+
</div>
537+
<div style="margin-bottom:12px">
538+
<div style="font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted);margin-bottom:4px">due date</div>
539+
<div id="pd-due" style="font-size:12px;color:var(--text)"></div>
540+
</div>
541+
<div style="margin-bottom:20px">
542+
<div style="font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted);margin-bottom:4px">tags</div>
543+
<div id="pd-tags" style="display:flex;gap:4px;flex-wrap:wrap"></div>
544+
</div>
545+
<div id="pd-edit-section" style="display:none;margin-bottom:16px">
546+
<div class="md-editor-wrap">
547+
<div class="md-tabs">
548+
<button class="md-tab active" onclick="switchPdTab('write',this)">write</button>
549+
<button class="md-tab" onclick="switchPdTab('preview',this)">preview</button>
550+
</div>
551+
<textarea class="md-textarea" id="pd-edit-textarea" style="min-height:160px" placeholder="edit description..."></textarea>
552+
<div class="md-preview md-content" id="pd-edit-preview"></div>
553+
</div>
554+
<div style="display:flex;gap:8px;margin-top:8px">
555+
<button class="btn-primary" onclick="pdSaveEdit()">[ save ]</button>
556+
<button class="btn-outline" onclick="pdCancelEdit()">[ cancel ]</button>
557+
</div>
558+
</div>
559+
<div style="display:flex;gap:8px;flex-wrap:wrap">
560+
<button class="btn-primary" id="pd-move-next" onclick="pdMoveNext()">[ move forward ]</button>
561+
<button class="btn-outline" onclick="pdStartEdit()">[ edit ]</button>
562+
<button class="btn-outline danger" id="pd-delete" onclick="pdDelete()">[ delete ]</button>
563+
</div>
564+
</div>
565+
</div>
566+
483567
<!-- ── Confirm modal ─────────────────────────────────────── -->
484568
<div class="modal-overlay" id="modal-overlay">
485569
<div class="modal">
@@ -558,7 +642,7 @@
558642
// ── Tab switching ─────────────────────────────────────────────
559643
function switchTab(name){
560644
document.querySelectorAll('.nav-tab').forEach((t,i)=>{
561-
const tabs=['overview','todos','plans','ai','settings'];
645+
const tabs=['overview','plans','ai','settings'];
562646
t.classList.toggle('active',tabs[i]===name);
563647
});
564648
document.querySelectorAll('.tab-panel').forEach(p=>p.classList.remove('active'));
@@ -818,14 +902,17 @@
818902
const pNow=new Date();
819903
const pOverdue=pDue&&pDue<pNow&&col!=='done';
820904
const pDueStr=pDue?pDue.toLocaleDateString('en-GB',{day:'2-digit',month:'short',year:'2-digit'}):'';
821-
return`<div class="kanban-card">
905+
return`<div class="kanban-card" draggable="true"
906+
ondragstart="dragStart(event,${p.id})"
907+
ondragend="this.style.opacity='1'"
908+
onclick="openPlanDetail(${p.id})">
822909
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:4px">
823910
<div class="kanban-card-title" style="flex:1">${p.title}</div>
824911
${pDueStr?`<span style="font-size:10px;color:${pOverdue?'var(--red)':'var(--text-dim)'};flex-shrink:0;white-space:nowrap">${pOverdue?'⚠ ':''}${pDueStr}</span>`:''}
825912
</div>
826-
${p.description?`<div class="kanban-card-desc">${p.description}</div>`:''}
913+
${p.description?`<div class="kanban-card-desc md-content" style="max-height:48px;overflow:hidden;mask-image:linear-gradient(180deg,#fff 60%,transparent)">${typeof marked !== 'undefined' ? marked.parse(p.description) : p.description}</div>`:''}
827914
${p.tags?.length?`<div class="kanban-card-tags">${p.tags.map(t=>`<span class="kanban-tag">${t}</span>`).join('')}</div>`:''}
828-
<div class="kanban-card-actions">
915+
<div class="kanban-card-actions" onclick="event.stopPropagation()">
829916
${prevCol?`<button class="kanban-move" onclick="movePlan(${p.id},'${prevCol}')">← ${prevCol==='backlog'?'backlog':'in progress'}</button>`:''}
830917
${nextCol?`<button class="kanban-move" onclick="movePlan(${p.id},'${nextCol}')">${nextCol==='done'?'done':'in progress'} →</button>`:''}
831918
<button class="kanban-del" onclick="deletePlan(${p.id})">del</button>
@@ -1040,6 +1127,133 @@
10401127
}
10411128
}
10421129

1130+
// ── MD tab switcher ───────────────────────────────────────────
1131+
function switchMdTab(tab, btn) {
1132+
document.querySelectorAll('.md-tab').forEach(b => b.classList.remove('active'));
1133+
btn.classList.add('active');
1134+
const textarea = document.getElementById('plan-desc-input');
1135+
const preview = document.getElementById('plan-desc-preview');
1136+
if (tab === 'preview') {
1137+
const md = textarea.value.trim();
1138+
preview.innerHTML = md ? (typeof marked !== 'undefined' ? marked.parse(md) : md) : '<span style="color:var(--text-dim);font-size:12px">nothing to preview</span>';
1139+
textarea.style.display = 'none';
1140+
preview.style.display = 'block';
1141+
} else {
1142+
textarea.style.display = 'block';
1143+
preview.style.display = 'none';
1144+
}
1145+
}
1146+
1147+
// ── Drag & drop ───────────────────────────────────────────────
1148+
let draggedPlanId = null;
1149+
1150+
function dragStart(e, id) {
1151+
draggedPlanId = id;
1152+
e.dataTransfer.effectAllowed = 'move';
1153+
setTimeout(() => { if(e.target) e.target.style.opacity = '0.4'; }, 0);
1154+
}
1155+
1156+
function dropPlan(e, col) {
1157+
e.preventDefault();
1158+
if (!draggedPlanId) return;
1159+
movePlan(draggedPlanId, col);
1160+
draggedPlanId = null;
1161+
}
1162+
1163+
// ── Plan detail panel ─────────────────────────────────────────
1164+
let activePlanId = null;
1165+
1166+
function openPlanDetail(id) {
1167+
const p = plans.find(x => x.id === id);
1168+
if (!p) return;
1169+
activePlanId = id;
1170+
const colLabel = {backlog:'Backlog', inprogress:'In Progress', done:'Done'};
1171+
const colColors = {backlog:'var(--text-muted)', inprogress:'var(--amber)', done:'var(--green)'};
1172+
document.getElementById('pd-title').textContent = p.title;
1173+
document.getElementById('pd-status').textContent = colLabel[p.plan_column] || p.plan_column;
1174+
document.getElementById('pd-status').style.color = colColors[p.plan_column] || 'var(--text)';
1175+
document.getElementById('pd-desc').innerHTML = p.description ? (typeof marked !== 'undefined' ? marked.parse(p.description) : p.description) : '<span style="color:var(--text-dim)">No description</span>';
1176+
const due = p.due_date ? new Date(p.due_date) : null;
1177+
const isOverdue = due && due < new Date() && p.plan_column !== 'done';
1178+
document.getElementById('pd-due').textContent = due
1179+
? due.toLocaleDateString('en-GB',{weekday:'short',day:'2-digit',month:'short',year:'numeric'}) + (isOverdue ? ' ⚠ overdue' : '')
1180+
: 'No due date';
1181+
document.getElementById('pd-due').style.color = isOverdue ? 'var(--red)' : 'var(--text)';
1182+
document.getElementById('pd-tags').innerHTML = (p.tags||[]).map(t=>`<span class="kanban-tag">${t}</span>`).join('') || '<span style="color:var(--text-dim);font-size:11px">no tags</span>';
1183+
const cols = ['backlog','inprogress','done'];
1184+
const nextCol = cols[cols.indexOf(p.plan_column)+1];
1185+
const moveBtn = document.getElementById('pd-move-next');
1186+
if (nextCol) {
1187+
moveBtn.textContent = `[ move to ${nextCol==='inprogress'?'in progress':nextCol} ]`;
1188+
moveBtn.style.display = 'block';
1189+
} else {
1190+
moveBtn.style.display = 'none';
1191+
}
1192+
document.getElementById('plan-detail-overlay').classList.add('active');
1193+
}
1194+
1195+
function closePlanDetail() {
1196+
document.getElementById('plan-detail-overlay').classList.remove('active');
1197+
activePlanId = null;
1198+
}
1199+
1200+
function pdMoveNext() {
1201+
if (!activePlanId) return;
1202+
const p = plans.find(x => x.id === activePlanId);
1203+
if (!p) return;
1204+
const cols = ['backlog','inprogress','done'];
1205+
const next = cols[cols.indexOf(p.plan_column)+1];
1206+
if (next) { closePlanDetail(); movePlan(activePlanId, next); }
1207+
}
1208+
1209+
function pdStartEdit() {
1210+
const p = plans.find(x => x.id === activePlanId);
1211+
if (!p) return;
1212+
document.getElementById('pd-edit-textarea').value = p.description || '';
1213+
document.getElementById('pd-edit-section').style.display = 'block';
1214+
document.getElementById('pd-edit-preview').style.display = 'none';
1215+
document.getElementById('pd-edit-textarea').style.display = 'block';
1216+
document.querySelectorAll('#pd-edit-section .md-tab').forEach((b,i) => b.classList.toggle('active', i===0));
1217+
}
1218+
1219+
function pdCancelEdit() {
1220+
document.getElementById('pd-edit-section').style.display = 'none';
1221+
}
1222+
1223+
async function pdSaveEdit() {
1224+
const desc = document.getElementById('pd-edit-textarea').value.trim();
1225+
const updated = await api('PUT', `/api/plans/${activePlanId}`, { description: desc || null });
1226+
if (updated.id) {
1227+
plans = plans.map(p => p.id === activePlanId ? updated : p);
1228+
document.getElementById('pd-desc').innerHTML = desc
1229+
? (typeof marked !== 'undefined' ? marked.parse(desc) : desc)
1230+
: '<span style="color:var(--text-dim)">No description</span>';
1231+
document.getElementById('pd-edit-section').style.display = 'none';
1232+
renderPlans();
1233+
}
1234+
}
1235+
1236+
function switchPdTab(tab, btn) {
1237+
document.querySelectorAll('#pd-edit-section .md-tab').forEach(b => b.classList.remove('active'));
1238+
btn.classList.add('active');
1239+
const ta = document.getElementById('pd-edit-textarea');
1240+
const pr = document.getElementById('pd-edit-preview');
1241+
if (tab === 'preview') {
1242+
pr.innerHTML = ta.value.trim()
1243+
? (typeof marked !== 'undefined' ? marked.parse(ta.value) : ta.value)
1244+
: '<span style="color:var(--text-dim);font-size:12px">nothing to preview</span>';
1245+
ta.style.display = 'none'; pr.style.display = 'block';
1246+
} else {
1247+
ta.style.display = 'block'; pr.style.display = 'none';
1248+
}
1249+
}
1250+
1251+
function pdDelete() {
1252+
if (!activePlanId) return;
1253+
closePlanDetail();
1254+
deletePlan(activePlanId);
1255+
}
1256+
10431257
// Enter key on todo input
10441258
document.addEventListener('DOMContentLoaded',()=>{
10451259
document.getElementById('todo-title-input')?.addEventListener('keydown',e=>{if(e.key==='Enter')addTodo()});

0 commit comments

Comments
 (0)