Rendi le email più fruibili con i componenti aggiuntivi di Google Workspace

1. Panoramica

In questo codelab, utilizzerai Google Apps Script per scrivere un componente aggiuntivo Google Workspace per Gmail che consente agli utenti di aggiungere i dati delle ricevute da un'email a un foglio di lavoro direttamente in Gmail. Quando un utente riceve una ricevuta via email, apre il componente aggiuntivo, che recupera automaticamente le informazioni sulle spese pertinenti dall'email. L'utente può modificare le informazioni sulle spese e inviarle per registrarle in un foglio di lavoro Fogli Google.

Obiettivi didattici

  • Creare un componente aggiuntivo di Google Workspace per Gmail utilizzando Google Apps Script
  • Analizzare un'email con Google Apps Script
  • Interagire con Fogli Google tramite Google Apps Script
  • Memorizzare i valori degli utenti utilizzando il servizio Properties di Google Apps Script

Che cosa ti serve

  • Accesso a internet e a un browser web
  • Un Account Google
  • Alcuni messaggi, preferibilmente ricevute via email, in Gmail

2. recupera il codice campione

Mentre procedi con questo codelab, potrebbe essere utile fare riferimento a una versione funzionante del codice che scriverai. Il repository GitHub contiene codice di esempio che puoi utilizzare come riferimento.

Per ottenere il codice campione, dalla riga di comando, esegui:

git clone https://github.com/googleworkspace/gmail-add-on-codelab.git

3. Creare un componente aggiuntivo di base

Inizia scrivendo il codice per una versione semplice del componente aggiuntivo che mostra un modulo di spesa accanto a un'email.

Per prima cosa, crea un nuovo progetto Apps Script e apri il relativo file manifest.

  1. Vai a script.google.com. Da qui puoi creare, gestire e monitorare i tuoi progetti Apps Script.
  2. Per creare un nuovo progetto, in alto a sinistra, fai clic su Nuovo progetto. Il nuovo progetto si apre con un file predefinito denominato Code.gs. Per il momento, lascia Code.gs così com'è. Ci lavorerai in un secondo momento.
  3. Fai clic su Progetto senza titolo, assegna al progetto il nome Expense It! e fai clic su Rinomina.
  4. A sinistra, fai clic su Impostazioni progetto Impostazioni progetto.
  5. Seleziona la casella di controllo Mostra il file manifest "appscript.json" nell'editor.
  6. Fai clic su Editor Editor.
  7. Per aprire il file manifest, fai clic su appscript.json a sinistra.

In appscript.json, specifica i metadati associati al componente aggiuntivo, ad esempio il nome e le autorizzazioni richieste. Sostituisci i contenuti di appsscript.json con queste impostazioni di configurazione:

{
  "timeZone": "GMT",
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute"
  ],
  "gmail": {
    "name": "Expense It!",
    "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
    "contextualTriggers": [{
      "unconditional": {
      },
      "onTriggerFunction": "getContextualAddOn"
    }],
    "primaryColor": "#41f470",
    "secondaryColor": "#94f441"
  }
}

Presta particolare attenzione alla parte del manifest chiamata contextualTriggers. Questa parte del manifest identifica la funzione definita dall'utente da chiamare quando il componente aggiuntivo viene attivato per la prima volta. In questo caso, chiama getContextualAddOn, che recupera i dettagli dell'email aperta e restituisce un insieme di schede da mostrare all'utente.

Per creare la funzione getContextualAddOn:

  1. A sinistra, tieni il puntatore sopra Code.gs, poi fai clic su Menu Menu Altro > Rinomina.
  2. Digita GetContextualAddOn e premi il tasto Enter. Apps Script aggiunge automaticamente .gs al nome del file, quindi non è necessario digitare un'estensione. Se digiti GetContextualAddOn.gs, Apps Script assegna al file il nome GetContextualAddOn.gs.gs.
  3. In GetContextualAddOn.gs, sostituisci il codice predefinito con la funzione getContextualAddOn:
/**
 * Returns the contextual add-on data that should be rendered for
 * the current e-mail thread. This function satisfies the requirements of
 * an 'onTriggerFunction' and is specified in the add-on's manifest.
 *
 * @param {Object} event Event containing the message ID and other context.
 * @returns {Card[]}
 */
