-
💬
-
Discussion
+
+
+
+
-
-
-
-
+
+
+
+
+
Read only in this channel.
+
+
+
+
@@ -137,7 +146,8 @@
renderRequest(currentRequest, currentData);
currentAnswers = children.filter(c => c.type === 'answer' || c.type === 'document');
renderAnswers(currentAnswers);
- renderComments(children.filter(c => c.type === 'comment'));
+ allComments = children.filter(c => c.type === 'comment');
+ await initChannels();
} catch(e) {
document.getElementById('reqTitle').textContent = 'Error loading request';
}
@@ -309,26 +319,7 @@
loadAll();
}
- function renderComments(comments) {
- const el = document.getElementById('comments');
- if (!comments || comments.length === 0) {
- el.innerHTML = '
No comments yet.
';
- return;
- }
- el.innerHTML = comments.map(c => {
- const d = parseData(c.data_text);
- return `
-
${(d.author||'?')[0].toUpperCase()}
-
-
- ${escHtml(d.author||'Unknown')}
- ${c.created_at ? new Date(c.created_at * 1000).toLocaleString() : ''}
-
-
${escHtml(d.text||'')}
-
-
`;
- }).join('');
- }
+ // renderComments replaced by channel system below
async function uploadFiles(files) {
if (!currentRequest) return;
@@ -358,20 +349,128 @@
loadAll();
}
- async function postComment() {
+ // ---- Channel-based comment system ----
+ let activeChannel = 'announcements';
+ let allComments = [];
+ let dealOrgs = [];
+ let replyingToId = null;
+
+ async function initChannels() {
if (!currentRequest) return;
- const text = document.getElementById('commentText').value.trim();
+ try {
+ const res = await fetchAPI('/api/projects/' + currentRequest.project_id + '/orgs');
+ dealOrgs = await res.json() || [];
+ } catch(e) { dealOrgs = []; }
+ renderChannelTabs();
+ switchChannel('announcements');
+ }
+
+ function renderChannelTabs() {
+ const container = document.getElementById('channelTabs');
+ const tabs = [
+ { key: 'announcements', label: '📢 Announcements' },
+ ...dealOrgs.map(o => {
+ const d = parseData(o.data_text);
+ return { key: 'org:' + o.entry_id, label: '🔒 ' + escHtml(d.org_name || d.name || 'Org') };
+ })
+ ];
+ container.innerHTML = tabs.map(t =>
+ `
`
+ ).join('');
+ }
+
+ function switchChannel(key) {
+ activeChannel = key;
+ cancelReply();
+ renderChannelTabs();
+ renderChannelComments();
+ const ta = document.getElementById('commentText');
+ ta.placeholder = key === 'announcements' ? 'Post an announcement...' : 'Message this channel...';
+ }
+
+ function renderChannelComments() {
+ const el = document.getElementById('channelComments');
+ const ch = allComments.filter(c => (parseData(c.data_text).channel || 'announcements') === activeChannel);
+ const top = ch.filter(c => !parseData(c.data_text).parent_comment_id);
+ const nested = ch.filter(c => !!parseData(c.data_text).parent_comment_id);
+ if (top.length === 0) {
+ el.innerHTML = '
No messages yet.
';
+ return;
+ }
+ el.innerHTML = top.map(c => {
+ const d = parseData(c.data_text);
+ const replies = nested.filter(r => parseData(r.data_text).parent_comment_id === c.entry_id);
+ return renderOneComment(c, d, false) + replies.map(r => renderOneComment(r, parseData(r.data_text), true)).join('');
+ }).join('');
+ el.scrollTop = el.scrollHeight;
+ }
+
+ function renderOneComment(c, d, isReply) {
+ const av = (d.author || '?')[0].toUpperCase();
+ const ts = c.created_at ? new Date(c.created_at * 1000).toLocaleString('en-US', {month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}) : '';
+ const replyBtn = isReply ? '' : `
`;
+ const indent = isReply ? 'ml-10 pl-4 border-l-2 border-[#c9a84c]/30' : '';
+ return `
+
${av}
+
+
+ ${escHtml(d.author || 'Unknown')}
+ ${ts}
+ ${replyBtn}
+
+
${escHtml(d.text || '')}
+
+
`;
+ }
+
+ function startReply(commentId) {
+ cancelReply();
+ replyingToId = commentId;
+ const el = document.querySelector(`[data-comment-id="${commentId}"]`);
+ if (!el) return;
+ const form = document.createElement('div');
+ form.id = 'replyForm';
+ form.className = 'ml-10 pl-4 border-l-2 border-[#c9a84c]/30 mt-2';
+ form.innerHTML = `
`;
+ el.insertAdjacentElement('afterend', form);
+ document.getElementById('replyText').focus();
+ }
+
+ function cancelReply() {
+ replyingToId = null;
+ const f = document.getElementById('replyForm');
+ if (f) f.remove();
+ }
+
+ async function postComment(parentCommentId) {
+ if (!currentRequest) return;
+ const text = parentCommentId
+ ? (document.getElementById('replyText')?.value || '').trim()
+ : (document.getElementById('commentText')?.value || '').trim();
if (!text) return;
const projectID = currentRequest.project_id;
const body = {
project_id: projectID,
parent_id: reqID,
type: 'comment',
- data: JSON.stringify({ text, author: user.name || user.email })
+ data: JSON.stringify({ text, author: user.name || user.email, channel: activeChannel, parent_comment_id: parentCommentId || null })
};
try {
const res = await fetchAPI('/api/projects/' + projectID + '/entries', { method: 'POST', body: JSON.stringify(body) });
- if (res.ok) { document.getElementById('commentText').value = ''; loadAll(); }
+ if (res.ok) {
+ if (parentCommentId) cancelReply(); else document.getElementById('commentText').value = '';
+ await loadAll();
+ }
} catch(e) {}
}