GASでwebページをつくって業務管理をおしゃんにやる

GASでwebページをつくって業務管理をおしゃんにやる

こんにちは。to Cサービスのカスタマーサポートをしてます平湯(ひらゆ)です。

GAS(Google App Script)・HTML・CSSを使って、ひとつの仕組みを簡便にした事例を紹介します。

やったことをざっくりいうと、「GASでHTMLを書いてWebページを作り、Webページに入力された情報をスプレッドシートに自動で転記できるやつを作った」です。

特にコーディングされてないので、綺麗なコードではないです。(指摘ぜひ欲しい)

誰でもやろうと思えば、今ある仕組みを効率的にやれるようになるんだよ、ということを伝えたくて書きました。強いCSチームづくりをしている方の参考になればうれしみ。

何を効率化したのか?

「毎朝の業務チェック」です。恥ずかしながら、何度か対応漏れが発生したり、作業量に対するリソースを見誤ったりしていたので、朝礼で業務チェックを行うようになりました。

具体的には、

  1. 項目ごとに「対応に漏れはないか?」と「作業量は処理可能な量か?」という視点で状況を共有する。(問題ありそうなら即リカバリ)
  2. チェックした記録をつける。

これを簡単にしました。

なんでやったの?

シンプルにスプレへの記録がめんどくさかったです。チェック項目は頻度がまばらで、一項目ずつ◯をつけなきゃいけない。しかもその前に、その日の日付を探さなきゃいけない。

業務管理はものすごく重要なんですが、何かを生み出すものじゃない(リスク低減はほぼそうだと思ってる)ので、モチベーションが非常に保ちづらいんですよね。(コンプラマンの皆さんすみません)なので、風化させないために、管理系のタスクはめちゃくちゃ簡便にしなきゃいけないと考えてます。

あと、自分のスキルを磨きたかったからです。自分のリソースで効率化できるとかなりストレスがないです。だって頼まなくてもいいし、自分のタイミングでやれるので気が楽です。いろんなことができるようになると仕事がやりやすいなと思って、やってみました。

HTML・CSSから始めたのは、業務効率化の観点とサポートページを自分でいじりたいと思ったからです。

テーマにしたこと

見やすくて操作しやすい画面にすることをテーマにしました。新しく使う人でも恐れずに操作できるものにしたかったので、今回は見た目を意識しました。あんま手を付けてこなかったCSSにチャレンジした感じです。

完成したもの

これがTOP画面です。確認した項目にチェックを入れていき、「記録する」ボタンを押すと、

image

       ~~(省略)~~

image

記録完了を伝えるサンクスページにいきます。「記録シートにいく」ボタンを押すと、

image

スプレのチェックシートにいきます。

当日の日付(2021/5/11)に、さっきのページでチェックした項目がスプレの項目で「◯」と記録されました。やったね。

image

コード

スクリプトは4つあります。①opsRecord.gs、②index.html、③result.html、④css.html です。

①opsRecord.gsで、webページの表示、webページに入力された情報の取得、スプレへの転記などを行います。②index.htmlは項目をチェックする画面のhtml、③result.htmlは作業完了画面のhtmlです。④css.htmlは文字通りCSSで、②と③の画面のスタイルを決めます。

コードとざっくり説明です。(細かいので気になる方はどうぞ)

実際はチェック対象の項目がもっと多いのですが、コードが長くなるので一部割愛してます。

①opsRecord.gs
  • doGet :htmlを表示させるおまじない。ブラウザのタブに表示されるタイトルとファビコンもここで設定してる。
  • doPost :webページで入力された内容をスプレに転記する。以下の流れ。
    • webページに入力された内容を取得して配列に入れる
    • 今日の日付と一致するスプレの日付があるレコードを見つける
    • webページに入力された内容とスプレのチェック項目が一致したら、そのレコードに丸をつける
    • 記録完了ページへ遷移する
  • recordDay :次に説明するisBusinessDay を使って、営業日の場合は当日の日付、営業日じゃなかったら「休」とスプレに記録する。トリガーを設定して日次で実行してる。
  • isBusinessDay :土日祝日を判定する。
function doGet() {
  const htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
  htmlOutput
  .setTitle('CS業務管理チェック')
  .setFaviconUrl('https://drive.google.com/uc?id=xxxxxxxxxx&.png');
  return htmlOutput;
}