function getContextualAddOn(event) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));

  var section = CardService.newCardSection();
  section.addWidget(CardService.newTextInput()
    .setFieldName('Date')
    .setTitle('Date'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Amount')
    .setTitle('Amount'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Description')
    .setTitle('Description'));
  section.addWidget(CardService.newTextInput()
    .setFieldName('Spreadsheet URL')
    .setTitle('Spreadsheet URL'));

  card.addSection(section);

  return [card.build()];
}

L'interfaccia utente di ogni componente aggiuntivo di Google Workspace è costituita da schede suddivise in una o più sezioni, ognuna contenente widget che possono visualizzare e ottenere informazioni dall'utente. La funzione getContextualAddOn crea una singola scheda che contiene i dettagli di una spesa trovata in un'email. La scheda ha una sezione contenente campi di input di testo per i dati pertinenti. La funzione restituisce un array delle schede del componente aggiuntivo. In questo caso, l'array restituito include una sola scheda.

Prima di eseguire il deployment del componente aggiuntivo Expense It!, devi disporre di un progetto Google Cloud Platform (GCP), che i progetti Apps Script utilizzano per gestire le autorizzazioni, i servizi avanzati e altri dettagli. Per saperne di più, visita Progetti Google Cloud Platform.

Per eseguire il deployment e l'esecuzione del componente aggiuntivo, segui questi passaggi:

  1. Apri il tuo progetto Google Cloud e copia il relativo numero.
  2. Nel progetto Apps Script, a sinistra, fai clic su Impostazioni progetto Impostazioni progetto.
  3. Nella sezione "Progetto Google Cloud Platform (GCP)", fai clic su Cambia progetto.
  4. Inserisci il numero di progetto del tuo progetto Google Cloud, quindi fai clic su Imposta progetto.
  5. Fai clic su Esegui il deployment > Test dei deployment.
  6. Assicurati che il tipo di deployment sia Componente aggiuntivo Google Workspace. Se necessario, nella parte superiore della finestra di dialogo, fai clic su Attiva tipi di deployment Abilita tipi di deployment e seleziona Componente aggiuntivo Google Workspace come tipo di deployment.
  7. Accanto ad Applicazione/i: Gmail, fai clic su Installa.
  8. Fai clic su Fine.

Ora puoi vedere il componente aggiuntivo nella Posta in arrivo di Gmail.

  1. Apri Gmail sul computer.
  2. Nel riquadro laterale a destra, l'app Expense It! Viene visualizzato il componente aggiuntivo Icona della ricevuta di Expense It!. Potrebbe essere necessario fare clic su Altri componenti aggiuntivi Altri componenti aggiuntivi per trovarlo.
  3. Apri un'email, preferibilmente una ricevuta con le spese.
  4. Per aprire il componente aggiuntivo, fai clic su Expense It! nel riquadro laterale a destra. Icona della ricevuta di Expense It!.
  5. Concedi a Expense It! l'accesso al tuo Account Google facendo clic su Autorizza accesso e segui le istruzioni.

Il componente aggiuntivo mostra un semplice modulo accanto a un messaggio Gmail aperto. Per ora non fa altro, ma ne amplierai la funzionalità nella sezione successiva.

Per visualizzare gli aggiornamenti al componente aggiuntivo mentre continui a svolgere questo lab, devi solo salvare il codice e aggiornare Gmail. Non sono necessari ulteriori deployment.

4. Accedere ai messaggi email

Aggiungi il codice che recupera i contenuti email e modularizza il codice per una maggiore organizzazione.

Accanto a File, fai clic su Aggiungi Aggiungi un file > Script e crea un file denominato Cards. Crea un secondo file di script chiamato Helpers. Cards.gs crea la scheda e utilizza le funzioni di Helpers.gs per compilare i campi del modulo in base ai contenuti dell'email.

Sostituisci il codice predefinito in Cards.gs con questo codice:

var FIELDNAMES = ['Date', 'Amount', 'Description', 'Spreadsheet URL'];

/**
 * Creates the main card users see with form inputs to log expenses.
 * Form can be prefilled with values.
 *
 * @param {String[]} opt_prefills Default values for each input field.
 * @param {String} opt_status Optional status displayed at top of card.
 * @returns {Card}
 */
function createExpensesCard(opt_prefills, opt_status) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
  
  if (opt_status) {
    if (opt_status.indexOf('Error: ') == 0) {
      opt_status = '<font color=\'#FF0000\'>' + opt_status + '</font>';
    } else {
      opt_status = '<font color=\'#228B22\'>' + opt_status + '</font>';
    }
    var statusSection = CardService.newCardSection();
    statusSection.addWidget(CardService.newTextParagraph()
      .setText('<b>' + opt_status + '</b>'));
    card.addSection(statusSection);
  }
  
  var formSection = createFormSection(CardService.newCardSection(),
                                      FIELDNAMES, opt_prefills);
  card.addSection(formSection);
  
  return card;
}

