TOP 画面 個別設計書(UI↔API マッピング中心)

0. ドキュメント概要

  • 対象画面:TOP(モデル一覧)
  • 目的:figma.make 由来 UI(ModelPortal.tsx / ModelCard.tsx)へ既存APIの実データを安全に差し込むための契約を固定
  • 変更方針:DOM/クラスは不変、必要な“受け口(props)”のみ追加
  • ルーティング:SPA の /(または /models)。Route 要素=コンテナ → figma箱(プレゼン)

1. 関係ファイル(figma.make の“箱”)

  • 一覧親:ModelPortal.tsx(検索・フィルタUI/カード並べ)
  • 行カード:ModelCard.tsx(1件表示・色分け・簡易バッジ)
  • UIアトム:components/ui/*(shadcn 相当。編集不可。必要なら props 追加のみ)

箱の内部で fetch/認証/i18n/権限 を実装しない。表示専用に徹する。


2. データソース(API)と契約の前提

  • エンドポイント(一覧):GET /models
    • クエリ(想定・OpenAPIに従う。該当が無い場合は未使用):
      • q(検索語)/pagelimitsort
  • 200 レスポンス形:どちらも受容
    • [{…}, {…}] または { items: [{…}], total?: number }
  • 参照用スキーマ:frontend/openapi/v1.0.0.json(生成型:apps/top/src/types/api.d.ts

コンテナ側の API 関数で 配列に正規化してアダプタへ渡す(箱は配列前提)。


3. UI ↔ API マッピング(確定)

3.1 カード(ModelCard)1枚に必要な最小 props

UI要素取得元(API フィールド)→ アダプタ変換フォールバック/備考必須
タイトルlabel_i18n.ja_JPlabelmodeli18n優先。すべて無ければ "(不明)"
サブタイトルmodel_tablemodel表示不要時は非表示
NOTEバッジnotes非空なら "note" を表示文字列の有無のみ判定
クリックIDidmodel → 乱数ルーティング・key用
モジュール(色/枠)model"." を抽出 → MODULE_INFO[module]見つからなければ MODULE_INFO.base
ステータスstatus があれば使用/無ければ "approved" 固定一覧フィルタと整合
機能バッジ(MENU/FORM/M2O/O2M/M2M)該当フィールドが あれば true/false で表示無ければすべて false
メタ(日付等)updated_at などが あれば toLocaleDateString('ja-JP')無ければ非表示

重要:色や枠のスタイルは MODULE_INFO[module] に依存。未知モジュールは必ず base にフォールバック


3.2 一覧親(ModelPortal)が期待する配列・UI動作

項目仕様備考
表示配列外部(コンテナ)から渡された配列を優先。無い場合は既存 MOCK_MODELS“モック回帰”でデザイナ検証を保証
検索入力→Enterでサーバ検索q)を推奨ない場合はクライアントフィルタでも可
ステータス絞りdraft/approved/applied(UI既定)APIが無ければクライアント側で実施
モジュール絞りMODULE_INFO のキーでフィルタ同上
集計表示配列からローカル集計APIが total を返す場合でもUIは独立可

4. アダプタ規則(API → UI props)

コンテナで取得した 1行(row) を、ModelCard が受け取る 最小オブジェクトへ変換。
i18n・既定値・非表示のルールをここに集約し、箱にロジックを入れない。

  • idrow.id ?? row.model ?? random()
  • modelNameJp(タイトル):row.label_i18n?.ja_JP ?? row.label ?? row.model ?? "(不明)"
  • modelNameTechrow.model ?? "unknown.model"
  • tableNamerow.model_table ?? row.model?.replace(/\./g, "_") ?? "unknown_table"
  • customizationSummaryrow.notes ?? ""
  • module(row.model?.split(".")[0]) ?? "base"(→ MODULE_INFO[module] || MODULE_INFO.base
  • statusrow.status ?? "approved"
  • hasMenu / hasForm / hasM2o / hasO2m / hasM2mBoolean(row.<field>)(無ければ false
  • updatedAt(任意):row.updated_at ? new Date(row.updated_at) : undefined

i18n フォールバック(固定)
label_i18n.ja_JP → label → model
※ ほかの画面でもこの順序を標準とし、共通関数化する。


5. 振る舞い(イベント/状態管理)

5.1 イベント(箱は props で受けるだけ)

  • 検索決定:Enter → コンテナで q を付けて再取得 → 配列を再注入
  • カードクリック:onClick(model) → ルーターで /models/:id へ(実装まではログでも可)

5.2 状態表示(共通スタイル)

  • Loading:スケルトン表示
  • Empty:0件時の案内(固定文言は i18n 辞書)
  • Error:Problem+JSON(title/detail/errors[])を共通整形関数でメッセージ化

6. 依存・前提(TOP で共通に使う前提)

  • OpenAPI を唯一の真実として最新を参照(openapi/v1.0.0.json
  • フロントのベースURL:VITE_API_BASE_URL=/api(ローカルは Vite dev proxy → PF/Prism、AWSは CF /api
  • 認証:TOP は Public 想定(今後 Private 化しても本マッピングに影響なし)
  • i18n:固定文言=辞書、DBラベル=*_i18n 優先(フォールバックは §4 に準拠)

7. figma.make への許容変更(これ以外は禁止

  • ModelPortal.tsxmodels: OdooModelLike[]onModelClick(model)onSearch(query) など受け口を追加
  • ModelCard.tsxmodel: OdooModelLikeonClick?() の受け口追加
  • 既存のモック参照は温存(外部 models が無いときのみモックを使う)

DOM/クラス・見た目の改変・依存追加・内部 fetch は不可


8. 非機能(パフォーマンス/キャッシュ)

  • 取得:TanStack Query(["models", { q, page, limit, sort }]
  • staleTime 目安:一覧=30〜60秒(後で調整可)
  • 失敗時リトライ:1〜2回(ネットワークのみ)
  • 再取得:検索確定・ルート再入場で明示的に

9. 受入(DoD)と検証手順

DoD(この画面が“つながった”と判定する条件)

  1. http://<host>/api/models200(Network タブで確認)
  2. タイトル/サブ/色/バッジ(NOTE)がAPI由来で正しく表示
  3. 検索→Enter でサーバ再取得が走り、件数/内容が変化
  4. 未知モジュールで base にフォールバック し見た目破綻しない
  5. Loading/Empty/Error の3状態が共通スタイルで出し分け
  6. figma 側の変更は props 追加のみ(DOM/クラス差分ゼロ)

検証シナリオ

  • 正常:q 有り/無し、件数 0/1/多
  • 例外:500/Timeout(MSWプリセット)→ エラーメッセージ→再試行で復帰
  • i18n:ja_JPen_US の切替で固定文言のみ即時反映(DBラベルは後続)

10. 今後の拡張(この設計に影響しない範囲で)

  • APIがステータス/機能バッジを返すようになったら、アダプタにフィールド追加で即対応
  • サーバ側ページングを導入する場合も、QueryKey とクエリパラメータを足すだけでUIは不変
  • モジュール語彙が増えた場合は、MODULE_INFO に項目追加(未知は常に base)

付記:この設計書の読み方(オフショア向け)

  • あなたがやること
    1. API関数(GET /models)で配列を取得({items:[]}[] 両対応)
    2. **アダプタ規則(§4)**で UI 用オブジェクト配列に変換
    3. それを ModelPortal の models props に渡す/クリック・検索は関数 props で受ける
  • やってはいけないこと:箱内部の fetch/DOM・クラス変更/依存追加/API契約の推測変更

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です