https://a.storyblok.com/f/270183/98502/35eb648eca/office-hours-reservation.png

Reserva de horários de atendimento usando Node.js e a Messages API do Vonage

Publicado em August 16, 2022

Tempo de leitura: 7 minutos

Introdução

Neste tutorial, vamos criar um aplicativo web que permite que os alunos reservem horários para atendimento com seus professores. Assim que a reserva for confirmada, o aluno receberá uma mensagem de texto confirmando a reserva por meio da Messages API do Vonage.

Vamos desenvolvê-lo usando Node.js, Express, SQLite e a Messages API do Vonage. O repositório do GitHub para este projeto também está disponível.

Criação de um aplicativo web

Página inicial

Precisaremos criar três modelos HTML para servir como nosso formulário de entrada (a página inicial), página de erro e página de confirmação. Depois de criarmos esses modelos, passaremos para o “motor” que impulsiona essa aplicação web em Node.js. Para começar, criaremos uma página inicial onde o usuário poderá inserir informações como nome, número de telefone, data desejada, horário preferido e quaisquer observações ou comentários por meio de um formulário web básico. Esse formulário será enviado à nossa aplicação web para processamento.

Vamos começar criando um index.html arquivo que inclua o título da nossa página, importe nosso estilo CSS e um formulário básico da web para coletar informações dos alunos. Ele deve ficar assim:

<html>
  <head>
    <title>Office Hours Reservation</title>
    <link type="text/css" rel="stylesheet" href="/assets/indexstyles.css" />
    <link
      rel="stylesheet"
      href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"
    />
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  </head>

  <body>
    <h1>Office Hours Reservation Form</h1>
    <div id="statusdiv">Current Status</div>
    <form
      id="scheduleform"
      name="scheduleform"
      method="post"
      action="/schedule"
    >
      <div id="FormInfo">
        <div class="FormTextBox">
          <div id="alignRight">Select Date for Reservation:</div>
          <div>
            <script>
              $(function () {
                $("#datepicker").datepicker();
              });
            </script>
            <input
              type="text"
              name="AppointmentDate"
              value="7/15/2022"
              id="datepicker"
            />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Select Professor:</div>
          <div>
            <select name="ProfessorName">
              <option value="Professor Nimoy">Professor Nimoy</option>
              <option value="Dr. Rory">Dr. Rory</option>
              <option value="Professor Amanda">Professor Amanda</option>
              <option value="Professor Dwane">Professor Dwane</option>
              <option value="Dr. Zach">Dr. Zach</option>
            </select>
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Choose Appointment Time:</div>
          <div>
            <label>
              <input type="radio" name="AppointmentTime" value="8" /> 8:00 AM
            </label>
            <label>
              <input type="radio" name="AppointmentTime" value="9" /> 9:00 AM
            </label>
            <label>
              <input type="radio" name="AppointmentTime" value="10" /> 10:00 AM
            </label>
            <label>
              <input type="radio" name="AppointmentTime" value="11" /> 11:00 AM
            </label>
          </div>
        </div>
        <!--FormTextAreaBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student First Name:</div>
          <div>
            <input
              type="text"
              name="studentfirstname"
              id="studentfirstname_id"
            />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Last Name:</div>
          <div>
            <input type="text" name="studentlastname" id="studentlastname_id" />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Phone Number:</div>
          <div>
            <input
              type="text"
              name="studentphonenumber"
              id="studentphonenumber_id"
            />
          </div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Comments, Questions, Notes...</div>
          <div>
            <textarea
              name="studentnotes"
              id="studentnotes_id"
              style="width: 300px; height: 100px"
            >
            </textarea>
          </div>
        </div>
        <!--FormTextAreaBox-->
        <div style="text-align: center">
          <button type="submit" name="booknowbutton" class="BigButton">
            Book Now
          </button>
        </div>
      </div>
      <!--FormInfo-->
    </form>
  </body>
</html>

No nosso exemplo, incluímos um campo de entrada para a data da reserva, um grupo de botões de opção para o horário da reserva, uma lista suspensa para selecionar as opções de professores, além do nome do aluno, do número de celular do aluno e de um campo para comentários.

