Files
2026-09-16 10:38:01 +07:00

152 lines
7.0 KiB
JavaScript

// ═══════════════════════════════════════════════════════════════════
// ISSUES PAGE — Kanban + List view for issue management
// ═══════════════════════════════════════════════════════════════════
let _issueView = 'kanban'; // 'kanban' | 'list'
function renderIssuesPage() {
const filtered = getFilteredIssues();
if (_issueView === 'kanban') renderKanbanView(filtered);
else renderListView(filtered);
}
function applyIssueFilters() {
renderIssuesPage();
}
function switchIssueView(view) {
_issueView = view;
document.getElementById('kanban-view-btn').classList.toggle('active', view === 'kanban');
document.getElementById('list-view-btn').classList.toggle('active', view === 'list');
renderIssuesPage();
}
function getFilteredIssues() {
const search = (document.getElementById('issues-search')?.value || '').toLowerCase().trim();
const program = document.getElementById('issues-filter-program')?.value || 'all';
const severity= document.getElementById('issues-filter-severity')?.value || 'all';
return APP_STATE.issues.filter(i => {
const matchSearch = !search || i.judul.toLowerCase().includes(search) || i.daerah.toLowerCase().includes(search) || i.provinsi.toLowerCase().includes(search) || i.id.toLowerCase().includes(search);
const matchProg = program === 'all' || i.programId === program;
const matchSev = severity === 'all' || i.severity === severity;
return matchSearch && matchProg && matchSev;
});
}
// ─── KANBAN VIEW ─────────────────────────────────────────────────
function renderKanbanView(issues) {
const cols = [
{ key: 'open', label: 'Terbuka', icon: '🔴', color: 'var(--red)' },
{ key: 'in_progress', label: 'Diproses', icon: '🔵', color: 'var(--blue)' },
{ key: 'resolved', label: 'Selesai', icon: '✅', color: 'var(--green)' }
];
document.getElementById('issues-body').innerHTML = `
<div class="kanban-view">
${cols.map(col => {
const colIssues = issues.filter(i => i.status === col.key);
return `
<div class="kanban-col">
<div class="kanban-col-header">
<div class="kanban-col-title">
<span>${col.icon}</span>
<span>${col.label}</span>
</div>
<span class="col-count">${colIssues.length}</span>
</div>
<div class="kanban-cards">
${colIssues.length === 0
? `<div class="empty-state" style="padding:32px 16px;"><div class="empty-icon" style="font-size:24px;opacity:0.3;">📭</div><div class="empty-desc" style="font-size:11px;">Tidak ada issue</div></div>`
: colIssues.map(i => renderIssueCard(i)).join('')
}
</div>
</div>
`;
}).join('')}
</div>
`;
}
function renderIssueCard(issue) {
const prog = getProgramById(issue.programId);
const survCount = issue.surveyIds.length;
const escCount = issue.escalationIds.length;
return `
<div class="issue-card ${issue.severity}-card fade-in" onclick="openIssueDetail('${issue.id}')">
<div class="ic-header">
<span class="ic-id">${issue.id}</span>
${getSevBadge(issue.severity)}
</div>
<div class="flex gap-2 items-center mb-2">
${getProgramBadge(issue.programId)}
<span style="font-size:10px;color:var(--text-3);">📍 ${issue.daerah}</span>
</div>
<div class="ic-title">${issue.judul}</div>
<div class="ic-desc">${issue.deskripsi}</div>
<div class="ic-footer">
<div style="display:flex;gap:8px;align-items:center;">
${survCount > 0 ? `<span style="font-size:10px;color:var(--purple);">📋 ${survCount} survey</span>` : ''}
${escCount > 0 ? `<span style="font-size:10px;color:var(--orange);">🔺 ${escCount} eskalasi</span>` : ''}
</div>
<div class="ic-meta">${formatDate(issue.createdAt)}</div>
</div>
<div style="margin-top:8px;padding-top:8px;border-top:1px solid var(--border);font-size:10px;color:var(--text-3);" class="truncate">
👤 ${issue.assignee}
</div>
</div>
`;
}
// ─── LIST / TABLE VIEW ────────────────────────────────────────────
function renderListView(issues) {
document.getElementById('issues-body').innerHTML = `
<div class="list-view">
${issues.length === 0
? `<div class="empty-state"><div class="empty-icon">🔍</div><div class="empty-title">Tidak ada issue</div><div class="empty-desc">Tidak ada issue yang cocok dengan filter yang dipilih</div></div>`
: `<div class="table-wrap">
<table>
<thead>
<tr>
<th>ID</th>
<th>Judul Issue</th>
<th>Program</th>
<th>Daerah</th>
<th>Keparahan</th>
<th>Status</th>
<th>Ditugaskan ke</th>
<th>Tanggal</th>
<th></th>
</tr>
</thead>
<tbody>
${issues.map(i => `
<tr style="cursor:pointer;" onclick="openIssueDetail('${i.id}')">
<td><span style="font-family:monospace;font-size:11px;color:var(--text-3);">${i.id}</span></td>
<td style="max-width:240px;">
<div style="font-size:12px;font-weight:500;color:var(--text-1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px;" title="${i.judul}">${i.judul}</div>
</td>
<td>${getProgramBadge(i.programId)}</td>
<td>
<div style="font-size:12px;font-weight:500;">${i.daerah}</div>
<div class="td-muted">${i.provinsi}</div>
</td>
<td>${getSevBadge(i.severity)}</td>
<td>${getStatBadge(i.status)}</td>
<td class="td-muted truncate" style="max-width:140px;">${i.assignee}</td>
<td class="td-muted">${formatDate(i.createdAt)}</td>
<td>
<div style="display:flex;gap:4px;">
${i.surveyIds.length > 0 ? `<span title="${i.surveyIds.length} survey" style="font-size:12px;">📋</span>` : ''}
${i.escalationIds.length > 0 ? `<span title="${i.escalationIds.length} eskalasi" style="font-size:12px;">🔺</span>` : ''}
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>`
}
</div>
`;
}