function doPost(e){
  var ss = SpreadsheetApp.openById('xxxxxxxxxx').getSheetByName('xxxxxx');
  var lastRow = ss.getLastRow();
  var dateList = ss.getRange(2,1,lastRow,1).getValues();
  var checkList = ss.getRange(1,2,1,2).getValues();
  var today = new Date();
  var today = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy-MM-dd');
  var cl1 = e.parameter.checklist1;
  var cl2 = e.parameter.checklist2;
  var answerList = [cl1, cl2];
  for(var i=0;i<dateList.length;i++){
    if(dateList[i][0]=='休'){
      continue;
    }
    else if(Utilities.formatDate(dateList[i][0], 'Asia/Tokyo', 'yyyy-MM-dd') == today){
      if(answerList.includes(checkList[0][0]) == true){
        ss.getRange(i+2,2).setValue("◯");
        }
      if(answerList.includes(checkList[0][1]) == true){
        ss.getRange(i+2,3).setValue("◯");
      break;
    }
  }
  var html = HtmlService.createTemplateFromFile("result").evaluate();
  html
  .setTitle('CS業務管理チェック')
  .setFaviconUrl('https://drive.google.com/uc?id=xxxxxxxxxx&.png');
  return html;
 }
}

function recordDay() { //営業日の場合に当日の日付を記録する関数
  var ss = SpreadsheetApp.openById('xxxxxxxxxx').getSheetByName('xxxxxx');
  const AValues = ss.getRange('A:A').getValues();
  const LastRow = AValues.filter(String).length;
  var pasteDestination = ss.getRange(LastRow+1,1);
  var today = new Date();
  if(isBusinessDay(today)==true){
    var today = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy-MM-dd');
    pasteDestination.setValue(today);
  }
  else{
    pasteDestination.setValue("休");
  }
}

function isBusinessDay(date){ //土日祝日を判定する関数
  if (date.getDay() == 0 || date.getDay() == 6) {
    return false;
  }
  const calJa = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com');
  if(calJa.getEventsForDay(date).length > 0){
    return false;
  }
  return true;
}
②index.html
  • form要素に実行するアプリケーションを設定して、入力内容を取得できるようにする。
  • チェック項目ごとに、項目名・頻度・リンク先を設定。
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
    <link rel="stylesheet" href="style.css">
    <title>CS業務管理チェック</title>
  </head>
  <body>
    <header>
      <h1>CSチーム:業務管理チェック</h1>
    </header>
    <form class="" action="xxx(実行するアプリケーションのURLを入れる)" method="post">
    <div class="container">
      <div class="list">
        <input type="checkbox" name="checklist1"  value="1. チケット対応" id="ticket"> <label for="ticket"><p class="text-everyday">1. チケット対応   <span>[毎営業日]</span>  <a target="_blank" href="xxxxxxxx" class="link">  zendesk Explore</a> </p></label>
      </div>
      <div class="list">
        <input type="checkbox" name="checklist2"  value="2. KYC対応" id="kyc" > <label for="kyc"><p class="text-everyday">2. KYC対応   <span>[毎営業日]</span>  <a target="_blank"  href="xxxxxxxx" class="link">kyc dashboard</a></p></label>
      </div>
      <input class="kiroku" type="submit" value="記録する">
    </div>
    </form>
  </body>
</html>
③result.html
  • 記録が完了したことを伝える。
  • チェックシート(スプレ)のリンク先を掲出する。
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
    <link rel="stylesheet" href="style.css">
    <title>記録完了ページ</title>
  </head>
  <body>
    <header>
      <h1>CSチーム:業務管理チェック</h1>
    </header>
    <div class="block">
      <h1>記録が完了しました!</h1>
    </div>
    <div class="record">
      <a href="xxxxxxx" target="_blank" >記録シートにいく</a>
    </div>
  </body>
</html>
④css.html
  • header :タイトルを大きく見せるようにした。
  • .list inputtransform: scale(2,2) でチェックボックスのサイズを大きくした。
  • .kiroku.record.record a:なるべく大きいボタンにした。
  • .kiroku:hover.record a:hover :カーソルあわせたときに変わる色を指定。
  • .text-everyday span :毎営業日確認するものにハイライトを付けた。
  • 他いろいろ
<style>

body {
    font-family:monospace;
}
header {
  font-size: 2vw;
  background-color: #EAF6FD;
  max-width: 100%;
  max-height: 150px;
  margin: 0 auto;
  padding: 50px;
}

