Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@
border:none;border-radius:8px;padding:5px 7px;-webkit-appearance:none;appearance:none;cursor:pointer}
.ptask .rowsz:hover{background:#e4e7ec;color:var(--ink)}
.ptask .rowpts{flex:none;font-size:11px;font-weight:700;color:var(--ink-3);background:#eef0f4;border-radius:8px;padding:5px 8px}
.ptask .duepill{flex:none}

/* ---------- detail sheet ---------- */
.scrim{position:fixed;inset:0;background:rgba(20,24,35,.32);z-index:40;opacity:0;pointer-events:none;transition:opacity .22s}
Expand All @@ -188,6 +189,7 @@
.tbox .frow select,.tbox .frow input{flex:1;font:inherit;font-size:15px;border:none;background:transparent;
color:var(--ink);min-height:34px;-webkit-appearance:none;appearance:none}
.tbox .frow select:focus,.tbox .frow input:focus{outline:none}
.tbox .frow .duepill{flex:none}
/* make the borderless selects (Owner / Size / Move to) read as editable dropdowns:
size to their value and show a caret + hover so it's clear you can change them */
.tbox .frow select{flex:0 1 auto;max-width:100%;cursor:pointer;min-height:32px;
Expand Down Expand Up @@ -467,11 +469,18 @@
.opill select{border:none;background:transparent;font:inherit;font-size:12px;font-weight:600;color:var(--ink-2);
-webkit-appearance:none;appearance:none;max-width:96px}
.opill select:focus{outline:none}
/* due pill */
.duepill{display:inline-flex;align-items:center;background:#eef0f4;border-radius:999px;padding:3px 11px}
.duepill input{border:none;background:transparent;font:inherit;font-size:12px;font-weight:600;color:var(--ink-2);width:104px}
.duepill input:focus{outline:none}
.duepill.sm input{width:96px;font-size:11.5px}
/* due pill — formatted label; hidden native input opened via pickDueEl */
.duepill{position:relative;display:inline-flex;align-items:center;justify-content:center;
background:#eef0f4;border-radius:999px;padding:3px 11px;min-height:28px;min-width:92px;
cursor:pointer;user-select:none;border:none;font:inherit}
.duepill:hover,.duepill:focus-within{background:#e4e7ec}
.duepill .duelbl{font:inherit;font-size:12px;font-weight:600;color:var(--ink-2);white-space:nowrap;
line-height:1.3;pointer-events:none}
.duepill .duelbl.empty{color:var(--ink-3)}
.duepill input.duepick{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
clip:rect(0,0,0,0);white-space:nowrap;border:0}
.duepill.sm{min-height:26px;min-width:86px;padding:2px 9px}
.duepill.sm .duelbl{font-size:11.5px}
/* segmented t-shirt size */
.szseg{display:inline-flex;background:#eef0f4;border-radius:999px;padding:2px}
.szb{padding:3px 10px;border-radius:999px;font-size:11px;font-weight:800;color:var(--ink-3);line-height:1.5}
Expand Down
19 changes: 15 additions & 4 deletions src/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ function openDetail(id){
document.getElementById("dBody").innerHTML=`
<div class="frow"><span class="lbl">Owner</span>${av(n.owner)}<select onchange="updTask(${id},'owner',this.value)">
${Object.entries(PEOPLE).map(([k,pp])=>`<option value="${k}" ${k===n.owner?'selected':''}>${pp.name}</option>`).join("")}</select></div>
<div class="frow"><span class="lbl">Due</span><input type="date" value="${n.due||""}" onchange="updTask(${id},'due',this.value)">${dueChip(n.due,leaf&&n.done)}</div>
<div class="frow"><span class="lbl">Due</span>${duePill(n.due,`updTask(${id},'due',this.value)`)}${dueChip(n.due,leaf&&n.done)}</div>
${sizeFld}
${leaf?`<div class="frow"><span class="lbl">Status</span>
<button class="chip" style="${n.done?'background:var(--green-soft);color:var(--green)':'background:#eef0f4;color:var(--ink-2)'}"
Expand All @@ -869,7 +869,7 @@ function openDetail(id){
<button class="t" style="text-align:left" onclick="openDetail(${ch.id})">${ch.title}</button>
${ownerPill(ch.owner,`updTask(${ch.id},'owner',this.value,true);openDetail(${id})`)}
${szCtl}
${dueChip(ch.due,lleaf&&ch.done)}</div>`;}).join("")}
${duePill(ch.due,`updTask(${ch.id},'due',this.value,true);openDetail(${id})`,true)}</div>`;}).join("")}
${path.length>=3?"":`<div class="subadd" style="margin-top:10px"><input id="dSubNew" placeholder="Add a ${path.length>1?"subtask":"task"}…"><button onclick="addChild(${id})">Add</button></div>`}
<button class="danger" onclick="deleteTask(${id})">Delete ${path.length===1?"project":path.length>=3?"subtask":"task"}</button>`;
document.getElementById("tmodal").classList.add("show");
Expand Down Expand Up @@ -1209,7 +1209,18 @@ const ownerOpts=v=>`<option value="">Owner…</option>`+Object.entries(PEOPLE).m
function ownerPill(v,onch){ const col=v?PEOPLE[v].color:"#c2c8d2";
return `<span class="opill" style="--oc:${col}"><span class="odot"></span>
<select onchange="${onch}">${ownerOpts(v)}</select></span>`; }
function duePill(v,onch,sm){ return `<span class="duepill ${sm?'sm':''}"><input type="date" value="${v||''}" onchange="${onch}"></span>`; }
function duePill(v,onch,sm){
const lbl=v?fmtD(v):"Set date";
return `<label class="duepill ${sm?'sm':''}" onclick="pickDueEl(this)">`
+`<span class="duelbl${v?'':' empty'}">${lbl}</span>`
+`<input type="date" class="duepick" value="${v||''}" onchange="${onch}" tabindex="-1" aria-label="Due date"></label>`;
}
function pickDueEl(el){
const inp=el&&el.querySelector("input.duepick");
if(!inp) return;
if(typeof inp.showPicker==="function"){ try{ inp.showPicker(); return; }catch(_){ /* fall through */ } }
inp.focus(); inp.click();
}
function szSeg(v,onch){ return `<span class="szseg">${["s","m","l","xl"].map(z=>
`<button class="szb ${v===z?'on':''}" onclick="${onch.replace('Z',"'"+z+"'")}">${SIZE_NAMES[z]}</button>`).join("")}</span>`; }
function taskCardHTML(tk,i){ tk.subs=tk.subs||[];
Expand Down Expand Up @@ -1530,7 +1541,7 @@ const _globals = {
toggleExp, updTask, refreshBarMenu, addChild, addProject, deleteTask, addCapTask, barDown, barContext, pickSearch,
uploadPhoto, removePhoto, rvToggle, rvText, rvOwner, rvDue, rvSize, pushApproved, attachTranscript,
doSearch, refreshCard, delCapTask, setTask, setTaskOwner, setTaskSize, setSub, setSubOwner, addSub,
delSub, commitCapture, toggleListen, stopListen, renderAll, moveTask, setKeyVal,
delSub, commitCapture, toggleListen, stopListen, renderAll, moveTask, setKeyVal, pickDueEl,
};
Object.assign(window, _globals);

Expand Down
7 changes: 7 additions & 0 deletions tests/smoke.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ describe("project layout", () => {
expect(dates.createDateHelpers).toBeTypeOf("function");
expect(capture.mockTranscript).toBeTypeOf("function");
});

it("allows inline due-date editing on tasks and subtasks in detail panels", () => {
const main = readFileSync("src/app/main.js", "utf8");
expect(main).toContain("duePill(ch.due,`updTask(${ch.id},'due',this.value,true);openDetail(${id})`,true)");
expect(main).toContain('class="duelbl');
expect(main).toContain("pickDueEl");
});
});
Loading