Por fim, crie um botão de envio chamado “Reservar agora” para enviar o conteúdo do formulário. Estamos enviando esse formulário para o nosso servidor web neste URL: /postschedule, o que discutiremos mais detalhadamente em nosso tutorial.

Página de erro

Caso o usuário insira dados inválidos ou haja um erro ao processar a solicitação, exibiremos uma página de erro em HTML informando ao usuário o que deu errado. Trata-se de uma página HTML simples que será processada pelo Node.js e exibirá variáveis específicas do lado do servidor (por meio do Nunjucks) que definimos de acordo com o tipo de problema ocorrido. Aqui está um exemplo de como nossa error.html página se parece:

<html>
  <head>
    <title>There is an Error processing your request</title>

    <link type="text/css" rel="stylesheet" href="/assets/errorstyles.css" />
    <link
      rel="stylesheet"
      href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"
    />
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  </head>

  <body>
    <h1>There was an Error processing your request</h1>
    <div
      id="statusdiv"
    >
      Please correct the following:
      <ul>
        {{ErrorMessage}}
      </ul>
    </div>
    <form
      id="scheduleform"
      name="scheduleform"
      method="post"
      action="/schedule"
    >
      <div id="FormInfo">
        <div class="FormTextBox">
          <div id="alignRight">Date for Reservation:</div>
          <div class="readonlydata">{{AppointmentDate}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Appointment Time:</div>
          <div class="readonlydata">{{AppointmentTime}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div class="FormTextBox">
          <div id="alignRight">Professor:</div>
          <div class="readonlydata">{{ProfessorName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Name:</div>
          <div class="readonlydata">{{StudentName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Phone Number:</div>
          <div class="readonlydata">{{StudentPhoneNumber}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Comments, Questions, Notes...</div>
          <div class="readonlydata">{{StudentNotes}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div id="returnButton">
          <a href="/" class="BigButton"> Return to Reservation Form </a>
        </div>
      </div>
      <!--FormInfo-->
    </form>
  </body>
</html>

Nossa página de erro renderizada ficará assim:

Error pageError page

As variáveis entre colchetes duplos, por exemplo: {{AppointmentDate}}, são as variáveis do lado do servidor que são exibidas pelo Node.js. Discutiremos essa configuração mais adiante neste tutorial. Por fim, criaremos um botão “Voltar”. Esse botão levará o usuário de volta à página inicial caso ocorra um erro ao processar sua solicitação de reserva.

Página de confirmação

Agora que já temos uma resposta de erro, precisamos também configurar uma resposta de confirmação. Essa página será exibida apenas se a solicitação for bem-sucedida. Da mesma forma, essa página exibirá todas as informações inseridas pelo usuário. Para começar, vamos criar um confirmation.html arquivo e configurá-lo de forma semelhante ao nosso error.html arquivo com variáveis do lado do servidor (Nunjucks). Aqui está um exemplo de como nossa confirmation.html página:

<html>
  <head>
    <title>Your Reservation has been confirmed</title>
    <link
      type="text/css"
      rel="stylesheet"
      href="/assets/confirmationstyles.css"
    />
    <link
      rel="stylesheet"
      href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css"
    />
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  </head>
  <body>
    <h1>Your Reservation has been confirmed</h1>
    <form
      id="scheduleform"
      name="scheduleform"
      method="post"
      action="/schedule"
    >
      <div id="FormInfo">
        <div class="FormTextBox">
          <div id="alignRight">Date for Reservation:</div>
          <div class="readonlydata">{{AppointmentDate}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Appointment Time:</div>
          <div class="readonlydata">{{AppointmentTime}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div class="FormTextBox">
          <div id="alignRight">Professor:</div>
          <div class="readonlydata">{{ProfessorName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Name:</div>
          <div class="readonlydata">{{StudentName}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextBox">
          <div id="alignRight">Student Phone Number:</div>
          <div class="readonlydata">{{StudentPhoneNumber}}</div>
        </div>
        <!--FormTextBox-->
        <div class="FormTextAreaBox">
          <div>Comments, Questions, Notes...</div>
          <div class="readonlydata">{{StudentNotes}}</div>
        </div>
        <!--FormTextAreaBox-->
        <div id="returnButton">
          <a href="/" class="BigButton"> Return to Reservation Form </a>
        </div>
      </div>
      <!--FormInfo-->
    </form>
  </body>
</html>

Nossa página de confirmação renderizada ficará assim:

Confirmation pageConfirmation page

Observe que nossas variáveis do lado do servidor serão derivadas do formulário enviado pelo aluno e são as seguintes: AppointmentDate, AppointmentTime, ProfessorName, StudentName, StudentPhoneNumber, e StudentNotes. Por fim, criaremos um botão “Voltar”. Esse botão levará o usuário de volta à página inicial caso ele deseje criar outra solicitação de reserva.

Back-end

Opção de mecânica: Dotenv

Antes de começarmos a implementação do backend, precisamos criar um arquivo chamado .env. Esse arquivo é extremamente importante, pois armazena nossa chave secreta (API Secret) e nossa chave de API (API Key) privadas. Essas variáveis nos permitem enviar nossa mensagem de confirmação usando a Messages API da Vonage. Comece criando um novo arquivo chamado .env e colocá-lo no diretório raiz do seu projeto. Agora, adicione as seguintes variáveis com as informações específicas da sua conta da Vonage: VONAGE_API_KEY, VONAGE_API_SECRET, e FROM_PHONE_NUMBER.

VONAGE_API_KEY= VONAGE_API_SECRET= FROM_PHONE_NUMBER=

Configurando o Node.js como servidor de aplicativos web

Para concluir nossa aplicação web, vamos adicionar funcionalidades aos nossos arquivos HTML usando Node.js, Express e SQLite. Para simplificar, colocaremos todas as funcionalidades da nossa aplicação web em um único arquivo chamado index.js. Assim que o Node.js estiver instalado com sucesso em nosso sistema, precisaremos adicionar mais alguns pacotes à configuração: Express, Nunjucks, Fetch e SQLite.

npm install express 

npm install nunjucks 

npm install node-fetch 

npm install sqlite3

npm install dotenv 

npm install @vonage/server-sdk

Configuramos nosso index.js arquivo para usar as bibliotecas recém-instaladas e dar ao nosso aplicativo a capacidade de servir páginas da web na porta 3000. O início do nosso index.js arquivo é assim:

require("dotenv").config();
const express = require("express");
const nunjucks = require("nunjucks");
const webserver = express();
const Vonage = require("@vonage/server-sdk");
const SMS = require("@vonage/server-sdk/lib/Messages/SMS");

const fetch = (...args) =>
  import("node-fetch").then(({ default: fetch }) => fetch(...args));

webserver.use("/assets", express.static("assets"));

nunjucks.configure("html", { autoescape: false, express: webserver });

webserver.use(express.json());

webserver.use(express.urlencoded({ extended: true }));

// Global database
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database("reservations.db");

const vonage = new Vonage({
  apiKey: process.env.VONAGE_API_KEY,
  apiSecret: process.env.VONAGE_API_SECRET,
});

webserver.listen(3000);

Algumas observações sobre o código:

  • Estamos acessando o servidor web da instância do Express

  • Colocamos nossos três arquivos HTML (index.html, error.html, confirmation.html) em um subdiretório chamado HTML, que o Nunjucks está configurado para acessar.

  • Configuramos o SQLite para usar um banco de dados chamado reservations.db que estará no diretório raiz desta aplicação web.

  • Estamos ocultando as variáveis “API Key” e “API Secret”, que nos foram fornecidas pela Messages API do Vonage, em um .env arquivo, mas você precisará substituir essas variáveis pelas suas próprias credenciais seguras.

Estamos configurando o servidor web para ouvir solicitações na porta 3000. Isso significa que, para executar este aplicativo, você precisará especificar essa porta; por exemplo, http://localhost:3000.

Utilizando o Node.js como servidor de aplicativos web

Configuramos nosso index.js código para oferecer três funções:

  • Configure o banco de dados para armazenar solicitações de reserva (reservations.db)

  • Exibir uma página inicial na qual o aluno possa enviar uma solicitação de agendamento (index.html)

  • Processar a solicitação de reserva do aluno enviada para /schedule e, após a análise do envio, responder com sucesso (confirmation.html) ou erro (error.html)

SetUpDatabase();

DisplayHomePage();

PostSchedule();

Configurando o banco de dados

Neste projeto, estamos usando o SQLite como banco de dados de back-end para armazenar nossas informações. No entanto, você pode usar qualquer banco de dados compatível com o Node.js.

function SetUpDatabase() {
  db.serialize(() => {
    db.run(
      "CREATE TABLE if not exists Appointments (appointmentdate text,    appointmenttime text, professorname text, studentfirstname text, studentlastname text, studentphonenumber text, studentnotes text)"
    );
  });
}

Algumas observações sobre essa função:

  • Isso verificará se existe uma tabela de compromissos no reservations.db banco de dados; caso a tabela não exista, ela será criada

  • Dependendo do sistema de banco de dados que você decidir usar com o Node.js, será necessário ajustar a data e a hora do compromisso para que sejam compatíveis com as funções de data e hora desse sistema

Configurando a página inicial

Nossa DisplayHomePage() função simplesmente aguarda a solicitação na raiz do diretório (‘/’) e, em seguida, renderiza nosso index.html arquivo encontrado no diretório HTML. Você deve se lembrar que já configuramos o index.html arquivo para ser o formulário de reserva de horário de atendimento.

function DisplayHomePage() {
  // Displaying Homepage after reading an HTML file locally

  webserver.get("/", function (req, res) {
    res.render("index.html");
  });
}

Configurando a função de agendamento de publicações

PostSchedule() é o motor principal do nosso aplicativo web. Esse processo ficará à espera do /schedule handler e realizará algumas verificações básicas de erros nas entradas do usuário. Se os dados enviados passarem pelo nosso processo rudimentar de verificação de erros, salvaremos essas informações em nossa tabela de agendamentos no reservations.db banco de dados. Em seguida, encapsulamos essas informações em um objeto JSON que enviamos à Messages API do back-end da Vonage para o envio de uma mensagem de texto de confirmação.

function PostSchedule() {
  // POST /new Route Handler
  webserver.post("/schedule", function (req, res) {
    // Student info/variables collected from HTML form

    let professorname = req.body.ProfessorName;
    let appointmentdate = req.body.AppointmentDate;
    let appointmenttime = req.body.AppointmentTime;
    let studentfirstname = req.body.studentfirstname;
    let studentlastname = req.body.studentlastname;
    let studentphonenumber = req.body.studentphonenumber;
    let studentnotes = req.body.studentnotes;

    // Text message info sent to Student
    let message =
      "Dear " +
      studentfirstname +
      " " +
      studentlastname +
      "," +
      " you have successfully booked your appointment! Here are your additional notes: " +
      studentnotes;

    let ValidationCheck = true; // Assume success, prove otherwise
    let ErrorMessage = "";

    if (appointmentdate == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter an appointment date</li>";
    } else if (appointmenttime == "" || !appointmenttime) {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter an appointment time</li>";
    } else if (professorname == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Select a Professor</li>";
    } else if (studentfirstname == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter a Student first name</li>";
    } else if (studentlastname == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter a Student last name</li>";
    } else if (studentphonenumber == "") {
      ValidationCheck = false;
      ErrorMessage = ErrorMessage + "<li>Enter a mobile phone number</li>";
    }

    if (ValidationCheck == false) {
      res.render("error.html", {
        ProfessorName: professorname,
        StudentName: studentfirstname + " " + studentlastname,
        AppointmentDate: appointmentdate,
        AppointmentTime: appointmenttime,
        StudentNotes: studentnotes,
        StudentPhoneNumber: studentphonenumber,
        ErrorMessage: ErrorMessage,
      });
    } else {

Algumas observações sobre este código:

  • Nossa instância do Express chamada “web server” está à espera de uma solicitação POST enviada para /schedule e só é executada caso essa condição se verifique

  • Criamos as seguintes variáveis do Node.js para os pares nome/valor correspondentes do formulário de solicitação em HTML:

professorname = req.body.ProfessorName
  appointmentdate = req.body.AppointmentDate
  appointmenttime = req.body.AppointmentTime
  studentfirstname = req.body.studentfirstname
  studentlastname = req.body.studentlastname
  studentphonenumber = req.body.studentphonenumber
  studentnotes = req.body.studentnotes
  • Nossa variável booleana ValidationCheck verifica se há dados com string vazia e é definida como falsa em caso de falha. É recomendável realizar verificações adicionais de erros para outros cenários de dados incorretos, como datas e horários de compromissos inválidos

  • Se ocorrer um erro, exibimos a error.html página, o que inclui definir a variável Nunjucks {{ErrorMessage}} com uma descrição do problema

  • Após ValidationCheck o sucesso (retornando: true) continuaremos a adicionar os dados do agendamento à tabela de agendamentos e enviaremos a confirmação por SMS usando a Messages API da Vonage

Após a validação bem-sucedida dos dados inseridos, agora encapsulamos a mensagem em um objeto JSON que enviaremos à Vonage. Criamos a instância da classe cliente da Vonage, inicializamos com a chave e o segredo da API da Vonage que você adicionou anteriormente ao seu arquivo .env, e as variáveis necessárias são o número de telefone, o número de telefone de destino da mensagem de texto, o texto da mensagem e nossa chave e segredo da API.

else {

    vonage.messages.send(
      new SMS(message, studentphonenumber, process.env.FROM_PHONE_NUMBER),
      (err, data) => {
        if (err) {
          console.error(err);
        } else {
          console.log(data.message_uuid);
        }
      }
    );

    // Build SQL string --> insert string to add record to appointments table
    let sqlstring =
      "INSERT INTO Appointments (appointmentdate, appointmenttime, professorname, studentfirstname, studentlastname, studentphonenumber, studentnotes) " +
      "VALUES (?, ?, ?, ?, ?, ?, ?)";

    // Execute SQL string into database (Using paramaterized queries to prevent SQL injection)
    db.run(
      sqlstring,
      appointmentdate,
      appointmenttime,
      professorname,
      studentfirstname,
      studentlastname,
      studentphonenumber,
      studentnotes
    );
    
    // For testing purposes query all records in appointments table and display to console
    // The last record shown should be the record we just inserted
    db.each(
      "SELECT appointmentdate, appointmenttime, professorname, studentfirstname, studentlastname info FROM Appointments",
      (err, row) => {
        console.log(
          row.appointmentdate +
            ": " +
            row.appointmenttime +
            ":" +
            row.professorname
        );
      }
    );

    res.render("confirmation.html", {
      ProfessorName: professorname,
      StudentName: studentfirstname + " " + studentlastname,
      AppointmentDate: appointmentdate,
      AppointmentTime: appointmenttime + ":00 AM",
      StudentNotes: studentnotes,
      StudentPhoneNumber: studentphonenumber,
    });
}

Em seguida, criamos nossa instrução SQL de inserção usando as variáveis definidas a partir do formulário de envio em HTML e executamos essa instrução por meio da db.run função. Por fim, renderizamos a confirmation.html configuração da página para que as variáveis do Nunjucks sejam exibidas.

Próximos passos

Sempre incentivamos a participação da comunidade. Fique à vontade para se juntar a nós no GitHub e na Slack da Comunidade Vonage ou nos enviar uma mensagem no Twitter.

Compartilhar:

https://a.storyblok.com/f/270183/400x520/992948a3b3/zoe-mithaug.png
Zoe MithaugEx-funcionários da Vonage

Zoe Mithaug é uma ex-integrante da equipe da Vonage. Ela foi estagiária na área de Relações com Desenvolvedores na Vonage. Atualmente, ela é aluna do 4º ano de graduação na Universidade da Flórida.

Ela é apaixonada por programação, desenvolvimento de software, desenvolvimento de videogames, animação e design digital.