アダプティブカードに社員検索機能を設けてみる

:::note info
[People Picker in Adaptive Cards](https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/people-picker?tabs=desktop)を[ギークフジワラ先生](https://x.com/geekfujiwara)にお伺いしたため、**手直ししました！**
:::

https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/people-picker?tabs=desktop

# はじめに
*Power Automate*のなかでも[アダプティブカード](https://learn.microsoft.com/ja-jp/power-automate/overview-adaptive-cards)の機能は非常に魅力的です。

https://learn.microsoft.com/ja-jp/power-automate/overview-adaptive-cards

**オシャレ**に、**インタラクティブ**な機能をサクっと設けることができます。
 - UXの向上
 - 回答内容をコントロールしたい

上記の目的にもうってつけです。

特に「ここのシーンだけ、この回答が欲しい！」というときに特化した照会にも利用できます。
また*ChatGPT*のおかげで少しとっつきにくかったアダプティブカードのデザインも手が届きやすくなりました。
**今回はその力を借りて、目的特化型のアダプティブカードを設けてみます。**

そんな中でとりあえず
- 社員の選択
- *Yes* or *No*

これだけ確認したい！そんなフォームできないかな～という気持ちがきっかけです。

# シナリオ
1. ~~[Office 365 Usersコネクタ](https://learn.microsoft.com/ja-jp/connectors/office365users/)で社員情報を取得~~
1. ~~[選択](https://learn.microsoft.com/ja-jp/power-automate/data-operations)で必要な列だけ列名を変更して取得~~
1. *People Picker in Adaptive Cards*も含めたアダプティブカードを投稿
1. 後続のアクションの例として、[承認コネクタ](https://learn.microsoft.com/ja-jp/connectors/approvals/)を実行する

:::note info
シナリオをアップデートしました!!
2024.06.30
:::


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/30d18785-04ba-fadc-6352-c4f8e3ad4d26.png)

<details><summary>Office 365 Usersコネクタを使った選択式はコチラ！</summary>

1. [Office 365 Usersコネクタ](https://learn.microsoft.com/ja-jp/connectors/office365users/)で社員情報を取得
1. [選択](https://learn.microsoft.com/ja-jp/power-automate/data-operations)で必要な列だけ列名を変更して取得
1. カスタマイズしたアダプティブカードを投稿
1. 後続のアクションの例として、[承認コネクタ](https://learn.microsoft.com/ja-jp/connectors/approvals/)を実行する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f4d96c00-a24c-a35b-9804-5921272e851f.png)

</details>

アダプティブカードでは、
- 経費精算で事前申請を取得しているか
    - 確認担当者の設定
- インボイス制度の対象か
    - 確認担当者の設定
というあるかどうかもわからない状況を想定。

# People Picker in Adaptive Cards
社員検索ができる[People Picker in Adaptive Cards](https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/people-picker?tabs=desktop)を[ギークフジワラ先生](https://x.com/geekfujiwara)を活用してみます。
**Outlookの宛先のサジェストのように名前を入力すると、候補が出てくる素敵な機能です！**

```json:
{
    "type": "Input.ChoiceSet",
    "choices": [],
    "choices.data": {
        "type": "Data.Query",
        "dataset": "graph.microsoft.com/users"
    },
    "id": "people-picker",
    "value": "{Defaultを設定したい場合は Entra IDをここで設定}",
    "isMultiSelect": false
}
```

既定の値を設定したい場合は`value`に対象の社員のEntra IDを設定します。
`choices.data`のプロパティでこんな便利ができるのかと衝撃でした👀✨

<details><summary>以前の文章</summary>
~~課題に感じたポイントは`社員の選択`をどう実現するか。~~
~~`テキスト入力`や`ラジオボタン`、`ドロップダウン`と豊富に使えますが、コネクタと接続して使う、となると一癖いる印象です。~~

~~今回は発想を変え、~~
~~**アダプティブカードに配列で選択肢が設けられるのであれば、~~
~~「あらかじめ選択肢を準備して差し込めばいい！」という考え方**で進めてみました。~~

:::note info
良いテクニックがあればご教授いただければという点です🐟💦
ご承知おきください🙇🙇
:::

</details>


# アダプティブカードのデザイン
 - カード全体の`タイトル`の設定
 - 承認依頼 セクションの`テキストラベル`
     - `ラジオボタン`
     - 社員選択用の`ドロップダウン`
 - インボイス制度 セクションの`テキストラベル`
     - `ラジオボタン`
     - 社員選択用の`ドロップダウン`
 - 申請ボタン

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/1964b9a1-ad72-4fc3-1146-17df77174265.png)

こんなデザインです。

## JSON
アダプティブカードの`JSON`はコチラ。

```json:アダプティブカード
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.4",
    "body": [
        {
            "type": "TextBlock",
            "text": "経費精算手続き",
            "weight": "Bolder",
            "size": "Large",
            "horizontalAlignment": "Center",
            "color": "Accent",
            "spacing": "Medium"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "承認依頼",
                    "weight": "Bolder",
                    "size": "Medium",
                    "color": "Accent",
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "事前に承認を得ていますか？",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "id": "radioChoice1",
                    "style": "expanded",
                    "isMultiSelect": false,
                    "value": "true",
                    "choices": [
                        {
                            "title": "はい",
                            "value": "true"
                        },
                        {
                            "title": "いいえ",
                            "value": "false"
                        }
                    ],
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "承認担当者を選択してください。",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "choices": [],
                    "choices.data": {
                        "type": "Data.Query",
                        "dataset": "graph.microsoft.com/users"
                    },
                    "id": "people-picker1",
                    "value": "{Defaultを設定したい場合は Entra IDをここで設定}",
                    "isMultiSelect": false
                }

            ],
            "style": "emphasis"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "支払い依頼",
                    "weight": "Bolder",
                    "size": "Medium",
                    "color": "Accent",
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "インボイス制度の対象ですか？",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "id": "radioChoice2",
                    "style": "expanded",
                    "isMultiSelect": false,
                    "value": "false",
                    "choices": [
                        {
                            "title": "はい",
                            "value": "true"
                        },
                        {
                            "title": "いいえ",
                            "value": "false"
                        }
                    ],
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "経費精算の担当を選択してください。。",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "choices": [],
                    "choices.data": {
                        "type": "Data.Query",
                        "dataset": "graph.microsoft.com/users"
                    },
                    "id": "people-picker2",
                    "isMultiSelect": false
                }
            ],
            "style": "emphasis"
        },
        {
            "type": "ActionSet",
            "actions": [
                {
                    "type": "Action.Submit",
                    "title": "申請",
                    "style": "positive"
                }
            ],
            "spacing": "Large"
        }
    ]
}
```

:::note info
`GPT-4o`のお力借りまくったデザインです
:::

セクションを視覚的に区切っているフォームです。
`Yes` or `No`のみ確認するためには`ラジオボタン`を採用しています。

入力漏れを防止するために初期値を設けています。

```json:ラジオボタン1
{
  "type": "Input.ChoiceSet",
  "id": "radioChoice1",
  "style": "expanded",
  "isMultiSelect": false,
  "value": true,
  "choices": [
    {
      "title": "はい",
      "value": true
    },
    {
      "title": "いいえ",
      "value": false
    }
  ],
  "spacing": "Medium"
}
```

ラジオボタンのひとつを担っているJSONの箇所です。
- `choices`が文字通り選択肢
- `value`が初期値
- `isMultiSelect`が`true`の場合、チェックボックス形式になります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/303c2cd4-4d64-408f-d632-86bf1f2648e7.png)

今回の目的は入力規則を設けたいことにあるため、入力漏れは論外です。
初期値を設定しておくと、その可能性も避けられますね。

###  社員の選択でOffice 365 Usersコネクタを使ったバージョン

社員選択の部品はコチラ。
初期値である`values`は、一旦配列の最初の値にしていますが、場合に応じてコチラでプリセットできそうです。

```json:ドロップダウン
{
  "type": "Input.ChoiceSet",
  "id": "selectedPerson1",
  "style": "compact",
  "value": "@{first(body('選択'))?['value']}",
  "choices": @{body('選択')},
  "spacing": "Medium"
}
```

`choices`は、アダプティブカードの実行前に、[Office 365 Usersコネクタのユーザーを検索する (V2)](https://learn.microsoft.com/ja-jp/connectors/office365users/#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B-(v2))で社員情報を取得し、列を絞り込むことでFitさせています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/954874d6-46e8-c310-e8a5-c3f97e0d24d6.png)

:::note info
既定では`検索文字列が必須`が`true`です
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/8d104057-f0ce-8d3c-0b39-b4bbd0088f21.png)

選択で`title`と`value`のみに絞り込み

```json:選択
{
    "title": "@item()?['DisplayName']",
    "value": "@item()?['Mail']"
}
```

`Office 365 Usersコネクタ`を使う時点で部門単位での絞り込みもできそうです👀✨
そもそも豊富なコネクタがある*Power Automate*なんですから組み合わせてナンボですね！

入力に必要な項目は自由にカスタマイズできることも魅力です。


## 入力値の取得方法
[アダプティブ カードを投稿して応答を待機する](https://learn.microsoft.com/ja-jp/power-automate/create-adaptive-cards)アクションで、ユーザーの入力を取得できます。

https://learn.microsoft.com/ja-jp/power-automate/create-adaptive-cards

入力を待機できる期間は*30*日です。
*30*日ほったらかすのはまずいので十分でしょう。

今回の例のスキーマはコチラ！

```json:Schema
{
    "type": "object",
    "properties": {
        "responseTime": {
            "type": "string"
        },
        "responder": {
            "type": "object",
            "properties": {
                "objectId": {
                    "type": "string"
                },
                "tenantId": {
                    "type": "string"
                },
                "email": {
                    "type": "string"
                },
                "userPrincipalName": {
                    "type": "string"
                },
                "displayName": {
                    "type": "string"
                }
            }
        },
        "submitActionId": {
            "type": "string"
        },
        "messageId": {
            "type": "string"
        },
        "messageLink": {
            "type": "string"
        },
        "data": {
            "type": "object",
            "properties": {
                "radioChoice1": {
                    "type": "string"
                },
                "selectedPerson1": {
                    "type": "string"
                },
                "radioChoice2": {
                    "type": "string"
                },
                "selectedPerson2": {
                    "type": "string"
                }
            }
        }
    }
}
```

`data`がユーザーの入力値です。
`title`と`value`で構成される`choices`の項目は`value`が返ってきます。
社員検索の部分では`value`をメールアドレスに設定し、後続のアクションに使用。

ここまで取得できれば、後続のアクションに繋げる上では十分です！

# 後続のアクションの例「設定した値から承認ワークフローを実行」
今回の入力値をもとに承認ワークフローも実行できますね。
[開始して承認を待機](https://learn.microsoft.com/ja-jp/connectors/approvals/#%E9%96%8B%E5%A7%8B%E3%81%97%E3%81%A6%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AE%E6%89%BF%E8%AA%8D%E3%82%92%E5%BE%85%E6%A9%9F)にて下記の設定を試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/75fc457d-585f-b7f5-0d96-09cbd05d2f0c.png)

- 承認の種類　`承認/拒否 - すべてのユーザーの承認が必須`
- タイトル `@{body('JSON_の解析')?['responder']?['displayName']}さんの経費精算 申請`
- 担当者 `@{body('JSON_の解析')?['data']?['selectedPerson1']};@{body('JSON_の解析')?['data']?['selectedPerson2']}

```plaintext:詳細
経費精算を確認してください

### 内容

| 質問 | 回答 |
|---|---|
| 申請者 | @{body('JSON_の解析')?['responder']?['displayName']} |
| 事前承認? | @{body('JSON_の解析')?['data']?['radioChoice1']} |
| インボイス制度? | @{body('JSON_の解析')?['data']?['radioChoice2']} |

```

https://learn.microsoft.com/ja-jp/connectors/approvals/#%E9%96%8B%E5%A7%8B%E3%81%97%E3%81%A6%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AE%E6%89%BF%E8%AA%8D%E3%82%92%E5%BE%85%E6%A9%9F


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/33eeb26b-0a07-7a2d-e667-fb928d10324c.png)


**アダプティブカードからワークフローが実行しました！**
お手軽に確認ステップが踏める！メッチャいい！

# 応用 - 承認機能も組み込む
[Microsoft MVPのHiroさんのBlog](https://mofumofupower.hatenablog.com/entry/2020/09/14/160046)を参考に、今回のアダプティブカード自体に、承認・否認機能を持たせてみましょう。

https://mofumofupower.hatenablog.com/entry/2020/09/14/160046

1. [承認を作成](https://learn.microsoft.com/ja-jp/connectors/approvals/#%E6%89%BF%E8%AA%8D%E3%82%92%E4%BD%9C%E6%88%90)
1. (1)の情報をJSONで取得
1. アダプティブカードに組み込み
1. アダプティブカードを投稿
1. [承認を待機](https://learn.microsoft.com/ja-jp/connectors/approvals/#%E6%89%BF%E8%AA%8D%E3%82%92%E5%BE%85%E6%A9%9F)で値の取得

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/77b63884-dc48-0566-2ddd-3846eb65138e.png)

`rgba(255,0,0,1)`赤枠のところを押すと・・・

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/93adabf7-1f7d-a3ae-b017-6b083acf9eb4.png)

承認担当者の回答内容もコントロールできるってことぉ！？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f4d9fb8d-1e8b-bde0-b526-13b1023d3123.png)

ぶちあたっている課題に効果的に機能しそう！

発見されていることに脱帽です。精進します。
最後に`json`を！良いPower Lifeを！

```json:承認を組み込んだアダプティブカード
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.4",
    "body": [
        {
            "type": "TextBlock",
            "text": "経費精算手続き",
            "weight": "Bolder",
            "size": "Large",
            "horizontalAlignment": "Center",
            "color": "Accent",
            "spacing": "Medium"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "承認依頼",
                    "weight": "Bolder",
                    "size": "Medium",
                    "color": "Accent",
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "事前に承認を得ていますか？",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "id": "radioChoice1",
                    "style": "expanded",
                    "isMultiSelect": false,
                    "value": "true",
                    "choices": [
                        {
                            "title": "はい",
                            "value": "true"
                        },
                        {
                            "title": "いいえ",
                            "value": "false"
                        }
                    ],
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "承認担当者を選択してください。",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "id": "selectedPerson1",
                    "style": "compact",
                    "value": "@{first(body('選択'))?['value']}",
                    "choices": @{body('選択')
                    },
                    "spacing": "Medium"
                }
            ],
            "style": "emphasis"
        },
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "支払い依頼",
                    "weight": "Bolder",
                    "size": "Medium",
                    "color": "Accent",
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "インボイス制度の対象ですか？",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "id": "radioChoice2",
                    "style": "expanded",
                    "isMultiSelect": false,
                    "value": "false",
                    "choices": [
                        {
                            "title": "はい",
                            "value": "true"
                        },
                        {
                            "title": "いいえ",
                            "value": "false"
                        }
                    ],
                    "spacing": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": "経費精算の担当を選択してください。",
                    "spacing": "Small"
                },
                {
                    "type": "Input.ChoiceSet",
                    "id": "selectedPerson2",
                    "style": "compact",
                    "value": "@{first(body('選択'))?['value']}",
                    "choices": @{body('選択')
                    },
                    "spacing": "Medium"
                }
            ],
            "style": "emphasis"
        },
        @{outputs('承認ボディ')[4]} // ここが承認の機能
    ]
}
```