.container {
  display: flex;
  flex-direction: column;
  margin-top: 100px;
  margin:10%;
  padding:0;
}
.list {
  display: flex;
  font-size: 24px;
  height: 64px;
  min-width: 24px;
  padding: 8px 16px 0 0;
}
.link {
  font-size: 20px;
  color: blue;
  line-height: 8px;
  margin-top: 8px;
}
.list span {
  font-size: 20px;
}
.text-everyday span {
  background: linear-gradient(transparent 40%, gold 60%);
}
.list input {
  transform: scale(2,2);
  margin: 32px;
}
.kiroku {
  display: inline-block;
  text-align: center;
  background-color: #00A5DD;
  color: white;
  border-radius: 24px;
  max-width: 320px;
  margin: 72px;
  padding:24px;
  font-size:24px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kiroku:hover {
  background-color: #0090AA;
  color: white;
  border-radius: 24px;
}

/サンクスページ/
.block {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 112px;
  margin: 10px auto;
}
.record {
  display: inline-block;
  text-align: center;
  width: 100%;
  height: 32px;
  margin: 10px auto;
}
.record a {
  background: #00A5DD;
  text-decoration:none;
  color: white;
  border-radius: 24px;
  padding:24px;
  font-size:20px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.record a:hover {
  background-color: #0090AA;
  border-radius: 24px;
}

</style>
image

学びとか工夫したこと

  1. GASではCSSを読み込むのにひと工夫が必要
  2. ファビコン入れられるよ
  3. 文字列型と日付型を同じカラムにしたので苦労した
  4. チェックボックスを同じnameにするとうまくいかない
  5. labelタグでチェックしやすく
  6. デプロイが2回必要だった
  7. CSSはぜんぶ学び

✏️GASではCSSを読み込むのにひと工夫が必要

GASでHTMLファイルとCSSファイルを分けるときは、headタグにcssを読み込むための呪文を入れます。

<?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?> こいつです。 headタグはこんな感じになります。

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
    <title>CS業務チェック</title>
  </head>
②index.html

この辺りは、タカハシ先生のwebサイトで勉強しました。非常にわかりやすく、「やってみよう形式」になってるので、一回バーっと体験してみることをオススメします。

GASでWebページを作るときにHTMLとCSSを別ファイルに記述する方法

みなさん、こんにちは! タカハシ(@ntakahashi0505)です。 GASユーザーのためのHTML入門として簡単なWebページの作り方 についてお伝えしています。 前回の記事はコチラ。 範囲を好きに指定することができるdivタグとspanタグについてお伝えしました。 これでスタイルし放題だ...! って、なればいいんですが、CSSの記述をHTMLファイルにじゃんじゃん書き込んでいくと、長くなるし混雑感が半端なくなってきます。 なら、分けちゃいましょう!それで、スッキリさせちゃいましょう! ということで、 GASでWebページを作るときに、HTMLとCSSを別ファイルに記述する方法 です。 強制出力スクリプトレットタグとか、 HtmlTemplateオブジェクト とか、新しく登場するものがたくさんあって若干長いですが、どうぞお付き合いください。 では、行ってみましょう! 前回 作成したHTMLファイルはコチラです。 #h2-orange { color: orange; font-size: x-large; } .p-small { color: gray; font-size: small; } .gas { font-size: large; font-weight: bold; } いつも隣にITのお仕事 GASのおすすめ記事一覧 いつも隣にITのお仕事から GAS のおすすめの記事をピックアップしています。 連載目次:超初心者向けGASでBotを作りながら基礎を学ぶ Google Apps Script(GAS) をはじめるためのメリットは山程ありますが、何を作ったらいいの?と悩んでしまうこともありますよね。そんな時に、おすすめしたいのが「Bot」の作成です。このシリーズでは、超初心者向けにGASでBotを作る方法を題材としながら、 GAS プログラミングの一通りの流れと書き方について学んでいきます。 【初心者向けGAS】本当の最初の一歩!スクリプトエディタでプロジェクトを開く 【初心者向けGAS】はじめてのスクリプトを作成し、保存し、実行する 【初心者向けGAS】プログラミングに必須の変数の使い方とデータ型について 【初心者向けGAS】ログを表示するLogger.logの使い方 【初心者向けGAS】スクリプト実行時の「承認」でびっくりしないために (以下略) 連載目次:GASユーザーのための初めてのHTML・CSS講座 <p ...

GASでWebページを作るときにHTMLとCSSを別ファイルに記述する方法

✏️ファビコン入れられるよ

Google driveのフォルダに画像を入れて、 doGet でファイルidを入力すればファビコンを出せます。webサイトっぽさマシマシです。素敵😍

image

.setFaviconUrl('https://drive.google.com/uc?id=xxxxxxxxxx&.png'); この部分ですね。「xxx…」の部分にファイルidを入れます。

function doGet() {
  const htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
  htmlOutput
  .setTitle('CS業務管理チェック')
  .setFaviconUrl('https://drive.google.com/uc?id=xxxxxxxxxx&.png');
  return htmlOutput;
}
①opsRecord.gs

これもタカハシ先生のwebサイトで知見を得ました。

✏️文字列型と日付型を同じカラムにしたので苦労した

チェックシート(スプレ)には、日付のカラムがあって、営業日の日付と休日には「休」が日次で自動記録されていきます。で、当日の日付を探してチェックシートに「◯」を記録していきます。

image

日付のカラムに文字列型と日付型を一緒にしないほうがいいと思うんですけど、チェックシートでその日が休みであることを表現したかったので無理やりいきました。

continue を使って「休」であれば処理をスキップするようにしました。何回もエラーになって原因探るのに割と時間使ったので書いておきます 📝

for(var i=0;i<dateList.length;i++){
    if(dateList[i][0]=='休'){
      continue;
    }
    else if(Utilities.formatDate(dateList[i][0], 'Asia/Tokyo', 'yyyy-MM-dd') == today){
      if(
①opsRecord.gs

✏️チェックボックスを同じnameにするとうまくいかない

form要素の input をチェック項目ごとに同じnameにしてしまうと、先頭の情報しか取得できませんでした。ググると同じnameでも取得できるような記述があったのですが、うまくいきませんでした。 name="checklist1"name="checklist2" のように項目ごとに別の名前をつけてます。

<div class="list">
	<input type="checkbox" name="checklist1"  value="1. チケット対応" id="ticket"> <label for="ticket"><p class="text-everyday">1. チケット対応   <span>[毎営業日]</span>  <a target="_blank" href="xxxxxxxx" class="link">  zendesk Explore</a> </p></label>
</div>
<div class="list">
  <input type="checkbox" name="checklist2"  value="2. KYC対応" id="kyc" > <label for="kyc"><p class="text-everyday">2. KYC対応   <span>[毎営業日]</span>  <a target="_blank"  href="xxxxxxxx" class="link">kyc dashboard</a></p></label>
</div>
②index.html

✏️labelタグでチェックしやすく

label を使って、チェックボックス(「□」)だけじゃなく文言をクリックしてもチェックボックスが反応するようになってます。こんな小技あるんですね、便利。操作しやすさを意識したので細かいとこもやってみました。

あと target="_blank" で新しいタブで開くことができます。これも細かい小技。

<label for="ticket">
	<p class="text-everyday">1. チケット対応   <span>[毎営業日]</span>
	  <a target="_blank" href="xxxxxxxx" class="link">  zendesk Explore
		</a> 
	</p>
</label>
②index.html

✏️デプロイが2回必要だった

これも気づくのに結構時間がかかったので書いておきます。

form要素の action で opsRecord.gs のスクリプトを実行しているので、デプロイしたら最新のアプリケーションのURLを入れる必要があります。

なので、なにか修正を入れたときは、デプロイ →form要素の action のURLを修正 →再度デプロイ、をしないと修正が反映されません。

<form class="" action="xxx(実行するアプリケーションのURLを入れる)" method="post">
②index.html

✏️CSSはぜんぶ学び

業務っぽさをなるべく排したかったです。だるいって思い始めたら続かないので操作性もそうだけど見栄えも綺麗にしたかった。

ヘッダーのタイトルやボタン・チェックボックスを大きくするCSSを書いたのもそういう背景からです。

CSSを設定しなかったバージョンを見ると…業務感がすごいですね。チェックボックスもボタンもリンク先も押しづらそう🤦🏼‍♂️

image
CSSを設定しなかったindex.html
CSSを設定しなかったindex.html
CSSを設定しなかったresult.html
CSSを設定しなかったresult.html

今回記事にしたwebページを社内のデザイナー(osanai)に見てもらって、もっと良い案のレビューをもらえたので近々更新します。

イメージはこんな感じ。素敵すぎる見栄え!やるぞ!

image

感想とか

CSSって奥が深いんですね。漠然とHTMLが重要と思ってたんですけど、見た目はほぼCSSなんだなっていう学びを得ました。

今回の経験でやれることの範囲が広がりました。普段の業務のなかで、「あれ、これならGASでやれるな」とか「こいつはwebページ作って簡易的な管理画面として使えたら早くできるな」とかアンテナが前より立つようになりました。

自分の気持ちはいつでもこれです。

おしまい