https://codestudio80.blogspot.com/2026/01/data-entry-using-html-form-with-file.html
PROMPT
Buatkan aplikasi Single Page Application (SPA) berbasis Google Apps Script
dengan backend menggunakan Code.gs dan frontend index.html.
Spesifikasi Umum:
- Platform: Google Apps Script Web App
- Arsitektur: SPA (tanpa reload halaman)
- Frontend dipanggil melalui function doGet(e)
- Komunikasi frontend-backend menggunakan google.script.run
- UI sederhana dan responsif (gunakan Bulma CSS)
- Data disimpan di Google Spreadsheet
- Upload file disimpan ke Google Drive
Backend (Code.gs):
1. Gunakan function doGet(e) untuk memanggil file index.html
2. Tambahkan konfigurasi:
- SPREADSHEET_ID
- FOLDER_ID
- SHEET_NAME
3. Buat function initializeSheet():
- Mengecek apakah sheet sudah ada
- Jika belum, buat sheet
- Jika sheet kosong, buat header kolom:
ID
Timestamp
Client Name
Email
Password
DOB
Gender
Notes
Agree To Terms
File URL
- Freeze baris header
4. Buat function submitForm(data):
- Menerima data dari frontend
- Generate UUID sebagai ID
- Timestamp otomatis
- Upload file (Base64) ke Google Drive
- Simpan URL file ke Spreadsheet
- Kembalikan response JSON {status, message}
5. Buat function uploadFile_(file):
- Decode Base64
- Simpan file ke Drive folder
- Return URL file
Frontend (index.html):
1. SPA berbasis HTML + JavaScript
2. Gunakan Bulma CSS
3. Buat form dengan field:
- Client Name (required)
- Email (required, type email)
- Password
- Date of Birth
- Gender (radio)
- Notes (textarea)
- Agree to Terms (checkbox)
- File upload
4. Tidak menggunakan fetch API
5. Submit data menggunakan google.script.run.submitForm()
6. Tampilkan notifikasi:
- Loading
- Success
- Error
7. Setelah submit berhasil:
- Form reset
- Tidak reload halaman
Tambahan:
- Kode harus rapi, modular, dan mudah dikembangkan
- Siap dikembangkan menjadi CRUD (Read, Update, Delete)
- Sertakan komentar penjelasan pada setiap bagian kode
Output yang diharapkan:
- File Code.gs lengkap
- File index.html lengkap
- Siap langsung dideploy sebagai Web App
Code.gs
/********** KONFIGURASI **********/
const SPREADSHEET_ID = '1tI5_Q6Yu06OG_sJgFkodkjB37J5tNVN6hKcacqObtLE';
const FOLDER_ID = '1IHFhDlDi_hIuteGZ7Kx07VgNfKK4Hly4';
const SHEET_NAME = 'Data';
/********** ENTRY POINT SPA **********/
function doGet(e) {
initializeSheet();
return HtmlService.createHtmlOutputFromFile('index')
.setTitle('SISKO DB | Smart School System')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
.addMetaTag('viewport', 'width=device-width, initial-scale=1.0')
.setFaviconUrl('https://cdn-icons-png.flaticon.com/512/201/201614.png');
}
/********** INITIALIZE SHEET **********/
function initializeSheet() {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = ss.getSheetByName(SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(SHEET_NAME);
}
if (sheet.getLastRow() === 0) {
sheet.appendRow([
'ID',
'Timestamp',
'Client Name',
'Email',
'Password',
'DOB',
'Gender',
'Notes',
'Agree To Terms',
'File URL'
]);
sheet.setFrozenRows(1);
}
}
/********** SUBMIT FORM (SPA) **********/
function submitForm(data) {
try {
initializeSheet();
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const sheet = ss.getSheetByName(SHEET_NAME);
let fileUrl = '';
if (data.fileData) {
fileUrl = uploadFile_(data.fileData);
}
sheet.appendRow([
Utilities.getUuid(),
new Date(),
data['Client Name'],
data['Email'],
data['Password'],
data['DOB'],
data['Gender'],
data['Notes'],
data['Agree To Terms'] || '',
fileUrl
]);
return {
status: 'success',
message: 'Data berhasil disimpan'
};
} catch (err) {
return {
status: 'error',
message: err.message
};
}
}
/********** UPLOAD FILE **********/
function uploadFile_(file) {
const folder = DriveApp.getFolderById(FOLDER_ID);
const blob = Utilities.newBlob(
Utilities.base64Decode(file.data),
file.mimeType,
file.fileName
);
const createdFile = folder.createFile(blob);
return createdFile.getUrl();
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"
>
<title>SISKO DB</title>
</head>
<body>
<section class="hero is-primary">
<div class="hero-body">
<h1 class="title">Data Entry Form (SPA)</h1>
<p class="subtitle">Google Apps Script + Spreadsheet</p>
</div>
</section>
<section class="section">
<form id="form" class="box">
<input class="input mb-3" name="Client Name" placeholder="Client Name" required>
<input class="input mb-3" name="Email" type="email" placeholder="Email" required>
<input class="input mb-3" name="Password" type="password" placeholder="Password" required>
<input class="input mb-3" name="DOB" type="date">
<div class="mb-3">
<label class="radio">
<input type="radio" name="Gender" value="Male"> Male
</label>
<label class="radio ml-3">
<input type="radio" name="Gender" value="Female"> Female
</label>
</div>
<textarea class="textarea mb-3" name="Notes" placeholder="Notes"></textarea>
<label class="checkbox mb-3">
<input type="checkbox" name="Agree To Terms" value="Yes"> Agree to terms
</label>
<div class="file mb-4">
<label class="file-label">
<input class="file-input" type="file" id="fileInput">
<span class="file-cta">
<span class="file-label">Choose file</span>
</span>
</label>
</div>
<button class="button is-primary is-fullwidth" type="submit">
Submit
</button>
</form>
<div id="message" class="notification is-hidden mt-4"></div>
</section>
<script>
const form = document.getElementById('form');
const fileInput = document.getElementById('fileInput');
const message = document.getElementById('message');
function readFile(file) {
return new Promise(resolve => {
const fr = new FileReader();
fr.onload = e => {
const data = e.target.result.split(',');
resolve({
fileName: file.name,
mimeType: data[0].match(/:(.*?);/)[1],
data: data[1]
});
};
fr.readAsDataURL(file);
});
}
form.addEventListener('submit', async e => {
e.preventDefault();
message.className = 'notification is-info';
message.textContent = 'Submitting...';
message.classList.remove('is-hidden');
const fd = new FormData(form);
const data = Object.fromEntries(fd.entries());
if (fileInput.files.length > 0) {
data.fileData = await readFile(fileInput.files[0]);
}
google.script.run
.withSuccessHandler(res => {
message.className =
res.status === 'success'
? 'notification is-success'
: 'notification is-danger';
message.textContent = res.message;
form.reset();
})
.withFailureHandler(err => {
message.className = 'notification is-danger';
message.textContent = err.message;
})
.submitForm(data);
});
</script>
</body>
</html>

Tidak ada komentar:
Posting Komentar