#JP95 Website Surat Masuk, Surat Keluar, Approve, Tracking Surat Menggunakan Google Apps Script

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
  };
}


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>


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>


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>


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>&copy; 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>


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>


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>


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 !!!















Previous Post Next Post

Promo