Kawalpreet Kaur
Kawalpreet Kaur

Reputation: 77

onClick event for Google Forms Submit button

This is driving me crazy, I have a simple Google Form that accepts subscriber code and their slot timings. My purpose is to get data via Google Forms and save it to Google Sheets without having any clashes in the slots timings.

To show you an example, I am attaching an image of my form and sheet. After the first entry was done, if another slot is to be given to same subscriber, we need to ensure that the slot timings do not clash with the saved timings in google sheet that is assigned to the same subscriber. If you see the second entry, it just accepts it and for this we need a validation check which is not available. My question is, is it even possible to have an onClick validation on the submit button? I have been searching for it on google like crazy but haven't got a solution. If this is not possible to do it in Google forms, what are my alternatives or is there any workaround available?

Eagerly awaiting responses for my query. Thank you.

enter image description here

Upvotes: 3

Views: 3801

Answers (2)

Muhammet Yunus
Muhammet Yunus

Reputation: 589

As @Cooper pointed out, it seems best that you create your own web app.

After a few hours of work I've made a web app that will work for you using Google Apps Script. Here are the spreadsheet and script file.

Server Side Codes:

var ss = SpreadsheetApp.getActiveSheet()

function doGet() {
  return HtmlService.createTemplateFromFile('form').evaluate()
}

function submitData (data) {
  
  ss.appendRow([new Date(),data.ssCode, new Date(data.startDateTime), new Date(data.endDateTime)])
}

function isAvailible (start, end) {
  var data = ss.getDataRange().getValues()
  var isAvailible = true;
  var message = '';

  data.forEach(row => {

    if(start > row[2].valueOf() && start < row[3].valueOf() || end > row[2].valueOf() && end < row[3].valueOf()) {
      isAvailible = false;
      message = new Date(row[2].valueOf()) +'<br> to ' + new Date(row[3].valueOf()) + '<br> isn\'t availible.'
    }
  })

  var response = {status: isAvailible, message: message}

  return response;
  
}

Client Side Codes:

M.FormSelect.init(document.querySelectorAll('select'));

var dps = document.querySelectorAll('.datepicker');
var dpsInstances = M.Datepicker.init(dps, {
  showClearBtn: true,
  autoClose: true
});

dpsInstances[0].options.onSelect = function(day) {
  dpsInstances[1].setDate(day);
  endDate.value = dpsInstances[1].toString()
  M.updateTextFields();
}

var tps = document.querySelectorAll('.timepicker');
var tpsInstances = M.Timepicker.init(tps, {
  twelveHour: false,
  showClearBtn: true,
  autoClose: true
});

function formSubmit(data) {

  var startDate = new Date(data.startDate.value);
  var startTime = data.startTime.value.split(':');
  startDate.setHours(startTime[0]);
  startDate.setMinutes(startTime[1]);

  var endDate = new Date(data.endDate.value);
  var endTime = data.endTime.value.split(':')
  endDate.setHours(endTime[0]);
  endDate.setMinutes(endTime[1]);
  
  if (startDate.valueOf() >= endDate.valueOf()) {
    M.toast({html: 'End date should be greater then start date.'})
    return;
  }
  
  var dataToSubmit = {
    ssCode: data.ssCode.value,
    startDateTime: startDate.valueOf(),
    endDateTime: endDate.valueOf()
  }

  google.script.run.withSuccessHandler(function(response) {
    if (response.status) {
      google.script.run.withSuccessHandler(function() {
        myForm.reset()
        M.toast({
          html: "Successful"
        })
      }).submitData(dataToSubmit)
    } else {
      M.toast({
        html: response.message
      })
    }

  }).isAvailible(dataToSubmit.startDateTime, dataToSubmit.endDateTime)


}
body {
  background: rgb(244, 235, 234)
}

.outer-field {
  border-radius: 15px;
  background: white;
  height: 150px;
  margin: 10px;
  padding: 20px;
}

