<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kode Google Apps Script & Embed WordPress - Rekap Publikasi Jurnal</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f8fafc;
color: #1e293b;
line-height: 1.6;
margin: 0;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
background: #ffffff;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
}
h1, h2, h3 {
color: #0f766e;
}
pre {
background-color: #1e293b;
color: #f8fafc;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
font-size: 14px;
}
.step {
margin-bottom: 25px;
padding: 15px;
border-left: 4px solid #0d9488;
background-color: #f0fdfa;
}
code {
background-color: #e2e8f0;
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>Panduan & Kode Integrasi Tabel Publikasi Jurnal MPI</h1>
<p>Sistem ini dirancang khusus untuk mengambil data <strong>hanya dari sheet "rekap publikasi artikel jurnal"</strong> dari Google Sheets yang Anda berikan, lalu menampilkannya secara interaktif, modern, dan responsif di WordPress.</p>
<hr style="margin: 20px 0; border: 0; border-top: 1px solid #e2e8f0;">
<!-- BAGIAN 1 -->
<div class="step">
<h2>Langkah 1: Kode Google Apps Script (Code.gs)</h2>
<p>Buka <a href="https://script.google.com/" target="_blank">Google Apps Script</a>, buat proyek baru, lalu ganti isi berkas <code>Code.gs</code> dengan kode di bawah ini:</p>
</div>
<pre><code>// ==========================================
// KODE BACKEND: Code.gs
// ==========================================
function doGet() {
return HtmlService.createTemplateFromFile('Index')
.evaluate()
.setTitle('Rekap Publikasi Artikel Jurnal Mahasiswa MPI')
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getSheetData() {
// ID Spreadsheet dari link yang Anda berikan
var SPREADSHEET_ID = '1shgSCYX7pCSkP7xMxG9C6CfKCfU9IyafpQLl_afD8jc';
var TARGET_SHEET_NAME = 'rekap publikasi artikel jurnal';
try {
var ss = SpreadsheetApp.openById(SPREADSHEET_ID);
var sheet = ss.getSheetByName(TARGET_SHEET_NAME);
// Jika nama sheet tidak ketemu persis, fallback ke sheet pertama
if (!sheet) {
sheet = ss.getSheets()[0];
}
var data = sheet.getDataRange().getValues();
return JSON.stringify({ success: true, data: data });
} catch (error) {
return JSON.stringify({ success: false, error: error.toString() });
}
}
</code></pre>
<!-- BAGIAN 2 -->
<div class="step">
<h2>Langkah 2: Kode Tampilan Frontend (Index.html)</h2>
<p>Di Google Apps Script, klik ikon <strong>+</strong> di samping 'Files', pilih <strong>HTML</strong>, beri nama <code>Index</code> (sehingga menjadi <code>Index.html</code>), lalu salin seluruh kode di bawah ini:</p>
</div>
<pre><code><!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rekap Publikasi Artikel Jurnal MPI</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Inter -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<style>
body { font-family: 'Inter', sans-serif; background-color: #f8fafc; }
.custom-scrollbar::-webkit-scrollbar { height: 6px; width: 6px; }
.custom-scrollbar::-webkit-scrollbar-track { background: #f1f5f9; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
.custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
</style>
</head>
<body class="p-3 sm:p-6">
<div class="max-w-7xl mx-auto space-y-4">
<!-- Card Header & Kontrol -->
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-4 sm:p-6">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
<div>
<div class="flex items-center gap-2 text-teal-600 font-semibold text-sm mb-1">
<i data-lucide="book-open" class="w-4 h-4"></i>
<span>Manajemen Pendidikan Islam (MPI)</span>
</div>
<h1 class="text-2xl sm:text-3xl font-bold text-slate-800 tracking-tight">
Rekap Publikasi Artikel Jurnal
</h1>
<p class="text-slate-500 text-sm mt-1">
Daftar karya tulis dan publikasi ilmiah mahasiswa secara real-time.
</p>
</div>
<!-- Statistik Ringkas -->
<div class="flex items-center gap-3 bg-teal-50 border border-teal-100 rounded-xl p-3 px-4 self-start md:self-auto">
<div class="p-2 bg-teal-600 text-white rounded-lg">
<i data-lucide="file-text" class="w-5 h-5"></i>
</div>
<div>
<p class="text-xs text-teal-700 font-medium">Total Publikasi</p>
<p id="totalCount" class="text-xl font-bold text-teal-900">0</p>
</div>
</div>
</div>
<!-- Search & Filter Bar -->
<div class="flex flex-col sm:flex-row items-center gap-3 pt-2 border-t border-slate-100">
<div class="relative w-full sm:w-80">
<i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
<input
type="text"
id="searchInput"
placeholder="Cari judul, penulis, jurnal..."
class="w-full pl-9 pr-4 py-2 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-teal-500 focus:bg-white transition"
oninput="filterData()"
/>
</div>
<button
onclick="loadData()"
class="w-full sm:w-auto px-4 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-xl text-sm font-medium flex items-center justify-center gap-2 transition">
<i data-lucide="refresh-cw" class="w-4 h-4" id="refreshIcon"></i>
Refresh Data
</button>
</div>
</div>
<!-- State Loading -->
<div id="loadingState" class="bg-white rounded-2xl shadow-sm border border-slate-200 p-12 text-center">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-4 border-teal-500 border-t-transparent mb-3"></div>
<p class="text-slate-600 font-medium text-sm">Memuat data publikasi jurnal...</p>
</div>
<!-- State Error -->
<div id="errorState" class="hidden bg-red-50 border border-red-200 rounded-2xl p-6 text-center text-red-700">
<i data-lucide="alert-circle" class="w-8 h-8 mx-auto mb-2 text-red-500"></i>
<p class="font-semibold">Gagal memuat data!</p>
<p id="errorMessage" class="text-xs mt-1 text-red-600"></p>
</div>
<!-- Container Tabel -->
<div id="tableContainer" class="hidden bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full text-left text-xs sm:text-sm text-slate-600">
<thead id="tableHead" class="bg-slate-50 border-b border-slate-200 text-slate-700 uppercase font-semibold tracking-wider text-[11px]">
<!-- Header di-generate via JavaScript -->
</thead>
<tbody id="tableBody" class="divide-y divide-slate-100">
<!-- Data di-generate via JavaScript -->
</tbody>
</table>
</div>
<!-- Footer Info -->
<div class="p-4 bg-slate-50 border-t border-slate-200 flex flex-col sm:flex-row items-center justify-between gap-2 text-xs text-slate-500">
<span id="dataInfo">Menampilkan 0 data</span>
<span class="text-[11px] text-slate-400">Terhubung dengan Google Sheets MPI</span>
</div>
</div>
</div>
<script>
let originalData = [];
let headers = [];
document.addEventListener("DOMContentLoaded", function() {
lucide.createIcons();
loadData();
});
function loadData() {
const loading = document.getElementById("loadingState");
const error = document.getElementById("errorState");
const table = document.getElementById("tableContainer");
const refreshIcon = document.getElementById("refreshIcon");
loading.classList.remove("hidden");
error.classList.add("hidden");
table.classList.add("hidden");
refreshIcon.classList.add("animate-spin");
google.script.run
.withSuccessHandler(onSuccess)
.withFailureHandler(onError)
.getSheetData();
}
function onSuccess(responseJson) {
const refreshIcon = document.getElementById("refreshIcon");
refreshIcon.classList.remove("animate-spin");
const response = JSON.parse(responseJson);
if (!response.success) {
onError(response.error);
return;
}
const rawData = response.data;
if (!rawData || rawData.length === 0) {
onError("Data tidak ditemukan atau sheet kosong.");
return;
}
// Pisahkan header dan data isi
headers = rawData[0];
originalData = rawData.slice(1).filter(row => row.some(cell => cell.toString().trim() !== ""));
renderTableHeader(headers);
renderTableBody(originalData);
document.getElementById("loadingState").classList.add("hidden");
document.getElementById("tableContainer").classList.remove("hidden");
lucide.createIcons();
}
function onError(err) {
document.getElementById("refreshIcon").classList.remove("animate-spin");
document.getElementById("loadingState").classList.add("hidden");
document.getElementById("errorState").classList.remove("hidden");
document.getElementById("errorMessage").innerText = err;
}
function renderTableHeader(headData) {
const thead = document.getElementById("tableHead");
let html = "<tr>";
html += `<th class="py-3 px-4 w-12 text-center">No</th>`;
headData.forEach((col) => {
html += `<th class="py-3 px-4 font-semibold text-slate-700 whitespace-nowrap">${escapeHtml(col)}</th>`;
});
html += "</tr>";
thead.innerHTML = html;
}
function renderTableBody(bodyData) {
const tbody = document.getElementById("tableBody");
const totalCount = document.getElementById("totalCount");
const dataInfo = document.getElementById("dataInfo");
totalCount.innerText = bodyData.length;
dataInfo.innerText = `Menampilkan ${bodyData.length} data publikasi`;
if (bodyData.length === 0) {
tbody.innerHTML = `<tr><td colspan="${headers.length + 1}" class="text-center py-8 text-slate-400">Tidak ada data yang sesuai.</td></tr>`;
return;
}
let html = "";
bodyData.forEach((row, index) => {
html += `<tr class="hover:bg-slate-50/80 transition-colors">`;
html += `<td class="py-3 px-4 text-center font-medium text-slate-400 text-xs">${index + 1}</td>`;
row.forEach((cell) => {
let cellValue = escapeHtml(cell.toString());
let content = cellValue;
// Format otomatis jika berisi link URL
if (cellValue.startsWith("http://") || cellValue.startsWith("https://")) {
content = `<a href="${cellValue}" target="_blank" class="inline-flex items-center gap-1 px-2.5 py-1 bg-teal-50 text-teal-700 hover:bg-teal-100 rounded-md font-medium text-xs border border-teal-200/60 transition">
<span>Buka Link</span>
<i data-lucide="external-link" class="w-3 h-3"></i>
</a>`;
}
html += `<td class="py-3 px-4 align-middle">${content}</td>`;
});
html += `</tr>`;
});
tbody.innerHTML = html;
lucide.createIcons();
}
function filterData() {
const query = document.getElementById("searchInput").value.toLowerCase();
const filtered = originalData.filter(row => {
return row.some(cell => cell.toString().toLowerCase().includes(query));
});
renderTableBody(filtered);
}
function escapeHtml(text) {
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
</script>
</body>
</html>
</code></pre>
<!-- BAGIAN 3 -->
<div class="step">
<h2>Langkah 3: Terapkan / Deploy Web App</h2>
<ol>
<li>Di halaman Apps Script, klik tombol biru <strong>Deploy</strong> > <strong>New deployment</strong> (Penerapan baru).</li>
<li>Klik ikon gerigi ⚙️ di samping <em>Select type</em>, pilih <strong>Web app</strong>.</li>
<li>Atur konfigurasi:
<ul>
<li><strong>Description:</strong> Tabel Jurnal MPI</li>
<li><strong>Execute as:</strong> Me (Email Anda)</li>
<li><strong>Who has access:</strong> <u>Anyone</u> (Siapa saja)</li>
</ul>
</li>
<li>Klik <strong>Deploy</strong>, lakukan otorisasi akun Google jika diminta.</li>
<li>Salin <strong>Web App URL</strong> yang dihasilkan (Berawalan <code>https://script.google.com/macros/s/.../exec</code>).</li>
</ol>
</div>
<!-- BAGIAN 4 -->
<div class="step">
<h2>Langkah 4: Salin Kode Ini ke WordPress</h2>
<p>Buka editor WordPress Anda, tambahkan blok <strong>Custom HTML</strong>, lalu tempelkan kode di bawah ini (Ganti teks <code>URL_WEB_APP_ANDA_DISINI</code> dengan Web App URL dari Langkah 3):</p>
</div>
<pre><code><!-- ====================================================== -->
<!-- EMBED TABEL PUBLIKASI JURNAL MPI UNTUK WORDPRESS -->
<!-- ====================================================== -->
<div class="mpi-wrapper" style="width:100%; max-width:1200px; margin:20px auto;">
<iframe
src="URL_WEB_APP_ANDA_DISINI"
style="width:100%; height:850px; border:none; border-radius:16px; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05);"
allowfullscreen
loading="lazy">
</iframe>
</div>
</code></pre>
</div>
</body>
</html>
```
### Ringkasan Keunggulan Kode Ini:
1. **Khusus Sheet Rekap**: Kode secara eksplisit memanggil `getSheetByName('rekap publikasi artikel jurnal')`, sehingga sheet lain tidak akan muncul.
2. **Desain Modern & Responsif**: Menggunakan Tailwind CSS, Font Inter, dan Lucide Icons.
3. **Pencarian Real-time**: Pembaca halaman web WordPress bisa mengetik nama penulis, judul artikel, atau nama jurnal untuk menyaring data dengan instan.
4. **Deteksi Link Otomatis**: Jika ada cell berisi URL/link jurnal, tampilan otomatis diubah menjadi tombol "Buka Link" yang rapi.
Silakan ikuti petunjuk langkah 1 sampai 4 di atas untuk langsung mengaktifkannya di WordPress Anda.
