Phase 1: tksafety에 출입신청/체크리스트 API·웹 추가, tkfb 안전 코드 삭제
Phase 2: 사용자 관리 페이지 삭제, API 축소, 알림 수신자 tkuser 이관
Phase 3: tkuser 권한 페이지 정의 업데이트
Phase 4: 전체 34개 페이지 Tailwind CSS + tkfb-core.js 전환,
미사용 CSS 20개·인프라 JS 10개·템플릿·컴포넌트 삭제
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
120 lines
6.2 KiB
JavaScript
120 lines
6.2 KiB
JavaScript
/* ===== 부적합 현황 (Nonconformity List) ===== */
|
|
|
|
const CATEGORY_TYPE = 'nonconformity';
|
|
|
|
const STATUS_LABELS = {
|
|
reported: '신고', received: '접수', in_progress: '처리중',
|
|
completed: '완료', closed: '종료'
|
|
};
|
|
|
|
const STATUS_BADGE = {
|
|
reported: 'badge-blue', received: 'badge-orange', in_progress: 'badge-purple',
|
|
completed: 'badge-green', closed: 'badge-gray'
|
|
};
|
|
|
|
function getReportUrl() {
|
|
const h = location.hostname;
|
|
if (h.includes('technicalkorea.net')) return 'https://tkreport.technicalkorea.net/pages/safety/issue-report.html?type=nonconformity';
|
|
return location.protocol + '//' + h + ':30180/pages/safety/issue-report.html?type=nonconformity';
|
|
}
|
|
|
|
function getIssueDetailUrl(reportId) {
|
|
const h = location.hostname;
|
|
if (h.includes('technicalkorea.net')) return `https://tkreport.technicalkorea.net/pages/safety/issue-detail.html?id=${reportId}&from=nonconformity`;
|
|
return `${location.protocol}//${h}:30180/pages/safety/issue-detail.html?id=${reportId}&from=nonconformity`;
|
|
}
|
|
|
|
async function loadStats() {
|
|
try {
|
|
const data = await api(`/work-issues/stats/summary?category_type=${CATEGORY_TYPE}`);
|
|
if (data.success && data.data) {
|
|
document.getElementById('statReported').textContent = data.data.reported || 0;
|
|
document.getElementById('statReceived').textContent = data.data.received || 0;
|
|
document.getElementById('statProgress').textContent = data.data.in_progress || 0;
|
|
document.getElementById('statCompleted').textContent = data.data.completed || 0;
|
|
}
|
|
} catch {
|
|
document.getElementById('statsGrid').style.display = 'none';
|
|
}
|
|
}
|
|
|
|
async function loadIssues() {
|
|
const params = new URLSearchParams();
|
|
params.append('category_type', CATEGORY_TYPE);
|
|
|
|
const status = document.getElementById('filterStatus').value;
|
|
const startDate = document.getElementById('filterStartDate').value;
|
|
const endDate = document.getElementById('filterEndDate').value;
|
|
|
|
if (status) params.append('status', status);
|
|
if (startDate) params.append('start_date', startDate);
|
|
if (endDate) params.append('end_date', endDate);
|
|
|
|
try {
|
|
const data = await api(`/work-issues?${params.toString()}`);
|
|
if (data.success) renderIssues(data.data || []);
|
|
} catch {
|
|
document.getElementById('issueList').innerHTML =
|
|
'<div class="bg-white rounded-xl shadow-sm p-8 text-center text-gray-400 text-sm">목록을 불러올 수 없습니다. 잠시 후 다시 시도해주세요.</div>';
|
|
}
|
|
}
|
|
|
|
function renderIssues(issues) {
|
|
const el = document.getElementById('issueList');
|
|
if (!issues.length) {
|
|
el.innerHTML = '<div class="bg-white rounded-xl shadow-sm p-8 text-center"><p class="font-semibold text-gray-700 mb-1">등록된 부적합 신고가 없습니다</p><p class="text-sm text-gray-400">새로운 부적합을 신고하려면 \'부적합 신고\' 버튼을 클릭하세요.</p></div>';
|
|
return;
|
|
}
|
|
|
|
el.innerHTML = issues.map(issue => {
|
|
const reportDate = formatDateTime(issue.report_date);
|
|
let loc = escapeHtml(issue.custom_location || '');
|
|
if (issue.factory_name) {
|
|
loc = escapeHtml(issue.factory_name);
|
|
if (issue.workplace_name) loc += ` - ${escapeHtml(issue.workplace_name)}`;
|
|
}
|
|
const title = escapeHtml(issue.issue_item_name || issue.issue_category_name || '부적합 신고');
|
|
const categoryName = escapeHtml(issue.issue_category_name || '부적합');
|
|
const reportId = parseInt(issue.report_id) || 0;
|
|
const validStatuses = ['reported', 'received', 'in_progress', 'completed', 'closed'];
|
|
const safeStatus = validStatuses.includes(issue.status) ? issue.status : 'reported';
|
|
const reporter = escapeHtml(issue.reporter_full_name || issue.reporter_name || '-');
|
|
const assigned = issue.assigned_full_name ? escapeHtml(issue.assigned_full_name) : '';
|
|
|
|
const photos = [issue.photo_path1, issue.photo_path2, issue.photo_path3, issue.photo_path4, issue.photo_path5].filter(Boolean);
|
|
|
|
return `<div class="bg-white rounded-xl shadow-sm p-4 border border-transparent hover:border-orange-200 hover:shadow-md transition-all cursor-pointer" onclick="location.href='${getIssueDetailUrl(reportId)}'">
|
|
<div class="flex justify-between items-start mb-2">
|
|
<span class="text-sm text-gray-400">#${reportId}</span>
|
|
<span class="badge ${STATUS_BADGE[safeStatus] || 'badge-gray'}">${STATUS_LABELS[issue.status] || escapeHtml(issue.status || '-')}</span>
|
|
</div>
|
|
<div class="mb-2">
|
|
<span class="badge badge-orange mr-1 text-xs">${categoryName}</span>
|
|
<span class="font-semibold text-gray-800">${title}</span>
|
|
</div>
|
|
<div class="flex flex-wrap gap-3 text-sm text-gray-500">
|
|
<span class="flex items-center gap-1"><i class="fas fa-user text-xs"></i>${reporter}</span>
|
|
<span class="flex items-center gap-1"><i class="fas fa-calendar text-xs"></i>${reportDate}</span>
|
|
${loc ? `<span class="flex items-center gap-1"><i class="fas fa-map-marker-alt text-xs"></i>${loc}</span>` : ''}
|
|
${assigned ? `<span class="flex items-center gap-1"><i class="fas fa-user-cog text-xs"></i>담당: ${assigned}</span>` : ''}
|
|
</div>
|
|
${photos.length > 0 ? `<div class="flex gap-2 mt-3">${photos.slice(0, 3).map(p => `<img src="${encodeURI(p)}" alt="사진" loading="lazy" class="w-14 h-14 object-cover rounded border border-gray-200">`).join('')}${photos.length > 3 ? `<span class="flex items-center text-sm text-gray-400">+${photos.length - 3}</span>` : ''}</div>` : ''}
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
/* ===== Init ===== */
|
|
(async function() {
|
|
if (!await initAuth()) return;
|
|
|
|
// 신고 버튼 URL 설정
|
|
document.getElementById('btnNewReport').href = getReportUrl();
|
|
|
|
// 필터 이벤트
|
|
document.getElementById('filterStatus').addEventListener('change', loadIssues);
|
|
document.getElementById('filterStartDate').addEventListener('change', loadIssues);
|
|
document.getElementById('filterEndDate').addEventListener('change', loadIssues);
|
|
|
|
await Promise.all([loadStats(), loadIssues()]);
|
|
})();
|