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

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



*Untuk Mendapatkan Full Script tanpa password silahkan Klik Disini*

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!










Previous Post Next Post

Promo