Skip to content

メール通知システムの実装 #28

@oumelab

Description

@oumelab

イベント申し込みキャンセル機能実装

概要

  • 認証済みユーザーが申し込み済みのイベントをキャンセルできる機能を実装する。
  • ユーザーの利便性向上と、予定変更・急用等への柔軟な対応を可能にする。

目的

  • ユーザーが申し込み後に予定変更した場合の対応
  • 誤申し込みやダブルブッキングの解決
  • 定員管理の柔軟性向上
  • ユーザー体験の大幅改善

背景

現在のシステムでは、一度イベントに申し込むとキャンセルができず、以下の問題が発生している:

  • 急用や予定変更への対応不可
  • 誤申し込み時の修正手段なし
  • 定員に達したイベントでのキャンセル待ち機能なし
  • ユーザーの利便性が低い

実際のイベント管理サービス(connpass等)では申し込みキャンセル機能は標準機能として提供されており、本アプリでも必須の機能と判断。

実装内容

1. バックエンドAPI

  • キャンセルAPI実装(DELETE /api/events/[id]/cancel
  • ユーザー申し込み履歴取得API(GET /api/user/registrations
  • 権限チェック(申し込み者本人のみキャンセル可能)
  • 定員数の自動更新
  • データ整合性の確保

2. メール通知機能

  • キャンセル完了メール(申し込み者へ)
  • キャンセル通知メール(イベント作成者へ)
  • メールテンプレートの作成

3. フロントエンドUI

  • ユーザーの申し込み履歴ページ
  • キャンセルボタンとモーダル
  • キャンセル確認ダイアログ
  • イベント詳細ページでのキャンセル機能
  • 申し込み状況の動的更新

4. UX改善

  • キャンセル理由の選択(オプション)
  • キャンセル期限の設定(イベント開始前のみ等)
  • 申し込み・キャンセル履歴の表示
  • Toast通知による操作フィードバック

技術的詳細

API設計例

キャンセルAPI

// DELETE /api/events/[id]/cancel
export async function onRequest(context: RequestContext) {
  // 1. 認証チェック
  // 2. 申し込み状況確認
  // 3. キャンセル処理実行
  // 4. 定員数更新
  // 5. メール通知送信
}

申し込み履歴API

// GET /api/user/registrations
export async function onRequest(context: RequestContext) {
  // 1. 認証チェック
  // 2. ユーザーの申し込み履歴取得
  // 3. イベント情報と結合
  // 4. キャンセル可能フラグ付与
}

データベース更新

-- attendees テーブルに status カラム追加検討
ALTER TABLE attendees ADD COLUMN status TEXT DEFAULT 'registered';
-- 'registered', 'cancelled' 等の状態管理

-- または論理削除での実装
ALTER TABLE attendees ADD COLUMN cancelled_at INTEGER;

フロントエンド実装例

// ユーザー申し込み履歴表示
function MyRegistrations() {
  const { data: registrations } = useQuery({
    queryKey: ['user-registrations'],
    queryFn: getUserRegistrations,
  });

  const cancelMutation = useMutation({
    mutationFn: cancelEventRegistration,
    onSuccess: () => {
      toast.success('申し込みをキャンセルしました');
      queryClient.invalidateQueries(['user-registrations']);
    },
  });

  return (
    <div>
      {registrations?.map(reg => (
        <EventRegistrationCard 
          key={reg.id}
          registration={reg}
          onCancel={() => cancelMutation.mutate(reg.event_id)}
        />
      ))}
    </div>
  );
}

ユーザーストーリー

基本フロー

1. ユーザーがマイページで申し込み済みイベント一覧を確認
2. キャンセルしたいイベントの「キャンセル」ボタンをクリック
3. 確認ダイアログで理由を選択(オプション)
4. 「キャンセル確定」で処理実行
5. キャンセル完了の通知とメール送信
6. イベント詳細ページで定員数が更新される

エラーケース

- イベント開始後のキャンセル不可
- 既にキャンセル済みの場合
- 申し込んでいないイベントのキャンセル試行
- 権限のないユーザーによる操作

受け入れ基準

  • 認証済みユーザーが申し込み済みイベントをキャンセルできる
  • キャンセル後、定員に空きが生まれる
  • キャンセル完了時にメール通知が送信される
  • 申し込み履歴でキャンセル状況が確認できる
  • 不正なキャンセル操作は適切にエラーハンドリングされる
  • イベント詳細ページの参加者数が正確に更新される
  • UXに配慮した確認フローが実装されている

依存関係

前提条件

関連Issue

制約事項

  • イベント開始後のキャンセルは不可
  • キャンセル期限はイベント開始24時間前まで(設定可能)
  • 1つのイベントに対して何度も申し込み・キャンセルを繰り返すことの制限検討

テスト計画

  • 正常系:申し込み→キャンセルの一連フロー
  • 異常系:権限なし、期限切れ、重複操作等
  • 定員管理:キャンセル後の定員数更新確認
  • メール送信:各種通知メールの送信確認
  • UI/UX:操作フローの直感性確認

見積もり

実装時間: 1-2日
テスト時間: 半日

ブランチ

feature/event-registration-cancel

Metadata

Metadata

Assignees

No one assigned

    Labels

    blocked他の機能の実装完了後までfeature新機能の追加priority: medium中優先度

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions