stackchain-dashboard/frontend/create-issue-sheet.js
timmy b6390d3298
All checks were successful
CI / lint (pull_request) Successful in 13s
CI / build-frontend (pull_request) Successful in 5s
feat: triage mobile issue capture with labels (#163)
2026-08-07 03:55:51 +00:00

73 lines
2.6 KiB
JavaScript

function createIssueCapture({ fetchJson, storage }) {
const storageKey = 'stackchain.issue-capture.v1';
let pending = null;
const safeLabelIds = value => Array.from(new Set(
(Array.isArray(value) ? value : []).filter(id => Number.isInteger(id) && id > 0)
)).slice(0, 20);
const emptyDraft = () => ({ repository: '', title: '', body: '', labelIds: [] });
function saveDraft(draft) {
const safe = {
repository: String(draft?.repository || ''),
title: String(draft?.title || ''),
body: String(draft?.body || ''),
labelIds: safeLabelIds(draft?.labelIds),
};
try { storage.setItem(storageKey, JSON.stringify(safe)); }
catch (_error) { /* Keep the form as the in-memory fallback. */ }
return safe;
}
function loadDraft() {
try {
const parsed = JSON.parse(storage.getItem(storageKey) || 'null');
return parsed && typeof parsed === 'object' ? {
repository: String(parsed.repository || ''),
title: String(parsed.title || ''),
body: String(parsed.body || ''),
labelIds: safeLabelIds(parsed.labelIds),
} : emptyDraft();
} catch (_error) {
return emptyDraft();
}
}
function clearDraft() {
try { storage.removeItem(storageKey); }
catch (_error) { /* Confirmed creation remains authoritative. */ }
}
function loadLabels(repository) {
const encoded = String(repository || '').split('/').map(encodeURIComponent).join('/');
const priorities = new Set(['p0', 'priority-high', 'critical']);
return fetchJson('api/v1/repos/' + encoded + '/labels').then(labels =>
(Array.isArray(labels) ? labels : []).slice().sort((left, right) => {
const leftPriority = priorities.has(String(left?.name || '').toLowerCase());
const rightPriority = priorities.has(String(right?.name || '').toLowerCase());
return Number(rightPriority) - Number(leftPriority);
})
);
}
function submit(draft) {
if (pending) return pending;
const saved = saveDraft(draft);
const repository = saved.repository.split('/').map(encodeURIComponent).join('/');
pending = fetchJson('api/v1/repos/' + repository + '/issues', {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({
title: saved.title, body: saved.body, label_ids: saved.labelIds,
}),
}).then(issue => {
clearDraft();
return issue;
}).finally(() => { pending = null; });
return pending;
}
return { saveDraft, loadDraft, loadLabels, submit };
}
if (typeof module !== 'undefined' && module.exports) module.exports = createIssueCapture;