Replace thread status dropdown/text with inline status buttons
Card and modal both use a clickable button row for status instead of static text or a select, so changing status no longer requires opening the full edit form. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+106
-15
@@ -133,17 +133,22 @@ function buildThreadCard(thread) {
|
||||
|
||||
if (expandedId === thread.id) {
|
||||
card.classList.add('expanded');
|
||||
card.appendChild(buildThreadDetail(thread));
|
||||
card.appendChild(buildThreadDetail(thread, card));
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function buildThreadDetail(thread) {
|
||||
function buildThreadDetail(thread, card) {
|
||||
const detail = document.createElement('div');
|
||||
detail.className = 'thread-card-detail';
|
||||
|
||||
FIELD_DEFS.forEach(({ key, label }) => {
|
||||
if (key === 'status') {
|
||||
detail.appendChild(buildStatusButtonRow(thread, card));
|
||||
return;
|
||||
}
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'thread-field';
|
||||
|
||||
@@ -153,7 +158,7 @@ function buildThreadDetail(thread) {
|
||||
|
||||
const valueEl = document.createElement('span');
|
||||
valueEl.className = 'thread-field-value';
|
||||
valueEl.textContent = key === 'status' ? statusLabel(thread.status) : (thread[key] || '—');
|
||||
valueEl.textContent = thread[key] || '—';
|
||||
|
||||
row.append(labelEl, valueEl);
|
||||
detail.appendChild(row);
|
||||
@@ -180,6 +185,66 @@ function buildThreadDetail(thread) {
|
||||
return detail;
|
||||
}
|
||||
|
||||
function buildStatusButtonRow(thread, card) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'thread-field';
|
||||
|
||||
const labelEl = document.createElement('span');
|
||||
labelEl.className = 'thread-field-label';
|
||||
labelEl.textContent = 'Status';
|
||||
row.appendChild(labelEl);
|
||||
|
||||
const buttonRow = document.createElement('div');
|
||||
buttonRow.className = 'thread-status-buttons';
|
||||
|
||||
STATUSES.forEach((status) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'thread-status-btn';
|
||||
btn.dataset.status = status;
|
||||
btn.classList.toggle('active', status === thread.status);
|
||||
btn.textContent = statusLabel(status);
|
||||
btn.addEventListener('click', () => handleStatusChange(thread, status, card));
|
||||
buttonRow.appendChild(btn);
|
||||
});
|
||||
|
||||
row.appendChild(buttonRow);
|
||||
return row;
|
||||
}
|
||||
|
||||
async function handleStatusChange(thread, newStatus, card) {
|
||||
if (newStatus === thread.status) return;
|
||||
|
||||
const campaign = getActiveCampaign();
|
||||
if (!campaign) return;
|
||||
|
||||
const payload = {};
|
||||
FIELD_DEFS.forEach(({ key }) => {
|
||||
payload[key] = thread[key];
|
||||
});
|
||||
payload.status = newStatus;
|
||||
|
||||
try {
|
||||
const updated = await updateThread(campaign.id, thread.id, payload);
|
||||
Object.assign(thread, updated);
|
||||
updateCardStatusUI(card, thread);
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCardStatusUI(card, thread) {
|
||||
const badge = card.querySelector('.thread-status-badge');
|
||||
if (badge) {
|
||||
badge.className = `thread-status-badge thread-status-${thread.status}`;
|
||||
badge.textContent = statusLabel(thread.status);
|
||||
}
|
||||
|
||||
card.querySelectorAll('.thread-status-btn').forEach((btn) => {
|
||||
btn.classList.toggle('active', btn.dataset.status === thread.status);
|
||||
});
|
||||
}
|
||||
|
||||
function buildModalForm(values) {
|
||||
const body = container.querySelector('#threadModalBody');
|
||||
body.innerHTML = '';
|
||||
@@ -191,21 +256,18 @@ function buildModalForm(values) {
|
||||
const labelEl = document.createElement('label');
|
||||
labelEl.className = 'modal-field-label';
|
||||
labelEl.textContent = label;
|
||||
labelEl.setAttribute('for', `thread-field-${key}`);
|
||||
group.appendChild(labelEl);
|
||||
|
||||
let input;
|
||||
if (type === 'select') {
|
||||
input = document.createElement('select');
|
||||
input.className = 'modal-select';
|
||||
STATUSES.forEach((status) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = status;
|
||||
option.textContent = statusLabel(status);
|
||||
input.appendChild(option);
|
||||
});
|
||||
input.value = values.status || 'active';
|
||||
} else if (type === 'textarea') {
|
||||
group.appendChild(buildModalStatusButtons(values.status || 'active'));
|
||||
body.appendChild(group);
|
||||
return;
|
||||
}
|
||||
|
||||
labelEl.setAttribute('for', `thread-field-${key}`);
|
||||
|
||||
let input;
|
||||
if (type === 'textarea') {
|
||||
input = document.createElement('textarea');
|
||||
input.className = 'modal-textarea';
|
||||
input.rows = 3;
|
||||
@@ -223,6 +285,35 @@ function buildModalForm(values) {
|
||||
});
|
||||
}
|
||||
|
||||
function buildModalStatusButtons(currentStatus) {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'modal-status-buttons';
|
||||
|
||||
const hiddenInput = document.createElement('input');
|
||||
hiddenInput.type = 'hidden';
|
||||
hiddenInput.id = 'thread-field-status';
|
||||
hiddenInput.value = currentStatus;
|
||||
wrapper.appendChild(hiddenInput);
|
||||
|
||||
STATUSES.forEach((status) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'modal-status-btn';
|
||||
btn.dataset.status = status;
|
||||
btn.classList.toggle('active', status === currentStatus);
|
||||
btn.textContent = statusLabel(status);
|
||||
btn.addEventListener('click', () => {
|
||||
hiddenInput.value = status;
|
||||
wrapper.querySelectorAll('.modal-status-btn').forEach((b) => {
|
||||
b.classList.toggle('active', b.dataset.status === status);
|
||||
});
|
||||
});
|
||||
wrapper.appendChild(btn);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function collectFormData() {
|
||||
const data = {};
|
||||
FIELD_DEFS.forEach(({ key }) => {
|
||||
|
||||
Reference in New Issue
Block a user