管理画面 — 店舗別キーワード上限設定
概要
#1-3 キーワード上限拡張 Phase 2 で新設する管理画面の UI 設計。
営業 / CS が ユーザ配下の店舗ごとに キーワード上限を 8〜15 件の範囲で調整できるようにする。Phase 1 で運用 SQL によって行っていた INSERT を、画面操作に置き換える。
| 項目 | 内容 |
|---|---|
| 配置 | 既存の MappyUserDetail ページに 「キーワード上限」タブを追加(URL: /admin/users/:userId/details) |
| ガード | auth:admin_api(既存) |
| 対象データ | mappy_keyword_limit_settings(scope_type='location') |
| 監査 | ActivityLog 連携 |
既存画面への組み込み方針
mappy admin の既存ナビゲーションを踏襲する(新規ルートは作らない):
/admin/login
↓
/admin/users [MappyUserList] ← サイドバー「MAPPY管理」
↓ (loginId クリック)
/admin/users/:userId/details [MappyUserDetail] ← 既存 2 タブに 1 タブ追加
├ Tab1: アカウント情報 (既存・改修なし)
├ Tab2: 通知設定 (既存・改修なし)
└ Tab3: キーワード上限 ★ Phase 2 で新設改修対象ファイル
| ファイル | 改修内容 |
|---|---|
resources/js/mappy/views/admin/UserDetail.vue | タブ 3 を追加。tab state を保持する <v-tabs> を 2 → 3 に拡張 |
resources/js/mappy/views/admin/UserKeywordLimitTab.vue ★ 新設 | 本画面の本体(店舗一覧テーブル + インライン編集) |
resources/js/mappy/include/selectors/StoreAndGroupSelector.vue | 改修不要(タブ 1 の機能でそのまま使う) |
app/Http/Controllers/Mappy/Admin/LocationController.php | getUserLocationsAndGroups() のレスポンスに max_keywords を含める |
app/Http/Controllers/Mappy/Admin/KeywordLimitController.php ★ 新設 | PATCH / DELETE エンドポイントを実装 |
routes/mappy/api.php | /admin/locations/{loc}/keyword-limit を 2 行追加 |
サイドバー(変更なし)
resources/js/mappy/_navigationAdmin.js に 項目追加は不要。既存の「MAPPY管理」配下に潜るため、トップレベルメニューは触らない。
画面モック
アカウント情報
user-standard-planプラン上限:8 件契約:2025-04-01 〜 2026-03-31(12ヶ月)所属店舗数:5
対象ユーザ配下の店舗一覧
| 店舗名 | プラン値 | 現在の上限 | 新しい上限 | メモ(課金根拠) | 有効期間 | 操作 |
|---|---|---|---|---|---|---|
渋谷本店 location_id: 8801 | 8 | 15 | 2026-06-01 〜 無期限 | |||
新宿西口店 location_id: 8802 | 8 | 8 | — 〜 無期限 | |||
池袋東口店 location_id: 8803 | 8 | 12 | 2026-05-15 〜 2026-12-31 | |||
横浜西口店 location_id: 8804 | 8 | 8 | — 〜 無期限 | |||
川崎駅前店 location_id: 8805 | 8 | 10 | 2026-06-01 〜 無期限 |
上限の範囲は 8〜15 件。8 にすると override が解除され、プランデフォルト適用に戻ります。
呼び出し API(参考)
| Method | Path | 役割 |
|---|---|---|
GET | /api/mappy/admin/users/{user}/locations-and-groups | 店舗一覧取得(既存 API を拡張:resolved + override を含める) |
PATCH | /api/mappy/admin/locations/{loc}/keyword-limit | 店舗別上限を upsert(新設) |
DELETE | /api/mappy/admin/locations/{loc}/keyword-limit | override 解除(新設) |
タブの 1 つとして配置されている点に注目。アカウント情報 / 通知設定 タブはダミー表示(既存実装をそのまま使うため本案件では改修しない)。
画面要素
ヘッダー部(MappyUserDetail のヘッダーを継承)
| 要素 | 用途 |
|---|---|
| パンくず | 管理画面 / MAPPY 管理(ユーザ一覧)/ 該当 loginId |
| ユーザ情報チップ | プラン名 / プラン上限 / 契約期間 / 所属店舗数 |
| タブバー | アカウント情報 / 通知設定 / キーワード上限(override 件数バッジ付き) |
キーワード上限タブのツールバー
| 要素 | 用途 |
|---|---|
| 説明テキスト | 「このユーザ配下の店舗ごとに 8〜15 件の範囲で…」 |
| 監査ログトグル | 直近 5 件の変更履歴をインライン表示 |
| 一括保存ボタン | 変更があるときのみ active、変更件数バッジ付き |
店舗一覧テーブル
| カラム | 内容 | 編集可否 |
|---|---|---|
| 店舗名 | 名称 + location_id | 不可(リンクは店舗詳細へ) |
| プラン値 | user.plan.maxKeywords のデフォルト値 | 不可 |
| 現在の上限 | resolved 値(badge カラーで状態表示) | 不可 |
| 新しい上限 | 8〜15 のステッパー入力 | 可 |
| メモ | 営業メモ・課金根拠(255 文字) | 可 |
| 有効期間 | effective_from 〜 effective_to | 不可(自動設定) |
| 操作 | 「解除」ボタン → override 削除 | 可 |
Badge カラールール
| 状態 | 色 | 意味 |
|---|---|---|
| プラン値のみ | グレー | override なし、max_keywords = NULL |
| override 中 | 黄色 | プラン値を上回るが 15 未満 |
| 最大設定 | 緑 | 15 件(システム上限) |
フィルタ
- 店舗名インクリメンタル検索
- セレクト:すべて / 上書き設定あり / プラン値のみ
操作フロー
ケース 1: 単一店舗の上限を 8 → 15 に上げる
ケース 2: override を解除(プラン値に戻す)
| ステップ | 操作 | 結果 |
|---|---|---|
| 1 | 対象行の「解除」ボタンを押下 | 入力欄が 8(プランデフォルト)に戻り、行が「changed」にハイライト |
| 2 | 一括保存ボタンを押下 | DELETE /api/mappy/admin/locations/{loc}/keyword-limit 呼び出し |
| 3 | API がレコード論理削除 | effective_to = CURDATE() - 1 day 設定 |
| 4 | ActivityLog 記録 | 「○○店: 12 → 8, by xxx」 |
ケース 3: 一括変更
複数行の入力欄を編集 → 一括保存ボタンが「(N件)」表示 → 1 トランザクションで全部 PATCH。
バリデーション
| 項目 | ルール | エラー時の表示 |
|---|---|---|
| max_keywords | 整数、8 ≤ N ≤ 15 | 入力欄の枠が赤、ツールチップで理由 |
| memo | 0〜255 文字 | 文字数オーバーで保存ボタン disabled |
| effective_to | NULL または ≥ effective_from | バックエンドで弾き 400 を返す |
| override = プラン値 | warning なし、UI 側で DELETE 扱い | — |
ActivityLog 連携
PATCH / DELETE 成功時に ActivityLog へ自動追記する。記録項目:
json
{
"action": "keyword_limit.updated",
"actor_id": 42,
"target_type": "gbp_location",
"target_id": 8801,
"before": { "max_keywords": 8, "memo": null },
"after": { "max_keywords": 15, "memo": "営業 #4521 / 月額 ¥9,800 追加" },
"created_at": "2026-06-01 14:32:00"
}この情報は 課金プラットフォーム連携(後続案件) で payload 拡張のソースになる。
レスポンシブ対応
| 画面幅 | 挙動 |
|---|---|
| PC(≥768px) | 全カラム表示、テーブル横スクロールなし |
| Mobile(<768px) | メモ・有効期間カラムを非表示にしてコア機能(店舗名・上限編集・保存)を優先 |
権限・アクセス制御
| ロール | 閲覧 | 編集 | 解除 |
|---|---|---|---|
admin | ✅ | ✅ | ✅ |
cs_operator | ✅ | ✅(8〜12 のみ) | ✅ |
sales_viewer | ✅ | ❌ | ❌ |
max_keywords > 12 の設定は admin ロール限定(営業判断 / 課金確認が必要)。
アクセシビリティ
- ステッパー入力は
<button>+<input type="number">の組み合わせ(キーボード操作可) - 変更行は背景色だけでなく
aria-labelで読み上げ対応 - フォーカストラップなし(モーダルではなくインライン編集のため)