/**
 * Creates form section to be displayed on card.
 *
 * @param {CardSection} section The card section to which form items are added.
 * @param {String[]} inputNames Names of titles for each input field.
 * @param {String[]} opt_prefills Default values for each input field.
 * @returns {CardSection}
 */
function createFormSection(section, inputNames, opt_prefills) {
  for (var i = 0; i < inputNames.length; i++) {
    var widget = CardService.newTextInput()
      .setFieldName(inputNames[i])
      .setTitle(inputNames[i]);
    if (opt_prefills && opt_prefills[i]) {
      widget.setValue(opt_prefills[i]);
    }
    section.addWidget(widget);
  }
  return section;
}

La funzione createExpensesCard accetta un array di valori per precompilare il modulo come argomento facoltativo. La funzione può visualizzare un messaggio di stato facoltativo, colorato di rosso se lo stato inizia con "Errore:", altrimenti è verde. Anziché aggiungere manualmente ogni campo al modulo, una funzione helper chiamata createFormSection esegue un ciclo nel processo di creazione dei widget di input di testo, imposta ogni valore predefinito con setValue e poi aggiunge i widget alle rispettive sezioni della scheda.

Ora sostituisci il codice predefinito in Helpers.gs con questo codice:

/**
 * Finds largest dollar amount from email body.
 * Returns null if no dollar amount is found.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getLargestAmount(message) {
  return 'TODO';
}

/**
 * Determines date the email was received.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getReceivedDate(message) {
  return 'TODO';
}

/**
 * Determines expense description by joining sender name and message subject.
 *
 * @param {Message} message An email message.
 * @returns {String}
 */
function getExpenseDescription(message) {
  return 'TODO';
}

/**
 * Determines most recent spreadsheet URL.
 * Returns null if no URL was previously submitted.
 *
 * @returns {String}
 */
function getSheetUrl() {
  return 'TODO';
}

Le funzioni in Helpers.gs vengono chiamate da getContextualAddon per determinare i valori precompilati nel modulo. Per ora, queste funzioni restituiranno solo la stringa "TODO" perché implementerai la logica di precompilazione in un passaggio successivo.

Successivamente, aggiorna il codice in GetContextualAddon.gs in modo che utilizzi il codice in Cards.gs e Helpers.gs. Sostituisci il codice in GetContextualAddon.gs con questo codice:

/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Returns the contextual add-on data that should be rendered for
 * the current e-mail thread. This function satisfies the requirements of
 * an 'onTriggerFunction' and is specified in the add-on's manifest.
 *
 * @param {Object} event Event containing the message ID and other context.
 * @returns {Card[]}
 */
function getContextualAddOn(event) {
  var message = getCurrentMessage(event);
  var prefills = [getReceivedDate(message),
                  getLargestAmount(message),
                  getExpenseDescription(message),
                  getSheetUrl()];
  var card = createExpensesCard(prefills);

  return [card.build()];
}

/**
 * Retrieves the current message given an action event object.
 * @param {Event} event Action event object
 * @return {Message}
 */
function getCurrentMessage(event) {
  var accessToken = event.messageMetadata.accessToken;
  var messageId = event.messageMetadata.messageId;
  GmailApp.setCurrentMessageAccessToken(accessToken);
  return GmailApp.getMessageById(messageId);
}

