Memeriksa Koneksi...
Dokumentasi Klinis Apotek & Integrasi WhatsApp
Akun Demo Default:
Username: admin | Password: 123
Pratinjau & sesuaikan pesan sebelum dikirim
Format otomatis dikonversi ke format internasional (+62).
Solusi koneksi terputus: Pastikan Web App diset ke "Execute as: Me" dan "Who has access: Anyone".
var SHEET_HEADERS = {
Pengkajian: [
'id','noResep','tanggal','pasien','namaPasien','noHp','umur','bbTb',
'dokter','namaDokter','diagnosa','riwayatAlergi','kehamilan',
'penyakitPenyerta','checklist','kesimpulan','statusPengkajian','catatan'
],
PIO: [
'id','tanggal','pasien','namaPasien','penanya','namaPenanya','noHp',
'jenisPelayanan','kategoriPenanya','materi','catatan'
],
Konseling: [
'id','tanggal','pasien','namaPasien','noHp','diagnosis','obat','teachBack','catatan'
],
CekKesehatan: [
'id','tanggal','pasien','namaPasien','noHp','jenisKelamin','sistol','diastol','gulaDarah',
'jenisGula','kolesterol','asamUrat','statusTekananDarah','statusGulaDarah',
'statusKolesterol','statusAsamUrat','statusKeseluruhan','catatan'
],
Pasien: [
'id','nama','noHp','umur','bbTb','riwayatAlergi'
]
};
/**
* JALANKAN SEKALI SECARA MANUAL dari editor Apps Script.
* Fungsi ini menyimpan ID spreadsheet dan membuat semua tab + kolom.
*/
function setupDatabase() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (!ss) {
throw new Error('Spreadsheet aktif tidak ditemukan. Buka Apps Script dari menu Ekstensi di Google Sheet yang akan dipakai.');
}
PropertiesService.getScriptProperties().setProperty('APOCARE_SPREADSHEET_ID', ss.getId());
ensureDatabaseStructure(ss);
SpreadsheetApp.flush();
return 'Database ApoCare Pro siap. Semua sheet dan kolom sudah dibuat.';
}
function getDatabase() {
var spreadsheetId = PropertiesService.getScriptProperties().getProperty('APOCARE_SPREADSHEET_ID');
if (spreadsheetId) {
return SpreadsheetApp.openById(spreadsheetId);
}
// Fallback hanya untuk eksekusi langsung dari spreadsheet terikat.
var active = SpreadsheetApp.getActiveSpreadsheet();
if (active) {
PropertiesService.getScriptProperties().setProperty('APOCARE_SPREADSHEET_ID', active.getId());
return active;
}
throw new Error('Database belum diinisialisasi. Jalankan fungsi setupDatabase() satu kali dari editor Apps Script.');
}
function ensureDatabaseStructure(ss) {
for (var sheetName in SHEET_HEADERS) {
if (SHEET_HEADERS.hasOwnProperty(sheetName)) {
ensureSheetHeaders(ss, sheetName, SHEET_HEADERS[sheetName]);
}
}
}
function ensureSheetHeaders(ss, sheetName, requiredHeaders) {
var sheet = ss.getSheetByName(sheetName);
if (!sheet) {
sheet = ss.insertSheet(sheetName);
}
var existingHeaders = [];
var lastColumn = sheet.getLastColumn();
var lastRow = sheet.getLastRow();
if (lastRow >= 1 && lastColumn >= 1) {
var rawHeaders = sheet.getRange(1, 1, 1, lastColumn).getValues()[0];
for (var i = 0; i < rawHeaders.length; i++) {
var value = String(rawHeaders[i] || '').trim();
if (value) existingHeaders.push(value);
}
}
// Sheet benar-benar kosong: buat semua header sekaligus.
if (existingHeaders.length === 0) {
sheet.clearContents();
sheet.getRange(1, 1, 1, requiredHeaders.length).setValues([requiredHeaders]);
} else {
// Sheet lama: tambahkan kolom yang belum tersedia tanpa menghapus data lama.
for (var h = 0; h < requiredHeaders.length; h++) {
var header = requiredHeaders[h];
if (existingHeaders.indexOf(header) === -1) {
existingHeaders.push(header);
sheet.getRange(1, existingHeaders.length).setValue(header);
}
}
}
// Format sederhana agar header mudah dikenali.
var finalLastColumn = sheet.getLastColumn();
if (finalLastColumn > 0) {
sheet.getRange(1, 1, 1, finalLastColumn).setFontWeight('bold');
sheet.setFrozenRows(1);
}
return sheet;
}
function doGet() {
try {
var ss = getDatabase();
ensureDatabaseStructure(ss);
return ContentService
.createTextOutput('ApoCare Pro Backend Active - Database Ready')
.setMimeType(ContentService.MimeType.TEXT);
} catch (err) {
return ContentService
.createTextOutput('ApoCare Pro Backend Error: ' + String(err))
.setMimeType(ContentService.MimeType.TEXT);
}
}
function doPost(e) {
try {
if (!e || !e.postData || !e.postData.contents) {
return responseJSON({ status: 'error', message: 'Request body kosong' });
}
var contents = JSON.parse(e.postData.contents);
var action = contents.action || '';
var data = contents.data || {};
var ss = getDatabase();
ensureDatabaseStructure(ss);
if (action === 'getAllData') {
return responseJSON({
status: 'success',
data: {
pengkajian: getSheetData(ss, 'Pengkajian'),
pio: getSheetData(ss, 'PIO'),
konseling: getSheetData(ss, 'Konseling'),
cekKesehatan: getSheetData(ss, 'CekKesehatan'),
pasien: getSheetData(ss, 'Pasien')
}
});
}
if (action === 'savePengkajian') {
saveToSheet(ss, 'Pengkajian', data);
return responseJSON({ status: 'success' });
}
if (action === 'deletePengkajian') {
deleteFromSheet(ss, 'Pengkajian', data.id);
return responseJSON({ status: 'success' });
}
if (action === 'savePIO') {
saveToSheet(ss, 'PIO', data);
return responseJSON({ status: 'success' });
}
if (action === 'deletePIO') {
deleteFromSheet(ss, 'PIO', data.id);
return responseJSON({ status: 'success' });
}
if (action === 'saveKonseling') {
saveToSheet(ss, 'Konseling', data);
return responseJSON({ status: 'success' });
}
if (action === 'deleteKonseling') {
deleteFromSheet(ss, 'Konseling', data.id);
return responseJSON({ status: 'success' });
}
if (action === 'saveCekKesehatan') {
saveToSheet(ss, 'CekKesehatan', data);
return responseJSON({ status: 'success' });
}
if (action === 'deleteCekKesehatan') {
deleteFromSheet(ss, 'CekKesehatan', data.id);
return responseJSON({ status: 'success' });
}
if (action === 'savePasien') {
saveToSheet(ss, 'Pasien', data);
return responseJSON({ status: 'success' });
}
return responseJSON({ status: 'error', message: 'Action tidak dikenal: ' + action });
} catch (err) {
return responseJSON({ status: 'error', message: String(err) });
}
}
function responseJSON(obj) {
return ContentService
.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}
function getSheetData(ss, sheetName) {
var requiredHeaders = SHEET_HEADERS[sheetName] || [];
var sheet = ensureSheetHeaders(ss, sheetName, requiredHeaders);
if (sheet.getLastRow() < 2) {
return [];
}
var lastColumn = sheet.getLastColumn();
var data = sheet.getRange(1, 1, sheet.getLastRow(), lastColumn).getValues();
var headers = data[0];
var result = [];
for (var i = 1; i < data.length; i++) {
var row = data[i];
var obj = {};
var hasValue = false;
for (var j = 0; j < headers.length; j++) {
var header = String(headers[j] || '').trim();
if (!header) continue;
var val = row[j];
if (val !== '' && val !== null) hasValue = true;
if (header === 'checklist' || header === 'materi') {
if (typeof val === 'string' && val !== '') {
try {
val = JSON.parse(val);
} catch (parseErr) {
// Nilai non-JSON tetap dipakai apa adanya.
}
}
}
obj[header] = val;
}
if (hasValue) result.push(obj);
}
return result;
}
function saveToSheet(ss, sheetName, item) {
if (!item || typeof item !== 'object') {
throw new Error('Data yang akan disimpan tidak valid');
}
var itemKeys = Object.keys(item);
if (itemKeys.length === 0) {
throw new Error('Data kosong');
}
var baseHeaders = SHEET_HEADERS[sheetName] || [];
var requiredHeaders = baseHeaders.slice();
// Jika aplikasi mengirim field baru, tambahkan otomatis sebagai kolom baru.
for (var k = 0; k < itemKeys.length; k++) {
if (requiredHeaders.indexOf(itemKeys[k]) === -1) {
requiredHeaders.push(itemKeys[k]);
}
}
var sheet = ensureSheetHeaders(ss, sheetName, requiredHeaders);
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
for (var h = 0; h < headers.length; h++) {
headers[h] = String(headers[h] || '').trim();
}
var existingRowIndex = -1;
var idColIndex = headers.indexOf('id');
if (idColIndex !== -1 && sheet.getLastRow() > 1 && item.id !== undefined) {
var idValues = sheet.getRange(2, idColIndex + 1, sheet.getLastRow() - 1, 1).getValues();
for (var r = 0; r < idValues.length; r++) {
if (String(idValues[r][0]) === String(item.id)) {
existingRowIndex = r + 2;
break;
}
}
}
var rowData = [];
for (var c = 0; c < headers.length; c++) {
var headerName = headers[c];
var value = item[headerName] !== undefined ? item[headerName] : '';
if (value !== null && typeof value === 'object') {
value = JSON.stringify(value);
}
rowData.push(value);
}
if (existingRowIndex !== -1) {
sheet.getRange(existingRowIndex, 1, 1, rowData.length).setValues([rowData]);
} else {
sheet.appendRow(rowData);
}
}
function deleteFromSheet(ss, sheetName, itemId) {
var requiredHeaders = SHEET_HEADERS[sheetName] || [];
var sheet = ensureSheetHeaders(ss, sheetName, requiredHeaders);
if (sheet.getLastRow() < 2) {
return;
}
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var idColIndex = -1;
for (var i = 0; i < headers.length; i++) {
if (String(headers[i]).trim() === 'id') {
idColIndex = i;
break;
}
}
if (idColIndex === -1) return;
var idValues = sheet.getRange(2, idColIndex + 1, sheet.getLastRow() - 1, 1).getValues();
for (var r = 0; r < idValues.length; r++) {
if (String(idValues[r][0]) === String(itemId)) {
sheet.deleteRow(r + 2);
return;
}
}
}
Apakah Anda yakin ingin menghapus data ini dari database?
Arahkan kamera ke barcode/QR resep digital