.title {
  padding-left: 2%;
  font-weight: bold;
}
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

  </head>
  <body>
  
    <div class="row">
      <div class="col s10 offset-s1 l4 offset-l4">
        <div class="outer-field" style="height: 100px">
          <h4>Slot Timings</h4>
        </div>
        <form id="myForm" onsubmit="event.preventDefault(); formSubmit(this) ">
          <div class="outer-field">
            <div class="title">Subscriber Code</div>
            <div class="input-field col s6 l6">
              <select form="myForm" name="ssCode" required>
                <option value="001">001</option>
                <option value="002">002</option>
                <option value="003">003</option>
              </select>
            </div>
          </div>
          <div class="outer-field">
            <div class="title">From</div>
            <div class="col s6 l6">
              <div class="input-field">
                <input type="text" id="startDate" name="startDate" class="datepicker validate" form="myForm" required>
                <label for="startDate">Date</label>
              </div>
            </div>
            <div class="col s6 l6">
              <div class="input-field">
                <input type="text" id="startTime" name="startTime" class="timepicker validate" form="myForm" required>
                <label for="startTime">Time</label>
              </div>
            </div>
          </div>
          <div class="outer-field">
            <div class="title">To</div>
            <div class="col s6 l6">
              <div class="input-field">
                <input type="text" id="endDate" name="endDate" class="datepicker" form="myForm" required>
                <label for="endDate">Date</label>
              </div>
            </div>
            <div class="col s6 l6">
              <div class="input-field">
                <input type="text" id="endTime" name="endTime" class="timepicker" form="myForm" required>
                <label for="endTime">Time</label>
              </div>
            </div>
          </div>
          <button class="btn waves-effect waves-light" type="submit" name="action" style="margin-left: 3%" >Submit
            <i class="material-icons right">send</i>
          </button>
        </form>
        
      </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
  </body>
</html>

Upvotes: 4

NightEye
NightEye

Reputation: 11194

Another idea you can try on your current setup is that you could use an onFormSubmit trigger. And once the response is submitted, it is compared to the previous rows that were added to the sheet to check if there have been any conflict with your data.

Upon confirming there is, just remove the last response. If no conflict, then let it be and end the script there.

Sheet Script:

function checkConflict(e) {
  var sheet = e.range.getSheet();
  var lastRow = sheet.getLastRow();
  // exclude the last submitted response
  var timeSlots = sheet.getRange(2, 3, lastRow - 2, 2).getValues();
  var lastResponse = e.values;
  var lastStart = new Date(lastResponse[2]).getTime();
  var lastEnd = new Date(lastResponse[3]).getTime();

  // convert timeslots to comparable data type
  timeSlots = timeSlots.map(timeSlot => [timeSlot[0].getTime(), timeSlot[1].getTime()]);
  // get conflicts
  var conflict = timeSlots.filter(timeSlot => (timeSlot[0] <= lastStart && timeSlot[1] >= lastStart) || 
                                              (timeSlot[0] <= lastEnd && timeSlot[1] >= lastEnd));
  // remove lastRow if conflict is 1
  if(conflict.length) {
    sheet.deleteRow(lastRow);
    MailApp.sendEmail(lastResponse[4], 
                      "Time Slot conflict", 
                      "Your recent response was removed due to a conflict. Your submitted time slot is the following:\n" + 
                      "From: " + new Date(lastStart) + "\n" + 
                      "To: " + new Date(lastEnd));
  }
  else {
    MailApp.sendEmail(lastResponse[4], 
                      "Time Slot confirmed", 
                      "Your time slot is now confirmed. Confirmed time slot is the following:\n " + 
                      "From: " + new Date(lastStart) + "\n" + 
                      "To: " + new Date(lastEnd));
  }
}

Sample Data:

sample

Trigger:

trigger

Emails confirm:

confirm

Emails conflict:

conflict

Output:

output

Note:

  • This approach doesn't check your input before the submission. It just removes the last response afterwards IF there have been conflicts with the previous slot timings to that one.
  • Also, the one who sent the response wouldn't know that his slot was confirmed or not, not unless you ask for his email in the form and send confirmation afterwards the checking (whether his submitted response was removed due to conflict, or approved due to it having no conflict)
  • If you have a time slot of 9:00 - 10:00, it won't accept another response with 9:00 in it since (>=, <=) is inclusive. What you can do is to pick timeslots where starting has 1/31 minute (e.g. 9:01-10:00, 8:31-9:30) or the ending slots has 59/29 minutes (e.g. 9:00-9:59, 8:30-9:29).
  • If that is a hassle, then you need to adjust the conditional on checking conflicts. You should be able to figure it out.

Upvotes: 2

Related Questions