LoginSignup
9
13

【業務効率化】GoogleSheetAPIとJavaScriptで社内検索botを作ってみた

Last updated at Posted at 2023-09-08

こういう人に向けて書いてます

本記事には、

・面倒くさい事務作業を減らしたい人
・会社のために、何かを作りたいけど、バックエンドが苦手な人
・みんなが使えるツールを作りたい人

に向けて書いています。

なぜ社内検索botを作ったのか?

去年、ジーズアカデミーに入学して、プログラミングを学ぶきっかけの一つっは、どうしても解決したい会社での小さな問題であった。それは、社内でいつも発生する、カスタマー対応の煩雑さ

流れはこちら:
メールが読み→該当するテンプレを探す→お客様の名前を書き換わる→自分のお名前を書き換わる→確認する→送信

(自分の業務じゃないけど、だるい!)

ここで考えたのが:

1、全社にテンプレ共有
2、書き換わる部分をわかりやすくする。ミスの少なくする。

目的

・問い合わせメールをもっと早く返したい
・テンプレを探す手間を省きたい
・担当不在の時でも、情報共有によって、誰でもある程度業務を引き継げられる環境作りたい
・顧客対応関連で、社内で小さな衝突をなくしたい
・内容更新も簡単で、誰でも使える簡単なシステムにしたい(めちゃくちゃ重要)

こんなものです!

chatbot.gif

  • 選択式で、親カテゴリーから絞っていき、該当テンプレを探し出す。

  • 担当スタッフの名前を選択

  • お客様のお名前を入力

  • 検索ボタンをクリックすると同時に、スタッフ、お客様の名前が入った状態でテンプレを出す。

  • コピーボタンを押してから、メールを貼り付けて完了。

  • 探す手間も、全選択(Ctrl+A)の手間も省けます。

仕組み

JavaScriptからfetch関数を使って、スプレッドシートの情報を取得
整列して、ブラウザに表示する。

無題のプレゼンテーション.png

Google Cloud Platform (GCP)の設定

前回の記事で、LaravelでGoogleSheetAPIを使ってみたが、今回はサーバーなど関係なし、ローカルで使っていただくことになりますので、比較的にセキュリティーへの懸念が少なく、APIキーを取得するやり方にしました。

サービスアカウントのやり方に興味ある方はこちらの記事へ ^^

設定方法はまず、GCPでAPI / サービス詳細ページで、SheetAPIキーを有効にする

スクリーンショット 2023-09-09 1.12.36.png

そして、認証情報ページに、APIキーを生成。大事なのは、必ずキーの制限を設定すること。設定しない場合、動かなかったりします > <

JavaScriptでデータを取得する

