ui: project switcher dropdown in header; consistent type hierarchy throughout

This commit is contained in:
James 2026-03-12 02:32:40 -04:00
parent 4abac19f35
commit 9368261ce2
3 changed files with 44 additions and 8 deletions

Binary file not shown.

View File

@ -215,6 +215,19 @@ input:focus, textarea:focus, select:focus { border-color: var(--ds-ac) !importan
/* ===== TREE TABLE ===== */ /* ===== TREE TABLE ===== */
#reqTree { border-collapse: collapse; width: 100%; font-size: 13px; } #reqTree { border-collapse: collapse; width: 100%; font-size: 13px; }
/* ===== TYPE HIERARCHY ===== */
/* Level 1 — Logo / Page title : 2xl (19.5px) — set inline */
/* Level 2 — Project name in header : xl (16.25px) — set inline */
/* Level 3 — Section / group labels : 13px bold — set inline in renderTree */
/* Level 4 — Body / table rows : 13px — base */
/* Level 5 — Metadata / counts / dates : 11px — set inline */
/* Tabs, sidebar links : text-base (13px) */
/* Badges, labels, pill text : 11px */
.tab { font-size: 13px; }
.sidebar-link { font-size: 13px; }
#reqTree th { font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }
#reqTree td { font-size: 13px; }
#reqTree th { #reqTree th {
text-align: left; text-align: left;
padding: 6px 10px; padding: 6px 10px;

View File

@ -2,9 +2,10 @@
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<a href="/app/projects" class="text-2xl font-bold text-white tracking-tight"><span class="text-[#c9a84c]">Deal</span>space</a> <a href="/app/projects" class="text-2xl font-bold text-white tracking-tight"><span class="text-[#c9a84c]">Deal</span>space</a>
<span class="text-white/20 text-lg">/</span> <span class="text-white/20 text-lg">/</span>
<a href="/app/projects" class="text-sm text-[#94a3b8] hover:text-white transition">Projects</a> <select id="projectSwitcher" onchange="switchProject(this.value)"
<span class="text-white/20 text-lg">/</span> class="bg-transparent border-none outline-none text-xl font-semibold text-white cursor-pointer appearance-none pr-6 focus:outline-none" style="background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 2px center">
<span id="projectName" class="text-xl font-semibold text-white select-text cursor-text">Loading...</span> <option id="projectName" value="">Loading...</option>
</select>
</div> </div>
{{end}} {{end}}
@ -28,8 +29,8 @@
<!-- Tabs --> <!-- Tabs -->
<div class="flex gap-6 border-b border-white/[0.08] mb-6"> <div class="flex gap-6 border-b border-white/[0.08] mb-6">
<button class="tab active pb-3 text-sm font-medium transition" onclick="switchTab('requests', this)">Requests</button> <button class="tab active pb-3 text-base font-medium transition" onclick="switchTab('requests', this)">Requests</button>
<button class="tab pb-3 text-sm font-medium text-[#94a3b8] transition" onclick="switchTab('orgs', this)">Organizations</button> <button class="tab pb-3 text-base font-medium text-[#94a3b8] transition" onclick="switchTab('orgs', this)">Organizations</button>
</div> </div>
<!-- Requests Tab --> <!-- Requests Tab -->
@ -330,7 +331,25 @@
const d = parseData(p.data_text); const d = parseData(p.data_text);
const name = d.name || p.summary_text || p.summary || 'Untitled'; const name = d.name || p.summary_text || p.summary || 'Untitled';
document.title = name + ' — Dealspace'; document.title = name + ' — Dealspace';
document.getElementById('projectName').textContent = name; // Populate project switcher
const sel = document.getElementById('projectSwitcher');
const opt = document.getElementById('projectName');
opt.value = projectID;
opt.textContent = name;
// Load all projects for switcher
fetchAPI('/api/projects').then(r=>r.json()).then(projects=>{
sel.innerHTML = '';
(projects||[]).forEach(p=>{
const o = document.createElement('option');
o.value = p.entry_id;
const d = p.data_text ? (()=>{try{return JSON.parse(p.data_text)}catch{return{}}})() : {};
o.textContent = d.name || p.search_key || p.entry_id;
if(p.entry_id === projectID) o.selected = true;
sel.appendChild(o);
});
}).catch(()=>{
sel.innerHTML = `<option value="${projectID}">${name}</option>`;
});
// projectTitle removed — name shown in header breadcrumb only // projectTitle removed — name shown in header breadcrumb only
document.getElementById('projectDesc').textContent = d.description || ''; document.getElementById('projectDesc').textContent = d.description || '';
const status = d.status || 'active'; const status = d.status || 'active';
@ -417,7 +436,7 @@
<td></td> <td></td>
<td colspan="12" style="padding-left:${indent+10}px"> <td colspan="12" style="padding-left:${indent+10}px">
${chevron} ${chevron}
<strong style="color:var(--ds-tx);margin-left:4px">${escHtml(name)}</strong> <strong style="color:var(--ds-tx);margin-left:4px;font-size:13px;font-weight:600">${escHtml(name)}</strong>
${visBadge} ${visBadge}
<span style="color:var(--ds-tx3);margin-left:8px;font-size:11px">${childCount} items</span> <span style="color:var(--ds-tx3);margin-left:8px;font-size:11px">${childCount} items</span>
<button onclick="editVisibility('${item.entry_id}')" style="margin-left:6px;background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 4px" title="Change visibility">&#9881;</button> <button onclick="editVisibility('${item.entry_id}')" style="margin-left:6px;background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 4px" title="Change visibility">&#9881;</button>
@ -431,7 +450,7 @@
<td class="row-num" style="padding-left:${indent}px">${escHtml(num)}</td> <td class="row-num" style="padding-left:${indent}px">${escHtml(num)}</td>
<td colspan="11" style="padding-left:${indent}px"> <td colspan="11" style="padding-left:${indent}px">
${chevron} ${chevron}
<span style="color:var(--ds-tx);font-weight:600;margin-left:4px">${escHtml(name)}</span> <span style="color:var(--ds-tx);font-weight:600;font-size:13px;margin-left:4px">${escHtml(name)}</span>
<span style="color:var(--ds-tx3);margin-left:8px;font-size:11px">${item.children_count||0} requests</span> <span style="color:var(--ds-tx3);margin-left:8px;font-size:11px">${item.children_count||0} requests</span>
</td> </td>
</tr>`; </tr>`;
@ -878,6 +897,10 @@
loadGlobalOrgs(); loadGlobalOrgs();
} }
function switchProject(pid) {
if (pid && pid !== projectID) window.location.href = '/app/projects/' + pid;
}
function closeAddOrgModal() { function closeAddOrgModal() {
document.getElementById('addOrgModal').classList.add('hidden'); document.getElementById('addOrgModal').classList.add('hidden');
document.getElementById('orgNameDropdown').classList.add('hidden'); document.getElementById('orgNameDropdown').classList.add('hidden');