export class DocumentFormatter { static MONTHS_ID = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']; static CATEGORY_RULES = [ { key: 'skripsi', match: ['skripsi', 'tugas akhir', 'ta'] }, { key: 'akademik', match: ['akademik', 'kalender', 'kaldik'] }, { key: 'praktik', match: ['praktik', 'praktek', 'kerja praktik', 'kp'] }, ]; static TAG_ABBREVIATIONS = new Set(['kp', 'sop', 'ta', 'krs', 'khs', 'sks', 'pkl', 'mbkm']); static FILE_TYPE_RULES = [ { extensions: ['pdf'], mimeTypes: ['application/pdf'], icon: 'fas fa-file-pdf', css: 'doc-type-pdf' }, { extensions: ['doc', 'docx'], mimeTypes: [ 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ], fallbackType: 'docx', icon: 'fas fa-file-word', css: 'doc-type-docx', }, { extensions: ['ppt', 'pptx'], mimeTypes: [ 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ], fallbackType: 'pptx', icon: 'fas fa-file-powerpoint', css: 'doc-type-pptx', }, { extensions: ['xls', 'xlsx'], mimeTypes: [ 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ], fallbackType: 'xlsx', icon: 'fas fa-file-excel', css: 'doc-type-xlsx', }, { extensions: ['csv'], mimeTypes: ['text/csv'], icon: 'fas fa-file-csv', css: 'doc-type-xlsx' }, { extensions: ['txt', 'rtf', 'md'], mimeTypes: ['text/plain', 'application/rtf'], icon: 'fas fa-file-alt', css: 'doc-type-txt' }, { extensions: ['zip', 'rar', '7z', 'tar', 'gz'], mimeTypes: ['application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed', 'application/gzip'], icon: 'fas fa-file-archive', css: 'doc-type-archive', }, { extensions: ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'], mimePrefix: 'image/', icon: 'fas fa-file-image', css: 'doc-type-image', }, ]; static formatDate(iso) { if (!iso) return ''; const date = new Date(iso); if (Number.isNaN(date.getTime())) return ''; const day = String(date.getDate()).padStart(2, '0'); const month = this.MONTHS_ID[date.getMonth()]; return `${day} ${month} ${date.getFullYear()}`; } static categoryFromTags(tags) { if (!tags || tags.length === 0) return 'umum'; const lower = tags.map(tag => tag.toLowerCase()); for (const rule of this.CATEGORY_RULES) { if (rule.match.some(match => lower.includes(match))) return rule.key; } return 'umum'; } static fileTypeFromName(name) { return this.fileInfoFromName(name).type; } static fileInfoFromName(name) { const extension = this.extensionFromName(name); const rule = this.FILE_TYPE_RULES.find(item => item.extensions.includes(extension)); if (!rule) { return { type: extension || 'file', icon: 'fas fa-file', css: 'doc-type-file', }; } return { type: extension, icon: rule.icon, css: rule.css, }; } static fileInfoFromItem(item) { const file = item?.file && typeof item.file === 'object' ? item.file : {}; const nameCandidates = [ file.filename_download, file.filename_disk, item?.nama_berkas, ]; for (const name of nameCandidates) { const info = this.fileInfoFromName(name); if (info.type !== 'file') return info; } return this.fileInfoFromMime(file.type); } static fileInfoFromMime(type) { const mime = String(type || '').trim().toLowerCase(); if (!mime) return this.fileInfoFromName(''); const rule = this.FILE_TYPE_RULES.find(item => ( item.mimeTypes?.includes(mime) || (item.mimePrefix && mime.startsWith(item.mimePrefix)) )); if (!rule) return this.fileInfoFromName(''); return { type: rule.fallbackType || rule.extensions[0], icon: rule.icon, css: rule.css, }; } static extensionFromName(name) { const normalized = String(name || '').trim().toLowerCase(); const lastSegment = normalized.split(/[\\/]/).pop() || ''; const extensionStart = lastSegment.lastIndexOf('.'); if (extensionStart <= 0 || extensionStart === lastSegment.length - 1) return ''; return lastSegment.slice(extensionStart + 1); } static downloadFilename(name, type) { const ext = `.${type}`; if (!name) return `dokumen${ext}`; return name.toLowerCase().endsWith(ext.toLowerCase()) ? name : `${name}${ext}`; } static downloadFilenameFromItem(item) { const file = item?.file && typeof item.file === 'object' ? item.file : {}; const originalFilename = file.filename_download || file.filename_disk; if (originalFilename) return originalFilename; const fileType = this.fileInfoFromItem(item).type; return this.downloadFilename(item?.nama_berkas || 'dokumen', fileType); } static escapeHtml(str) { if (str == null) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } static escapeAttr(str) { return this.escapeHtml(str); } static formatTag(tag) { const value = String(tag).trim().replace(/\s+/g, ' '); if (!value) return value; return value.split(' ').map(word => { const lower = word.toLowerCase(); if (this.TAG_ABBREVIATIONS.has(lower) || /^[A-Z0-9]{2,}$/.test(word)) { return lower.toUpperCase(); } return lower.charAt(0).toUpperCase() + lower.slice(1); }).join(' '); } static parseTags(raw) { if (!raw) return []; const str = String(raw).trim(); if (!str) return []; if (str.startsWith('[')) { try { const arr = JSON.parse(str); if (Array.isArray(arr)) { return arr.map(tag => String(tag).trim()).filter(Boolean); } } catch { // Fall through to comma-separated parsing. } } return str.split(',').map(tag => tag.trim()).filter(Boolean); } }