Tambah Data
// ============================================================================
// CONFIGURATION & STATE
// ============================================================================
// Ganti URL ini dengan Web App URL dari Google Apps Script Anda nanti
const GOOGLE_SHEET_API_URL = 'https://script.google.com/macros/s/AKfycbxEtVspnTq9Ac9gko4qFLXcQmiQ2L-YmhJ-Zke4gka-sOruvh5hdQy_d9wmMY9ROPRiTw/exec';
// State variables
let currentData = {};
let isUsingDummyData = true;
let isAdmin = false;
let formState = { mode: 'add', category: '', editId: null };
// Dummy Data (Diperbarui dengan ID dan Foto untuk KTA)
const dummyData = {
atlit: [
{ id: "A001", nama: "Budi Santoso", divisi: "Recurve", club: "Kebumen Archery Club", url_foto: "https://placehold.co/150x200/14532d/ffffff?text=Budi" },
{ id: "A002", nama: "Siti Aminah", divisi: "Compound", club: "Walet Archery", url_foto: "https://placehold.co/150x200/14532d/ffffff?text=Siti" },
{ id: "A003", nama: "Agus Pratama", divisi: "Standard Bow", club: "Gombong Arrow", url_foto: "https://placehold.co/150x200/14532d/ffffff?text=Agus" },
{ id: "A004", nama: "Dewi Lestari", divisi: "Recurve", club: "Kebumen Archery Club", url_foto: "https://placehold.co/150x200/14532d/ffffff?text=Dewi" }
],
pelatih: [
{ id: "P001", nama: "Joko Supriyanto", lisensi: "Tingkat Nasional", spesialisasi: "Recurve" },
{ id: "P002", nama: "Hendro Wibowo", lisensi: "Tingkat Daerah", spesialisasi: "Compound" }
],
club: [
{ id: "C001", nama: "Kebumen Archery Club", ketua: "Ahmad Fauzi", kontak: "08123456789" },
{ id: "C002", nama: "Walet Archery", ketua: "Bambang Pamungkas", kontak: "08987654321" }
],
tempat: [
{ id: "T001", nama: "Stadion Candradimuka", alamat: "Jl. Arungbinang, Kebumen", jadwal: "Sabtu & Minggu, 15.00 - 17.30" },
{ id: "T002", nama: "Lapangan Gombong", alamat: "Kec. Gombong", jadwal: "Jumat, 15.30 - 17.30" }
],
galeri: [
{ id: "G001", judul: "Kejurkab Kebumen 2025", tanggal: "15 Mei 2025", url_gambar: "https://placehold.co/600x400/e2e8f0/1e293b?text=Kejurkab+2025" },
{ id: "G002", judul: "Latihan Gabungan", tanggal: "02 April 2025", url_gambar: "https://placehold.co/600x400/e2e8f0/1e293b?text=Latgab" }
]
};
// Initialize App
if (tabId === 'atlit') renderAtlit(contentArea);
else if (tabId === 'pelatih') renderPelatih(contentArea);
else if (tabId === 'club') renderClub(contentArea);
else if (tabId === 'tempat') renderTempat(contentArea);
else if (tabId === 'galeri') renderGaleri(contentArea);
else if (tabId === 'kta') renderKTA(contentArea); // Render KTA
else if (tabId === 'dashboard') renderDashboard(contentArea);
}
function renderKTA(container) {
const dataAtlit = currentData.atlit || [];
let html = `
`;
container.innerHTML = html;
// Tampilkan beberapa atlit awal
tampilkanHasilKTA(dataAtlit.slice(0, 6));
}
function filterKTA() {
const input = document.getElementById('search-kta').value.toLowerCase();
const dataAtlit = currentData.atlit || [];
const filtered = dataAtlit.filter(a => a.nama.toLowerCase().includes(input));
tampilkanHasilKTA(filtered.slice(0, 6)); // Batasi 6 hasil
}
function tampilkanHasilKTA(data) {
const resultsContainer = document.getElementById('kta-results');
if(!resultsContainer) return;
if (data.length === 0) {
resultsContainer.innerHTML = `
`;
});
resultsContainer.innerHTML = html;
}
function bukaPreviewKTA(atlit) {
document.getElementById('kta-results').classList.add('hidden');
document.getElementById('search-kta').parentElement.classList.add('hidden');
document.getElementById('print-area').classList.remove('hidden');
document.getElementById('print-area').classList.add('flex');
const noAnggota = atlit.id || "KBM-" + Math.floor(Math.random() * 10000);
// Template Kartu (Depan)
const cardHtml = `
`;
document.getElementById('kta-card-preview').innerHTML = cardHtml;
}
function tutupPreviewKTA() {
document.getElementById('print-area').classList.add('hidden');
document.getElementById('print-area').classList.remove('flex');
document.getElementById('kta-results').classList.remove('hidden');
document.getElementById('search-kta').parentElement.classList.remove('hidden');
document.getElementById('search-kta').value = '';
filterKTA();
}
function renderAtlit(container) {
function renderDashboard(container) {
if (!isAdmin) {
container.innerHTML = getEmptyState();
return;
}
let html = `
Mode Online/Edit Aktif
`;
container.innerHTML = html;
}
function renderAdminTable(kategori) {
const adminContent = document.getElementById('admin-content');
const data = currentData[kategori] || [];
let titles = {
'atlit': 'Kelola Data Atlit',
'pelatih': 'Kelola Data Pelatih',
'club': 'Kelola Data Club',
'tempat': 'Kelola Tempat Latihan',
'galeri': 'Kelola Galeri'
};
// Setup Header Tabel
let tableHead = '';
if(kategori === 'atlit') tableHead = 'ID Nama Divisi Club Aksi ';
if(kategori === 'pelatih') tableHead = 'ID Nama Lisensi Aksi ';
if(kategori === 'club') tableHead = 'ID Nama Club Ketua Aksi ';
if(kategori === 'tempat') tableHead = 'ID Nama Venue Jadwal Aksi ';
if(kategori === 'galeri') tableHead = 'ID Judul Gambar Aksi ';
// Setup Isi Tabel
let tableRows = '';
data.forEach((item) => {
let rowContent = '';
let idItem = item.id || '-';
// Escape string untuk dikirim via parameter onClick
let jsonItem = JSON.stringify(item).replace(/'/g, "'").replace(/"/g, """);
if(kategori === 'atlit') rowContent = `${idItem} ${item.nama} ${item.divisi} ${item.club} `;
if(kategori === 'pelatih') rowContent = `${idItem} ${item.nama} ${item.lisensi} `;
if(kategori === 'club') rowContent = `${idItem} ${item.nama} ${item.ketua} `;
if(kategori === 'tempat') rowContent = `${idItem} ${item.nama} ${item.jadwal} `;
if(kategori === 'galeri') rowContent = `${idItem} ${item.judul} 
`;
tableRows += `
${rowContent}
`;
});
if(data.length === 0) {
tableRows = `Belum ada data untuk kategori ini. `;
}
// Render ke Layar Admin
adminContent.innerHTML = `
`;
}
// ============================================================================
// CRUD LOGIC & FORMS (Create, Update, Delete)
// ============================================================================
function openFormModal(mode, category, dataString = null) {
formState = { mode: mode, category: category, editId: null };
let data = null;
document.getElementById('form-modal-title').innerText = mode === 'add' ? 'Tambah Data ' + category.toUpperCase() : 'Edit Data ' + category.toUpperCase();
if (mode === 'edit' && dataString) {
// Parse the stringified JSON passed from button click
const textarea = document.createElement("textarea");
textarea.innerHTML = dataString; // Handle HTML entities
data = JSON.parse(textarea.value);
formState.editId = data.id;
}
const inputsContainer = document.getElementById('form-inputs');
let formHTML = '';
// Generate Input Fields dynamically based on category
if (category === 'atlit') {
formHTML = `
`;
} else if (category === 'pelatih') {
formHTML = `
`;
} else if (category === 'club') {
formHTML = `
`;
} else if (category === 'tempat') {
formHTML = `
`;
} else if (category === 'galeri') {
formHTML = `
`;
}
inputsContainer.innerHTML = formHTML;
document.getElementById('form-modal').style.display = 'flex';
}
function closeFormModal() {
document.getElementById('form-modal').style.display = 'none';
}
async function submitForm() {
const btn = document.getElementById('btn-save-form');
const originalText = btn.innerHTML;
btn.innerHTML = ' Menyimpan...';
btn.disabled = true;
const category = formState.category;
// Mengumpulkan data dari form berdasarkan input IDs
let payloadData = {};
if(formState.mode === 'edit') payloadData.id = formState.editId;
else payloadData.id = category.charAt(0).toUpperCase() + Math.floor(Math.random() * 10000); // Generate simple ID if add
const inputs = document.getElementById('form-inputs').querySelectorAll('input');
inputs.forEach(input => {
const key = input.id.replace('input_', '');
payloadData[key] = input.value;
});
const payload = {
action: formState.mode === 'add' ? 'create' : 'update',
sheetName: category,
data: payloadData
};
await sendDataToGoogleSheets(payload, "Data berhasil disimpan!", () => {
closeFormModal();
renderAdminTable(category); // Refresh table
});
btn.innerHTML = originalText;
btn.disabled = false;
}
function deleteData(category, id, name) {
// Because we can't use standard confirm(), we create a custom interaction using our modal
// but for simplicity in this single-file app, we'll bypass the prompt and execute,
// OR simulate it cleanly.
const btn = document.createElement("button");
btn.innerHTML = "Ya, Hapus!";
btn.className = "bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded mt-4 w-full";
// Show prompt modal
showModal("Konfirmasi Hapus", `Apakah Anda yakin ingin menghapus data: ${name}? Tindakan ini tidak dapat dibatalkan.`, "error");
// Replace the "Tutup" button action temporarily
const modalBtn = document.querySelector('#custom-modal button');
const oldAction = modalBtn.onclick;
modalBtn.innerHTML = "Hapus Sekarang";
modalBtn.className = "bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-6 rounded-lg transition-colors w-full";
modalBtn.onclick = async () => {
modalBtn.innerHTML = "Menghapus...";
modalBtn.disabled = true;
const payload = {
action: 'delete',
sheetName: category,
id: id
};
await sendDataToGoogleSheets(payload, "Data berhasil dihapus!", () => {
closeModal();
// Reset button
modalBtn.innerHTML = "Tutup";
modalBtn.className = "bg-archery-green hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg transition-colors w-full";
modalBtn.onclick = oldAction;
modalBtn.disabled = false;
renderAdminTable(category); // Refresh table
});
};
}
// Fungsi Inti Penghubung API / Simulasi Lokal
async function sendDataToGoogleSheets(payload, successMessage, callback) {
if (!GOOGLE_SHEET_API_URL || GOOGLE_SHEET_API_URL === '') {
// ==========================================
// FALLBACK: Simulasi Lokal (Jika URL belum diisi)
// ==========================================
console.log("Simulating API call:", payload);
// Jeda 800ms seolah-olah mengirim ke internet
await new Promise(r => setTimeout(r, 800));
const cat = payload.sheetName;
if (!currentData[cat]) currentData[cat] = [];
if (payload.action === 'create') {
currentData[cat].unshift(payload.data); // Tambah di atas
} else if (payload.action === 'update') {
const index = currentData[cat].findIndex(item => item.id === payload.data.id);
if (index !== -1) currentData[cat][index] = payload.data;
} else if (payload.action === 'delete') {
currentData[cat] = currentData[cat].filter(item => item.id !== payload.id);
}
showModal("Sukses (Simulasi)", `${successMessage}
Catatan: Data hanya berubah sementara di layar karena URL Google Apps Script belum dimasukkan di kode sumber.`, "info"); if(callback) callback(); return; } // ========================================== // REAL API FETCH: Kirim ke Google Apps Script // ========================================== try { const response = await fetch(GOOGLE_SHEET_API_URL, { method: 'POST', mode: 'no-cors', // Penting untuk mencegah blokir CORS browser ke GAS headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) }); // Catatan: mode 'no-cors' membuat response type jadi 'opaque' (tidak bisa baca status code aslinya). // Sehingga kita asumsikan sukses jika fetch berhasil dijalankan (tidak melempar error). // Karena kita pakai opaque response, kita simulasikan update data lokal agar UI terupdate // Di aplikasi profesional, Anda harus memanggil fungsi fetch/GET data ulang di sini. const cat = payload.sheetName; if (payload.action === 'create') currentData[cat].unshift(payload.data); else if (payload.action === 'update') { const idx = currentData[cat].findIndex(item => item.id === payload.data.id); if(idx!==-1) currentData[cat][idx] = payload.data; } else if (payload.action === 'delete') { currentData[cat] = currentData[cat].filter(item => item.id !== payload.id); } showModal("Berhasil!", successMessage, "info"); if(callback) callback(); } catch (error) { console.error('API Error:', error); showModal("Terjadi Kesalahan", "Gagal menghubungi server. Periksa koneksi internet atau konfigurasi URL Script Anda.", "error"); } } // Initialize App window.onload = () => {
Cetak Kartu Tanda Anggota
Cari nama atlit untuk menampilkan dan mencetak KTA Resmi PERPANI Kebumen.
Pratinjau Kartu (Siap Cetak)
Atlit tidak ditemukan.
`;
return;
}
let html = '';
data.forEach(atlit => {
html += `
${atlit.nama}
${atlit.club}
PERPANI
KAB. KEBUMEN
Kartu Tanda Anggota
${noAnggota}
${atlit.nama}
| Klub | : | ${atlit.club} |
| Divisi | : | ${atlit.divisi} |
| Status | : | Atlit Aktif |
Berlaku selama menjadi anggota aktif PERPANI Kab. Kebumen. Menemukan kartu ini? Hubungi Pengurus.
Ringkasan Sistem
Gunakan menu di samping untuk mengelola basis data secara langsung.
Sistem CRUD Aktif (Create, Read, Update, Delete)
Perintah tambah, edit, dan hapus yang Anda lakukan dari halaman ini akan langsung dikirimkan ke Google Sheets menggunakan metode API POST. Pastikan koneksi internet stabil.
${titles[kategori]}
Catatan: Data hanya berubah sementara di layar karena URL Google Apps Script belum dimasukkan di kode sumber.`, "info"); if(callback) callback(); return; } // ========================================== // REAL API FETCH: Kirim ke Google Apps Script // ========================================== try { const response = await fetch(GOOGLE_SHEET_API_URL, { method: 'POST', mode: 'no-cors', // Penting untuk mencegah blokir CORS browser ke GAS headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) }); // Catatan: mode 'no-cors' membuat response type jadi 'opaque' (tidak bisa baca status code aslinya). // Sehingga kita asumsikan sukses jika fetch berhasil dijalankan (tidak melempar error). // Karena kita pakai opaque response, kita simulasikan update data lokal agar UI terupdate // Di aplikasi profesional, Anda harus memanggil fungsi fetch/GET data ulang di sini. const cat = payload.sheetName; if (payload.action === 'create') currentData[cat].unshift(payload.data); else if (payload.action === 'update') { const idx = currentData[cat].findIndex(item => item.id === payload.data.id); if(idx!==-1) currentData[cat][idx] = payload.data; } else if (payload.action === 'delete') { currentData[cat] = currentData[cat].filter(item => item.id !== payload.id); } showModal("Berhasil!", successMessage, "info"); if(callback) callback(); } catch (error) { console.error('API Error:', error); showModal("Terjadi Kesalahan", "Gagal menghubungi server. Periksa koneksi internet atau konfigurasi URL Script Anda.", "error"); } } // Initialize App window.onload = () => {