feat: tambah terjemahan bilingual untuk data SOP, FAQ, dan Staf
This commit is contained in:
+52
-18
@@ -12,6 +12,29 @@ function t(key, fallback) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mendapatkan bahasa aktif dari localStorage atau default 'id'.
|
||||
* @returns {'id'|'en'} Kode bahasa aktif
|
||||
*/
|
||||
function getLang() {
|
||||
return localStorage.getItem('if_untan_lang') || 'id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengambil teks dari field bilingual JSON { id: '...', en: '...' }.
|
||||
* Jika field bukan objek (string lama / belum diupdate), kembalikan apa adanya.
|
||||
* @param {string|Object} field - Field data dari JSON
|
||||
* @returns {string} Teks sesuai bahasa aktif
|
||||
*/
|
||||
function loc(field) {
|
||||
if (!field) return '';
|
||||
if (typeof field === 'object' && field !== null) {
|
||||
const lang = getLang();
|
||||
return field[lang] || field['id'] || '';
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalisasi nomor telepon ke format standard e.g. 628...
|
||||
* @param {string} phone - Nomor telepon mentah
|
||||
@@ -98,14 +121,17 @@ export function renderStaffCards(staffs) {
|
||||
tasks
|
||||
} = staff;
|
||||
|
||||
const roleText = loc(role);
|
||||
const locationText = loc(location);
|
||||
|
||||
const scheduleHTML = schedule.map(sched =>
|
||||
`<div class="schedule-item">` +
|
||||
`<span class="s-day">${sched.day}</span>` +
|
||||
`<span class="s-time">${sched.time} <span class="s-break">| ${sched.break}</span></span>` +
|
||||
`<span class="s-day">${loc(sched.day)}</span>` +
|
||||
`<span class="s-time">${sched.time} <span class="s-break">| ${loc(sched.break)}</span></span>` +
|
||||
`</div>`
|
||||
).join('');
|
||||
|
||||
const tasksHTML = tasks.map(task => `<li>${task}</li>`).join('');
|
||||
const tasksHTML = tasks.map(task => `<li>${loc(task)}</li>`).join('');
|
||||
const isPhoneValid = isValidWhatsAppNumber(phone);
|
||||
const encodedPhone = isPhoneValid ? encodePhone(phone) : '';
|
||||
const displayPhone = isPhoneValid ? formatPhoneForDisplay(phone) : '-';
|
||||
@@ -124,12 +150,12 @@ export function renderStaffCards(staffs) {
|
||||
</div>
|
||||
<div>
|
||||
<div class="staff-name">${name}</div>
|
||||
<div class="staff-role">${role}</div>
|
||||
<div class="staff-role">${roleText}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="staff-card-body">
|
||||
<div class="staff-info-grid">
|
||||
<div class="staff-info-item"><span class="icon">📍</span><div><span class="label">${t('grp_c4a_label_location', 'Lokasi')}</span><span class="val">${location}</span></div></div>
|
||||
<div class="staff-info-item"><span class="icon">📍</span><div><span class="label">${t('grp_c4a_label_location', 'Lokasi')}</span><span class="val">${locationText}</span></div></div>
|
||||
<div class="staff-info-item">
|
||||
<span class="icon">🕒</span>
|
||||
<div style="flex:1">
|
||||
@@ -200,29 +226,32 @@ function buildSopCard(sop) {
|
||||
procedures
|
||||
} = sop;
|
||||
|
||||
const titleText = loc(title);
|
||||
const descText = loc(description);
|
||||
|
||||
const catLabel = getCatLabel(category);
|
||||
const catClass = CATEGORY_COLORS[category] || '';
|
||||
|
||||
return `
|
||||
<article class="sop-grid-card reveal" data-tab-id="${tabId}" data-category="${category}" data-title="${title.toLowerCase()}">
|
||||
<article class="sop-grid-card reveal" data-tab-id="${tabId}" data-category="${category}" data-title="${titleText.toLowerCase()}">
|
||||
<div class="sgc-header">
|
||||
<div class="sgc-icon-wrap">
|
||||
<i class="fas ${icon}"></i>
|
||||
</div>
|
||||
<div class="sgc-meta">
|
||||
<h3 class="sgc-title">${title}</h3>
|
||||
<h3 class="sgc-title">${titleText}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sgc-category-row">
|
||||
<span class="sgc-category ${catClass}">${catLabel}</span>
|
||||
</div>
|
||||
<p class="sgc-desc">${description}</p>
|
||||
<p class="sgc-desc">${descText}</p>
|
||||
<div class="sgc-footer">
|
||||
<div class="sgc-steps">
|
||||
<i class="fas fa-list-ol"></i>
|
||||
<span>${procedures.length} ${t('grp_c4a_label_steps', 'Langkah')}</span>
|
||||
</div>
|
||||
<button class="sgc-detail-btn" data-tab-id="${tabId}" aria-label="${t('grp_c4a_label_view_detail', 'Lihat Detail')} ${title}">
|
||||
<button class="sgc-detail-btn" data-tab-id="${tabId}" aria-label="${t('grp_c4a_label_view_detail', 'Lihat Detail')} ${titleText}">
|
||||
${t('grp_c4a_label_view_detail', 'Lihat Detail')} <i class="fas fa-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -247,11 +276,16 @@ function buildSopModalContent(sop) {
|
||||
additional_info: additionalInfo
|
||||
} = sop;
|
||||
|
||||
const titleText = loc(title);
|
||||
const descText = loc(description);
|
||||
const purposeText = loc(purpose);
|
||||
const additionalInfoText = loc(additionalInfo);
|
||||
|
||||
const catLabel = getCatLabel(category);
|
||||
const catClass = CATEGORY_COLORS[category] || '';
|
||||
|
||||
const reqsHTML = requirements.map(req => `<li>${req}</li>`).join('');
|
||||
const procsHTML = procedures.map(proc => `<li>${proc}</li>`).join('');
|
||||
const reqsHTML = requirements.map(req => `<li>${loc(req)}</li>`).join('');
|
||||
const procsHTML = procedures.map(proc => `<li>${loc(proc)}</li>`).join('');
|
||||
|
||||
// staffResponsible bersifat opsional — sembunyikan blok jika tidak ada
|
||||
const hasStaff = Array.isArray(staffResponsible) && staffResponsible.length > 0;
|
||||
@@ -266,10 +300,10 @@ function buildSopModalContent(sop) {
|
||||
: '';
|
||||
|
||||
// additionalInfo bersifat opsional — sembunyikan blok jika tidak ada
|
||||
const additionalInfoHTML = additionalInfo
|
||||
const additionalInfoHTML = additionalInfoText
|
||||
? `<div class="sop-info-box">
|
||||
<span class="iico">ℹ️</span>
|
||||
<p class="sop-info-box-text"><strong>${t('grp_c4a_label_additional_info', 'Informasi Tambahan (Bersifat Opsional):')}</strong> ${additionalInfo}</p>
|
||||
<p class="sop-info-box-text"><strong>${t('grp_c4a_label_additional_info', 'Informasi Tambahan (Bersifat Opsional):')}</strong> ${additionalInfoText}</p>
|
||||
</div>`
|
||||
: '';
|
||||
|
||||
@@ -283,15 +317,15 @@ function buildSopModalContent(sop) {
|
||||
<div class="sop-modal-badges">
|
||||
<span class="sgc-category ${catClass}">${catLabel}</span>
|
||||
</div>
|
||||
<h2 class="sop-modal-title" id="sop-modal-title">${title}</h2>
|
||||
<p class="sop-modal-desc">${description}</p>
|
||||
<h2 class="sop-modal-title" id="sop-modal-title">${titleText}</h2>
|
||||
<p class="sop-modal-desc">${descText}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sop-modal-body">
|
||||
<div class="sop-block">
|
||||
<div class="sop-block-label">${t('grp_c4a_label_purpose', 'Tujuan')}</div>
|
||||
<p class="sop-block-text">${purpose}</p>
|
||||
<p class="sop-block-text">${purposeText}</p>
|
||||
</div>
|
||||
<div class="sop-two-col">
|
||||
<div>
|
||||
@@ -626,11 +660,11 @@ export function renderFaqSection(faqs) {
|
||||
return `
|
||||
<div class="faq-item reveal ${delayClass}">
|
||||
<div class="faq-question">
|
||||
<span class="faq-question-text">${faq.question}</span>
|
||||
<span class="faq-question-text">${loc(faq.question)}</span>
|
||||
<div class="faq-icon">+</div>
|
||||
</div>
|
||||
<div class="faq-answer">
|
||||
<div class="faq-answer-inner">${faq.answer}</div>
|
||||
<div class="faq-answer-inner">${loc(faq.answer)}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
Reference in New Issue
Block a user