#JP34 Membuat Tanda Tangan Pada Form Input Dengan Apps Script (Signature Unlimited)

Membuat Tanda Tangan Pada Form Input Dengan Apps Script (Signature Unlimited)




1. Silahkan copy Spreadsheet (Klik Disini)


2. Pada file Spreadsheet yang sudah di copy, terdapat 1 sheet yang bernama Responses, dan ada beberapa tabel/kolom yang akan otomatis terisi apabila kita berhasil menginputkan data dari form.


3. Buatlah lembar kerja Apps Script, dengan cara klik menu Ekstensi/Extensions lalu pilih Apps Script.


4. Pada lembar kerja Apps Script terdapat 3 file yaitu
  • backend.gs
  • index.html
  • vuejs.html


5. Copy dan pastekan script di bawah ini ke beckend,gs

Masukkan Password Untuk Melihat Script (Password ada di dalam video)

const SETTINGS = {
  APP_NAME: "JP34 Input Data Dengan Tanda Tangan Dengan Apps Script",
  SHEET_NAME: {
    RESPONSES: "Responses"
  },
  HEADERS: [
    { key: "timestamp", value: "Timestamp" },
    { key: "id", value: "ID" },
    { key: "name", value: "Nama" },
    { key: "email", value: "Email" },
    { key: "phone", value: "No Telp" },
    { key: "gender", value: "Jenis Kelamin" },
    { key: "city", value: "Alamat" },
    { key: "date", value: "Tanggal" },
    { key: "signature", value: "Tanda Tangan" },
  ]
}

function link(filename) {
  return HtmlService.createTemplateFromFile(filename).evaluate().getContent()
}

function doGet() {
  return HtmlService.createTemplateFromFile("index.html")
    .evaluate()
    .setTitle(SETTINGS.APP_NAME)
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
}

function submit(data) {
  data = JSON.parse(data)
  const headers = SETTINGS.HEADERS.map(({value}) => value)
  const id = Utilities.getUuid()
  const signatures = []
  const values = SETTINGS.HEADERS.map(({key}, index) => {
    if (key === "id") return id
    if (key === "timestamp") return new Date()
    if (!key in data) return null
    if (Array.isArray(data[key])) return data[key].join(",")
    if (data[key].startsWith("data:image")) {
      signatures.push(index)
      return SpreadsheetApp.newCellImage().setSourceUrl(data[key]).build().toBuilder()
    }
    return data[key]
  })
  const ws = SpreadsheetApp.getActive().getSheetByName(SETTINGS.SHEET_NAME.RESPONSES) || SpreadsheetApp.getActive().insertSheet(SETTINGS.SHEET_NAME.RESPONSES)
  ws.getRange(1,1, 1, headers.length).setValues([headers])
  const lastRow = ws.getLastRow()
  ws.getRange(lastRow + 1, 1, 1, values.length).setValues([values])
  signatures.forEach(index => {
    ws.getRange(lastRow + 1, index + 1).setValue(values[index])
  })
  return JSON.stringify({success: true, message: `Terima Kasih, Data berhasil di input dengan ! ID: ${id}`})
}



6. Copy dan pastekan script di bawah ini ke index.html

<!DOCTYPE html>
<html>

<head>
  <base target="_top">

  <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>

<body>
  <div id="app">
    <v-app>
      <v-main>
        <v-container>
          <v-form ref="form" @submit.prevent="submit" :disabled="loading">
            <v-card :loading="loading" outlined>
              <v-img class="white--text align-end" height="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8n4NyX5mHYCdP4VSahYIo_3Bq9YSsmn4oO_ZoezeY4VYWYkMMIz0ynW0bEe4Hdc_wexBznUzDSvfM6fLTXjU__UrLXzLcLkfs9BDYQKs5Ivv5WlKytTxEmJbMWKIGf1-pf5waejuaXpZltwcUc4Ml3TmJA0GHSUlyiz1-z96g7t1FK4IPS5bR0o63/s16000/asasa.jpg">
               <br>
               </br>
                <marquee><v-card-subtitle v-if="subtitle" style="color:black;">{{ subtitle }}</v-card-subtitle></marquee>
              </v-img>
              <v-card-text>
                <v-row>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.name"></my-input>
                  </v-col>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.email"></my-input>
                  </v-col>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.phone"></my-input>
                  </v-col>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.gender"></my-input>
                  </v-col>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.city"></my-input>
                  </v-col>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.date"></my-input>
                  </v-col>
                  <v-col cols="12" sm="12" md="6" lg="4" xl="3">
                    <my-input :item="form.signature"></my-input>
                  </v-col>
                  <v-col cols="12">
                    <v-btn type="submit" color="primary" :disabled="loading" large depressed>Submit</v-btn>
                  </v-col>
                </v-row>
              </v-card-text>
          </v-form>
          <v-snackbar v-model="snackbar.show" :color="snackbar.color" bottom>
            {{ snackbar.message }}
            <template v-slot:action="{ attrs }">
              <v-btn color="white" text v-bind="attrs" :timeout="snackbar.timeout" @click="snackbar.show = false">
                <v-icon>mdi-close</v-icon>
              </v-btn>
            </template>
          </v-snackbar>
        </v-container>
      </v-main>
    </v-app>
  </div>

  <?!= link("vuejs.html"); ?>