Prendi nota della nuova funzione getCurrentMessage, che utilizza l'evento fornito da Gmail per leggere il messaggio attualmente aperto dall'utente. Affinché questa funzione funzioni, aggiungi un ambito aggiuntivo al manifest del progetto di script che consenta l'accesso in sola lettura ai messaggi Gmail.

In appscript.json, aggiorna oauthScopes in modo che richieda anche l'ambito https://www.googleapis.com/auth/gmail.addons.current.message.readonly.

"oauthScopes": [
  "https://www.googleapis.com/auth/gmail.addons.execute",
   "https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
],

In Gmail, esegui il componente aggiuntivo e autorizza l'accesso a Expense It! per visualizzare i messaggi email. I campi del modulo ora sono precompilati con "TODO".

5. Interagire con Fogli Google

Il componente aggiuntivo Expense It! ha un modulo in cui l'utente può inserire i dettagli di una spesa, ma questi dettagli non vanno da nessuna parte. Aggiungiamo un pulsante che invia i dati del modulo a un foglio Google.

Per aggiungere un pulsante, utilizzeremo la classe ButtonSet. Per interagire con Fogli Google, utilizzeremo il servizio Fogli Google.

Modifica createFormSection per restituire un pulsante con l'etichetta "Invia" come parte della sezione del modulo della scheda. Procedi in questo modo:

  1. Crea un pulsante di testo utilizzando CardService.newTextButton(), etichettando il pulsante "Invia" utilizzando CardService.TextButton.setText().
  2. Progetta il pulsante in modo che, quando viene fatto clic, venga chiamata la seguente azione submitForm tramite CardService.TextButton.setOnClickAction():
/**
 * Logs form inputs into a spreadsheet given by URL from form.
 * Then displays edit card.
 *
 * @param {Event} e An event object containing form inputs and parameters.
 * @returns {Card}
 */
function submitForm(e) {
  var res = e['formInput'];
  try {
    FIELDNAMES.forEach(function(fieldName) {
      if (! res[fieldName]) {
        throw 'incomplete form';
      }
    });
    var sheet = SpreadsheetApp
      .openByUrl((res['Spreadsheet URL']))
      .getActiveSheet();
    sheet.appendRow(objToArray(res, FIELDNAMES.slice(0, FIELDNAMES.length - 1)));
    return createExpensesCard(null, 'Logged expense successfully!').build();
  }
  catch (err) {
    if (err == 'Exception: Invalid argument: url') {
      err = 'Invalid URL';
      res['Spreadsheet URL'] = null;
    }
    return createExpensesCard(objToArray(res, FIELDNAMES), 'Error: ' + err).build();
  }
}

/**
 * Returns an array corresponding to the given object and desired ordering of keys.
 *
 * @param {Object} obj Object whose values will be returned as an array.
 * @param {String[]} keys An array of key names in the desired order.
 * @returns {Object[]}
 */
function objToArray(obj, keys) {
  return keys.map(function(key) {
    return obj[key];
  });
}
  1. Crea un widget di set di pulsanti utilizzando CardService.newButtonSet() e aggiungi il pulsante di testo al set di pulsanti con CardService.ButtonSet.addButton().
  2. Aggiungi il widget del set di pulsanti alla sezione del modulo della scheda utilizzando CardService.CardSection.addWidget().

Con poche righe di codice, possiamo aprire un foglio di lavoro tramite il suo URL e aggiungere una riga di dati al foglio. Tieni presente che gli input del modulo vengono passati alla funzione come parte dell'evento e e verifichiamo che l'utente abbia fornito tutti i campi. Supponendo che non si verifichino errori, creiamo una scheda delle spese vuota con uno stato favorevole. Se rileviamo un errore, restituiamo la scheda compilata originale insieme al messaggio di errore. La funzione helper objToArray semplifica la conversione delle risposte del modulo in un array, che può essere aggiunto al foglio di lavoro.

Infine, aggiorna la sezione oauthScopes in appsscript.json e richiedi di nuovo l'ambito https://www.googleapis.com/auth/spreadsheets. Se autorizzato, questo ambito consente al componente aggiuntivo di leggere e modificare i Fogli Google di un utente.

