メインコンテンツへスキップ
見出し画像

Slackの中に“ダッシュボード”を作る。Block Kitで組み立てるApp Home

    こんにちは!Accel-Hack(アクセルハック) 開発チームです。

    社内AIアシスタント「Airi」の開発プロセスをお届けする開発記ブログ。
    第12回目の今回は、ユーザーが Airi と出会う「顔」となる場所――Slack App Home(ホームタブ)の作り方をお届けします。

    前回までで、Vol.10 のコマンド、Vol.11 の外部連携と、Airi の中身は着実に充実してきました。

    ですが、機能がいくら増えても、ユーザーがそれに気づけなければ意味がありません。

    「Airi って何ができるの?」「私の freee 連携、今どうなってる?」――そんな疑問に一目で答える常設の入り口が欲しい。

    そこで作ったのが、Slack の中に住むダッシュボード、App Home です。


    1. コマンドを打つ前の「入り口」が欲しい

    スラッシュコマンドは強力ですが、弱点もあります。
    「何が使えるか」を、ユーザーがあらかじめ知っている必要があるのです。入社したばかりの人が、いきなり /airi attendance summary と打てるはずもありません。

    そこで役立つのが Slack App Home です。
    Slack のサイドバーから Airi を選ぶと現れる、アプリ専用の「ホームタブ」。
    ここに、使える機能の一覧や、各種連携の状況、その日の情報などをまとめて表示しておけば、ユーザーはコマンドを覚えていなくても、ボタンをポチッと押すだけで操作を始められます。

    Airi にとっての「トップページ」であり「受付」でもある場所です。


    2. Block Kit:SlackのUIを“部品”で組み立てる

    では、この画面をどう作るのか。
    ここで登場するのが Slack の UI フレームワーク Block Kit です。

    Block Kit の考え方はとてもシンプルで、HTML や CSS を書く代わりに、「ブロック」という決められた部品を並べていくだけ。
    見出しの header、本文の section、ボタン置き場の actions、区切り線の divider、小さな補足の context……こうした部品を、TypeScript のオブジェクトとして宣言的に記述します。

    たとえば、Airi のホームにある「Web UI を開く」セクションは、こう書かれています。

    export function buildWebUiBlocks(): AnyHomeTabBlock[] {
      return [
        {
          type: "header",
          text: { type: "plain_text", text: "🌐 Web UI", emoji: true },
        },
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: "ダッシュボードや各種設定にアクセスできます。",
          },
        },
        {
          type: "actions",
          elements: [
            {
              type: "button",
              text: { type: "plain_text", text: "Web UIを開く ↗" },
              style: "primary",
              action_id: "open_web_ui",
            },
          ],
        },
      ];
    }

    見出し・説明文・ボタン。
    この3つの部品を配列に並べるだけで、Slack がリッチな見た目に描画してくれます。
    ピクセル単位のデザインに悩む必要はなく、私たちは「何を、どの順で見せるか」に集中できます。

    3. app_home_opened を起点に描画する

    このホーム画面は、いつ作られるのでしょうか。
    答えは「ユーザーがホームタブを開いた瞬間」です。

    ユーザーがホームを開くと、Slack から app_home_opened というイベントが飛んできます。
    Airi は Vol.9 で紹介した仕組みでこのイベントを受け取り、publishHomeView(userId) を呼び出して、そのユーザー専用のホーム画面を組み立てます。

    export function registerHomeHandlers(app: SlackApp<AppEnv>): void {
      app.event("app_home_opened", appHomeOpened); // → publishHomeView(userId)
    }

    開くたびに最新の状態で描き直されるので、常に「今」の情報が表示される、というわけです。

    4. 設計の肝:各機能がブロックを「持ち寄る」

    この App Home の設計で最も気に入っているのが、「各機能が自分のブロックを持ち寄る」という点です。

    publishHomeView の中身は、拍子抜けするほど素直です。
    勤怠、ニュース、有給、リモートワーク、GitHub連携……各機能が用意した「ブロックビルダー」を順に呼び出し、返ってきたブロックの配列をただ連結していくだけ。

    const sections = [
      webUi.buildWebUiBlocks(),
      await newsUi.buildNewsBlocks(),
      await attendanceUi.buildAttendanceBlocks(db, userId),
      await githubOauthUi.buildGithubBlocks(db, userId),
      // ...(機能ごとに続く)
    ].filter((blocks) => blocks.length > 0); // 空の機能は自動でスキップ

    これは、Vol.4 で紹介した Feature-First アーキテクチャの思想が、UIの世界にもそのまま貫かれている姿です。

    ホームに新しいセクションを追加したくなったら、その機能に build*Blocks() を1つ用意して、この配列に一行足すだけ。
    ホーム全体を管理する巨大なファイルは存在しません。

    表示すべきものが無い機能は空配列を返し、filter で自動的にスキップされます。

    各セクションの間には区切り線(divider)が入り、最後に「AccelHackのAI秘書 あいり より」という context フッターが添えられます。

    この流れを図にすると、次のようになります。

    画像


    5. 状態に応じて“出し分ける”リッチさ

    App Home のもう一つの強みは、ユーザーや状況に応じて表示を動的に変えられることです。
    ブロックはコードで組み立てるので、条件分岐でいくらでも出し分けられます。

    たとえば GitHub 連携のセクションは、そのユーザーが連携済みかどうかで表示を変えています。

    if (user) {
      blocks.push({
        type: "section",
        text: { type: "mrkdwn", text: `✅ *連携済み* (@${user.githubUsername})` },
      });
    } else {
      blocks.push(/* ⚠️ 未連携メッセージ +「GitHubと連携する」ボタン */);
    }

    連携済みの人には緑のチェックとアカウント名を、未連携の人には連携をうながすボタンを。
    Vol.11 で触れた freee 連携の「連携済み/未連携」表示も、まさにこの仕組みです。
    一人ひとりに最適化された入り口を、その場で組み立てられるのです。


    6. 描画:views.publish に渡すだけ

    こうして組み上がったブロックの配列は、最後に Slack の views.publish API へ渡します。

    await fetch("https://slack.com/api/views.publish", {
      method: "POST",
      headers: { Authorization: `Bearer ${slackToken}` /* ... */ },
      body: JSON.stringify({
        user_id: userId,
        view: { type: "home", blocks },
      }),
    });

    view.type に "home" を指定し、組み立てたブロックを渡すだけ。これでそのユーザーのホームタブが更新されます。
    しかもこの呼び出しは、ホームを開いたときだけでなく、任意のタイミングで実行できるのがポイントです。
    Vol.11 で「連携が完了したら publishHomeView を呼んで表示を更新する」と書いたのは、まさにこれ。
    ユーザーが何もしなくても、状態の変化に合わせてホームが自動で描き直されるのです。

    7. おわりに

    今回は、Slack の中にリッチなダッシュボードを作る App Home の仕組みを見てきました。
    Block Kit で部品を宣言的に並べ、各機能がブロックを持ち寄り、状態に応じて出し分け、views.publish で描画する。
    ブロックの「足し算」で成り立つこの設計のおかげで、機能がいくら増えてもホームは散らからず、Airi の顔はいつも整っています。

    土台となる仕組み(インフラ・コマンド・認証・UI)が、これでひととおり揃いました。
    次回からはいよいよ、これらを組み合わせた具体的な業務機能の実装編に入ります。
    まずは 「Vol.13: 勤怠管理をシームレスに『attendance機能』」 として、Slackのボタン一つで freee に打刻する仕組みと、その裏側のエラーハンドリングの工夫をお届けします。お楽しみに!


    この記事は以下の体制で作成されました。

    • ドラフト作成・執筆: Airi (社内AIアシスタント)

    • アイデア出し・レビュー: CTO

    • デザイン: 事務スタッフ

    Accel-Hackでは、テクノロジーを駆使して世の中の課題を解決していく仲間を募集しています。
    私たちの取り組みに少しでも興味を持っていただけたら、ぜひ採用ページや会社サイトも覗いてみてくださいね。


    あなたへのおすすめ