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

“動く”申請フォーム。Slackモーダルで作る、迷わせない有給申請

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

    社内AIアシスタント「Airi」の開発プロセスをお届けする開発記ブログ。

    第14回目の今回は、業務機能の第二弾――有給休暇の申請(paid-holiday機能)をお届けします。

    前回の Vol.13 では、ボタン一つで完結する打刻を作りました。
    今回のテーマは「申請」。

    打刻より少し複雑で、日付や時間、理由といった複数の情報を入力してもらう必要があります。

    この入力を、いかにストレスなく行ってもらうか。その鍵が Slack のモーダル(ダイアログ) でした。


    1. 申請業務こそ、対話でその場で完結させたい

    有給申請というと、紙の申請書や、共有スプレッドシートへの記入を思い浮かべる方も多いでしょう。
    しかしこの方式は、記入漏れ、転記ミス、承認者への催促……と、地味な手間がついて回ります。

    Airi が目指したのは、Slack から一歩も出ずに申請を完結させることです。

    ホームの「有給を申請する」ボタンを押すと、その場に入力フォームがふわりと開く。
    必要事項を選んで送信すれば、あとは Airi が freee への申請まで面倒を見てくれる。

    この体験を実現するのが、Slack の モーダル という仕組みです。


    2. 入り口:ボタンから、まず連携確認へ

    「有給を申請する」ボタンが押されると、handlePaidHolidayModalOpen が動き出します。

    ですが、いきなりフォームを開くわけではありません。

    まず Vol.11 の ensureFreeeAccessToken を使って、そのユーザーが freee と連携済みかを確認します。
    トークンが取れて初めて、views.open で休暇種別・日付・理由を備えた申請モーダルを開きます。

    そして freee 側で何か起きたときのために、二段構えの受け皿を用意しました。
    API エラーなら「メンテナンス中、または一時的な障害の可能性があります」と伝えるエラー専用のモーダルを開き、同時に sendErrorAlert で開発チームのアラートチャンネルにも飛ばします。

    ただし「申請経路が見つかりませんでした」という設定不備だけは、モーダルではなく本人への DM で伝えます。

    これはユーザー自身では直せない類の問題なので、あとから読み返せる場所に残したかったからです。


    3. モーダルは“動く”――選択に応じて姿を変える

    Slack のモーダルの面白いところは、開いたあとも中身を書き換えられることです。
    paid-holiday機能では、この特性を活かして「ユーザーに不要な入力欄を見せない」工夫をしています。

    種別が「全休」の間は、時間の入力欄は必要ありません。

    しかし「半休」を選んだ瞬間、開始時間と終了時間のピッカーがモーダルに現れます。

    これを成立させている仕掛けは、実は1行です。

    休暇種別のブロックに dispatch_action: true を付けておくと、選択した瞬間に Slack がアクションを飛ばしてくれます。

    // 選択のたびにアクションが飛んでくるようにする
    {
      type: "input",
      block_id: "leave_type_block",
      dispatch_action: true,
      element: { type: "static_select", action_id: "leave_type_select", /* 全休 / 半休 */ },
    }

    飛んできたアクションを受けるのが handleLeaveTypeSelect です。
    やっていることは潔く、モーダルを丸ごと組み直して差し替えるだけ。

    await context.client.views.update({
      view_id: payload.view.id,
      view: buildPaidHolidayModalView({ selectedType }), // 半休なら時間欄を足して再構築
    });

    buildPaidHolidayModalView の中では、半休のときだけ blocks に timepicker を2つ push しています。

    差分を計算して部分更新するのではなく、毎回まっさらに組み立て直す。

    状態が「選ばれた種別」ひとつに集約されるので、バグが入り込む隙がありません。

    ちなみに理由の入力欄は optional: true。
    書きたい人だけ書けば十分です。

    常に全部の欄を見せて「半休のときだけ入力してください」と注意書きを添えるより、必要なときに必要な欄だけを出す。
    この小さな配慮が、フォームの分かりやすさを大きく左右します。


    4. 送信、そして freee の承認フローへ

    送信ボタンが押されると handlePaidHolidaySubmission が呼ばれます。

    モーダルの入力値は、ブロックIDとアクションIDをキーにした少し入り組んだ形で届くので、getModalValue という薄いヘルパーで種別・日付・時間・理由を取り出します。

    あとは submitPaidHoliday が、あらかじめ設定した承認フロー(FREEE_APPROVAL_FLOW_ID)へ申請を登録するだけ。
    上長の承認を待つ通常の流れにそのまま乗ります。

    申請が通ると、Airi が「有給休暇の申請が完了しました。(2026-06-10)」と DM でお知らせします。

    失敗した場合も、sendErrorAlert で開発チームに通知しつつ、本人には理由を噛み砕いた DM を送ります。

    Vol.13 と同じ考え方で、エラーは必ず「人が読める言葉」にしてから届けるという方針は、機能をまたいで共通です。


    画像

    5. この時点で、あえて残っていた宿題

    正直に書いておくと、この段階の paid-holiday機能には送信前の検証がまったくありません。

    送信を受け取る登録は、検証用の関数を挟める形になっているのですが、そこは空のまま。

    取り出した値も「未入力なら空文字」として素通ししていました。

    つまり、日付を選ばずに送っても、半休なのに時間を入れずに送っても、そのまま freee に飛んでいきます。
    運が良ければ freee がエラーを返してくれますが、そうでなければ中途半端な申請が承認フローに乗ってしまうわけです。

    これは手抜きというより、「まず動くものを出して、実際に困ってから直す」という判断でした。

    実際このあと、想定していなかった入力の組み合わせが本当に申請として通ってしまい、送信のその瞬間に弾く仕組みを入れ直すことになります。
    その話は、また改めて。

    6. おわりに

    今回は、有給申請を題材に、Slack モーダルで「迷わせない」申請体験を作る方法を見てきました。

    dispatch_action ひとつで選択に応じて姿を変えるフォームが作れること、そして差分更新ではなく毎回組み立て直すという割り切りが、結果的にシンプルさを守ってくれること。

    この2つが、今回いちばんの収穫です。

    次回は、モーダルの応用編にして、複数のシステムを連動させる少し欲張りな機能――「Vol.15: Google・Slack・freeeを一気通貫。入社処理を自動化する設計とその第一歩」 をお届けします。

    Slack のモーダルから入社手続きを実行し、Google Workspace・Slack・freee のアカウント作成を一気通貫でつなぐ仕組みに迫ります。お楽しみに!


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

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

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

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

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


    あなたへのおすすめ