"oauthScopes": [
  "https://www.googleapis.com/auth/gmail.addons.execute",
  "https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
  "https://www.googleapis.com/auth/spreadsheets"
],

Se non hai ancora creato un nuovo foglio di lavoro, creane uno all'indirizzo https://docs.google.com/spreadsheets/.

Ora esegui di nuovo il componente aggiuntivo e prova a inviare il modulo. Assicurati di inserire l'URL completo dell'URL di destinazione nel campo del modulo URL foglio di lavoro.

6. Memorizzare i valori con il servizio delle proprietà

Spesso gli utenti registrano molte spese nello stesso foglio di lavoro, quindi sarebbe comodo offrire l'URL del foglio di lavoro più recente come valore predefinito nella scheda. Per conoscere l'URL del foglio di lavoro più recente, dobbiamo memorizzare queste informazioni ogni volta che viene utilizzato il componente aggiuntivo.

Il servizio Proprietà ci consente di memorizzare coppie chiave-valore. Nel nostro caso, una chiave ragionevole sarebbe "SPREADSHEET_URL", mentre il valore sarebbe l'URL stesso. Per memorizzare un valore di questo tipo, devi modificare submitForm in Cards.gs in modo che l'URL del foglio di lavoro venga memorizzato come proprietà quando aggiungi una nuova riga al foglio.

Tieni presente che le proprietà possono avere uno dei tre ambiti: script, utente o documento. L'ambito documento non si applica ai componenti aggiuntivi per Gmail, anche se è pertinente a un tipo separato di componente aggiuntivo quando si archiviano informazioni specifiche di un determinato documento o foglio Google. Per il nostro componente aggiuntivo, il comportamento desiderato è che un utente veda il proprio foglio di lavoro più recente (e non quello di qualcun altro) come opzione predefinita nel modulo. Di conseguenza, selezioniamo l'ambito utente anziché l'ambito script.

Utilizza PropertiesService.getUserProperties().setProperty() per memorizzare l'URL del foglio di lavoro. Aggiungi quanto segue a submitForm in Cards.gs:

PropertiesService.getUserProperties().setProperty('SPREADSHEET_URL', 
    res['Spreadsheet URL']);

Poi modifica la funzione getSheetUrl in Helpers.gs per restituire la proprietà memorizzata in modo che l'utente visualizzi l'URL più recente ogni volta che utilizza il componente aggiuntivo. Utilizza PropertiesService.getUserProperties().getProperty() per ottenere il valore della proprietà.

/**
 * Determines most recent spreadsheet URL.
 * Returns null if no URL was previously submitted.
 *
 * @returns {String}
 */
function getSheetUrl() {
  return PropertiesService.getUserProperties().getProperty('SPREADSHEET_URL');
}

Infine, per accedere al servizio Proprietà, lo script dovrà essere autorizzato. Aggiungi l'ambito https://www.googleapis.com/auth/script.storage al manifest come prima per consentire al componente aggiuntivo di leggere e scrivere le informazioni sulla proprietà.

7. Analizzare il messaggio di Gmail

Per far risparmiare davvero tempo agli utenti, precompiliamo il modulo con le informazioni pertinenti sulla spesa contenute nell'email. Abbiamo già creato funzioni in Helpers.gs che svolgono questo ruolo, ma finora abbiamo restituito solo "TODO" per la data, l'importo e la descrizione della spesa.

Ad esempio, possiamo ottenere la data di ricezione dell'email e utilizzarla come valore predefinito per la data della spesa.

/**
 * Determines date the email was received.
 *
 * @param {Message} message - The message currently open.
 * @returns {String}
 */
function getReceivedDate(message) {
  return message.getDate().toLocaleDateString();
}

Implementa le due funzioni rimanenti:

  1. getExpenseDescription potrebbe comportare l'unione del nome del mittente e dell'oggetto del messaggio, anche se esistono modi più sofisticati per analizzare il corpo del messaggio e fornire una descrizione ancora più accurata.
  2. Per getLargestAmount, valuta la possibilità di cercare simboli specifici associati al denaro. Le ricevute spesso riportano più valori, ad esempio tasse e altre commissioni. Pensa a come potresti identificare l'importo corretto. Anche le espressioni regolari potrebbero essere utili.

