<BoothStatus /> / <BoothManager />

模擬店ステータス

各模擬店の混雑度 (空き / やや混雑 / 混雑) および在庫状況 (在庫あり / 残りわずか / 完売) をリアルタイムに集約・表示するコンポーネント。

使い方

参加者向け

import BoothStatus from "@/features/booth/components/BoothStatus";
      
<BoothStatus />

2つのカードに分割する場合

import BoothStatus from "@/features/booth/components/BoothStatus";
      
<BoothStatus split="first"/>
<BoothStatus split="second"/>

模擬店管理者向け

import BoothManager from "@/features/booth/components/BoothManager";
      
<BoothManager />

API src/features/booth/api.ts

クライアントからのデータ取得・更新は以下の API を通じて行われます。更新 API performMutation は実行完了時に自動でキャッシュを破棄します。

API 関数引数 / ペイロード役割
fetchStallsOnly(ttl)ttl: number混雑・在庫ステータスのみを軽量 RPC get_stalls_only で取得
updateStallStatus(stallName, updates)stallName: string
updates: { crowdLevel?: number, stockLevel?: number }
指定した模擬店の混雑度・在庫状況を更新し、all, stalls_only キャッシュを無効化

コード

isDirty による差分検出 useBoothManager.ts

選択値がDBの現在値と異なる場合のみ「未反映」バナーを表示し、余分な更新リクエストを防止します。

useBoothManager.ts
const checkDirty = (currentCrowd: StatusLevel, currentStock: StatusLevel) => {
  if (fetchedData?.stalls && assignedStall) {
    const myStall = fetchedData.stalls.find((s) => s.stallName === assignedStall);
    if (myStall) {
      const dirty = currentCrowd !== myStall.crowdLevel || currentStock !== myStall.stockLevel;
      setIsDirtyInternal(dirty);
      isDirtyRef.current = dirty;
      return dirty;
    }
  }
  return false;
};

共通キャッシュレイヤー src/lib/Server/baseApi.ts

fetchWithCache はメモリキャッシュ + SessionStorage の2層でキャッシュし、performMutation は書き込み後に指定キーを即座に無効化します。

src/lib/Server/baseApi.ts
// performMutation: 書き込み後に指定キーのキャッシュを無効化
export const performMutation = async <T>(
  action: () => Promise<T>,
  cacheKeysToInvalidate: string[] = ["all"]
): Promise<T> => {
  const result = await action();
  invalidateCache(cacheKeysToInvalidate);
  return result;
};

データベーススキーマ

stalls_status
CREATE TABLE stalls_status (
  id SERIAL PRIMARY KEY,
  stall_name TEXT NOT NULL UNIQUE,
  crowd_level SMALLINT DEFAULT 0, -- 0: 空き, 1: やや混雑, 2: 混雑
  stock_level SMALLINT DEFAULT 0, -- 0: 在庫あり, 1: 残りわずか, 2: 完売
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
);