redesign: request detail — card-based layout, max-w-4xl centered, proper hierarchy, empty state

This commit is contained in:
James 2026-03-12 05:11:07 -04:00
parent 51928a1cdc
commit 19fccab7fd
1 changed files with 69 additions and 57 deletions

View File

@ -9,90 +9,99 @@
{{end}}
{{define "content"}}
<div class="px-8 pt-4 pb-8">
<div class="max-w-4xl mx-auto px-6 py-8 space-y-6">
<!-- REQUEST -->
<div class="mb-6">
<div class="flex items-start justify-between gap-3 mb-2">
<div class="flex items-center gap-2 flex-wrap">
<span id="reqItemBadge" class="hidden px-2 py-0.5 rounded text-xs font-medium" style="background:var(--ds-ac);color:#fff;opacity:.85"></span>
<span id="reqPriority" class="hidden px-2 py-0.5 rounded-full text-xs font-medium"></span>
<span id="reqStatus" class="px-2 py-0.5 rounded-full text-xs font-medium capitalize"></span>
<span id="reqDue" class="hidden text-xs" style="color:var(--ds-tx3)"></span>
<!-- REQUEST CARD -->
<div class="rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<!-- Section header with meta -->
<div class="px-6 pt-5 pb-4 border-b" style="border-color:var(--ds-bd)">
<div class="flex items-start justify-between gap-4">
<div class="flex flex-wrap items-center gap-2">
<span id="reqItemBadge" class="hidden px-2.5 py-1 rounded text-xs font-semibold tracking-wide" style="background:var(--ds-ac);color:var(--ds-act)"></span>
<span id="reqStatus" class="px-2.5 py-1 rounded-full text-xs font-medium capitalize"></span>
<span id="reqPriority" class="hidden px-2.5 py-1 rounded-full text-xs font-medium"></span>
<span id="reqDue" class="hidden text-xs font-medium" style="color:var(--ds-tx3)"></span>
</div>
<button id="editReqBtn" onclick="startEditRequest()" class="shrink-0 p-1.5 rounded hover:bg-white/[0.08] transition" style="color:var(--ds-tx3)" title="Edit">
<button id="editReqBtn" onclick="startEditRequest()" class="shrink-0 p-1.5 rounded hover:bg-white/[0.08] transition" style="color:var(--ds-tx3)" title="Edit request">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
</button>
</div>
<p id="reqTitle" class="text-sm leading-relaxed" style="color:var(--ds-tx)">Loading...</p>
<p id="reqDesc" class="text-sm leading-relaxed mt-2" style="color:var(--ds-tx2)"></p>
</div>
<!-- Request body -->
<div class="px-6 py-5">
<p id="reqTitle" class="text-base leading-relaxed font-medium" style="color:var(--ds-tx)">Loading...</p>
<p id="reqDesc" class="text-sm leading-relaxed mt-3" style="color:var(--ds-tx2)"></p>
<!-- Edit mode -->
<div id="reqEditMode" class="hidden mt-4 space-y-3 p-4 rounded-lg" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<div><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Request text</label>
<textarea id="editReqTitle" rows="4" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)"></textarea></div>
<div id="reqEditMode" class="hidden mt-5 space-y-3 p-4 rounded-lg" style="background:var(--ds-bg);border:1px solid var(--ds-bd)">
<div><label class="block text-xs font-medium mb-1.5" style="color:var(--ds-tx3)">Request text</label>
<textarea id="editReqTitle" rows="4" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)"></textarea></div>
<div class="flex gap-3">
<div class="flex-1"><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Priority</label>
<select id="editReqPriority" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)">
<div class="flex-1"><label class="block text-xs font-medium mb-1.5" style="color:var(--ds-tx3)">Priority</label>
<select id="editReqPriority" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)">
<option value="critical">Critical</option><option value="high">High</option><option value="medium">Medium</option><option value="low">Low</option>
</select></div>
<div class="flex-1"><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Status</label>
<select id="editReqStatus" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)">
<div class="flex-1"><label class="block text-xs font-medium mb-1.5" style="color:var(--ds-tx3)">Status</label>
<select id="editReqStatus" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)">
<option value="open">Open</option><option value="in_process">In Process</option><option value="partial">Partial</option><option value="complete">Complete</option>
</select></div>
</div>
<div><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Description (optional)</label>
<textarea id="editReqDesc" rows="2" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)"></textarea></div>
<div class="flex gap-2">
<div><label class="block text-xs font-medium mb-1.5" style="color:var(--ds-tx3)">Description (optional)</label>
<textarea id="editReqDesc" rows="2" class="w-full px-3 py-2 rounded text-sm focus:outline-none" style="background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)"></textarea></div>
<div class="flex gap-2 mt-1">
<button onclick="saveEditRequestDetail()" class="px-4 py-2 rounded text-sm font-semibold transition" style="background:var(--ds-ac);color:var(--ds-act)">Save</button>
<button onclick="cancelEditRequestDetail()" class="px-4 py-2 rounded text-sm transition" style="background:var(--ds-hv);color:var(--ds-tx)">Cancel</button>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-5 gap-6">
<!-- RESPONSE (left, wider) -->
<div class="lg:col-span-3 p-5 rounded-lg" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<div class="flex items-center justify-between mb-3">
<span class="text-xs font-semibold uppercase tracking-wider" style="color:var(--ds-tx3)">Response</span>
<div id="answeredBanner" class="hidden px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-500/15 text-green-300">&#10003; Answered</div>
</div>
<div id="answers" class="space-y-2 mb-3"></div>
<div id="uploadArea" class="border border-dashed rounded-lg px-4 py-5 text-center cursor-pointer transition" style="border-color:var(--ds-bd)" onclick="document.getElementById('fileInput').click()"
ondragover="event.preventDefault();this.style.borderColor='var(--ds-ac)'" ondragleave="this.style.borderColor='var(--ds-bd)'"
<!-- RESPONSE CARD -->
<div class="rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<div class="px-6 pt-4 pb-3 border-b flex items-center justify-between" style="border-color:var(--ds-bd)">
<span class="text-xs font-semibold uppercase tracking-wider" style="color:var(--ds-tx3)">Files &amp; Response</span>
<div id="answeredBanner" class="hidden px-3 py-1 rounded-full text-xs font-semibold bg-green-500/15 text-green-300">&#10003; Answered</div>
</div>
<div class="px-6 py-4">
<div id="answers" class="space-y-2 mb-4"></div>
<!-- empty state shown by JS if no answers -->
<div id="answersEmpty" class="hidden py-6 text-center text-sm" style="color:var(--ds-tx3)">No files submitted yet.</div>
<div id="uploadArea" class="mt-2 border border-dashed rounded-lg px-5 py-6 text-center cursor-pointer transition" style="border-color:var(--ds-bd)"
onclick="document.getElementById('fileInput').click()"
ondragover="event.preventDefault();this.style.borderColor='var(--ds-ac)'"
ondragleave="this.style.borderColor='var(--ds-bd)'"
ondrop="event.preventDefault();this.style.borderColor='var(--ds-bd)';uploadFiles(event.dataTransfer.files)">
<p class="text-sm" style="color:var(--ds-tx2)">Drop files or click to upload response</p>
<p class="text-xs mt-0.5" style="color:var(--ds-tx3)">PDF, DOCX, XLSX, images</p>
<svg class="w-6 h-6 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="color:var(--ds-tx3)"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/></svg>
<p class="text-sm font-medium" style="color:var(--ds-tx2)">Drop files or click to upload</p>
<p class="text-xs mt-0.5" style="color:var(--ds-tx3)">PDF, DOCX, XLSX, images accepted</p>
<input id="fileInput" type="file" multiple class="hidden" onchange="uploadFiles(this.files)">
</div>
<div id="uploadStatus" class="mt-2 text-xs" style="color:var(--ds-tx2)"></div>
<button id="markAnsweredBtn" onclick="markAnswered()" class="hidden mt-3 w-full py-2 rounded text-sm font-semibold bg-green-600 hover:bg-green-700 text-white transition">
<div id="uploadStatus" class="mt-3 text-sm" style="color:var(--ds-tx2)"></div>
<button id="markAnsweredBtn" onclick="markAnswered()" class="hidden mt-4 px-5 py-2.5 rounded text-sm font-semibold bg-green-600 hover:bg-green-700 text-white transition">
&#10003; Mark as Answered
</button>
</div>
</div>
<!-- DISCUSSION (right) -->
<div class="lg:col-span-2 rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<!-- Channel tabs -->
<div class="border-b border-white/[0.08] flex items-center gap-0 overflow-x-auto" id="channelTabs">
<div class="px-4 py-3 text-xs text-[#475569]">Loading channels...</div>
</div>
<!-- Messages -->
<div id="channelComments" class="p-6 space-y-4 min-h-[120px] max-h-[480px] overflow-y-auto">
<p class="text-[#475569] text-sm">Select a channel above.</p>
</div>
<!-- Compose -->
<div id="commentComposer" class="border-t border-white/[0.08] p-4">
<div id="composeReadOnly" class="hidden py-1 text-xs text-[#475569] text-center">Read only in this channel.</div>
<div id="composeForm" class="flex gap-3 items-end">
<textarea id="commentText" rows="2" placeholder="Post an announcement..."
class="flex-1 px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white placeholder-[#475569] focus:outline-none focus:border-[#c9a84c] focus:ring-1 focus:ring-[#c9a84c] text-sm resize-none"></textarea>
<button onclick="postComment(null)"
class="px-4 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition shrink-0">Post</button>
</div>
<!-- DISCUSSION CARD -->
<div class="rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<div id="channelTabs" class="flex items-center overflow-x-auto border-b" style="border-color:var(--ds-bd)">
<div class="px-5 py-3 text-xs" style="color:var(--ds-tx3)">Loading channels...</div>
</div>
<div id="channelComments" class="px-6 py-4 space-y-4 min-h-[80px] max-h-[400px] overflow-y-auto">
<p class="text-sm" style="color:var(--ds-tx3)">Select a channel above.</p>
</div>
<div id="commentComposer" class="border-t px-5 py-4" style="border-color:var(--ds-bd)">
<div id="composeReadOnly" class="hidden py-1 text-xs text-center" style="color:var(--ds-tx3)">Read only in this channel.</div>
<div id="composeForm" class="flex gap-3 items-end">
<textarea id="commentText" rows="2" placeholder="Post an announcement..."
class="flex-1 px-3 py-2.5 rounded-lg text-sm resize-none focus:outline-none focus:ring-1"
style="background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx);--tw-ring-color:var(--ds-ac)"></textarea>
<button onclick="postComment(null)" class="px-4 py-2.5 rounded-lg text-sm font-semibold transition shrink-0" style="background:var(--ds-ac);color:var(--ds-act)">Post</button>
</div>
</div>
</div><!-- end two-col grid -->
</div>
</div>
{{end}}
{{define "scripts"}}
@ -252,10 +261,13 @@
// ---- Answers / Files ----
function renderAnswers(docs) {
const el = document.getElementById('answers');
const emptyEl = document.getElementById('answersEmpty');
if (!docs || docs.length === 0) {
el.innerHTML = '<p class="text-[#475569] text-sm">No response submitted yet.</p>';
el.innerHTML = '';
if (emptyEl) emptyEl.classList.remove('hidden');
return;
}
if (emptyEl) emptyEl.classList.add('hidden');
const projectID = currentRequest.project_id;
el.innerHTML = docs.map(a => {
const d = parseData(a.data_text);