Website Surat Masuk, Surat Keluar, Approve, Tracking Surat Menggunakan Google Apps Script
1. Copy Spreadsheet (Klik disini)
2. Pada Spreadsheet yang telah di copy di atas memilik beberapa sheet diantaranya :
- sheet login
- sheet Form
- sheet suratkeluar
- sheet suratmasuk
- sheet tracking
3. Buatlah folder baru pada Google Drive untuk menyimpan berkas.
(Pastikan status folder sharing view)
4. Buatlah lembar kerja Apps Script dengan cara klik menu Ekstensi/Extensions lalu pilih Apps Script.
5. Pada lembar kerja Apps Script terdapat beberapa file dan layanan API (free) default yaitu :
File
- Code.gs
- header.html
- login.html
- register.html
- main.html
- suratkeluar.html
- suratmasuk.html
- trackingsurat.html
- visit.html
- csstable.html
- cssmain.html
- cssloading.html
Layanan
- Drive API (free)
- Sheet API (free)
6. Copy dan pastekan script di bawah ini ke Code.gs
Masukkan Password Untuk Melihat Script (Password ada di dalam video)
//Source Code by www.javabitpro.com
let MySheets = SpreadsheetApp.getActiveSpreadsheet();
let LoginSheet = MySheets.getSheetByName("login");
let FormSheet = MySheets.getSheetByName("Form");
let EmpSheet = MySheets.getSheetByName("suratkeluar");
function doGet(e) {
var output = HtmlService.createTemplateFromFile('login');
var sess = getSession();
if (sess.loggedIn) {
let page = e.parameter.page;
if (page == null) page = "main";
var output = HtmlService.createTemplateFromFile(page);
}
return output.evaluate();
}
function includeHeader() {
return HtmlService.createTemplateFromFile("header.html").evaluate().getContent();
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function myURL() {
return ScriptApp.getService().getUrl();
}
function setSession(session) {
var sId = Session.getTemporaryActiveUserKey();
var uProp = PropertiesService.getUserProperties();
uProp.setProperty(sId, JSON.stringify(session));
}
function getSession() {
var sId = Session.getTemporaryActiveUserKey();
var uProp = PropertiesService.getUserProperties();
var sData = uProp.getProperty(sId);
return sData ? JSON.parse(sData) : { loggedIn: false };
}
function loginUser(pUID, pPassword) {
if (loginCheck(pUID, pPassword)) {
var sess = getSession();
sess.loggedIn = true;
sess.userId = pUID; // Simpan ID pengguna ke sesi
setSession(sess);
return 'success';
} else {
return 'failure';
}
}
function getUserId() {
var sess = getSession();
return sess.userId ? sess.userId : '';
}
function logoutUser() {
var sess = getSession();
sess.loggedIn = false;
setSession(sess);
}
function loginCheck(pUID, pPassword) {
let LoginPass = false;
let ReturnData = LoginSheet.getRange("A:A").createTextFinder(pUID).matchEntireCell(true).findAll();
ReturnData.forEach(function (range) {
let StartRow = range.getRow();
let TmpPass = LoginSheet.getRange(StartRow, 2).getValue();
if (TmpPass == pPassword) {
LoginPass = true;
}
});
return LoginPass;
}
//Source Code by www.javabitpro.com
function getInputsList() {
let table = FormSheet.getRange("A2:C").getValues().filter(r => r.every(Boolean));
// Ambil username yang sedang login
let sess = getSession();
let loggedInUser = sess.userId;
// Ambil daftar username dari sheet login
let allUsers = LoginSheet.getRange("A2:A").getValues().flat().filter(Boolean);
// Filter untuk mengecualikan username yang sama dengan username yang sedang login
let otherUsers = allUsers.filter(user => user !== loggedInUser);
return { table, loggedInUser, otherUsers };
}
//Source Code by www.javabitpro.com
function getSubmit(jsonData, index) {
let data = JSON.parse(jsonData);
let fileUrl = '';
// Handle file upload if present
if (data.file) {
let blob = Utilities.newBlob(Utilities.base64Decode(data.file), 'application/octet-stream', 'uploaded_file');
let folder = DriveApp.getFolderById("ID_FOLDER"); // Replace with your folder ID
let uploadedFile = folder.createFile(blob);
fileUrl = uploadedFile.getUrl();
}
// Prepare data for sheet
let rowData = [];
for (let key in data) {
if (data.hasOwnProperty(key) && key !== 'file') {
rowData.push(data[key]);
}
}
rowData.push(fileUrl);
if (index === 0) {
// Add new data
EmpSheet.appendRow(rowData);
} else {
// Update existing data
let cols = EmpSheet.getLastColumn();
EmpSheet.getRange(index, 1, 1, cols).setValues([rowData]);
}
}
function getASOpenById(id) {
let cols = EmpSheet.getLastColumn();
let colnm = FormSheet.getRange("A2:C").getValues().filter(r => r.every(Boolean)); // Mendapatkan nama kolom dan tipe field
// Cari baris berdasarkan ID
let rowToUpdate = EmpSheet.getRange(2, 1, EmpSheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToUpdate) {
let rowIndex = rowToUpdate.getRow(); // Ambil indeks baris yang akan di-update
let data = EmpSheet.getRange(rowIndex, 1, 1, cols).getValues();
// Loop melalui data dan cek tipe file atau tanggal
data = data.map(function(row) {
return row.map(function(cell, idx) {
let colType = colnm[idx][2]; // Ambil tipe field (misal: 'file')
// Jika field bertipe file, kembalikan URL file
if (colType === 'file' && cell) {
return `<a href="${cell}" target="_blank">Lihat File</a>`; // Tampilkan link ke file
}
// Jika field berupa tanggal, konversi ke format 'yyyy-MM-dd'
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}
return cell; // Kembalikan nilai lain apa adanya
});
});
return [colnm, data, rowIndex]; // Kembalikan nama kolom, data, dan indeks baris
}
return [colnm, [], 0]; // Jika ID tidak ditemukan, kembalikan data kosong
}
function deleteRecord(id) {
// Cari baris berdasarkan ID (asumsi ID ada di kolom pertama)
let rowToDelete = EmpSheet.getRange(2, 1, EmpSheet.getLastRow()-1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToDelete) {
let rowIndex = rowToDelete.getRow(); // Ambil indeks baris
EmpSheet.deleteRow(rowIndex); // Hapus baris
return "success";
}
return "failure";
}
function getFilteredSuratKeluar() {
var sess = getSession();
var userId = sess.userId; // Ambil ID pengguna dari sesi
if (!userId) {
return [[], []]; // Jika tidak ada ID pengguna, kembalikan array kosong
}
let cols = EmpSheet.getLastColumn();
let rows = EmpSheet.getLastRow() - 1;
let tHead = EmpSheet.getRange(1, 1, 1, cols).getValues();
let allData = EmpSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan ID pengguna
let filteredData = allData.filter(row => row[1] === userId);
// Konversi setiap nilai tanggal menjadi string
filteredData = filteredData.map(function(row) {
return row.map(function(cell) {
// Jika cell adalah URL, biarkan seperti itu
if (typeof cell === 'string' && (cell.startsWith('http://') || cell.startsWith('https://'))) {
return cell; // URL akan ditangani di sisi klien
}
// Jika cell adalah tanggal, konversi ke format string (misalnya: dd/MM/yyyy)
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
}
return cell; // Kembalikan nilai lain apa adanya
});
});
return [tHead, filteredData];
}
function getASOpen(i) {
let cols = EmpSheet.getLastColumn();
let colnm = FormSheet.getRange("A2:C").getValues().filter(r => r.every(Boolean));
let data = EmpSheet.getRange(i, 1, 1, cols).getValues();
// Loop melalui data dan cek jika ada nilai yang merupakan objek Date
data = data.map(function(row) {
return row.map(function(cell) {
// Jika cell adalah objek Date, konversi ke format string 'yyyy-MM-dd'
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}
return cell;
});
});
return [colnm, data];
}
function OpenPage(PageName) {
return HtmlService.createHtmlOutputFromFile(PageName).getContent();
}
function UserRegister(pUID, pPassword, pName) {
let RetMsg = '';
let ReturnData = LoginSheet.getRange("A:A").createTextFinder(pUID).matchEntireCell(true).findAll();
let StartRow = 0;
ReturnData.forEach(function (range) {
StartRow = range.getRow();
});
if (StartRow > 0) {
RetMsg = 'danger, User Already Exists';
} else {
LoginSheet.appendRow([pUID, pPassword, pName]);
RetMsg = 'success, User Successfully Registered';
}
return RetMsg;
}
// SURAT MASUK
function getFilteredSuratMasuk() {
var sess = getSession();
var userId = sess.userId; // Ambil ID pengguna dari sesi
if (!userId) {
return [[], []]; // Jika tidak ada ID pengguna, kembalikan array kosong
}
let cols = EmpSheet.getLastColumn();
let rows = EmpSheet.getLastRow() - 1;
let tHead = EmpSheet.getRange(1, 1, 1, cols).getValues();
let allData = EmpSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan kolom 3 (misalnya kolom yang berisi ID pengguna)
let filteredData = allData.filter(row => row[2] === userId);
// Ubah URL menjadi hyperlink dan format data lainnya jika perlu
filteredData = filteredData.map(function(row) {
return row.map(function(cell, index) {
if (index === 5 && typeof cell === "string" && cell.startsWith("http")) { // Misalkan kolom 6 berisi URL
return `<a href="${cell}" target="_blank">Lihat File</a>`;
}
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
}
return cell;
});
});
return [tHead, filteredData];
}
function submitApproval(id, approvalDate, status, rejectionReason) {
let sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("suratmasuk");
let rowToUpdate = sheet.getRange(2, 1, sheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToUpdate) {
let rowIndex = rowToUpdate.getRow();
let currentData = sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn()).getValues()[0];
let newData = [...currentData];
newData[4] = approvalDate; // Assuming date goes into the 5th column
newData[5] = status; // Assuming status goes into the 6th column
newData[6] = rejectionReason; // Assuming rejection reason goes into the 7th column
sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn()).setValues([newData]);
return "success";
}
return "failure";
}
function getFilteredSuratMasuk() {
var sess = getSession();
var userId = sess.userId;
if (!userId) {
return [[], []];
}
let cols = EmpSheet.getLastColumn();
let rows = EmpSheet.getLastRow() - 1;
let tHead = EmpSheet.getRange(1, 1, 1, cols).getValues();
let allData = EmpSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan kolom yang berisi ID pengguna
let filteredData = allData.filter(row => row[2] === userId);
// Ubah URL menjadi hyperlink dan format data lainnya jika perlu
filteredData = filteredData.map(function(row) {
return row.map(function(cell, index) {
if (index === 5 && typeof cell === "string" && cell.startsWith("http")) {
return `<a href="${cell}" target="_blank">Lihat File</a>`;
}
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
}
return cell;
});
});
return [tHead, filteredData];
}
function getSuratKeluarById(id) {
let rowToUpdate = EmpSheet.getRange(2, 1, EmpSheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToUpdate) {
let rowIndex = rowToUpdate.getRow();
let data = EmpSheet.getRange(rowIndex, 1, 1, EmpSheet.getLastColumn()).getValues();
return data[0];
}
return [];
}
function approveData(id, approvalStatus, date, note) {
var EmpSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('suratkeluar');
var MasukSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('suratmasuk');
// Cari baris di sheet suratkeluar berdasarkan ID
var rowToApprove = EmpSheet.getRange(2, 1, EmpSheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToApprove) {
var rowIndex = rowToApprove.getRow();
var rowData = EmpSheet.getRange(rowIndex, 1, 1, EmpSheet.getLastColumn()).getValues()[0];
// Tambahkan tanggal, status, dan keterangan jika ditolak
rowData.push(date); // Tambahkan tanggal
rowData.push(approvalStatus); // Tambahkan status approve/reject
rowData.push(approvalStatus === 'rejected' ? note : ''); // Tambahkan keterangan jika reject
// Salin data ke sheet suratmasuk
MasukSheet.appendRow(rowData);
// Hapus baris dari suratkeluar
EmpSheet.deleteRow(rowIndex);
return "success";
}
return "failure";
}
//Tracking
function getFilteredTracking() {
var sess = getSession();
var userId = sess.userId;
if (!userId) {
return [[], []]; // Jika tidak ada ID pengguna, kembalikan array kosong
}
let TrackingSheet = MySheets.getSheetByName("tracking");
let cols = TrackingSheet.getLastColumn();
let rows = TrackingSheet.getLastRow() - 1;
let tHead = TrackingSheet.getRange(1, 1, 1, cols).getValues();
let allData = TrackingSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan salah satu dari dua kondisi
let filteredData = allData.filter(row => row[1] === userId || row[2] === userId);
// Konversi setiap nilai menjadi string dan jika URL, buat hyperlink
filteredData = filteredData.map(function(row) {
return row.map(function(cell, index) {
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
} else if (typeof cell === 'string' && cell.startsWith('http')) {
return `<a href="${cell}" target="_blank">Lihat File</a>`;
} else if (index === 3) { // Misalnya, kolom ke-4 berisi status
switch (cell) {
case 'Approved':
return `<span style="color: green;"><i class="fa fa-check"></i> Approved</span>`;
case 'Reject':
return `<span style="color: red;"><i class="fa fa-times"></i> Reject</span>`;
default:
return cell;
}
}
return cell;
});
});
return [tHead, filteredData];
}
//Main
function getSuratKeluarCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratKeluar: 0 }; // Return zero if no user ID is found
}
var SuratKeluarSheet = MySheets.getSheetByName("tracking");
if (!SuratKeluarSheet) {
return { totalSuratKeluar: 0 }; // Return zero if the sheet is missing
}
let cols = SuratKeluarSheet.getLastColumn();
let rows = SuratKeluarSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratKeluar: 0 }; // Return zero if the sheet has no data
}
let allData = SuratKeluarSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId);
// Return the count of filtered data
return { totalSuratKeluar: filteredData.length };
}
function getSuratKeluarPendingCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratKeluarPending: 0 }; // Return zero if no user ID is found
}
var SuratKeluarPendingSheet = MySheets.getSheetByName("suratkeluar");
if (!SuratKeluarPendingSheet) {
return { totalSuratKeluarPending: 0 }; // Return zero if the sheet is missing
}
let cols = SuratKeluarPendingSheet.getLastColumn();
let rows = SuratKeluarPendingSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratKeluarPending: 0 }; // Return zero if the sheet has no data
}
let allData = SuratKeluarPendingSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId);
// Return the count of filtered data
return { totalSuratKeluarPending: filteredData.length };
}
function getSuratKeluarSuksesCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratKeluarSukses: 0 }; // Return zero if no user ID is found
}
var SuratKeluarSuksesSheet = MySheets.getSheetByName("suratmasuk");
if (!SuratKeluarSuksesSheet) {
return { totalSuratKeluarSukses: 0 }; // Return zero if the sheet is missing
}
let cols = SuratKeluarSuksesSheet.getLastColumn();
let rows = SuratKeluarSuksesSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratKeluarSukses: 0 }; // Return zero if the sheet has no data
}
let allData = SuratKeluarSuksesSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId);
// Return the count of filtered data
return { totalSuratKeluarSukses: filteredData.length };
}
function getSuratMasukBelumApproveCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratMasukBelumApprove: 0 }; // Return zero if no user ID is found
}
var SuratMasukBelumApproveSheet = MySheets.getSheetByName("suratkeluar");
if (!SuratMasukBelumApproveSheet) {
return { totalSuratMasukBelumApprove: 0 }; // Return zero if the sheet is missing
}
let cols = SuratMasukBelumApproveSheet.getLastColumn();
let rows = SuratMasukBelumApproveSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratMasukBelumApprove: 0 }; // Return zero if the sheet has no data
}
let allData = SuratMasukBelumApproveSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[2] === userId);
// Return the count of filtered data
return { totalSuratMasukBelumApprove: filteredData.length };
}
function getSuratMasukSudahApproveCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratMasukSudahApprove: 0 }; // Return zero if no user ID is found
}
var SuratMasukSudahApproveSheet = MySheets.getSheetByName("suratmasuk");
if (!SuratMasukSudahApproveSheet) {
return { totalSuratMasukSudahApprove: 0 }; // Return zero if the sheet is missing
}
let cols = SuratMasukSudahApproveSheet.getLastColumn();
let rows = SuratMasukSudahApproveSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratMasukSudahApprove: 0 }; // Return zero if the sheet has no data
}
let allData = SuratMasukSudahApproveSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[2] === userId);
// Return the count of filtered data
return { totalSuratMasukSudahApprove: filteredData.length };
}
function getSuratMasukCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratMasuk: 0 }; // Return zero if no user ID is found
}
var SuratMasukSheet = MySheets.getSheetByName("tracking");
if (!SuratMasukSheet) {
return { totalSuratMasuk: 0 }; // Return zero if the sheet is missing
}
let cols = SuratMasukSheet.getLastColumn();
let rows = SuratMasukSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratMasuk: 0 }; // Return zero if the sheet has no data
}
let allData = SuratMasukSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[2] === userId);
// Return the count of filtered data
return { totalSuratMasuk: filteredData.length };
}
function getTrackingCounts() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSurat: 0 }; // Return zero if no user ID is found
}
var trackingSheet = MySheets.getSheetByName("tracking");
if (!trackingSheet) {
return { totalSurat: 0 }; // Return zero if the sheet is missing
}
let cols = trackingSheet.getLastColumn();
let rows = trackingSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSurat: 0 }; // Return zero if the sheet has no data
}
let allData = trackingSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId || row[2] === userId);
// Return the count of filtered data
return { totalSurat: filteredData.length };
}
function getDashboardCounts() {
var trackingCount = getTrackingCounts(); // Call the function to get the tracking count
var suratKeluarCount = getSuratKeluarCount(); // Call the function to get the suratkeluar count
var suratKeluarPendingCount = getSuratKeluarPendingCount();
var suratKeluarSuksesCount = getSuratKeluarSuksesCount();
var suratMasukBelumApproveCount = getSuratMasukBelumApproveCount();
var suratMasukSudahApproveCount = getSuratMasukSudahApproveCount();
var suratMasukCount = getSuratMasukCount();
return {
totalSurat: trackingCount.totalSurat,
totalSuratKeluar: suratKeluarCount.totalSuratKeluar,
totalSuratKeluarPending: suratKeluarPendingCount.totalSuratKeluarPending,
totalSuratKeluarSukses: suratKeluarSuksesCount.totalSuratKeluarSukses,
totalSuratMasukBelumApprove: suratMasukBelumApproveCount.totalSuratMasukBelumApprove,
totalSuratMasukSudahApprove: suratMasukSudahApproveCount.totalSuratMasukSudahApprove,
totalSuratMasuk: suratMasukCount.totalSuratMasuk
};
}
//Source Code by www.javabitpro.com
let MySheets = SpreadsheetApp.getActiveSpreadsheet();
let LoginSheet = MySheets.getSheetByName("login");
let FormSheet = MySheets.getSheetByName("Form");
let EmpSheet = MySheets.getSheetByName("suratkeluar");
function doGet(e) {
var output = HtmlService.createTemplateFromFile('login');
var sess = getSession();
if (sess.loggedIn) {
let page = e.parameter.page;
if (page == null) page = "main";
var output = HtmlService.createTemplateFromFile(page);
}
return output.evaluate();
}
function includeHeader() {
return HtmlService.createTemplateFromFile("header.html").evaluate().getContent();
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function myURL() {
return ScriptApp.getService().getUrl();
}
function setSession(session) {
var sId = Session.getTemporaryActiveUserKey();
var uProp = PropertiesService.getUserProperties();
uProp.setProperty(sId, JSON.stringify(session));
}
function getSession() {
var sId = Session.getTemporaryActiveUserKey();
var uProp = PropertiesService.getUserProperties();
var sData = uProp.getProperty(sId);
return sData ? JSON.parse(sData) : { loggedIn: false };
}
function loginUser(pUID, pPassword) {
if (loginCheck(pUID, pPassword)) {
var sess = getSession();
sess.loggedIn = true;
sess.userId = pUID; // Simpan ID pengguna ke sesi
setSession(sess);
return 'success';
} else {
return 'failure';
}
}
function getUserId() {
var sess = getSession();
return sess.userId ? sess.userId : '';
}
function logoutUser() {
var sess = getSession();
sess.loggedIn = false;
setSession(sess);
}
function loginCheck(pUID, pPassword) {
let LoginPass = false;
let ReturnData = LoginSheet.getRange("A:A").createTextFinder(pUID).matchEntireCell(true).findAll();
ReturnData.forEach(function (range) {
let StartRow = range.getRow();
let TmpPass = LoginSheet.getRange(StartRow, 2).getValue();
if (TmpPass == pPassword) {
LoginPass = true;
}
});
return LoginPass;
}
//Source Code by www.javabitpro.com
function getInputsList() {
let table = FormSheet.getRange("A2:C").getValues().filter(r => r.every(Boolean));
// Ambil username yang sedang login
let sess = getSession();
let loggedInUser = sess.userId;
// Ambil daftar username dari sheet login
let allUsers = LoginSheet.getRange("A2:A").getValues().flat().filter(Boolean);
// Filter untuk mengecualikan username yang sama dengan username yang sedang login
let otherUsers = allUsers.filter(user => user !== loggedInUser);
return { table, loggedInUser, otherUsers };
}
//Source Code by www.javabitpro.com
function getSubmit(jsonData, index) {
let data = JSON.parse(jsonData);
let fileUrl = '';
// Handle file upload if present
if (data.file) {
let blob = Utilities.newBlob(Utilities.base64Decode(data.file), 'application/octet-stream', 'uploaded_file');
let folder = DriveApp.getFolderById("ID_FOLDER"); // Replace with your folder ID
let uploadedFile = folder.createFile(blob);
fileUrl = uploadedFile.getUrl();
}
// Prepare data for sheet
let rowData = [];
for (let key in data) {
if (data.hasOwnProperty(key) && key !== 'file') {
rowData.push(data[key]);
}
}
rowData.push(fileUrl);
if (index === 0) {
// Add new data
EmpSheet.appendRow(rowData);
} else {
// Update existing data
let cols = EmpSheet.getLastColumn();
EmpSheet.getRange(index, 1, 1, cols).setValues([rowData]);
}
}
function getASOpenById(id) {
let cols = EmpSheet.getLastColumn();
let colnm = FormSheet.getRange("A2:C").getValues().filter(r => r.every(Boolean)); // Mendapatkan nama kolom dan tipe field
// Cari baris berdasarkan ID
let rowToUpdate = EmpSheet.getRange(2, 1, EmpSheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToUpdate) {
let rowIndex = rowToUpdate.getRow(); // Ambil indeks baris yang akan di-update
let data = EmpSheet.getRange(rowIndex, 1, 1, cols).getValues();
// Loop melalui data dan cek tipe file atau tanggal
data = data.map(function(row) {
return row.map(function(cell, idx) {
let colType = colnm[idx][2]; // Ambil tipe field (misal: 'file')
// Jika field bertipe file, kembalikan URL file
if (colType === 'file' && cell) {
return `<a href="${cell}" target="_blank">Lihat File</a>`; // Tampilkan link ke file
}
// Jika field berupa tanggal, konversi ke format 'yyyy-MM-dd'
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}
return cell; // Kembalikan nilai lain apa adanya
});
});
return [colnm, data, rowIndex]; // Kembalikan nama kolom, data, dan indeks baris
}
return [colnm, [], 0]; // Jika ID tidak ditemukan, kembalikan data kosong
}
function deleteRecord(id) {
// Cari baris berdasarkan ID (asumsi ID ada di kolom pertama)
let rowToDelete = EmpSheet.getRange(2, 1, EmpSheet.getLastRow()-1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToDelete) {
let rowIndex = rowToDelete.getRow(); // Ambil indeks baris
EmpSheet.deleteRow(rowIndex); // Hapus baris
return "success";
}
return "failure";
}
function getFilteredSuratKeluar() {
var sess = getSession();
var userId = sess.userId; // Ambil ID pengguna dari sesi
if (!userId) {
return [[], []]; // Jika tidak ada ID pengguna, kembalikan array kosong
}
let cols = EmpSheet.getLastColumn();
let rows = EmpSheet.getLastRow() - 1;
let tHead = EmpSheet.getRange(1, 1, 1, cols).getValues();
let allData = EmpSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan ID pengguna
let filteredData = allData.filter(row => row[1] === userId);
// Konversi setiap nilai tanggal menjadi string
filteredData = filteredData.map(function(row) {
return row.map(function(cell) {
// Jika cell adalah URL, biarkan seperti itu
if (typeof cell === 'string' && (cell.startsWith('http://') || cell.startsWith('https://'))) {
return cell; // URL akan ditangani di sisi klien
}
// Jika cell adalah tanggal, konversi ke format string (misalnya: dd/MM/yyyy)
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
}
return cell; // Kembalikan nilai lain apa adanya
});
});
return [tHead, filteredData];
}
function getASOpen(i) {
let cols = EmpSheet.getLastColumn();
let colnm = FormSheet.getRange("A2:C").getValues().filter(r => r.every(Boolean));
let data = EmpSheet.getRange(i, 1, 1, cols).getValues();
// Loop melalui data dan cek jika ada nilai yang merupakan objek Date
data = data.map(function(row) {
return row.map(function(cell) {
// Jika cell adalah objek Date, konversi ke format string 'yyyy-MM-dd'
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}
return cell;
});
});
return [colnm, data];
}
function OpenPage(PageName) {
return HtmlService.createHtmlOutputFromFile(PageName).getContent();
}
function UserRegister(pUID, pPassword, pName) {
let RetMsg = '';
let ReturnData = LoginSheet.getRange("A:A").createTextFinder(pUID).matchEntireCell(true).findAll();
let StartRow = 0;
ReturnData.forEach(function (range) {
StartRow = range.getRow();
});
if (StartRow > 0) {
RetMsg = 'danger, User Already Exists';
} else {
LoginSheet.appendRow([pUID, pPassword, pName]);
RetMsg = 'success, User Successfully Registered';
}
return RetMsg;
}
// SURAT MASUK
function getFilteredSuratMasuk() {
var sess = getSession();
var userId = sess.userId; // Ambil ID pengguna dari sesi
if (!userId) {
return [[], []]; // Jika tidak ada ID pengguna, kembalikan array kosong
}
let cols = EmpSheet.getLastColumn();
let rows = EmpSheet.getLastRow() - 1;
let tHead = EmpSheet.getRange(1, 1, 1, cols).getValues();
let allData = EmpSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan kolom 3 (misalnya kolom yang berisi ID pengguna)
let filteredData = allData.filter(row => row[2] === userId);
// Ubah URL menjadi hyperlink dan format data lainnya jika perlu
filteredData = filteredData.map(function(row) {
return row.map(function(cell, index) {
if (index === 5 && typeof cell === "string" && cell.startsWith("http")) { // Misalkan kolom 6 berisi URL
return `<a href="${cell}" target="_blank">Lihat File</a>`;
}
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
}
return cell;
});
});
return [tHead, filteredData];
}
function submitApproval(id, approvalDate, status, rejectionReason) {
let sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("suratmasuk");
let rowToUpdate = sheet.getRange(2, 1, sheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToUpdate) {
let rowIndex = rowToUpdate.getRow();
let currentData = sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn()).getValues()[0];
let newData = [...currentData];
newData[4] = approvalDate; // Assuming date goes into the 5th column
newData[5] = status; // Assuming status goes into the 6th column
newData[6] = rejectionReason; // Assuming rejection reason goes into the 7th column
sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn()).setValues([newData]);
return "success";
}
return "failure";
}
function getFilteredSuratMasuk() {
var sess = getSession();
var userId = sess.userId;
if (!userId) {
return [[], []];
}
let cols = EmpSheet.getLastColumn();
let rows = EmpSheet.getLastRow() - 1;
let tHead = EmpSheet.getRange(1, 1, 1, cols).getValues();
let allData = EmpSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan kolom yang berisi ID pengguna
let filteredData = allData.filter(row => row[2] === userId);
// Ubah URL menjadi hyperlink dan format data lainnya jika perlu
filteredData = filteredData.map(function(row) {
return row.map(function(cell, index) {
if (index === 5 && typeof cell === "string" && cell.startsWith("http")) {
return `<a href="${cell}" target="_blank">Lihat File</a>`;
}
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
}
return cell;
});
});
return [tHead, filteredData];
}
function getSuratKeluarById(id) {
let rowToUpdate = EmpSheet.getRange(2, 1, EmpSheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToUpdate) {
let rowIndex = rowToUpdate.getRow();
let data = EmpSheet.getRange(rowIndex, 1, 1, EmpSheet.getLastColumn()).getValues();
return data[0];
}
return [];
}
function approveData(id, approvalStatus, date, note) {
var EmpSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('suratkeluar');
var MasukSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('suratmasuk');
// Cari baris di sheet suratkeluar berdasarkan ID
var rowToApprove = EmpSheet.getRange(2, 1, EmpSheet.getLastRow() - 1, 1)
.createTextFinder(id)
.matchEntireCell(true)
.findNext();
if (rowToApprove) {
var rowIndex = rowToApprove.getRow();
var rowData = EmpSheet.getRange(rowIndex, 1, 1, EmpSheet.getLastColumn()).getValues()[0];
// Tambahkan tanggal, status, dan keterangan jika ditolak
rowData.push(date); // Tambahkan tanggal
rowData.push(approvalStatus); // Tambahkan status approve/reject
rowData.push(approvalStatus === 'rejected' ? note : ''); // Tambahkan keterangan jika reject
// Salin data ke sheet suratmasuk
MasukSheet.appendRow(rowData);
// Hapus baris dari suratkeluar
EmpSheet.deleteRow(rowIndex);
return "success";
}
return "failure";
}
//Tracking
function getFilteredTracking() {
var sess = getSession();
var userId = sess.userId;
if (!userId) {
return [[], []]; // Jika tidak ada ID pengguna, kembalikan array kosong
}
let TrackingSheet = MySheets.getSheetByName("tracking");
let cols = TrackingSheet.getLastColumn();
let rows = TrackingSheet.getLastRow() - 1;
let tHead = TrackingSheet.getRange(1, 1, 1, cols).getValues();
let allData = TrackingSheet.getRange(2, 1, rows, cols).getValues();
// Filter data berdasarkan salah satu dari dua kondisi
let filteredData = allData.filter(row => row[1] === userId || row[2] === userId);
// Konversi setiap nilai menjadi string dan jika URL, buat hyperlink
filteredData = filteredData.map(function(row) {
return row.map(function(cell, index) {
if (cell instanceof Date) {
return Utilities.formatDate(cell, Session.getScriptTimeZone(), 'dd/MM/yyyy');
} else if (typeof cell === 'string' && cell.startsWith('http')) {
return `<a href="${cell}" target="_blank">Lihat File</a>`;
} else if (index === 3) { // Misalnya, kolom ke-4 berisi status
switch (cell) {
case 'Approved':
return `<span style="color: green;"><i class="fa fa-check"></i> Approved</span>`;
case 'Reject':
return `<span style="color: red;"><i class="fa fa-times"></i> Reject</span>`;
default:
return cell;
}
}
return cell;
});
});
return [tHead, filteredData];
}
//Main
function getSuratKeluarCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratKeluar: 0 }; // Return zero if no user ID is found
}
var SuratKeluarSheet = MySheets.getSheetByName("tracking");
if (!SuratKeluarSheet) {
return { totalSuratKeluar: 0 }; // Return zero if the sheet is missing
}
let cols = SuratKeluarSheet.getLastColumn();
let rows = SuratKeluarSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratKeluar: 0 }; // Return zero if the sheet has no data
}
let allData = SuratKeluarSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId);
// Return the count of filtered data
return { totalSuratKeluar: filteredData.length };
}
function getSuratKeluarPendingCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratKeluarPending: 0 }; // Return zero if no user ID is found
}
var SuratKeluarPendingSheet = MySheets.getSheetByName("suratkeluar");
if (!SuratKeluarPendingSheet) {
return { totalSuratKeluarPending: 0 }; // Return zero if the sheet is missing
}
let cols = SuratKeluarPendingSheet.getLastColumn();
let rows = SuratKeluarPendingSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratKeluarPending: 0 }; // Return zero if the sheet has no data
}
let allData = SuratKeluarPendingSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId);
// Return the count of filtered data
return { totalSuratKeluarPending: filteredData.length };
}
function getSuratKeluarSuksesCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratKeluarSukses: 0 }; // Return zero if no user ID is found
}
var SuratKeluarSuksesSheet = MySheets.getSheetByName("suratmasuk");
if (!SuratKeluarSuksesSheet) {
return { totalSuratKeluarSukses: 0 }; // Return zero if the sheet is missing
}
let cols = SuratKeluarSuksesSheet.getLastColumn();
let rows = SuratKeluarSuksesSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratKeluarSukses: 0 }; // Return zero if the sheet has no data
}
let allData = SuratKeluarSuksesSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId);
// Return the count of filtered data
return { totalSuratKeluarSukses: filteredData.length };
}
function getSuratMasukBelumApproveCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratMasukBelumApprove: 0 }; // Return zero if no user ID is found
}
var SuratMasukBelumApproveSheet = MySheets.getSheetByName("suratkeluar");
if (!SuratMasukBelumApproveSheet) {
return { totalSuratMasukBelumApprove: 0 }; // Return zero if the sheet is missing
}
let cols = SuratMasukBelumApproveSheet.getLastColumn();
let rows = SuratMasukBelumApproveSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratMasukBelumApprove: 0 }; // Return zero if the sheet has no data
}
let allData = SuratMasukBelumApproveSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[2] === userId);
// Return the count of filtered data
return { totalSuratMasukBelumApprove: filteredData.length };
}
function getSuratMasukSudahApproveCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratMasukSudahApprove: 0 }; // Return zero if no user ID is found
}
var SuratMasukSudahApproveSheet = MySheets.getSheetByName("suratmasuk");
if (!SuratMasukSudahApproveSheet) {
return { totalSuratMasukSudahApprove: 0 }; // Return zero if the sheet is missing
}
let cols = SuratMasukSudahApproveSheet.getLastColumn();
let rows = SuratMasukSudahApproveSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratMasukSudahApprove: 0 }; // Return zero if the sheet has no data
}
let allData = SuratMasukSudahApproveSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[2] === userId);
// Return the count of filtered data
return { totalSuratMasukSudahApprove: filteredData.length };
}
function getSuratMasukCount() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSuratMasuk: 0 }; // Return zero if no user ID is found
}
var SuratMasukSheet = MySheets.getSheetByName("tracking");
if (!SuratMasukSheet) {
return { totalSuratMasuk: 0 }; // Return zero if the sheet is missing
}
let cols = SuratMasukSheet.getLastColumn();
let rows = SuratMasukSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSuratMasuk: 0 }; // Return zero if the sheet has no data
}
let allData = SuratMasukSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[2] === userId);
// Return the count of filtered data
return { totalSuratMasuk: filteredData.length };
}
function getTrackingCounts() {
var sess = getSession();
var userId = sess.userId; // Get the user ID from session
if (!userId) {
return { totalSurat: 0 }; // Return zero if no user ID is found
}
var trackingSheet = MySheets.getSheetByName("tracking");
if (!trackingSheet) {
return { totalSurat: 0 }; // Return zero if the sheet is missing
}
let cols = trackingSheet.getLastColumn();
let rows = trackingSheet.getLastRow() - 1;
if (rows <= 0) {
return { totalSurat: 0 }; // Return zero if the sheet has no data
}
let allData = trackingSheet.getRange(2, 1, rows, cols).getValues();
// Filter data based on user ID and column 2
let filteredData = allData.filter(row => row[1] === userId || row[2] === userId);
// Return the count of filtered data
return { totalSurat: filteredData.length };
}
function getDashboardCounts() {
var trackingCount = getTrackingCounts(); // Call the function to get the tracking count
var suratKeluarCount = getSuratKeluarCount(); // Call the function to get the suratkeluar count
var suratKeluarPendingCount = getSuratKeluarPendingCount();
var suratKeluarSuksesCount = getSuratKeluarSuksesCount();
var suratMasukBelumApproveCount = getSuratMasukBelumApproveCount();
var suratMasukSudahApproveCount = getSuratMasukSudahApproveCount();
var suratMasukCount = getSuratMasukCount();
return {
totalSurat: trackingCount.totalSurat,
totalSuratKeluar: suratKeluarCount.totalSuratKeluar,
totalSuratKeluarPending: suratKeluarPendingCount.totalSuratKeluarPending,
totalSuratKeluarSukses: suratKeluarSuksesCount.totalSuratKeluarSukses,
totalSuratMasukBelumApprove: suratMasukBelumApproveCount.totalSuratMasukBelumApprove,
totalSuratMasukSudahApprove: suratMasukSudahApproveCount.totalSuratMasukSudahApprove,
totalSuratMasuk: suratMasukCount.totalSuratMasuk
};
}
Pada point 108 gantilah ID Folder dengan ID Folder yang telah dibuat di atas.
7. Copy dan pastekan script di bawah ini ke header.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<style>
body {
background: #dedede;
}
.navbar-custom {
background: #351c75;;
}
.navbar-custom .nav-link, .navbar-custom .btn {
color: #fff;
}
.navbar-custom .navbar-text {
color: #fff;
padding :10px;
text-transform: uppercase;
}
.navbar-custom .nav-link:hover {
color: #FF4742;
}
.navbar-toggler {
color: #fff;
border: none;
}
/* CSS Button Logout*/
.button-24 {
background: #FF4742;
border: 1px solid #FF4742;
border-radius: 6px;
box-shadow: rgba(0, 0, 0, 0.1) 1px 2px 4px;
box-sizing: border-box;
color: #FFFFFF;
cursor: pointer;
display: inline-block;
font-family: nunito,roboto,proxima-nova,"proxima nova",sans-serif;
font-size: 16px;
font-weight: 800;
line-height: 16px;
min-height: 40px;
outline: 0;
padding: 12px 14px;
text-align: center;
text-rendering: geometricprecision;
text-transform: none;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: middle;
}
.button-24:hover,
.button-24:active {
background-color: initial;
background-position: 0 0;
color: #FF4742;
}
.button-24:active {
opacity: .5;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-custom px-2">
<div class="container-fluid">
<a class="navbar-brand text-white" href="<?= myURL(); ?>">Javabitpro</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"><i class="bi bi-list"></i></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="<?= myURL(); ?>"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-door-fill" viewBox="0 0 16 16">
<path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5"/>
</svg></i> Home |</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= myURL(); ?>?page=suratkeluar"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-send-arrow-up-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M15.854.146a.5.5 0 0 1 .11.54L13.026 8.03A4.5 4.5 0 0 0 8 12.5c0 .5 0 1.5-.773.36l-1.59-2.498L.644 7.184l-.002-.001-.41-.261a.5.5 0 0 1 .083-.886l.452-.18.001-.001L15.314.035a.5.5 0 0 1 .54.111M6.637 10.07l7.494-7.494.471-1.178-1.178.471L5.93 9.363l.338.215a.5.5 0 0 1 .154.154z"/>
<path fill-rule="evenodd" d="M12.5 16a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7m.354-5.354a.5.5 0 0 0-.722.016l-1.149 1.25a.5.5 0 1 0 .737.676l.28-.305V14a.5.5 0 0 0 1 0v-1.793l.396.397a.5.5 0 0 0 .708-.708z"/>
</svg></i> Surat Keluar |</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= myURL(); ?>?page=suratmasuk"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-paper-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M6.5 9.5 3 7.5v-6A1.5 1.5 0 0 1 4.5 0h7A1.5 1.5 0 0 1 13 1.5v6l-3.5 2L8 8.75zM1.059 3.635 2 3.133v3.753L0 5.713V5.4a2 2 0 0 1 1.059-1.765M16 5.713l-2 1.173V3.133l.941.502A2 2 0 0 1 16 5.4zm0 1.16-5.693 3.337L16 13.372v-6.5Zm-8 3.199 7.941 4.412A2 2 0 0 1 14 16H2a2 2 0 0 1-1.941-1.516zm-8 3.3 5.693-3.162L0 6.873v6.5Z"/>
</svg></i> Surat Masuk |</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= myURL(); ?>?page=trackingsurat"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
</svg></i> Tracking Surat |</a>
</li>
</ul>
<span class="navbar-text">
Pengguna: <b id="userIdDisplay">Loading...</b>
</span>
<button class="button-24" type="button" onclick="logout()">Log Out</button>
<a style="display:none" id="myid" href="<?= myURL(); ?>" target="_top">Link</a>
</div>
</div>
</nav>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<script>
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
console.log('User ID:', userId);
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
window.onload = function() {
loadUserId();
};
function logout() {
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function() {
$('#loadingModal').modal('hide');
document.getElementById("myid").click();
}).logoutUser();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<style>
body {
background: #dedede;
}
.navbar-custom {
background: #351c75;;
}
.navbar-custom .nav-link, .navbar-custom .btn {
color: #fff;
}
.navbar-custom .navbar-text {
color: #fff;
padding :10px;
text-transform: uppercase;
}
.navbar-custom .nav-link:hover {
color: #FF4742;
}
.navbar-toggler {
color: #fff;
border: none;
}
/* CSS Button Logout*/
.button-24 {
background: #FF4742;
border: 1px solid #FF4742;
border-radius: 6px;
box-shadow: rgba(0, 0, 0, 0.1) 1px 2px 4px;
box-sizing: border-box;
color: #FFFFFF;
cursor: pointer;
display: inline-block;
font-family: nunito,roboto,proxima-nova,"proxima nova",sans-serif;
font-size: 16px;
font-weight: 800;
line-height: 16px;
min-height: 40px;
outline: 0;
padding: 12px 14px;
text-align: center;
text-rendering: geometricprecision;
text-transform: none;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: middle;
}
.button-24:hover,
.button-24:active {
background-color: initial;
background-position: 0 0;
color: #FF4742;
}
.button-24:active {
opacity: .5;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-custom px-2">
<div class="container-fluid">
<a class="navbar-brand text-white" href="<?= myURL(); ?>">Javabitpro</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"><i class="bi bi-list"></i></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="<?= myURL(); ?>"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-door-fill" viewBox="0 0 16 16">
<path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5"/>
</svg></i> Home |</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= myURL(); ?>?page=suratkeluar"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-send-arrow-up-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M15.854.146a.5.5 0 0 1 .11.54L13.026 8.03A4.5 4.5 0 0 0 8 12.5c0 .5 0 1.5-.773.36l-1.59-2.498L.644 7.184l-.002-.001-.41-.261a.5.5 0 0 1 .083-.886l.452-.18.001-.001L15.314.035a.5.5 0 0 1 .54.111M6.637 10.07l7.494-7.494.471-1.178-1.178.471L5.93 9.363l.338.215a.5.5 0 0 1 .154.154z"/>
<path fill-rule="evenodd" d="M12.5 16a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7m.354-5.354a.5.5 0 0 0-.722.016l-1.149 1.25a.5.5 0 1 0 .737.676l.28-.305V14a.5.5 0 0 0 1 0v-1.793l.396.397a.5.5 0 0 0 .708-.708z"/>
</svg></i> Surat Keluar |</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= myURL(); ?>?page=suratmasuk"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-paper-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M6.5 9.5 3 7.5v-6A1.5 1.5 0 0 1 4.5 0h7A1.5 1.5 0 0 1 13 1.5v6l-3.5 2L8 8.75zM1.059 3.635 2 3.133v3.753L0 5.713V5.4a2 2 0 0 1 1.059-1.765M16 5.713l-2 1.173V3.133l.941.502A2 2 0 0 1 16 5.4zm0 1.16-5.693 3.337L16 13.372v-6.5Zm-8 3.199 7.941 4.412A2 2 0 0 1 14 16H2a2 2 0 0 1-1.941-1.516zm-8 3.3 5.693-3.162L0 6.873v6.5Z"/>
</svg></i> Surat Masuk |</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= myURL(); ?>?page=trackingsurat"><i><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
</svg></i> Tracking Surat |</a>
</li>
</ul>
<span class="navbar-text">
Pengguna: <b id="userIdDisplay">Loading...</b>
</span>
<button class="button-24" type="button" onclick="logout()">Log Out</button>
<a style="display:none" id="myid" href="<?= myURL(); ?>" target="_top">Link</a>
</div>
</div>
</nav>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<script>
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
console.log('User ID:', userId);
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
window.onload = function() {
loadUserId();
};
function logout() {
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function() {
$('#loadingModal').modal('hide');
document.getElementById("myid").click();
}).logoutUser();
}
</script>
</body>
</html>
8. Copy dan pastekan script di bawah ini ke login.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<base target="_top">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous">
</script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<!-- font-awesome@6.2.0 icon Visit -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<?!= include('visit')?>
<?!= include('cssloading')?>
<style>
body {
background-color: #f8f9fa; //rgba(205, 180, 219, 30%);
}
header {
position: relative;
height: 300px; /* Sesuaikan tinggi header */
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%23351c75" fill-opacity="1" d="M0,128L60,122.7C120,117,240,107,360,96C480,85,600,75,720,101.3C840,128,960,192,1080,192C1200,192,1320,128,1380,96L1440,64L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"></path></svg>') no-repeat;
background-size: cover;
}
.card {
width: 400px;
margin: auto;
margin-top: 10px;
}
.input-group-text {
width: 100px;
display: inline-block;
}
.mycolor {
background-color: #351c75; //#6d3b89;
}
.color {
color: #351c75; //#6d3b89;
}
.img {
width: 60px;
margin: auto;
display: inline-block;
}
</style>
<script>
function login() {
var username = document.getElementById("uid").value;
var password = document.getElementById("pass").value;
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
if (response === "success") {
document.getElementById("myid").click();
} else {
$("#RetMsg").removeClass("alert-danger").removeClass("alert-success").addClass("alert-danger");
$("#RetMsg").html("Invalid User ID or Password");
$("#RetMsg").show();
}
}).loginUser(username, password);
}
function ClearText()
{
$('#RetMsg').html("");
$('#RetMsg').hide();
$('#RetMsgReg').html("");
$('#RetMsgReg').hide();
}
function OpenRegisterPage()
{
google.script.run.withSuccessHandler(ShowRegister).OpenPage("register");
}
function ShowRegister(data)
{
$('#DivLogin').hide();
$('#DivRegister').html(data);
}
function ShowLogin() {
$('#DivRegister').html(''); // Hapus konten register
$('#DivLogin').show(); // Tampilkan kembali form login
}
</script>
</head>
<body>
<header>
<!-- Konten Header di sini -->
</header>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<div id="DivLogin" class="card shadow rounded-4 rounded ">
<h5 class="card-header bg-secondary- text-white p-3 mycolor">Login Sistem Surat</h5>
<a style="display:none" id="myid" href="<?= myURL(); ?>" target="_top">Link</a>
<div class="card-body p-4">
<center>
<i class="bi bi-people-fill fs-1 color"></i>
</center>
<br>
<div class="input-group mb-3">
<span class="input-group-text" >User</span>
<input type="text" class="form-control" id="uid" required placeholder="User ID" onchange="ClearText()">
</div>
<div class="input-group mb-3">
<span class="input-group-text" >Password</span>
<input type="password" class="form-control" id="pass" required placeholder="Password" onchange="ClearText()" >
</div>
<br>
<div id="RetMsg" class="alert alert-danger " style="display:none" role="alert"> </div>
<button type="button" onclick="login()" class="btn btn-primary- mycolor float-end px-5 text-white" >Login</button>
<span href="#" onclick="OpenRegisterPage()">Register (New User)</a>
</div>
<form >
</div>
<div id="DivRegister"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<base target="_top">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous">
</script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<!-- font-awesome@6.2.0 icon Visit -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<?!= include('visit')?>
<?!= include('cssloading')?>
<style>
body {
background-color: #f8f9fa; //rgba(205, 180, 219, 30%);
}
header {
position: relative;
height: 300px; /* Sesuaikan tinggi header */
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%23351c75" fill-opacity="1" d="M0,128L60,122.7C120,117,240,107,360,96C480,85,600,75,720,101.3C840,128,960,192,1080,192C1200,192,1320,128,1380,96L1440,64L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"></path></svg>') no-repeat;
background-size: cover;
}
.card {
width: 400px;
margin: auto;
margin-top: 10px;
}
.input-group-text {
width: 100px;
display: inline-block;
}
.mycolor {
background-color: #351c75; //#6d3b89;
}
.color {
color: #351c75; //#6d3b89;
}
.img {
width: 60px;
margin: auto;
display: inline-block;
}
</style>
<script>
function login() {
var username = document.getElementById("uid").value;
var password = document.getElementById("pass").value;
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
if (response === "success") {
document.getElementById("myid").click();
} else {
$("#RetMsg").removeClass("alert-danger").removeClass("alert-success").addClass("alert-danger");
$("#RetMsg").html("Invalid User ID or Password");
$("#RetMsg").show();
}
}).loginUser(username, password);
}
function ClearText()
{
$('#RetMsg').html("");
$('#RetMsg').hide();
$('#RetMsgReg').html("");
$('#RetMsgReg').hide();
}
function OpenRegisterPage()
{
google.script.run.withSuccessHandler(ShowRegister).OpenPage("register");
}
function ShowRegister(data)
{
$('#DivLogin').hide();
$('#DivRegister').html(data);
}
function ShowLogin() {
$('#DivRegister').html(''); // Hapus konten register
$('#DivLogin').show(); // Tampilkan kembali form login
}
</script>
</head>
<body>
<header>
<!-- Konten Header di sini -->
</header>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<div id="DivLogin" class="card shadow rounded-4 rounded ">
<h5 class="card-header bg-secondary- text-white p-3 mycolor">Login Sistem Surat</h5>
<a style="display:none" id="myid" href="<?= myURL(); ?>" target="_top">Link</a>
<div class="card-body p-4">
<center>
<i class="bi bi-people-fill fs-1 color"></i>
</center>
<br>
<div class="input-group mb-3">
<span class="input-group-text" >User</span>
<input type="text" class="form-control" id="uid" required placeholder="User ID" onchange="ClearText()">
</div>
<div class="input-group mb-3">
<span class="input-group-text" >Password</span>
<input type="password" class="form-control" id="pass" required placeholder="Password" onchange="ClearText()" >
</div>
<br>
<div id="RetMsg" class="alert alert-danger " style="display:none" role="alert"> </div>
<button type="button" onclick="login()" class="btn btn-primary- mycolor float-end px-5 text-white" >Login</button>
<span href="#" onclick="OpenRegisterPage()">Register (New User)</a>
</div>
<form >
</div>
<div id="DivRegister"></div>
</body>
</html>
9. Copy dan pastekan script di bawah ini ke register.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<script>
function Register()
{
var unm = document.getElementById("reg_uname").value.trim();
var uid = document.getElementById("reg_uid").value.trim();
var pass = document.getElementById("reg_pass").value.trim();
var repass = document.getElementById("reg_repass").value.trim();
if (pass != repass)
{
RegReturnMsg("danger,Password Not Matched...");
}
else
{
google.script.run.withSuccessHandler(RegReturnMsg).UserRegister(uid,pass,unm);
}
}
function RegReturnMsg(data)
{
let v = data.split(",");
let type=v[0];
$('#RetMsgReg').removeClass("alert-success").removeClass("alert-danger").addClass("alert-"+type);
$('#RetMsgReg').html(v[1]);
$('#RetMsgReg').show();
if (type == 'success')
{
setTimeout(function(){
document.getElementById("myid").click();
}, 3000);
}
}
function OpenLoginPage()
{
google.script.run.withSuccessHandler(ShowLogin).OpenPage("login");
}
function ShowLogin() {
$('#DivRegister').html(''); // Hapus konten register
$('#DivLogin').show(); // Tampilkan kembali form login
}
</script>
<div class="card shadow rounded-4 rounded ">
<h5 class="card-header bg-secondary- text-white p-3 mycolor">User Registration</h5>
<div class="card-body p-4">
<div id="RegBody">
<div class="input-group mb-3">
<span class="input-group-text" >User Name</span>
<input type="text" class="form-control" id="reg_uname" placeholder="User Name" onchange="ClearText()">
</div>
<div class="input-group mb-3">
<span class="input-group-text" >User ID</span>
<input type="text" class="form-control" id="reg_uid" placeholder="User ID" onchange="ClearText()">
</div>
<div class="input-group mb-3">
<span class="input-group-text" >Password</span>
<input type="password" class="form-control" id="reg_pass" placeholder="Password" onchange="ClearText()" >
</div>
<div class="input-group mb-3">
<span class="input-group-text" >Re-enter</span>
<input type="password" class="form-control" id="reg_repass" placeholder="Re-enter Password" onchange="ClearText()" >
</div>
</div>
<div id="RetMsgReg" class="alert alert-danger " style="display:none" role="alert"> </div>
<button type="button" class="btn btn-primary- mycolor float-end px-5 text-white" onclick="Register()">Register</button>
<span href="#" onclick="OpenLoginPage()">Back to Login</a>
</div>
</div>
<script>
function Register()
{
var unm = document.getElementById("reg_uname").value.trim();
var uid = document.getElementById("reg_uid").value.trim();
var pass = document.getElementById("reg_pass").value.trim();
var repass = document.getElementById("reg_repass").value.trim();
if (pass != repass)
{
RegReturnMsg("danger,Password Not Matched...");
}
else
{
google.script.run.withSuccessHandler(RegReturnMsg).UserRegister(uid,pass,unm);
}
}
function RegReturnMsg(data)
{
let v = data.split(",");
let type=v[0];
$('#RetMsgReg').removeClass("alert-success").removeClass("alert-danger").addClass("alert-"+type);
$('#RetMsgReg').html(v[1]);
$('#RetMsgReg').show();
if (type == 'success')
{
setTimeout(function(){
document.getElementById("myid").click();
}, 3000);
}
}
function OpenLoginPage()
{
google.script.run.withSuccessHandler(ShowLogin).OpenPage("login");
}
function ShowLogin() {
$('#DivRegister').html(''); // Hapus konten register
$('#DivLogin').show(); // Tampilkan kembali form login
}
</script>
<div class="card shadow rounded-4 rounded ">
<h5 class="card-header bg-secondary- text-white p-3 mycolor">User Registration</h5>
<div class="card-body p-4">
<div id="RegBody">
<div class="input-group mb-3">
<span class="input-group-text" >User Name</span>
<input type="text" class="form-control" id="reg_uname" placeholder="User Name" onchange="ClearText()">
</div>
<div class="input-group mb-3">
<span class="input-group-text" >User ID</span>
<input type="text" class="form-control" id="reg_uid" placeholder="User ID" onchange="ClearText()">
</div>
<div class="input-group mb-3">
<span class="input-group-text" >Password</span>
<input type="password" class="form-control" id="reg_pass" placeholder="Password" onchange="ClearText()" >
</div>
<div class="input-group mb-3">
<span class="input-group-text" >Re-enter</span>
<input type="password" class="form-control" id="reg_repass" placeholder="Re-enter Password" onchange="ClearText()" >
</div>
</div>
<div id="RetMsgReg" class="alert alert-danger " style="display:none" role="alert"> </div>
<button type="button" class="btn btn-primary- mycolor float-end px-5 text-white" onclick="Register()">Register</button>
<span href="#" onclick="OpenLoginPage()">Back to Login</a>
</div>
</div>
10. Copy dan pastekan script di bawah ini ke main.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<?!= include('cssmain')?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
<!-- font-awesome@6.2.0 icon Visit -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<?!= include('visit')?>
</head>
<body onload="loadCounts(); loadUserId();">
<div class="container">
<div id="cardsContainer" class="container border-card mt-4 p-4">Loading...
<!-- Card containers will be inserted here -->
</div>
</div>
<div class="container border-card mt-2 p-4">
<div class="row align-items-center">
<!-- Bagian Gambar -->
<div class="col-md-3 text-center">
<img src="https://cdn.jsdelivr.net/gh/javabitpro/javabitproimage@main/Untitled%20design.gif" alt="Logo Desa" class="img-fluid" id="logoku">
<p>JAVABITPRO</p>
</div>
<!-- Bagian Teks -->
<div class="col-md-9">
<h2>Halo, <strong>USER</strong></h2>
<p>Selamat datang di Web Aplikasi Surat Menyurat.</p>
<div class="text-right">
<p id="tanggal-sekarang"></p>
</div>
</div>
</div>
<footer class="mt-4">
<small>© e-SuratJavabitpro 2024 | Hak Cipta Dilindungi</small>
</footer>
</div>
<script>
function loadCounts() {
google.script.run.withSuccessHandler(function(data) {
let cardsContainer = document.getElementById('cardsContainer');
// Create card elements with counts for both tracking and suratkeluar
let cardHTML = `
<div class="row">
<div class="col-md-12">
<div class="card-counter primary">
<i class="fa fa-database"></i>
<span class="count-numbers">${data.totalSurat}</span>
<span class="count-name">Total Surat</span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="card-counter info">
<i class="fa fa-envelope"></i>
<span class="count-numbers">${data.totalSuratMasuk}</span>
<span class="count-name">Total Surat Masuk</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter danger">
<i class="fa fa-bell"></i>
<span class="count-numbers">${data.totalSuratMasukBelumApprove}</span>
<span class="count-name">Surat Masuk Belum Approve</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter success">
<i class="fa fa-envelope-open"></i>
<span class="count-numbers">${data.totalSuratMasukSudahApprove}</span>
<span class="count-name">Surat Masuk Sudah Approve</span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="card-counter info">
<i class="fa fa-paper-plane"></i>
<span class="count-numbers">${data.totalSuratKeluar}</span>
<span class="count-name">Total Surat Keluar</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter danger">
<i class="fa fa-clock-o"></i>
<span class="count-numbers">${data.totalSuratKeluarPending}</span>
<span class="count-name">Surat Keluar Pending</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter success">
<i class="fa fa-check"></i>
<span class="count-numbers">${data.totalSuratKeluarSukses}</span>
<span class="count-name">Surat Keluar Sukses</span>
</div>
</div>
</div>
`;
cardsContainer.innerHTML = cardHTML;
}).getDashboardCounts();
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
// Fungsi untuk mendapatkan nama hari dalam bahasa Indonesia
function getHariIndonesia(hari) {
const namaHari = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
return namaHari[hari];
}
// Fungsi untuk mendapatkan nama bulan dalam bahasa Indonesia
function getBulanIndonesia(bulan) {
const namaBulan = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];
return namaBulan[bulan];
}
// Mendapatkan tanggal sekarang
const today = new Date();
const hari = getHariIndonesia(today.getDay());
const tanggal = today.getDate();
const bulan = getBulanIndonesia(today.getMonth());
const tahun = today.getFullYear();
// Menampilkan tanggal dalam format: Sabtu, 21 Oktober 2023
document.getElementById('tanggal-sekarang').textContent = `${hari}, ${tanggal} ${bulan} ${tahun}`;
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<?!= include('cssmain')?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
<!-- font-awesome@6.2.0 icon Visit -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<?!= include('visit')?>
</head>
<body onload="loadCounts(); loadUserId();">
<div class="container">
<div id="cardsContainer" class="container border-card mt-4 p-4">Loading...
<!-- Card containers will be inserted here -->
</div>
</div>
<div class="container border-card mt-2 p-4">
<div class="row align-items-center">
<!-- Bagian Gambar -->
<div class="col-md-3 text-center">
<img src="https://cdn.jsdelivr.net/gh/javabitpro/javabitproimage@main/Untitled%20design.gif" alt="Logo Desa" class="img-fluid" id="logoku">
<p>JAVABITPRO</p>
</div>
<!-- Bagian Teks -->
<div class="col-md-9">
<h2>Halo, <strong>USER</strong></h2>
<p>Selamat datang di Web Aplikasi Surat Menyurat.</p>
<div class="text-right">
<p id="tanggal-sekarang"></p>
</div>
</div>
</div>
<footer class="mt-4">
<small>© e-SuratJavabitpro 2024 | Hak Cipta Dilindungi</small>
</footer>
</div>
<script>
function loadCounts() {
google.script.run.withSuccessHandler(function(data) {
let cardsContainer = document.getElementById('cardsContainer');
// Create card elements with counts for both tracking and suratkeluar
let cardHTML = `
<div class="row">
<div class="col-md-12">
<div class="card-counter primary">
<i class="fa fa-database"></i>
<span class="count-numbers">${data.totalSurat}</span>
<span class="count-name">Total Surat</span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="card-counter info">
<i class="fa fa-envelope"></i>
<span class="count-numbers">${data.totalSuratMasuk}</span>
<span class="count-name">Total Surat Masuk</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter danger">
<i class="fa fa-bell"></i>
<span class="count-numbers">${data.totalSuratMasukBelumApprove}</span>
<span class="count-name">Surat Masuk Belum Approve</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter success">
<i class="fa fa-envelope-open"></i>
<span class="count-numbers">${data.totalSuratMasukSudahApprove}</span>
<span class="count-name">Surat Masuk Sudah Approve</span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="card-counter info">
<i class="fa fa-paper-plane"></i>
<span class="count-numbers">${data.totalSuratKeluar}</span>
<span class="count-name">Total Surat Keluar</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter danger">
<i class="fa fa-clock-o"></i>
<span class="count-numbers">${data.totalSuratKeluarPending}</span>
<span class="count-name">Surat Keluar Pending</span>
</div>
</div>
<div class="col-md-4">
<div class="card-counter success">
<i class="fa fa-check"></i>
<span class="count-numbers">${data.totalSuratKeluarSukses}</span>
<span class="count-name">Surat Keluar Sukses</span>
</div>
</div>
</div>
`;
cardsContainer.innerHTML = cardHTML;
}).getDashboardCounts();
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
// Fungsi untuk mendapatkan nama hari dalam bahasa Indonesia
function getHariIndonesia(hari) {
const namaHari = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
return namaHari[hari];
}
// Fungsi untuk mendapatkan nama bulan dalam bahasa Indonesia
function getBulanIndonesia(bulan) {
const namaBulan = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];
return namaBulan[bulan];
}
// Mendapatkan tanggal sekarang
const today = new Date();
const hari = getHariIndonesia(today.getDay());
const tanggal = today.getDate();
const bulan = getBulanIndonesia(today.getMonth());
const tahun = today.getFullYear();
// Menampilkan tanggal dalam format: Sabtu, 21 Oktober 2023
document.getElementById('tanggal-sekarang').textContent = `${hari}, ${tanggal} ${bulan} ${tahun}`;
</script>
</body>
</html>
11. Copy dan pastekan script di bawah ini ke suratkeluar.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<?!= include('csstable')?>
</head>
<body onload="getFilteredData(); loadUserId(); getInputs();">
<div class="p-1">
<span class="h1"><strong>SURAT KELUAR</strong></span>
<button type="button" id="BtnCreate" class="button-24" onclick="myCreate()" style="float:right"> (+) Add Record </button>
</div>
<div id="DivForm" class="formBackground">
<div class="card col-4 mx-auto mt-4 shadow rounded-4 rounded">
<div class="card-header mycolor text-white">Detail Data</div>
<div class="card-body">
<form id="MyForm" enctype="multipart/form-data"></form>
<div class="row justify-content-end px-2">
<span id="SpanMsg"></span>
<button type="button" id="BtnClose" class="btn mycolor col-3 text-white" onclick="formClose()" style="margin-right:5px;">Close</button>
<button type="button" id="BtnSubmit" class="btn mycolor col-3 text-white" onclick="mySubmit()">Submit</button>
</div>
</div>
</div>
</div>
<div id="DivTable" class="mt-2 col-12 shadow rounded-4 rounded bg-light p-2" style="height:420px; overflow:auto">
<table class="table table-bordered">
<thead id="myTableHead" class="mycolor text-white"></thead>
<tbody id="myTableBody"></tbody>
</table>
</div>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<script>
let index = 0;
function getInputs() {
google.script.run.withSuccessHandler(function(data) {
let myHTML = "";
let inputs = data.table;
let loggedInUser = data.loggedInUser;
let otherUsers = data.otherUsers;
$.each(inputs, function(key, value) {
let myLabel = value[1];
let myName = value[0];
let myType = value[2];
if (myName === 'usernameField3') { // Ganti dengan nama field ke-3 yang sesuai
// Field ke-3, tampilkan dropdown dengan username yang sedang login
let dropdown = `<div class="form-group mb-3">
<label for="${myName}">${myLabel}</label>
<select class="form-control" id="${myName}" name="${myName}">
<option value="${loggedInUser}">${loggedInUser}</option>
</select>
</div>`;
myHTML += dropdown;
} else if (myName === 'usernameField4') { // Ganti dengan nama field ke-4 yang sesuai
// Field ke-4, tampilkan dropdown dengan username lain
let options = otherUsers.map(user => `<option value="${user}">${user}</option>`).join('');
let dropdown = `<div class="form-group mb-3">
<label for="${myName}">${myLabel}</label>
<select class="form-control" id="${myName}" name="${myName}">
${options}
</select>
</div>`;
myHTML += dropdown;
} else {
// Field lain
let inputField = `<div class="form-group mb-3">
<label for="${myName}">${myLabel}</label>
<input type="${myType}" class="form-control" id="${myName}" name="${myName}" placeholder="${myLabel}">
</div>`;
myHTML += inputField;
}
});
$("#MyForm").append(myHTML);
}).getInputsList();
}
function mySubmit() {
let form = document.getElementById('MyForm');
let formData = new FormData(form);
// Extract file from FormData
let file = formData.get('file');
if (!file) {
alert('No file selected.');
return;
}
let data = {};
formData.forEach((value, key) => {
if (key !== 'file') {
data[key] = value;
}
});
// Convert file to base64 for sending to server
let reader = new FileReader();
reader.onload = function(event) {
let base64File = event.target.result.split(',')[1]; // Extract base64 content
data.file = base64File;
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
let returnHTML = '<div class="alert alert-success" role="alert">Success</div>';
$("#MyForm input").val(''); // Clear form after submit
$("#SpanMsg").html(returnHTML); // Show success message
getFilteredData(); // Refresh table after submit
setTimeout(() => { formClose(); }, 2000); // Close form after submit
}).getSubmit(JSON.stringify(data), rowIndex); // Kirim data sebagai JSON, dengan rowIndex yang benar
};
reader.readAsDataURL(file); // Read file as base64
}
function myCreate() {
index = 0;
$('#SpanMsg').html('');
$("#MyForm input").val('');
$('#DivForm').show();
}
function formClose() {
$('#DivForm').hide();
}
function getFilteredData() {
google.script.run.withSuccessHandler(function(data) {
let thead = data[0];
let tbody = data[1];
let myRows = "";
$.each(thead, function(key, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
myRows = myRows + "<th>" + cell + "</th>";
});
myRows += "<th style='width:160px'></th>"; // Kolom untuk tombol Open dan Delete
myRows = myRows + "</tr>";
});
$("#myTableHead").html(myRows);
myRows = "";
$.each(tbody, function(index, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
// Jika cell adalah URL, ubah menjadi hyperlink dengan teks "Lihat File"
if (typeof cell === 'string' && (cell.startsWith('http://') || cell.startsWith('https://'))) {
myRows = myRows + "<td><a href='" + cell + "' target='_blank'>Lihat File</a></td>";
} else {
myRows = myRows + "<td>" + cell + "</td>";
}
});
// Tombol Open
let btnUpd = '<button class="btn btn-outline-success btn-sm" onclick="getJSOpen(\'' + row[0] + '\')">Open</button>';
// Tombol Delete
let btnDel = '<button class="btn btn-outline-danger btn-sm" onclick="confirmDelete(\'' + row[0] + '\')">Delete</button>';
myRows = myRows + "<td>" + btnUpd + " " + btnDel + "</td>";
myRows = myRows + "</tr>";
});
$("#myTableBody").html(myRows);
}).getFilteredSuratKeluar();
}
function confirmDelete(id) {
if (confirm("Are you sure you want to delete this record?")) {
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
if (response === "success") {
alert("Record deleted successfully.");
getFilteredData(); // Refresh the table
} else {
alert("Error deleting record.");
}
}).deleteRecord(id); // Panggil fungsi deleteRecord di Apps Script
}
}
let rowIndex = 0; // Variable global untuk menyimpan indeks baris asli
function getJSOpen(id) {
$('#SpanMsg').html('');
google.script.run.withSuccessHandler(function(data) {
let colnm = data[0];
let row = data[1][0]; // Data baris yang diambil dari Google Sheet
rowIndex = data[2]; // Simpan indeks baris yang sebenarnya
let idx = 0;
$.each(colnm, function(key, value) {
let colname = value[0];
let coltype = value[2]; // Tipe field (misal: 'file')
let colvalue = row[idx];
idx++;
if (coltype === 'file') {
// Jika tipe file, tampilkan link untuk melihat file
$('#'+colname).parent().append(colvalue);
} else {
// Field biasa, tampilkan nilai di input
$('#'+colname).val(colvalue);
}
});
$('#DivForm').show(); // Tampilkan form
}).getASOpenById(id); // Panggil fungsi dengan ID
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<?!= include('csstable')?>
</head>
<body onload="getFilteredData(); loadUserId(); getInputs();">
<div class="p-1">
<span class="h1"><strong>SURAT KELUAR</strong></span>
<button type="button" id="BtnCreate" class="button-24" onclick="myCreate()" style="float:right"> (+) Add Record </button>
</div>
<div id="DivForm" class="formBackground">
<div class="card col-4 mx-auto mt-4 shadow rounded-4 rounded">
<div class="card-header mycolor text-white">Detail Data</div>
<div class="card-body">
<form id="MyForm" enctype="multipart/form-data"></form>
<div class="row justify-content-end px-2">
<span id="SpanMsg"></span>
<button type="button" id="BtnClose" class="btn mycolor col-3 text-white" onclick="formClose()" style="margin-right:5px;">Close</button>
<button type="button" id="BtnSubmit" class="btn mycolor col-3 text-white" onclick="mySubmit()">Submit</button>
</div>
</div>
</div>
</div>
<div id="DivTable" class="mt-2 col-12 shadow rounded-4 rounded bg-light p-2" style="height:420px; overflow:auto">
<table class="table table-bordered">
<thead id="myTableHead" class="mycolor text-white"></thead>
<tbody id="myTableBody"></tbody>
</table>
</div>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<script>
let index = 0;
function getInputs() {
google.script.run.withSuccessHandler(function(data) {
let myHTML = "";
let inputs = data.table;
let loggedInUser = data.loggedInUser;
let otherUsers = data.otherUsers;
$.each(inputs, function(key, value) {
let myLabel = value[1];
let myName = value[0];
let myType = value[2];
if (myName === 'usernameField3') { // Ganti dengan nama field ke-3 yang sesuai
// Field ke-3, tampilkan dropdown dengan username yang sedang login
let dropdown = `<div class="form-group mb-3">
<label for="${myName}">${myLabel}</label>
<select class="form-control" id="${myName}" name="${myName}">
<option value="${loggedInUser}">${loggedInUser}</option>
</select>
</div>`;
myHTML += dropdown;
} else if (myName === 'usernameField4') { // Ganti dengan nama field ke-4 yang sesuai
// Field ke-4, tampilkan dropdown dengan username lain
let options = otherUsers.map(user => `<option value="${user}">${user}</option>`).join('');
let dropdown = `<div class="form-group mb-3">
<label for="${myName}">${myLabel}</label>
<select class="form-control" id="${myName}" name="${myName}">
${options}
</select>
</div>`;
myHTML += dropdown;
} else {
// Field lain
let inputField = `<div class="form-group mb-3">
<label for="${myName}">${myLabel}</label>
<input type="${myType}" class="form-control" id="${myName}" name="${myName}" placeholder="${myLabel}">
</div>`;
myHTML += inputField;
}
});
$("#MyForm").append(myHTML);
}).getInputsList();
}
function mySubmit() {
let form = document.getElementById('MyForm');
let formData = new FormData(form);
// Extract file from FormData
let file = formData.get('file');
if (!file) {
alert('No file selected.');
return;
}
let data = {};
formData.forEach((value, key) => {
if (key !== 'file') {
data[key] = value;
}
});
// Convert file to base64 for sending to server
let reader = new FileReader();
reader.onload = function(event) {
let base64File = event.target.result.split(',')[1]; // Extract base64 content
data.file = base64File;
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
let returnHTML = '<div class="alert alert-success" role="alert">Success</div>';
$("#MyForm input").val(''); // Clear form after submit
$("#SpanMsg").html(returnHTML); // Show success message
getFilteredData(); // Refresh table after submit
setTimeout(() => { formClose(); }, 2000); // Close form after submit
}).getSubmit(JSON.stringify(data), rowIndex); // Kirim data sebagai JSON, dengan rowIndex yang benar
};
reader.readAsDataURL(file); // Read file as base64
}
function myCreate() {
index = 0;
$('#SpanMsg').html('');
$("#MyForm input").val('');
$('#DivForm').show();
}
function formClose() {
$('#DivForm').hide();
}
function getFilteredData() {
google.script.run.withSuccessHandler(function(data) {
let thead = data[0];
let tbody = data[1];
let myRows = "";
$.each(thead, function(key, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
myRows = myRows + "<th>" + cell + "</th>";
});
myRows += "<th style='width:160px'></th>"; // Kolom untuk tombol Open dan Delete
myRows = myRows + "</tr>";
});
$("#myTableHead").html(myRows);
myRows = "";
$.each(tbody, function(index, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
// Jika cell adalah URL, ubah menjadi hyperlink dengan teks "Lihat File"
if (typeof cell === 'string' && (cell.startsWith('http://') || cell.startsWith('https://'))) {
myRows = myRows + "<td><a href='" + cell + "' target='_blank'>Lihat File</a></td>";
} else {
myRows = myRows + "<td>" + cell + "</td>";
}
});
// Tombol Open
let btnUpd = '<button class="btn btn-outline-success btn-sm" onclick="getJSOpen(\'' + row[0] + '\')">Open</button>';
// Tombol Delete
let btnDel = '<button class="btn btn-outline-danger btn-sm" onclick="confirmDelete(\'' + row[0] + '\')">Delete</button>';
myRows = myRows + "<td>" + btnUpd + " " + btnDel + "</td>";
myRows = myRows + "</tr>";
});
$("#myTableBody").html(myRows);
}).getFilteredSuratKeluar();
}
function confirmDelete(id) {
if (confirm("Are you sure you want to delete this record?")) {
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
if (response === "success") {
alert("Record deleted successfully.");
getFilteredData(); // Refresh the table
} else {
alert("Error deleting record.");
}
}).deleteRecord(id); // Panggil fungsi deleteRecord di Apps Script
}
}
let rowIndex = 0; // Variable global untuk menyimpan indeks baris asli
function getJSOpen(id) {
$('#SpanMsg').html('');
google.script.run.withSuccessHandler(function(data) {
let colnm = data[0];
let row = data[1][0]; // Data baris yang diambil dari Google Sheet
rowIndex = data[2]; // Simpan indeks baris yang sebenarnya
let idx = 0;
$.each(colnm, function(key, value) {
let colname = value[0];
let coltype = value[2]; // Tipe field (misal: 'file')
let colvalue = row[idx];
idx++;
if (coltype === 'file') {
// Jika tipe file, tampilkan link untuk melihat file
$('#'+colname).parent().append(colvalue);
} else {
// Field biasa, tampilkan nilai di input
$('#'+colname).val(colvalue);
}
});
$('#DivForm').show(); // Tampilkan form
}).getASOpenById(id); // Panggil fungsi dengan ID
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
</script>
</body>
</html>
12. Copy dan pastekan script di bawah ini ke suratmasuk.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<?!= include('csstable')?>
</head>
<body onload="getFilteredData(); loadUserId();">
<span class="h1"><strong>SURAT MASUK</strong></span>
<div id="DivTable" class="mt-2 col-12 shadow rounded-4 rounded bg-light p-2" style="height:420px; overflow:auto">
<table class="table table-bordered">
<thead id="myTableHead" class="mycolor text-white"></thead>
<tbody id="myTableBody"></tbody>
</table>
</div>
<div id="DivModal" class="formBackground">
<div class="card col-4 mx-auto mt-4 shadow rounded-4 rounded">
<div class="card-header mycolor text-white">Approval Form</div>
<div class="card-body">
<form id="ApprovalForm">
<div class="form-group mb-3">
<label for="approvalDate">Tanggal:</label>
<input type="date" class="form-control" id="approvalDate">
</div>
<div id="approvalActions">
<button type="button" class="btn btn-success" onclick="approve('approved')">Terima</button>
<button type="button" class="btn btn-danger" onclick="showRejectField()">Tolak</button>
</div>
<div id="rejectionNote" style="display:none;" class="form-group">
<label for="rejectionNoteText">Keterangan:</label>
<textarea class="form-control" id="rejectionNoteText"></textarea>
<button type="button" class="btn btn-danger mt-2" onclick="approve('rejected')">Submit Tolak</button>
</div>
<br>
<div class="row justify-content-end px-2">
<button type="button" class="btn btn-secondary" onclick="closeModal()">Tutup</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<script>
let selectedId = null;
function approve(status) {
let date = document.getElementById('approvalDate').value;
let note = status === 'rejected' ? document.getElementById('rejectionNoteText').value : '';
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
if (response === "success") {
alert("Data berhasil diproses.");
getFilteredData(); // Refresh the table
} else {
alert("Error processing data.");
}
}).approveData(selectedId, status, date, note);
getFilteredData(); // Refresh table after submit
setTimeout(() => { closeModal(); }, 2000); // Close form after submit
}
function getFilteredData() {
google.script.run.withSuccessHandler(function(data) {
let thead = data[0];
let tbody = data[1];
let myRows = "";
$.each(thead, function(key, row) {
myRows += "<tr>";
$.each(row, function(key, cell) {
myRows += "<th>" + cell + "</th>";
});
myRows += "<th style='width:160px'></th>"; // Kolom untuk tombol Approval
myRows += "</tr>";
});
$("#myTableHead").html(myRows);
myRows = "";
$.each(tbody, function(index, row) {
myRows += "<tr>";
$.each(row, function(key, cell) {
if (key === row.length - 1 && cell.startsWith('http')) {
// Jika cell berisi URL, tampilkan sebagai link
myRows += "<td><a href='" + cell + "' target='_blank'>Lihat File</a></td>";
} else {
myRows += "<td>" + cell + "</td>";
}
});
myRows += "<td><button class='btn btn-outline-primary btn-sm' onclick='openApprovalModal(\"" + row[0] + "\")'>Approval</button></td>";
myRows += "</tr>";
});
$("#myTableBody").html(myRows);
}).getFilteredSuratMasuk();
}
function openApprovalModal(id) {
selectedId = id;
$('#DivModal').show();
document.getElementById('rejectionNote').style.display = 'none'; // Reset to hide textarea
}
function closeModal() {
$('#DivModal').hide();
}
function showRejectField() {
document.getElementById('rejectionNote').style.display = 'block';
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<?!= include('csstable')?>
</head>
<body onload="getFilteredData(); loadUserId();">
<span class="h1"><strong>SURAT MASUK</strong></span>
<div id="DivTable" class="mt-2 col-12 shadow rounded-4 rounded bg-light p-2" style="height:420px; overflow:auto">
<table class="table table-bordered">
<thead id="myTableHead" class="mycolor text-white"></thead>
<tbody id="myTableBody"></tbody>
</table>
</div>
<div id="DivModal" class="formBackground">
<div class="card col-4 mx-auto mt-4 shadow rounded-4 rounded">
<div class="card-header mycolor text-white">Approval Form</div>
<div class="card-body">
<form id="ApprovalForm">
<div class="form-group mb-3">
<label for="approvalDate">Tanggal:</label>
<input type="date" class="form-control" id="approvalDate">
</div>
<div id="approvalActions">
<button type="button" class="btn btn-success" onclick="approve('approved')">Terima</button>
<button type="button" class="btn btn-danger" onclick="showRejectField()">Tolak</button>
</div>
<div id="rejectionNote" style="display:none;" class="form-group">
<label for="rejectionNoteText">Keterangan:</label>
<textarea class="form-control" id="rejectionNoteText"></textarea>
<button type="button" class="btn btn-danger mt-2" onclick="approve('rejected')">Submit Tolak</button>
</div>
<br>
<div class="row justify-content-end px-2">
<button type="button" class="btn btn-secondary" onclick="closeModal()">Tutup</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Loading -->
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Processing...</p>
</div>
</div>
</div>
</div>
<script>
let selectedId = null;
function approve(status) {
let date = document.getElementById('approvalDate').value;
let note = status === 'rejected' ? document.getElementById('rejectionNoteText').value : '';
$('#loadingModal').modal('show');
google.script.run.withSuccessHandler(function(response) {
$('#loadingModal').modal('hide');
if (response === "success") {
alert("Data berhasil diproses.");
getFilteredData(); // Refresh the table
} else {
alert("Error processing data.");
}
}).approveData(selectedId, status, date, note);
getFilteredData(); // Refresh table after submit
setTimeout(() => { closeModal(); }, 2000); // Close form after submit
}
function getFilteredData() {
google.script.run.withSuccessHandler(function(data) {
let thead = data[0];
let tbody = data[1];
let myRows = "";
$.each(thead, function(key, row) {
myRows += "<tr>";
$.each(row, function(key, cell) {
myRows += "<th>" + cell + "</th>";
});
myRows += "<th style='width:160px'></th>"; // Kolom untuk tombol Approval
myRows += "</tr>";
});
$("#myTableHead").html(myRows);
myRows = "";
$.each(tbody, function(index, row) {
myRows += "<tr>";
$.each(row, function(key, cell) {
if (key === row.length - 1 && cell.startsWith('http')) {
// Jika cell berisi URL, tampilkan sebagai link
myRows += "<td><a href='" + cell + "' target='_blank'>Lihat File</a></td>";
} else {
myRows += "<td>" + cell + "</td>";
}
});
myRows += "<td><button class='btn btn-outline-primary btn-sm' onclick='openApprovalModal(\"" + row[0] + "\")'>Approval</button></td>";
myRows += "</tr>";
});
$("#myTableBody").html(myRows);
}).getFilteredSuratMasuk();
}
function openApprovalModal(id) {
selectedId = id;
$('#DivModal').show();
document.getElementById('rejectionNote').style.display = 'none'; // Reset to hide textarea
}
function closeModal() {
$('#DivModal').hide();
}
function showRejectField() {
document.getElementById('rejectionNote').style.display = 'block';
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
</script>
</body>
</html>
13. Copy dan pastekan script di bawah ini ke trackingsurat.html
Masukkan Password Untuk Melihat Script (Password sama dengan di atas)
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<?!= include('csstable')?>
<!-- font-awesome@6.2.0 icon Visit -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<?!= include('visit')?>
</head>
<body onload="getFilteredData(); loadUserId();">
<div class="p-1">
<span class="h1">TRACKING SURAT</span>
</div>
<div id="DivTable" class=" mt-2 col-12 shadow rounded-4 rounded bg-light p-2 " style="height:420px; overflow:auto" >
<table class="table table-bordered">
<thead id="myTableHead" class="mycolor text-white"></thead>
<tbody id="myTableBody"></tbody>
</table>
</div>
<script>
function getFilteredData() {
google.script.run.withSuccessHandler(function(data) {
let thead = data[0];
let tbody = data[1];
let myRows = "";
$.each(thead, function(key, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
myRows = myRows + "<th>" + cell + "</th>";
});
myRows += "</tr>";
});
$("#myTableHead").html(myRows);
myRows = "";
$.each(tbody, function(index, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
myRows = myRows + "<td>" + cell + "</td>";
});
myRows = myRows + "</tr>";
});
$("#myTableBody").html(myRows);
}).getFilteredTracking();
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= includeHeader(); ?>
<?!= include('csstable')?>
<!-- font-awesome@6.2.0 icon Visit -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<?!= include('visit')?>
</head>
<body onload="getFilteredData(); loadUserId();">
<div class="p-1">
<span class="h1">TRACKING SURAT</span>
</div>
<div id="DivTable" class=" mt-2 col-12 shadow rounded-4 rounded bg-light p-2 " style="height:420px; overflow:auto" >
<table class="table table-bordered">
<thead id="myTableHead" class="mycolor text-white"></thead>
<tbody id="myTableBody"></tbody>
</table>
</div>
<script>
function getFilteredData() {
google.script.run.withSuccessHandler(function(data) {
let thead = data[0];
let tbody = data[1];
let myRows = "";
$.each(thead, function(key, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
myRows = myRows + "<th>" + cell + "</th>";
});
myRows += "</tr>";
});
$("#myTableHead").html(myRows);
myRows = "";
$.each(tbody, function(index, row) {
myRows = myRows + "<tr>";
$.each(row, function(key, cell) {
myRows = myRows + "<td>" + cell + "</td>";
});
myRows = myRows + "</tr>";
});
$("#myTableBody").html(myRows);
}).getFilteredTracking();
}
function loadUserId() {
google.script.run.withSuccessHandler(function(userId) {
document.getElementById("userIdDisplay").innerHTML = userId ? userId : "Guest";
}).getUserId();
}
</script>
</body>
</html>
14. Untuk script :
- visit.html
- csstable.html
- cssmain.html
- cssloading.html
Sudah include / script sudah disediakan di dalam project.
15. Klik ikon Save.
16. Klik tombol Terapkan/Deploy lalu Deployment baru/New deployment.
17. Pastikan jenisnya adalah Aplikasi web, hak aksesnya adalah Siapa saja/Anyone, lalu klik Terapkan/Deploy.
18. Lakukan otorisasi perijinan jika dipelukan (Tutorial di dalam video)
19. Silahkan klik URL atau salin dan bagikan URL yang sudah di Deploy
20. Website siap digunakan.
SELESAI !!!