Se hai bisogno di ulteriore ispirazione, esplora la documentazione di riferimento per GmailMessage o controlla il codice della soluzione che hai scaricato all'inizio del codelab. Una volta ideate le tue implementazioni per tutte le funzioni in Helpers.gs, prova il tuo componente aggiuntivo. Apri le ricevute e inizia a registrarle in un foglio di lavoro.

8. Cancellare il modulo con le azioni della scheda

Cosa succede se Expense It! identifica erroneamente una spesa in un'email aperta e precompila il modulo con informazioni errate? L'utente cancella il modulo. La classe CardAction ci consente di specificare una funzione che viene chiamata quando si fa clic sull'azione. Utilizziamolo per offrire all'utente un modo rapido per cancellare il modulo.

Modifica createExpensesCard in modo che la scheda restituita abbia un'azione della scheda denominata "Cancella modulo" e, quando viene selezionata, chiami la seguente funzione clearForm, che puoi incollare in Cards.gs. Dovrai passare opt_status come parametro denominato "Stato" all'azione per assicurarti che il messaggio di stato rimanga quando il modulo viene cancellato. Tieni presente che i parametri facoltativi per le azioni devono essere di tipo Object.<string, string>, quindi se opt_status non è disponibile, devi passare {'Status' : ''}.

/**
 * Recreates the main card without prefilled data.
 *
 * @param {Event} e An event object containing form inputs and parameters.
 * @returns {Card}
 */
function clearForm(e) {
  return createExpensesCard(null, e['parameters']['Status']).build();
}

9. Crea un foglio di lavoro

Oltre a utilizzare Google Apps Script per modificare un foglio di lavoro esistente, puoi creare un foglio di lavoro completamente nuovo in modo programmatico. Per il nostro componente aggiuntivo, consentiamo all'utente di creare un foglio di lavoro per le spese. Per iniziare, aggiungi la seguente sezione della scheda alla scheda restituita da createExpensesCard.

var newSheetSection = CardService.newCardSection();
var sheetName = CardService.newTextInput()
  .setFieldName('Sheet Name')
  .setTitle('Sheet Name');
var createExpensesSheet = CardService.newAction()
  .setFunctionName('createExpensesSheet');
var newSheetButton = CardService.newTextButton()
  .setText('New Sheet')
  .setOnClickAction(createExpensesSheet);
newSheetSection.addWidget(sheetName);
newSheetSection.addWidget(CardService.newButtonSet().addButton(newSheetButton));
card.addSection(newSheetSection);

Ora, quando l'utente fa clic sul pulsante "Nuovo foglio", il componente aggiuntivo genera un nuovo foglio di lavoro formattato con una riga di intestazione bloccata in modo che sia sempre visibile. L'utente specifica un titolo per il nuovo foglio di lavoro nel modulo, anche se includere un valore predefinito nel caso in cui il modulo sia vuoto potrebbe essere una buona scelta. Nell'implementazione di createExpensesSheet, restituisci una scheda quasi identica a quella esistente, con l'aggiunta di un messaggio di stato appropriato e il precompilamento del campo URL con l'URL del nuovo foglio di lavoro.

10. Complimenti!

Hai progettato e implementato correttamente un componente aggiuntivo Gmail che trova una spesa in un'email e aiuta gli utenti a registrarla in un foglio di lavoro in pochi secondi. Hai utilizzato Google Apps Script per interfacciarti con più API di Google e hai reso persistenti i dati tra più esecuzioni del componente aggiuntivo.

Possibili miglioramenti

Lascia che la tua immaginazione ti guidi mentre migliori Expense It!, ma ecco alcune idee per creare un prodotto ancora più utile:

  • Link al foglio di lavoro dopo che l'utente ha registrato una spesa
  • Aggiungere la possibilità di modificare/annullare la registrazione di una spesa
  • Integra le API esterne per consentire agli utenti di effettuare pagamenti e richiedere denaro

Scopri di più