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(検索語)/page/limit/sort
- クエリ(想定・OpenAPIに従う。該当が無い場合は未使用):
- 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_JP → label → model | i18n優先。すべて無ければ "(不明)" | ● |
| サブタイトル | model_table → model | 表示不要時は非表示 | ● |
| NOTEバッジ | notes が 非空なら "note" を表示 | 文字列の有無のみ判定 | ー |
| クリックID | id → model → 乱数 | ルーティング・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・既定値・非表示のルールをここに集約し、箱にロジックを入れない。
id:row.id ?? row.model ?? random()modelNameJp(タイトル):row.label_i18n?.ja_JP ?? row.label ?? row.model ?? "(不明)"modelNameTech:row.model ?? "unknown.model"tableName:row.model_table ?? row.model?.replace(/\./g, "_") ?? "unknown_table"customizationSummary:row.notes ?? ""module:(row.model?.split(".")[0]) ?? "base"(→MODULE_INFO[module] || MODULE_INFO.base)status:row.status ?? "approved"hasMenu / hasForm / hasM2o / hasO2m / hasM2m:Boolean(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.tsx:models: OdooModelLike[]、onModelClick(model)、onSearch(query)など受け口を追加ModelCard.tsx:model: OdooModelLike、onClick?()の受け口追加- 既存のモック参照は温存(外部
modelsが無いときのみモックを使う)
DOM/クラス・見た目の改変・依存追加・内部 fetch は不可。
8. 非機能(パフォーマンス/キャッシュ)
- 取得:TanStack Query(
["models", { q, page, limit, sort }]) staleTime目安:一覧=30〜60秒(後で調整可)- 失敗時リトライ:1〜2回(ネットワークのみ)
- 再取得:検索確定・ルート再入場で明示的に
9. 受入(DoD)と検証手順
DoD(この画面が“つながった”と判定する条件)
http://<host>/api/modelsが 200(Network タブで確認)- タイトル/サブ/色/バッジ(NOTE)がAPI由来で正しく表示
- 検索→Enter でサーバ再取得が走り、件数/内容が変化
- 未知モジュールで base にフォールバック し見た目破綻しない
- Loading/Empty/Error の3状態が共通スタイルで出し分け
- figma 側の変更は props 追加のみ(DOM/クラス差分ゼロ)
検証シナリオ
- 正常:
q有り/無し、件数 0/1/多 - 例外:500/Timeout(MSWプリセット)→ エラーメッセージ→再試行で復帰
- i18n:
ja_JP/en_USの切替で固定文言のみ即時反映(DBラベルは後続)
10. 今後の拡張(この設計に影響しない範囲で)
- APIがステータス/機能バッジを返すようになったら、アダプタにフィールド追加で即対応
- サーバ側ページングを導入する場合も、QueryKey とクエリパラメータを足すだけでUIは不変
- モジュール語彙が増えた場合は、
MODULE_INFOに項目追加(未知は常に base)
付記:この設計書の読み方(オフショア向け)
- あなたがやること:
- API関数(GET /models)で配列を取得(
{items:[]}/[]両対応) - **アダプタ規則(§4)**で UI 用オブジェクト配列に変換
- それを ModelPortal の
modelsprops に渡す/クリック・検索は関数 props で受ける
- API関数(GET /models)で配列を取得(
- やってはいけないこと:箱内部の fetch/DOM・クラス変更/依存追加/API契約の推測変更
コメントを残す