Flexプラグイン Tips（顧客住所を地図表示）

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズのTwilioエバンジェリストの高橋です。

[Twilio Flex Advent Calendar 2021](https://qiita.com/advent-calendar/2021/twilio-flex)の17日目の記事となります。
[前回の記事](https://qiita.com/mobilebiz/items/247d50e15a1c3e03c15d)では、Flexプラグインを使って着信音を鳴らす方法を紹介しました。着信音を鳴らすにはとくにUIに関するカスタマイズは不要でしたが、今回はいよいよUIをカスタマイズしていきましょう。

# シナリオ

今回のカスタマイズの前提となるシナリオは以下のとおりです。

- 顧客DBには、顧客の電話番号や名前、住所が含まれています。
- 着信時に顧客情報を電話番号で検索します。
- 顧客が存在する場合のみ、以下の処理を行います。
    - 着信者の名前を表示する。
    - 顧客の住所を地図にプロットして表示する。

![スクリーンショット 2021-12-23 13.18.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/3573f541-3743-5e35-3bd7-f9fed53a2203.png)


顧客DBとしては、[こちらの記事](https://qiita.com/mobilebiz/items/971683edee076b3a5686)で使用したGoogleSpreadsheetを流用し、住所情報を追加します。項目名は「住所」にしておいてください。
![スクリーンショット 2021-12-23 13.22.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/238c6117-7d7a-1687-0a7c-df6c999f035b.png)

地図情報と、住所情報を緯度経度に変換する（ジオコーディング）には、Google MapsAPIとGeoCodingAPIを利用します。GeoCodingAPIについては有料のAPIとなりますが、月$200の無料枠があります。Google Mapsの料金については[こちら](https://mapsplatform.google.com/pricing/?hl=ja)を参照してください。

なおGoogle GeoCoding APIは規約上、GoogleMapと一緒に利用しなくてはいけないため、ジオコーディングはFlexプラグイン内で行うこととし、Studioフローでは住所情報をTaskのAttributesに追加するのみとします。

# Flex UIコンポーネント

まずは地図情報をFlexのどこに表示するかを設計する必要があります。
Flex UIコンポーネント（エージェント画面）は以下のように定義されています。

![Overview-agentUI_2.width-800.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/f3bc86e3-a270-9bd5-5eac-d457a1b62755.png)
※ https://www.twilio.com/docs/flex/developer/ui/components より引用

住所情報は、Taskに紐付いて通知されるため、⑪のTaskCanvasTabsに「位置情報」というタブを作って、そこに表示させるのが良さそうです。
タブの生成方法は以下の通りです。

```javascript
    flex.TaskCanvasTabs.Content.add(
      <Tab label="位置情報" key="geolocation-tab">
        <GeoLocation key="geolocation-component" />
      </Tab>
    );
```

このコードを、プラグインの`init()`の中に記載すればOKです。
なお、タブのコンテンツとなる**GeoLocation**は後ほどReactコンポーネントとして作成する必要があります。

# ハンズオン

では早速プラグインを作成していきましょう。

## プラグインの新規作成

CLIを使って、Flexプラグインプロジェクトの作成していきます。
どこか作業フォルダに移動して、以下のコマンドを入力します。

```sh
% twilio flex:plugins:create plugin-geolocation --install
・・・
% cd plugin-geolocation
```

## GoogleのAPIキーを取得する

事前にGCP（Google Cloud Platform）のアカウントを取得しているものとします。

- [Google Cloud Platform](https://console.cloud.google.com/?hl=ja)にログインします。
- **APIとサービス**の中の**ライブラリ**を選択します。
- 「Maps」でサービスを検索します。
- 「Maps JavaScript API」を選択します。

![スクリーンショット 2021-12-23 7.51.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/000e1a74-49a9-1cb2-38ee-1c478ad7ec63.png)

- **有効にする**を押します。

Google Maps Platformの画面に遷移します。

![スクリーンショット 2021-12-23 7.55.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/8910fb31-bb3a-8481-5ae6-f1aed4c27c39.png)

- **認証情報**を選択します。
- 画面上部にある**認証情報を作成**を押します。
- プルダウンメニューから**APIキー**を選択します。

![スクリーンショット 2021-12-23 7.58.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/c6a8000d-91bd-abd4-3b0d-86a60eaac159.png)

- APIキーが生成されるので、内容をメモ帳などにコピーしておきます。※必要に応じてキーの制限をかけておきましょう。

では次に、Geocoding APIを有効にします。

![スクリーンショット 2021-12-23 8.01.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/9b74491f-3aa1-d676-bb8a-335a61df1e10.png)

- **API**を選択します。
- APIの一覧から**Geocoding API**を選択します。
![スクリーンショット 2021-12-23 8.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/229539b5-f241-00b8-0a4a-dc09d684dcfc.png)

- **有効にする**を押します。
- 以下のように２つのAPIが有効になっていればOKです。
![スクリーンショット 2021-12-23 8.05.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/7d8a06bf-65f6-007a-e273-b1ffccf6385e.png)

### 環境変数として登録

- Flexプラグインのプロジェクトフォルダの直下に`.env`という名前のファイルを作成します。
- 先程控えておいたAPIキーを`FLEX_APP_GOOGLE_API_KEY=XXXXXXXXXXXXXXXXXXXXX`と登録しておきます。

![スクリーンショット 2021-12-23 11.43.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/7561b09e-81de-9b9e-b5fb-8fbf02922461.png)

なお、Flexプラグイン内で環境変数にアクセスするためには、以下の手順を行っておく必要があります。
[Flexプラグイン Tips（環境変数の利用）](https://qiita.com/mobilebiz/items/4e7a32603878811b4af3)

## Google Maps APIのライブラリをインストール

今回はReact用の高機能なライブラリである、[@react-google-maps/api](https://github.com/JustFly1984/react-google-maps-api/tree/master/packages/react-google-maps-api)を利用したいと思います。
ただし、このライブラリはReact16.8以上を必要とするため、FlexのReactバージョン（React16.5.2）では動作しません。そこでFlex側もReact16.13.1に上げておきましょう。
以下のコマンドを使って、それぞれをインストールします。

```sh
% npm i -S react@16.13.1 react-dom@16.13.1
% npm i -S @react-google-maps/api
```

`package.json`は以下のようになります。

```json
{
  "name": "plugin-geolocation",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "postinstall": "flex-plugin pre-script-check"
  },
  "dependencies": {
    "@react-google-maps/api": "^2.7.0",
    "flex-plugin-scripts": "4.2.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1"
  },
  "devDependencies": {
    "@twilio/flex-ui": "^1",
    "dotenv-webpack": "^7.0.3",
    "react-test-renderer": "16.5.2"
  }
}
```

## 地図を表示するコンポーネントの作成

- エディタを開き、`src/components`フォルダの配下に、`GeoLocation`フォルダを新規作成します。
- 更にその配下に`GeoLocation.jsx`というファイルを作成します。
![スクリーンショット 2021-12-23 13.36.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/df64a1b3-ca88-e307-500b-7f1c7650f8cc.png)

- GeoLocation.jsに以下のコードを貼り付けます。

```javascript
import React, { useCallback, useRef, useState } from 'react';
import { withTaskContext } from '@twilio/flex-ui';
import { GoogleMap, LoadScript, Marker } from "@react-google-maps/api";

const libraries = ["places"];
const mapContainerStyle = {
  height: "60vh",
  width: "100%",
};  // 地図の大きさ

const options = {
  disableDefaultUI: true,  // デフォルトUI（衛星写真オプションなど）をキャンセルします。
  zoomControl: true,
};

const GeoLocation = (props) => {
  if (!props.task) return null;

  const [center, setCenter] = useState({ lat: 35.6802117, lng: 139.7576692 });

  // 住所をtask.attributesから取得する
  const { address = null } = props.task.attributes;
  if (!address) return (<div>住所情報なし</div>);

  const mapRef = useRef();
  const onMapLoad = useCallback((map) => {
    mapRef.current = map;
    geoCode();
  }, []);

  // 住所から位置情報を取得
  const geoCode = () => {
    const geoCoder = new window.google.maps.Geocoder();
    geoCoder.geocode({
      address,
      region: 'jp',
    }, (results, status) => {
      setCenter({
        lat: results[0].geometry.location.lat(),
        lng: results[0].geometry.location.lng(),
      });
    });
  };

  return (
    <LoadScript googleMapsApiKey={process.env.FLEX_APP_GOOGLE_API_KEY}>
      <GoogleMap
        key="GeoLocation-map"
        id="map"
        mapContainerStyle={mapContainerStyle}
        zoom={17}
        center={center}
        options={options}
        onLoad={onMapLoad}
      >
        <Marker position={center} />
      </GoogleMap>
    </LoadScript>
   )
};

export default withTaskContext(GeoLocation);
```

### コードの解説

Flexプラグインのコンポーネント内から、TaskのAttributesを参照するためには、コンポーネントに対して`@twilio/flex-ui`の`withTaskContext`をラップしてあげる必要があります。
コードの一番下に、

```javascript
export default withTaskContext(GeoLocation);
```
と書かれている部分がそれです。
このようにすることで、コンポーネントのプロパティに`task`というオブジェクトが入ってきます。

コードの後半に地図を表示させる部分があります。

```javascript
  return (
    <LoadScript googleMapsApiKey={process.env.FLEX_APP_GOOGLE_API_KEY}>
      <GoogleMap
        key="GeoLocation-map"
        id="map"
        mapContainerStyle={mapContainerStyle}
        zoom={17}
        center={center}
        options={options}
        onLoad={onMapLoad}
      >
        <Marker position={center} />
      </GoogleMap>
    </LoadScript>
   )
```

まず、`<LoadScript>`タグを使ってGoogleMapsAPIの認証を行うとともに、windowオブジェクト内にgoogleオブジェクトが生成されます。このgoogleオブジェクトを使ってジオコーディングを行います。

```
const geoCoder = new window.google.maps.Geocoder();
```

このようにしてジオコーダーのインスタンスを生成します。
実際のジオコーディングは、`<GoogleMap>`がロードされたときのイベント内で実行するようにしています。
なお、ジオコーディングの公式ドキュメントは[こちら](https://developers.google.com/maps/documentation/javascript/examples/geocoding-simple)にあります。

## GeoLocationコンポーネント呼び出す

つぎに、`src/GeolocationPlugin.js`をエディタで開きます。

```javascript
import React from 'react';
import { VERSION, Tab } from '@twilio/flex-ui';
import { FlexPlugin } from 'flex-plugin';

import GeoLocation from './components/GeoLocation/GeoLocation';
import reducers, { namespace } from './states';

const PLUGIN_NAME = 'GeolocationPlugin';

export default class GeolocationPlugin extends FlexPlugin {
  constructor() {
    super(PLUGIN_NAME);
  }

  /**
   * This code is run when your plugin is being started
   * Use this to modify any UI components or attach to the actions framework
   *
   * @param flex { typeof import('@twilio/flex-ui') }
   * @param manager { import('@twilio/flex-ui').Manager }
   */
  async init(flex, manager) {
    this.registerReducers(manager);

    flex.TaskCanvasTabs.Content.add(
      <Tab label='Location' key='GeoLocation-tab'>
        <GeoLocation key='GeoLocation-component' />
      </Tab>,
    );
  }

  /**
   * Registers the plugin reducers
   *
   * @param manager { Flex.Manager }
   */
  registerReducers(manager) {
    if (!manager.store.addReducer) {
      // eslint-disable-next-line
      console.error(
        `You need FlexUI > 1.9.0 to use built-in redux; you are currently on ${VERSION}`,
      );
      return;
    }

    manager.store.addReducer(namespace, reducers);
  }
}
```

自動的に作成される余計なコードを削除しています。
また、5行目で`GeoLocation`コンポーネントをインポートしています。
TaskCanvasにタブを追加するのは25行目からです。

```javascript
    flex.TaskCanvasTabs.Content.add(
      <Tab label='Location' key='GeoLocation-tab'>
        <GeoLocation key='GeoLocation-component' />
      </Tab>,
    );
```

## Studioフローの修正

では最後に、住所をTaskのAttributesに入れる部分を作りましょう。

今回は次のようなフローを作成しました。

![スクリーンショット 2021-12-23 14.01.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/5c796466-f807-694a-63bc-f41db6091682.png)

- **Run Function**ウィジェットを使って、GoogleSpreadsheetから住所を取得します。
- Functionは、[こちらの記事](https://qiita.com/mobilebiz/items/971683edee076b3a5686)で作成したものをそのまま利用しています。

TaskRouterへの引き渡しは以下のとおりです。

![スクリーンショット 2021-12-23 13.59.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/12265b61-e834-9cf9-f21e-3b97577fbb02.png)

- **WORKFLOW**は、「Assign to Anyone」にしていますが、どのワークフローでも構いません。
- **ATTRIBUTES**の中で、`address`パラメータにFunctionで取得した住所（`widgets.getCustomer.parsed['住所']`）を入れています。

## 電話番号への紐付け

今作成したStudioフローを着信番号に割り当てれば、とりあえず設定はすべて終了です。

# テスト

- GoogleSpreadsheetに住所を設定します。
- ローカルでプラグインを起動してみましょう。

```sh
% twilio flex:plugins:start
```

localhost:3000でFlexが起動しますので、設定した電話番号からFlexに架電をしてみましょう。

![スクリーンショット 2021-12-23 13.18.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/3573f541-3743-5e35-3bd7-f9fed53a2203.png)

- 着信が来たら、着信タスクをクリックします（着信ボタンを押さなくても位置情報は確認できます）。
- 右側にタスクの詳細パネルが開きます。
- **LOCATION**タブを選択すると図のように住所に合わせた地図が表示されます。

なお、このプラグインを本番環境にデプロイしようとするとReactのバージョンが合わないがどうするかと聞かれてしまいます。
そのため、サーバー側もReactのバージョンを上げておく必要があります。

- 管理者権限を持ったユーザがFlexにログインします。

![スクリーンショット 2021-12-23 14.31.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/d4838f40-e8b5-c34d-7598-47d9218cfa10.png)

- ダッシュボードパネルから、**DEVELOPER SETUP**の**EDIT**ボタンを押します。

![スクリーンショット 2021-12-23 14.28.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/a36290b7-c9b1-79cd-fe2f-454255cc016d.png)

- Reactのバージョンを選択できるので、最新の「16.13.1」にあげておきましょう。
 
# まとめ

今回はかなり大掛かりなカスタマイズでしたが、うまくいきましたでしょうか。
Flex UIがカスタマイズできるようになると、かなりいろいろなことができるのがおわかりいただけたかと思います。
Flexの魅力はなんといっても**カスタマイズ性の高さ**になります。ReactやUIコンポーネントなど細かいところは色々と勉強しなくてはいけないですが、その可能性は非常に大きいです。

★次の記事
[Twilio Flexで営業時間判定](https://qiita.com/mobilebiz/items/33f4172eda6f08dcfd80)

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilioは音声通話、メッセージング（SMS/チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2019 Twilio Champions