# schema.json の書き方

> お店の人が直せる項目（groups / collections）とフォーム（forms）を宣言するファイル。型と制約の一覧。

URL: https://sitekit.example/docs/schema

## 全体の形

schema.json は「このサイトで何を直せるか」「どんなフォームがあるか」の宣言です。管理画面の入力欄も、公開 JSON の形も、ここから作られます。

```json
{
  "locales": ["ja", "en"],
  "groups": [
    { "key": "hours", "label": "営業時間", "fields": [
      { "key": "days", "label": "営業日", "type": "text" },
      { "key": "time", "label": "時間", "type": "text", "translate": false },
      { "key": "notice", "label": "お休みのお知らせ", "type": "textarea",
        "hint": "臨時休業などがあれば。空にすると枠ごと消えます" }
    ] }
  ],
  "collections": [
    { "key": "news", "label": "お知らせ", "sort": "date desc", "fields": [
      { "key": "title", "label": "タイトル", "type": "text", "required": true },
      { "key": "date", "label": "日付", "type": "date", "translate": false },
      { "key": "tags", "label": "タグ", "type": "tags" },
      { "key": "body", "label": "本文", "type": "blocks" }
    ] }
  ],
  "forms": [
    { "key": "contact", "label": "お問い合わせ", "fields": [
      { "key": "name", "label": "お名前", "type": "text", "required": true },
      { "key": "email", "label": "メールアドレス", "type": "email", "required": true },
      { "key": "message", "label": "お問い合わせ内容", "type": "textarea" }
    ] }
  ],
  "tracking": { "gtm_id": "GTM-XXXXXXX" }
}
```

| キー | 必須 | 中身 |
|---|---|---|
| `locales` | 必須 | 言語の並び。先頭が主言語 |
| `groups` | | 1つずつ値を持つ項目のまとまり（営業時間・店舗情報など） |
| `collections` | | 件数が増えていく一覧（お知らせ・メニューなど） |
| `forms` | | フォームの定義 |
| `tracking` | | `gtm_id` だけ。管理画面の「設定」の GTM ID があればそちらが優先 |

知らないキーは保存されません。

## key の決まり

`groups` / `collections` / `forms` と、その中の `fields` の `key` は、どれも同じ決まりです。

- 英小文字で始まり、英小文字・数字・`_` だけ（`/^[a-z][a-z0-9_]*$/`）
- 40 文字まで
- 同じ並びの中で重複しない

## locales

- `ja` や `zh-tw` の形（`/^[a-z]{2}(-[a-z]{2})?$/`）。重複は不可。1つ以上必要
- 先頭がお店の人の入力する言語で、翻訳が無いときの表示にも使われます

## groups

1つずつ値を持つ項目のまとまりです。`label`（空でない文字列）と `fields` が必須です。

HTML では `<グループ>.<項目>` で指定します。

```html
<dd data-cms="hours.days">月〜土</dd>
```

## collections

件数が増えていく一覧です。`label`（空でない文字列）と `fields` が必須です。

- `sort` — `"<項目>"` または `"<項目> asc|desc"`。その collection に無い項目は指定できません。省略すると管理画面で並べた順
- `number` の項目で並べると数値として比べます
- 並び順は主言語の値で決めるので、どの言語でも同じ順になります