</body>

<style>.footer,.generic-footer{margin-bottom:98px}@media (min-width:52px){.footer,.generic-footer{margin-bottom:78px}}@media (min-width:52px){.footer,.generic-footer{margin-bottom:56px}}@media (min-width:52px){.footer,.generic-footer{margin-bottom:0}}.disclaimer{position:fixed;z-index:9999999;bottom:0;right:0;border-top:2px solid #ff5c62;text-align:center;font-size:14px;font-weight:400;background-color:#fff;padding:5px 10px 5px 10px}.disclaimer a:hover{text-decoration:underline}@media (min-width:52px){.disclaimer{text-align:right;border-left:2px solid red;border-top-left-radius:10px}}@media (min-width:1920px){.disclaimer{width:20%}}</style><div class="disclaimer">Version.01.05.22 @Copyright <a title="https://www.javabitpro.com/" target="_blank" href="https://www.javabitpro.com/" style="color: black;"><b>www.javabitpro.com</b></a></div>

</html>

7. Copy dan pastekan script di bawah ini ke vuejs.html

<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script>
  const apiCall = function (functionName, params = {}) {
  params = JSON.stringify(params);
  return new Promise((resolve, reject) => {
    google.script.run
      .withSuccessHandler((response) => resolve(JSON.parse(response)))
      .withFailureHandler((error) => reject(error))
      [functionName](params);
  });
};

const getFormData = (form) => {
  const data = {};
  Object.entries(form).forEach(([key, item]) => {
    data[key] = item.value;
  });
  return data
};

const form = {
  name: {
    label: "Nama",
    type: "text",
    value: "",
    disabled: false,
    placeholder: "",
    rules: [(v) => !!v || "Wajib di isi!"],
  },
  email: {
    label: "Email",
    type: "email",
    value: "",
    disabled: false,
    placeholder: "",
    rules: [(v) => !!v || "Wajib di isi!"],
  },
  phone: {
    label: "No Telp",
    type: "tel",
    value: "",
    disabled: false,
    placeholder: "",
    rules: [(v) => !!v || "Wajib di isi!"],
  },
  gender: {
    label: "Jenis Kelamin",
    type: "select",
    value: "",
    items: ["Laki-Laki", "Perempuan"],
    disabled: false,
    placeholder: "",
    rules: [(v) => !!v || "Wajib di isi!"],
  },
  city: {
    label: "Alamat",
    type: "select",
    value: "",
    items: ["Jember", "Banyuwangi", "Lumajang", "Surabaya", "Jakarta"],
    disabled: false,
    placeholder: "",
    rules: [(v) => !!v || "Wajib di isi!"],
  },
  date: {
    label: "Tanggal",
    type: "date",
    value: "",
    disabled: false,
    placeholder: "",
    rules: [(v) => !!v || "Wajib di isi!"],
  },
  signature: {
    label: "Tanda Tangan",
    type: "signature",
    value: "",
    disabled: false,
    placeholder: "Klik untuk membuka lembar tanda tangan",
    items: [],
    rules: [(v) => !!v || "Wajib di isi!"],
  },
};

const MySnackbar = Vue.component("my-snackbar", {
  template: `
    
  `,
  props: {
    show: true,
    message: "",
    color: "",
  },
  data: () => ({
    snackbar: this.show,
    timeout: 5000,
  })
})

const MySignature = Vue.component("my-signature", {
  template: `
    <div>
      <v-select
        v-model="item.value"
        :label="item.label"
        :placeholder="item.placeholder"
        :rules="item.rules"
        :type="item.type"
        :items="item.items"
        @click="openPad"
        small-chips
        filled
        ></v-select>
      <v-dialog
        v-model="dialog"
        width="400"
        eager
      >
        <v-card>
        <v-card-title class="text-h5 primary white--text">
           Lembar {{item.label}} 
          </v-card-title>
          <v-card-text class="pa-0">
            <canvas :ref="item.label" width="400" height="140"/>
          </v-card-text>
          <v-divider></v-divider>
          <v-card-actions>
            <v-spacer></v-spacer>
            <v-btn
              color="primary"
              text
              @click="savePad"
            >
              Selesai
            </v-btn>
            <v-btn
              color="error"
              text
              @click="clearPad"
            >
              Hapus
            </v-btn>
            <v-btn
              color="grey"
              text
              @click="closePad"
            >
              Tutup
            </v-btn>
          </v-card-actions>
        </v-card>
      </v-dialog>
    </div>
  `,
  props: {
    item: Object,
  },
  data: () => ({
    dialog: false,
    show: false,
    signaturePad: null,
  }),
  methods: {
    openPad: function(){
      this.dialog = true
      const label = this.item.label
      this.signaturePad = new SignaturePad(this.$refs[label])
      if (this.item.value) this.signaturePad.fromDataURL(this.item.value)
    },
    closePad: function(){
      this.dialog = false
    },
    clearPad(){
      this.signaturePad.clear()
    },
    savePad(){
      if (this.signaturePad.isEmpty()) {
        this.item.value = null
        this.item.items = []
      } else {
        this.item.value = this.signaturePad.toDataURL()
        this.item.items = [{
          text: `Signed at ${new Date().toLocaleString()}`,
          value: this.item.value
        }]
      }
      this.signaturePad.clear() 
      this.dialog = false
    }
  }
})

const MyInput = Vue.component("my-input", {
  components: {MySignature},
  template: `
    <my-signature v-if="item.type === 'signature' ":item="item"></my-signature>
    <v-autocomplete
      v-else-if="item.type === 'select'"
      v-model="item.value"
      :label="item.label"
      :placeholder="item.placeholder"
      :rules="item.rules"
      :type="item.type"
      :items="item.items"
      :multiple="item.multiple"
      small-chips
      filled
      ></v-autocomplete>
    <v-text-field
      v-else
      v-model="item.value"
      :label="item.label"
      :placeholder="item.placeholder"
      :rules="item.rules"
      :type="item.type"
      filled
      ></v-text-field>
    `,
  props: {
    item: Object,
  },
});



new Vue({
  el: "#app",
  vuetify: new Vuetify(),
  data: () => ({
    loading: false,
    title: "JP34 Input Data Dengan Tanda Tangan Dengan Apps Script",
    subtitle: "Source : www.javabitpro.com",
    form,
    snackbar: {
      show: false,
      message: "",
      color: "red",
      timeout: 5000,
    },
  }),
  methods: {
    showSnackbar: function({message, color}){
      this.snackbar.message = message
      this.snackbar.color = color
      this.snackbar.show = true
    },
    submit: async function () {
      if (!this.$refs.form.validate()) {
        return this.showSnackbar({message: "Form Tidak Valid", color: "warning"})
      } 
      this.loading = true;
      const data = getFormData(this.form);
      try {
        const result = await apiCall("submit", data);
        this.loading = false;
        this.$refs.form.reset()
        this.showSnackbar({message: result.message, color: "success"})
      } catch (error) {
        this.loading = false;
        this.showSnackbar({message: error.message, color: "error"})
      }
    },
  },
});
</script>

8. Klik ikon Save 


9. Klik tombol Deploy/Terapkan lalu pilih Deployment baru/New Deployment.


10. Pastikan jenis aplikasi yang aktif adalah Aplikasi Web dan Add on, serta hak akses adalah Siapa saja/Anyone lalu klik Terapkan.


11. Klik link atau copy link yang sudah di Deploy.







SELESAI!











Project #JP34 Membuat Tanda Tangan Pada Form Input Dengan Apps Script (Signature Unlimited)
Harga(IDR) Rp 45.000 Rp 29.000
Download
Memuat tombol...
Previous Post Next Post