次は、API経由で、スプレッドシートの情報を取得する。
そして、取得したスプシのjsonデータを配列にする。

  const apiKey = // あなたのAPIキーに置き換える
  const spreadsheetId = // あなたのスプレッドシートのIDに置き換える
  const sheetName =  // データがあるシート名に置き換える

  const endpoint = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${sheetName}?key=${apiKey}`;

  // endpointからデータを取得する
  fetch(endpoint)
    .then(response => response.json())
    .then(data => {
      const qaPairs = data.values.slice(1).map(row => ({
        course: row[0],
        category: row[1],
        question: row[2],//同じコードを使い回ししていますので、ここを保留しています。
        answer: row[3]
      }));

あとは取得したデータを処理する。

やることが主に3つ

  1. セレクトボックスを並び替えた状態で表示
  2. 選んだ結果をtextareaで表示
  3. コピー機能つつける

1 セレクトボックスを並び替えた状態で表示

HTMLタグにあるデータを取得し、セレクトボックスの中身を綺麗に整理する。

データ取得
→カテゴリーなど並べ替え
→セレクトボックスのオブションを更新

// データを取得したら、検索ボタンを有効にする
      const searchBtn = document.getElementById("searchBtn");

      // セレクトボックスを取得
      const categorySelect = document.getElementById("categorySelect");
      const courseSelect = document.getElementById("courseSelect");

      // カテゴリーのオプションを追加
      const queryInput = document.getElementById("query");
      const resultsDiv = document.getElementById("results");

      // カテゴリー、コースのオプションを追加
      const categories = [...new Set(qaPairs.map(qaPair => qaPair.category))];
      const courses = [...new Set(qaPairs.map(qaPair => qaPair.course))];

      // カテゴリーやコースのオプションを昇順に並び替え
      const sortedCategories = categories.sort();
      const sortedCourses = courses.sort();

      // カテゴリーセレクトボックスのオプションを更新
      categories.forEach(category => {
        const option = document.createElement("option");
        option.value = category;
        option.textContent = category;
        categorySelect.appendChild(option);
      });

      // コースセレクトボックスのオプションを更新
      courses.forEach(course => {
        const option = document.createElement("option");
        option.value = course;
        option.textContent = course;
        courseSelect.appendChild(option);
      });

  // コース選択時に関連するカテゴリーオプションを更新する関数
      function updateCategoryOptions(relatedCategories) {
        // カテゴリーセレクトボックスのオプションを一旦クリア
        categorySelect.innerHTML = '<option value="">すべてのカテゴリー</option>';

        // 重複を除去して昇順にソート
        const uniqueCategories = [...new Set(relatedCategories)].sort();

        uniqueCategories.forEach(category => {
          const option = document.createElement("option");
          option.value = category;
          option.textContent = category;
          categorySelect.appendChild(option);
        });
      }

2 選んだ結果をtextareaで表示

ここは一番簡単だけど、ちゃんとmarginもここで調理できるのはチャッピが教えてくれたおかげです。

            const spacingDiv = document.createElement("div");
            spacingDiv.style.marginBottom = "20px"; // 適切な間隔に調整

            const separator = document.createElement("hr");
            separator.style.margin = "20px 0"; // 適切な間隔に調整

            resultsDiv.appendChild(courseDiv);
            resultsDiv.appendChild(spacingDiv);
            resultsDiv.appendChild(categoryDiv);
            resultsDiv.appendChild(spacingDiv);
            resultsDiv.appendChild(answerDiv);
            resultsDiv.appendChild(answerTextarea); // テキストエリアを追加
            resultsDiv.appendChild(copyButton);
            resultsDiv.appendChild(separator);

3 コピー機能つつける

コピーボタンは割と検索したらすぐ出てきますが、今回面倒くさかったのが、変数で置き換えたスタッフ名とお客様の名前をコピーするので、replaceVariablesの関数で、置き換えたものを取得する。ここに気づくまで、3時間溶けました

      // コピーするボタンの作成
            const copyButton = document.createElement("button");
            copyButton.textContent = "回答をコピー";
            copyButton.addEventListener("click", () => {
              const answerText = replaceVariables(result.answer); //←ここが重要!! 
            //const answerText = result.answer; // ←最初これにして、詰んだ ^^;;;
              const textArea = document.createElement("textarea");
              textArea.value = answerText;
              document.body.appendChild(textArea);
              textArea.select();
              document.execCommand("copy");
              document.body.removeChild(textArea);
              alert("回答がコピーされました!");
            });

分類や質問や回答からキーワード検索
もしくは分類から検索

スプシを記入

スプレットシートはとても簡単。コース、カテゴリー、答えを設定します。

スクリーンショット 2023-09-09 0.56.14.png

そして、{staff_select}{customer}を表示させたい場所に設定すれば、完成 \^0^/

今回の件で実現できたこと(&伝えたいこと)

オフラインでの社内共有なので、分類ができれば、意外とすぐ実現しちゃう。

逆に、テンプレの取捨選択が難しかった。汎用性の高いテンプレを使うが、使えないものをどう処理するのか、その基準を考えるのに、1日かかりました。

そして、プログラミングを勉強した時、技術を使いたい欲があったが、時間が経ち、誰のために、何を作るにフォーカスできたら、すごいモノを作るより、誰でも触れる、誰でも改善できるモノがいいに気づいた。だから、誰でも更新できるスプレッドシートにしました。スプレッドシートをどんどん更新していけば、テンプレが貯めていける仕組みとなります。(やさしい世界!)

最後に、非エンジニアで、めちゃくちゃコードを書けるわけじゃないけど、限られてる能力で、少しでも身近にある面倒なことを減らせるのも幸せやん〜

という、今回の気づきでした。おしまい〜

9
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
13