Control Tickets: improvement type badge/filter + download shared module

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-06-15 14:41:05 +10:00
parent e1ddcd201f
commit d71f72cd5a

View File

@@ -77,10 +77,13 @@ function statusPill(s) {
} }
function typeBadge(t) { function typeBadge(t) {
const type = t === 'feature' ? 'feature' : 'bug'; const map = {
const c = type === 'feature' ? '#7aa2e0' : '#e0b24b'; feature: { c: '#7aa2e0', label: '✨ feature' },
return el('span', { class: 'badge', style: { background: 'transparent', border: `1px solid ${c}`, color: c } }, improvement: { c: '#5ec27a', label: '🛠 improvement' },
type === 'feature' ? '✨ feature' : '🐞 bug'); bug: { c: '#e0b24b', label: '🐞 bug' },
};
const m = map[t] || map.bug;
return el('span', { class: 'badge', style: { background: 'transparent', border: `1px solid ${m.c}`, color: m.c } }, m.label);
} }
// ---- group cache (used by approve/instances dropdowns) ---------------------- // ---- group cache (used by approve/instances dropdowns) ----------------------
@@ -311,7 +314,7 @@ async function renderTickets(panel) {
const statusSel = select([{ value: '', label: 'all' }, 'open', 'closed'], filter); const statusSel = select([{ value: '', label: 'all' }, 'open', 'closed'], filter);
statusSel.onchange = () => { filter = statusSel.value; loadList(); }; statusSel.onchange = () => { filter = statusSel.value; loadList(); };
const typeSel = select([{ value: '', label: 'all' }, { value: 'bug', label: '🐞 bug' }, { value: 'feature', label: '✨ feature' }], typeFilter); const typeSel = select([{ value: '', label: 'all' }, { value: 'bug', label: '🐞 bug' }, { value: 'feature', label: '✨ feature' }, { value: 'improvement', label: '🛠 improvement' }], typeFilter);
typeSel.onchange = () => { typeFilter = typeSel.value; loadList(); }; typeSel.onchange = () => { typeFilter = typeSel.value; loadList(); };
async function loadList() { async function loadList() {
@@ -357,6 +360,12 @@ async function renderTickets(panel) {
return el('a', { class: 'ghost', href: safeHref(`${A}/tickets/${id}/logs/${attId}`), target: '_blank', rel: 'noopener', style: { marginRight: '0.4rem' } }, return el('a', { class: 'ghost', href: safeHref(`${A}/tickets/${id}/logs/${attId}`), target: '_blank', rel: 'noopener', style: { marginRight: '0.4rem' } },
'↗ ' + (att.name || `log ${attId}`)); '↗ ' + (att.name || `log ${attId}`));
}); });
const modules = (t.modules || []).map(att => {
const attId = att.id ?? att;
return el('a', { class: 'lk-url', href: safeHref(`${A}/tickets/${id}/modules/${attId}`), download: `module-${attId}.tar.gz`,
style: { color: 'var(--accent,#5ec27a)', marginRight: '0.5rem', fontSize: '0.8rem' } },
'⬇ download shared module');
});
mount(detail, mount(detail,
el('div', { class: 'card', style: { display: 'grid', gap: '0.6rem' } }, el('div', { class: 'card', style: { display: 'grid', gap: '0.6rem' } },
@@ -370,6 +379,7 @@ async function renderTickets(panel) {
el('div', { style: { whiteSpace: 'pre-wrap' } }, t.body || t.text || t.description || '(no text)'), el('div', { style: { whiteSpace: 'pre-wrap' } }, t.body || t.text || t.description || '(no text)'),
images.length ? el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '0.5rem' } }, images) : null, images.length ? el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '0.5rem' } }, images) : null,
logs.length ? el('div', {}, logs) : null, logs.length ? el('div', {}, logs) : null,
modules.length ? el('div', {}, modules) : null,
field('Admin notes', notesInput), field('Admin notes', notesInput),
el('div', { style: { display: 'flex', alignItems: 'center', gap: '0.6rem' } }, el('div', { style: { display: 'flex', alignItems: 'center', gap: '0.6rem' } },
btn('Save notes', () => patch({ notes: notesInput.value }, 'notes saved'), 'primary'), msg))); btn('Save notes', () => patch({ notes: notesInput.value }, 'notes saved'), 'primary'), msg)));