HTML では `<template data-cms-list="<collection の key>">` で繰り返します（[直せる箇所の印](https://sitekit.example/docs/cms#お知らせなどの一覧)）。

## forms

フォームの定義です。`fields` が必須、`label` は任意です。

| キー | 中身 |
|---|---|
| `key` | フォームの `data-sitekit-form="…"` に書く値 |
| `label` | 管理画面・通知に出る名前 |
| `fields` | 項目。`key` が入力の `name` と一致する |
| `success_url` | JavaScript を使わない送信（`application/x-www-form-urlencoded`）のあとに移動する先。`http(s)` の URL で、サイトの許可オリジンの中だけ |

- **通知先（メール・Slack）とお礼メールの文面は schema.json には書きません。** 管理画面の「設定」で、お店のオーナーが変えられます。`forms[].notify` / `forms[].mail_template` は書いても無視され、警告が出ます
- JavaScript で送る通常のフォームの移動先は、HTML の `data-success-url` で指定します（[フォーム](https://sitekit.example/docs/forms#送信後に別ページへ移動する)）

## fields

| キー | 型 | 中身 |
|---|---|---|
| `key` | 文字列 | 必須。上の key の決まり |
| `label` | 文字列 | 管理画面の項目名 |
| `type` | 文字列 | 下の型のどれか。省略すると `text` |
| `required` | 真偽 | 必須にする |
| `translate` | 真偽 | `false` で翻訳しない。省略すると文章の型（`text` / `textarea` / `blocks`）は翻訳する |
| `hint` | 文字列 | 管理画面に出す入力の説明 |
| `options` | 配列 | `select` の選択肢（`select` では必須） |

## 型

| 型 | 使いどころ | フォームで受け付ける値 |
|---|---|---|
| `text` | 1行の文 | 200 文字まで |
| `textarea` | 複数行の文 | 4,000 文字まで |
| `blocks` | 記事本文（見出し・写真・動画などのブロック） | — |
| `date` | 日付 | 実在する `YYYY-MM-DD` |
| `image` | 写真（公開 JSON では絶対 URL） | — |
| `select` | 選択肢 | `options` のどれか |
| `number` | 数値 | 有限の数 |
| `email` | メールアドレス（フォームのお礼メールの宛先になる） | 254 文字まで。小文字にそろえて保存 |
| `tel` | 電話番号 | 数字・`+`・`-`・空白・括弧、32 文字まで |
| `checkbox` | チェック | `true` / `"on"` / `"1"` / `"true"` |
| `tags` | タグ（collection 用） | — |

`email` / `tel` / `checkbox` はフォーム向けの型です。groups や collections に書くと、ふつうの1行の文として扱われます。

## select の選択肢

文字列の配列でも、`value` / `label` / `color` を持つオブジェクトでも書けます（混在も可）。

```json
{ "key": "category", "label": "種別", "type": "select", "translate": false,
  "options": [
    { "value": "お知らせ", "color": "gray" },
    { "value": "新商品", "label": "新しい商品", "color": "red" },
    "その他"
  ] }
```

- `value` は空でない文字列で、重複は不可
- `label` を書くなら空でない文字列。省略すると `value` がそのまま表示に使われる
- `color` は `red` / `gold` / `gray` / `black` / `green` / `blue` のどれか
- 公開 JSON には `category_label` と `category_color`（色のある選択肢だけ）が足されます
- 種別は分類の札なので、`"translate": false` にしておきます

## tags

- 1件に 20 個まで、1つ 40 文字まで
- `/` `#` `?` は使えません（タグページの URL に入るため）
- 前後の空白は取り除かれ、重複は1つにまとめられます
- `translate` は書いても `false` になります（タグは全言語で同じ）

## 検証で出るエラー

schema.json に誤りがあると、どこが違うかの一覧が返り、保存されません。例:

```text
groups[0].fields[1].key must match /^[a-z][a-z0-9_]*$/ (max 40)
collections[0].sort names a field this collection does not have
forms[0].fields[2] is a select and needs a non-empty options array
forms[0].success_url must be an http(s) URL
tracking.gtm_id must match /^GTM-[A-Z0-9]+$/
```

## HTML と合わせるところ

| schema.json | HTML |
|---|---|
| `groups[].key` と `fields[].key` | `data-cms="<グループ>.<項目>"` |
| `collections[].key` | `<template data-cms-list="…">` / `data-cms-entry` / `data-cms-empty` |
| collection の `fields[].key` | template の中の `data-cms="…"` |
| `forms[].key` | `<form data-sitekit-form="…">` |
| form の `fields[].key` | 入力の `name="…"` |

フォームの入力で schema.json に無い `name` は、エラーにはならず無視されます（保存されません）。項目を足したら schema.json にも足します。
