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}} {{end}}
{{define "content"}} {{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 --> <!-- REQUEST CARD -->
<div class="mb-6"> <div class="rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<div class="flex items-start justify-between gap-3 mb-2"> <!-- Section header with meta -->
<div class="flex items-center gap-2 flex-wrap"> <div class="px-6 pt-5 pb-4 border-b" style="border-color:var(--ds-bd)">
<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> <div class="flex items-start justify-between gap-4">
<span id="reqPriority" class="hidden px-2 py-0.5 rounded-full text-xs font-medium"></span> <div class="flex flex-wrap items-center gap-2">
<span id="reqStatus" class="px-2 py-0.5 rounded-full text-xs font-medium capitalize"></span> <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="reqDue" class="hidden text-xs" style="color:var(--ds-tx3)"></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> </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> <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> </button>
</div> </div>
<p id="reqTitle" class="text-sm leading-relaxed" style="color:var(--ds-tx)">Loading...</p> </div>
<p id="reqDesc" class="text-sm leading-relaxed mt-2" style="color:var(--ds-tx2)"></p> <!-- 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 --> <!-- 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 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" style="color:var(--ds-tx3)">Request text</label> <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-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)"></textarea></div> <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 gap-3">
<div class="flex-1"><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Priority</label> <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-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)"> <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> <option value="critical">Critical</option><option value="high">High</option><option value="medium">Medium</option><option value="low">Low</option>
</select></div> </select></div>
<div class="flex-1"><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Status</label> <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-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)"> <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> <option value="open">Open</option><option value="in_process">In Process</option><option value="partial">Partial</option><option value="complete">Complete</option>
</select></div> </select></div>
</div> </div>
<div><label class="block text-xs font-medium mb-1" style="color:var(--ds-tx3)">Description (optional)</label> <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-bg);border:1px solid var(--ds-bd);color:var(--ds-tx)"></textarea></div> <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"> <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="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> <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> </div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-5 gap-6"> <!-- RESPONSE CARD -->
<!-- RESPONSE (left, wider) --> <div class="rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<div class="lg:col-span-3 p-5 rounded-lg" 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)">
<div class="flex items-center justify-between mb-3"> <span class="text-xs font-semibold uppercase tracking-wider" style="color:var(--ds-tx3)">Files &amp; Response</span>
<span class="text-xs font-semibold uppercase tracking-wider" style="color:var(--ds-tx3)">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 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> <div class="px-6 py-4">
<div id="answers" class="space-y-2 mb-3"></div> <div id="answers" class="space-y-2 mb-4"></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()" <!-- empty state shown by JS if no answers -->
ondragover="event.preventDefault();this.style.borderColor='var(--ds-ac)'" ondragleave="this.style.borderColor='var(--ds-bd)'" <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)"> 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> <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-xs mt-0.5" style="color:var(--ds-tx3)">PDF, DOCX, XLSX, images</p> <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)"> <input id="fileInput" type="file" multiple class="hidden" onchange="uploadFiles(this.files)">
</div> </div>
<div id="uploadStatus" class="mt-2 text-xs" style="color:var(--ds-tx2)"></div> <div id="uploadStatus" class="mt-3 text-sm" 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"> <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 &#10003; Mark as Answered
</button> </button>
</div> </div>
</div>
<!-- DISCUSSION (right) --> <!-- DISCUSSION CARD -->
<div class="lg:col-span-2 rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)"> <div class="rounded-xl overflow-hidden" style="background:var(--ds-sf);border:1px solid var(--ds-bd)">
<!-- Channel tabs --> <div id="channelTabs" class="flex items-center overflow-x-auto border-b" style="border-color:var(--ds-bd)">
<div class="border-b border-white/[0.08] flex items-center gap-0 overflow-x-auto" id="channelTabs"> <div class="px-5 py-3 text-xs" style="color:var(--ds-tx3)">Loading channels...</div>
<div class="px-4 py-3 text-xs text-[#475569]">Loading channels...</div> </div>
</div> <div id="channelComments" class="px-6 py-4 space-y-4 min-h-[80px] max-h-[400px] overflow-y-auto">
<!-- Messages --> <p class="text-sm" style="color:var(--ds-tx3)">Select a channel above.</p>
<div id="channelComments" class="p-6 space-y-4 min-h-[120px] max-h-[480px] overflow-y-auto"> </div>
<p class="text-[#475569] text-sm">Select a channel above.</p> <div id="commentComposer" class="border-t px-5 py-4" style="border-color:var(--ds-bd)">
</div> <div id="composeReadOnly" class="hidden py-1 text-xs text-center" style="color:var(--ds-tx3)">Read only in this channel.</div>
<!-- Compose --> <div id="composeForm" class="flex gap-3 items-end">
<div id="commentComposer" class="border-t border-white/[0.08] p-4"> <textarea id="commentText" rows="2" placeholder="Post an announcement..."
<div id="composeReadOnly" class="hidden py-1 text-xs text-[#475569] text-center">Read only in this channel.</div> class="flex-1 px-3 py-2.5 rounded-lg text-sm resize-none focus:outline-none focus:ring-1"
<div id="composeForm" class="flex gap-3 items-end"> style="background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx);--tw-ring-color:var(--ds-ac)"></textarea>
<textarea id="commentText" rows="2" placeholder="Post an announcement..." <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>
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>
</div> </div>
</div> </div>
</div><!-- end two-col grid -->
</div> </div>
</div>
{{end}} {{end}}
{{define "scripts"}} {{define "scripts"}}
@ -252,10 +261,13 @@
// ---- Answers / Files ---- // ---- Answers / Files ----
function renderAnswers(docs) { function renderAnswers(docs) {
const el = document.getElementById('answers'); const el = document.getElementById('answers');
const emptyEl = document.getElementById('answersEmpty');
if (!docs || docs.length === 0) { 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; return;
} }
if (emptyEl) emptyEl.classList.add('hidden');
const projectID = currentRequest.project_id; const projectID = currentRequest.project_id;
el.innerHTML = docs.map(a => { el.innerHTML = docs.map(a => {
const d = parseData(a.data_text); const d = parseData(a.data_text);