TENKEN — 要件定義書 兼 Claude Code 実装指示書
版数 1.0(DBレス構成 / 記事埋め込み型デモ)/ 作成日 2026-08-24 / 発行 ソウゾウ合同会社 用途 SEO記事「【デモで試せる】Claude Codeで点検表を電子化・自作する方法と費用|動画つき資料【2026年】」に組み込む実演デモ
このドキュメントの使い方(Claude Code へ)
これ1枚が、規約・仕様・タスクのすべて。リポジトリ直下に CLAUDE.md として置き、毎回読み込むこと。
- 実装は「12. 実装タスク」の順に進める。フェーズを飛ばさない。
- 各タスクの括弧内は要件ID。着手前に該当章を読むこと。
- 仕様がここに書かれていない場合は、推測で実装せず質問する。
- スコープ外(3章 Won't have)は、思いついても実装しない。
- 「デモだから」を理由に品質を落とす判断はしない。
- この領域だけの特別ルール:紙より速いことを常に確認する。 点検表の電子化は、1項目あたりのタップ数と所要秒数が紙を上回った瞬間に使われなくなる。機能を足すときは必ず「タップ数が増えないか」を確認すること(1章)。
姉妹プロジェクトとの関係 HIREBASE(求人)/ RELATE(顧客管理)/ CASTA(動画配信)/ FIELDPIN(位置情報)/ KANADE(音楽)/ MIWAKE(画像認識)/ KAMIWAZA(美容室予約)/ KURA(在庫管理)/ TOKIWA(勤怠・シフト)/ HIBI(日報)に続くシリーズ。並べて「同じテンプレ」と思われた時点で失敗。 特に FIELDPIN / HIBI との差別化に注意。 FIELDPIN=地図全画面・16px大型UI・屋外向け(現場で使わせる)/ HIBI=1カラム・広い余白・15px本文(読み書きさせる)/ TENKEN=1画面1項目の超大型UI。判定ボタンが画面の半分を占める。手袋・片手・薄暗い場所で、見ずに押せる。(黒地に近い高コントラスト+安全色) FIELDPIN より更に大きく、更に少ない。 TENKEN は1画面に1つのことしかさせない。8章参照。
目次
- プロジェクト概要
- アーキテクチャ方針
- スコープ定義
- ロールとデモ切替
- 画面一覧とユーザーフロー
- 機能要件
- データ設計
- デザイン要件
- 技術要件・ディレクトリ構成
- 非機能要件・法令
- 費用設計
- 実装タスク
- 受入基準
1. プロジェクト概要
背景と目的
「点検表を電子化したい」という相談の背景は、ほぼ次のいずれか。
- 紙の点検表が段ボールで積み上がっている。過去の記録を探せない
- 転記作業が地獄。現場で紙に書いて、事務所でExcelに打ち直している
- 不備が見つかっても、いつからそうだったのか分からない
- 法令で保存義務があるのに、紙が濡れる・破れる・なくなる
そして、この領域には他のテーマにない厳しさが3つある。
厳しさ①:紙より遅くなった瞬間に、使われなくなる
紙の点検表は驚くほど速い。チェック欄に鉛筆でレ点を入れるのに1秒もかからない。
これに対してアプリが「項目をタップ → セレクトを開く → 選択肢を選ぶ → 閉じる」だと、1項目に3〜4秒かかる。30項目あれば紙30秒 vs アプリ2分。 現場は紙に戻る。
点検表の電子化の成否は、機能ではなく「1項目あたりのタップ数」で決まる。 目標は 1項目1タップ。これを超えたら、その項目設計は失敗している。
だから TENKEN は1画面1項目の超大型UIを採用する。画面の半分を判定ボタンが占め、手袋のまま、見ずに、片手で押せる。
厳しさ②:点検は「記録」ではなく「異常の発見と是正」のプロセス
点検表を「記録を残すシステム」と考えると失敗する。点検の目的は異常を見つけて直すこと。
点検 → 異常あり → 誰かが直す → 直ったことを確認 → 次の点検
↑ │
└──────────────────────────────────────┘
ここが繋がっていないと、
「異常あり」が記録されるだけで放置される
紙の点検表の最大の欠点はここ。 異常を書いても、それが是正されたかどうかを追う仕組みがない。電子化の価値は「記録が楽になる」ことではなく、「異常が追跡される」ことにある。
TENKEN は異常(NG判定)を必ず是正タスクに変換し、完了まで追跡する。
厳しさ③:点検表は法令・取引先要求で形式が決まっていることが多い
日報や顧客管理と違い、点検表は自由に設計できないことが多い。
| 分野 | 形式が決まる理由 |
|---|---|
| 消防設備・防火設備 | 消防法に基づく点検報告 |
| 建設機械・フォークリフト | 労働安全衛生法に基づく始業前点検・定期自主検査 |
| 電気設備 | 電気事業法に基づく保安規程 |
| 食品(HACCP) | 衛生管理計画に基づく記録 |
| 車両(運行前点検) | 道路運送車両法 |
| 取引先監査 | 顧客の帳票フォーマットの指定 |
だから「既存の紙の点検表をそのまま再現できるか」が要件の出発点になる。 ゼロから項目を設計する案件は少ない。
そして保存義務がある。 記録の改ざん防止と、指定期間の保存が求められる。
TENKEN はこの3つの厳しさを前提に設計する。
| 目的 | 内容 |
|---|---|
| 実装力の証明 | 超大型UI・1項目1タップ・オフライン完結・是正タスク・写真・帳票出力・改ざん防止まで、実運用に耐える構造を見せる |
| 期待値の正常化 | **「紙より速くなければ意味がない」**を実物で示す。タップ数と所要秒数を可視化する |
| 異常追跡の可視化 | NG判定が是正タスクになり、完了まで追跡され、再発が見えることを示す |
| 費用判断の材料 | タブレット・帳票出力・保存要件を含めた総額、既製サービスとの比較を示す |
プロダクト定義
| 項目 | 内容 |
|---|---|
| プロダクト名 | TENKEN(点検)※仮称 |
| 一言定義 | 紙の点検表をそのまま電子化し、異常を是正完了まで追跡するシステム |
| 想定業態 | 設備保全、ビル管理、建設機械・車両、製造ライン、飲食(HACCP)、店舗開店前チェック、消防設備 |
| 提供形態 | レスポンシブWebアプリ(PWA)。フロントエンド完結(サーバー側の永続化なし) |
| 主戦場 | 現場はスマホ/タブレット(手袋・片手・薄暗い・騒音)。オフラインが前提。 事務所はPC(帳票・分析) |
| 想定利用者 | 記事の読者。登録なしで、点検する側と管理する側の両方を体験できる |
プロダクトコンセプト
「1項目、1タップ、0.8秒。」
TENKEN は点検の所要時間を計測し、紙との比較を表示する。1画面1項目、画面の半分を占める判定ボタン、スワイプで次へ。手袋のまま、画面を見ずに押せることを設計の中心に置く。そして押された「NG」は、必ず誰かのタスクになる。
デモとしての成功条件
- 速いことが分かる — 30項目の点検を通しで実施でき、所要時間とタップ数が表示される
- 1項目1タップである — OK判定はボタン1つ。セレクトを開かせない
- NGが追跡される — NG判定すると是正タスクが自動生成され、完了まで追える
- オフラインで完結する — 機内モードでも点検を最初から最後まで実施できる
- 紙の帳票が出る — 既存の紙と同じレイアウトのPDFが実際にダウンロードできる
- 改ざんできないことが分かる — 記録の修正が履歴として残り、元の値が消えない
- リセットできる — 誰が触った後でも初期状態に戻せる
2. アーキテクチャ方針
基本方針
バックエンドとデータベースを持たない。すべてブラウザ内で完結させる。
| 一般的な構成 | 本プロジェクト |
|---|---|
| PostgreSQL | シードデータ + ブラウザ内ストア |
| 認証・SSO | デモ用ロール切替(ワンクリック) |
| REST / GraphQL API | ストア上の同期的な操作 |
| オフライン同期基盤 | これは本物。 Service Worker + IndexedDB で実際に動く |
| 帳票PDF出力 | これも本物。 ブラウザ内で生成し、実際にダウンロードできる |
| QRコード読取 | カメラ読取(任意)+ キー入力(既定) |
| メール・チャット通知 | 送信内容のプレビュー表示で再現 |
| 所要時間・タップ数の計測 | これも本物。 実際に計測して表示する |
なぜこの構成にするか
- LPデモとして最適 — 訪問者は登録もログインもせずに、全機能を即座に触れる
- 点検記録が外に出ない — 設備の異常情報は機密。データを預からないことが訴求になる
- 開発速度 — 認証・APIの実装が不要になり、入力体験とオフラインの作り込みに全時間を投下できる
- 運用コストゼロ — Vercel の静的配信のみ
オフラインファーストの原則(最重要の設計判断)
点検現場は電波が入らない。 機械室、地下、屋上、山間部、工場内、冷蔵倉庫——点検すべき場所ほど電波がない。
だから TENKEN は「オフライン対応」ではなく**「オフラインが既定」**として設計する。
[ 起動 ] Service Worker からアプリを起動(通信不要)
↓
[ 点検表の取得 ] 事前にダウンロード済みのものを IndexedDB から読む
↓
[ 点検の実施 ] すべてローカル。1項目1タップ。写真も端末内
↓
[ 完了 ] ローカルに確定保存。★ この時点で記録は成立している
↓
[ 同期 ] 通信が回復したときに送信。★ 同期は「後片付け」であって、点検の一部ではない
| ID | 規約 |
|---|---|
| OFF-01 | 点検の開始から完了まで、通信を一切必要としないこと。 通信状態のチェックで操作をブロックしないこと |
| OFF-02 | 点検完了はローカル保存の時点で成立させること。 「同期中」を完了条件にしないこと |
| OFF-03 | 写真は IndexedDB に Blob で保存し、同期前でも閲覧できること |
| OFF-04 | オフラインであることを常時表示するが、警告色にしないこと(正常な状態だから)。「オフライン(点検は通常どおり行えます)」 |
| OFF-05 | 同期は自動 + 手動の両方を提供し、未同期件数を常時表示すること |
| OFF-06 | 同期の失敗で記録を失わないこと。 失敗時はキューに残し、再試行できること |
| OFF-07 | アプリの更新(Service Worker の新バージョン)が、点検中に強制リロードを起こさないこと |
OFF-02 が最も重要。 「同期できないと完了にならない」設計にすると、現場で作業が止まる。記録の成立とデータの送信は別物。
1項目1タップの原則(厳しさ①への回答)
| ID | 規約 |
|---|---|
| TAP-01 | OK/NG のような2値判定は、必ず1タップで完了すること。 セレクトボックス・ドロップダウンを使わない |
| TAP-02 | 判定ボタンは画面の40%以上を占めること。 手袋・片手・見ずに押せるサイズ(8章) |
| TAP-03 | 判定後、自動で次の項目に進むこと(設定でOFF可)。「次へ」を押させない |
| TAP-04 | 数値入力はテンキーを直接表示すること(inputMode="numeric" + 独自テンキーも検討)。範囲外は入力時に警告する |
| TAP-05 | 選択肢が3つ以下の項目は、すべてボタンとして並べること。 セレクトにしない |
| TAP-06 | タップ数と所要時間を計測し、完了時に表示すること(lib/metrics/) |
| TAP-07 | 1項目あたりの平均タップ数が 1.2 を超える点検表を、警告表示すること(テンプレート管理画面) |
| TAP-08 | 機能を追加するとき、タップ数が増えるなら実装しないこと。 増える場合は必ず質問すること |
記録の改ざん防止(厳しさ③への回答)
点検記録には保存義務があり、改ざん防止が求められる。
| ID | 規約 |
|---|---|
| REC-01 | 確定した点検記録の値を上書きしないこと。 修正は ResultRevision(修正履歴)として別レコードで持つ |
| REC-02 | 修正時は理由の入力を必須とし、修正者・日時を記録すること |
| REC-03 | 元の値を必ず参照できること。 帳票にも「修正あり」を表示すること |
| REC-04 | 点検記録に、実施者・実施日時・端末・(設定時)位置情報を記録すること |
| REC-05 | 記録の削除を実装しないこと。 誤って作成した点検は「無効化」(理由付き)とし、履歴に残す |
| REC-06 | 点検完了時に、記録内容のハッシュを算出して保持すること(改ざん検知の実装例として。実案件では署名の設計が必要な旨をコメントに残す) |
差し替え可能性の担保
データアクセスは必ず lib/repo/ のリポジトリ層を経由すること。 コンポーネントからストアを直接触らない。
[ コンポーネント ]
↓ 呼ぶのはこの層だけ
[ lib/repo/*.ts ] ← 全メソッドを async にしておく
↓
[ lib/inspection/ ] ← 判定・是正生成・完了判定(純粋関数)
[ lib/report/ ] ← 帳票レイアウト(純粋関数)
[ lib/metrics/ ] ← タップ数・所要時間(純粋関数 + タイマー)
[ lib/store/*.ts ] ← Zustand + persist(localStorage / IndexedDB)
↓
[ lib/seed/*.ts ] ← 初期データ
リポジトリの各メソッドは、中身が同期処理でも async で定義し、await で呼ぶ。実案件へ転用する際、リポジトリの実装だけをAPI呼び出しに差し替えれば、UI層は一行も変更せずに済む。
記事への組み込み
| 段 | 配置 | 中両 |
|---|---|---|
| ① 記事内インライン | 記事の冒頭〜中盤、<iframe> |
5項目の点検を実際に実施できる。タップ数と所要時間が出る |
| ② 全画面デモ | 「全画面で試す」カード → 別タブ | アプリ全体(30項目の点検・是正タスク・帳票出力・オフライン・分析) |
記事内で見せるのは「1項目1タップで、どれだけ速いか」だけに絞る。 これが厳しさ①そのものであり、スクロール中に体験できる唯一のことになる。
| ID | 要件 |
|---|---|
| EMB-01 | 記事内 iframe は loading="lazy" とし、ビューポート接近まで読み込まないこと |
| EMB-02 | 埋め込みモードのJS初期バンドルは 55KB以下(gzip後)とすること |
| EMB-03 | 埋め込み iframe が記事の LCP 要素にならないこと |
| EMB-04 | 埋め込みモードで5項目の点検を実際に完了でき、タップ数と所要時間が表示されること(これが主役) |
| EMB-05 | 完了時に「紙の点検表との比較」を表示すること(「1項目あたり 0.9秒/タップ 1.0回」) |
| EMB-06 | 埋め込みモードでカメラ・位置情報を呼ばないこと |
| EMB-07 | 埋め込みモードではアプリ内遷移をせず、「全画面で30項目の点検を試す」を target="_blank" で提示すること |
| EMB-08 | GA4 で計測すること:demo_open / inspection_completed(点検を完了した)/taps_per_item(1項目あたりのタップ数)/seconds_per_item(1項目あたりの秒数)/ng_recorded / correction_created / pdf_exported / offline_used / role_switch / doc_download / form_click / line_click |
| EMB-09 | seconds_per_item と taps_per_item は最重要指標。 読者が実際に何秒・何タップで点検できたかの分布は、記事の主張を裏付ける実データになる |
| EMB-10 | 点検記録の内容・設備名を GA4 に送らないこと。 送るのは秒数・タップ数などのメタ情報のみ |
| EMB-11 | ?embed=1 のURLは noindex とすること。デモ本体は固有の title / description を持つこと |
永続化の範囲
| 対象 | 挙動 |
|---|---|
| シードデータ(設備・点検表テンプレート・過去6ヶ月の点検記録・是正タスク) | 初期投入。テンプレートは編集可能 |
| 点検記録・修正履歴・是正タスク・承認 | localStorage に保存。リロードしても残る |
| 点検中の途中状態 | localStorage に保存。アプリを閉じても中断地点から再開できる |
| 写真 | IndexedDB(Blob)。サーバーには送らない(OFF-03) |
| アプリ本体・点検表定義 | Service Worker + Cache API(オフライン起動) |
| リセット | 「デモをリセット」で localStorage / IndexedDB / Cache をクリア |
擬似ディレイ は 150〜400ms。ただし判定タップ・次項目への遷移・途中保存は擬似ディレイを入れず、100ms以下とする(点検のテンポが崩れるのは、このアプリで最も致命的)。
3. スコープ定義
点検を実施する(現場担当)
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-I01 | 点検の開始 | 対象設備の選択(QR/一覧/履歴から)、点検表の選択 | Must |
| F-I02 | 1画面1項目の判定 | OK/NG を1タップ。判定後に自動で次へ | Must |
| F-I03 | 判定種別の対応 | 良否(OK/NG)/3値(良/要注意/不良)/数値/選択/テキスト/写真必須 | Must |
| F-I04 | 数値入力 | テンキー直接表示、範囲外は入力時に警告、前回値の表示 | Must |
| F-I05 | NG時の詳細入力 | 症状の選択、写真、コメント。NG以外では出さない | Must |
| F-I06 | 該当なし(N/A) | 対象外の項目をスキップ。理由の記録 | Must |
| F-I07 | 写真撮影 | 端末内で圧縮。NG時は必須化できる。複数枚 | Must |
| F-I08 | 中断と再開 | 点検途中でアプリを閉じても、中断地点から再開できる | Must |
| F-I09 | 進捗表示 | 「12 / 30」と残り項目数。プログレスバー | Must |
| F-I10 | 項目のスキップと戻り | 順番を飛ばす、前の項目に戻って修正 | Must |
| F-I11 | 一覧モード | 1画面1項目ではなく、リストで一気に見たい人向け(切替可) | Should |
| F-I12 | 点検の完了 | 未判定項目の確認、署名(手書き)、完了 | Must |
| F-I13 | オフライン完結 | 通信なしで開始から完了まで(2章 OFF) | Must |
| F-I14 | QRコード読取 | 設備のQRから点検表を直接開く | Should |
| F-I15 | 前回の点検結果の参照 | 点検中に前回値・前回のNGを確認できる | Must |
| F-I16 | 自分の点検履歴 | 実施した点検の一覧 | Should |
| F-I17 | 是正タスクの実施 | 割り当てられた是正作業の実施記録、写真、完了報告 | Must |
是正を追跡する(厳しさ②への回答)
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-C01 | NGからの是正タスク自動生成 | NG判定すると、必ず是正タスクが作られる | Must |
| F-C02 | 是正タスク一覧 | 未対応/対応中/完了/承認待ち。経過日数と期限 | Must |
| F-C03 | 担当者の割当 | 是正タスクを担当者に割り当て、通知 | Must |
| F-C04 | 是正の実施記録 | 対応内容、部品交換、写真(対応前/対応後) | Must |
| F-C05 | 是正の承認 | 管理者が確認して完了。差戻しも可 | Must |
| F-C06 | 未是正のエスカレーション | 期限超過・放置日数が閾値超のタスクを強調・通知 | Must |
| F-C07 | 再発の検出 | 同じ設備の同じ項目で繰り返しNGが出ていることを検出 | Must |
| F-C08 | 是正の履歴 | 設備ごとの是正履歴。次回点検時に前回の是正内容が見える | Must |
管理する(管理者)
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-A01 | ダッシュボード | 本日の実施予定/未実施、未是正、期限超過、再発 | Must |
| F-A02 | 点検実施状況 | 設備×日付のグリッド。実施済/未実施/期限超過 | Must |
| F-A03 | 点検表テンプレート管理 | 項目の追加・削除・並び替え、判定種別、1項目あたりの平均タップ数の警告 | Must |
| F-A04 | 紙の点検表からの再現 | 既存の紙のレイアウトを再現する設定(セクション・行順・帳票レイアウト) | Must |
| F-A05 | 点検スケジュール | 日次/週次/月次/年次、設備別、担当者割当、期限 | Must |
| F-A06 | 設備マスタ | 設備、設置場所、型式、管理番号、QRコード発行 | Must |
| F-A07 | 帳票PDF出力 | 既存の紙と同じレイアウト。期間・設備でまとめて出力 | Must |
| F-A08 | 記録の検索 | 設備・期間・判定結果・実施者で絞り込み。全文検索 | Must |
| F-A09 | 点検記録の修正 | 上書きせず修正履歴として保持(2章 REC) | Must |
| F-A10 | 分析 | NG発生率、設備別の異常傾向、是正リードタイム、実施率 | Must |
| F-A11 | 所要時間の分析 | 点検表別の平均所要時間・タップ数。紙との比較 | Must |
| F-A12 | 担当者・チーム管理 | メンバー、権限、担当設備 | Must |
| F-A13 | CSVエクスポート | 記録の一括出力 | Should |
| F-A14 | 操作ログ | 誰がいつ何を修正・承認・無効化したか | Must |
共通・基盤
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-S01 | ロール切替 | 現場担当/点検責任者/管理者 をワンクリック切替 | Must |
| F-S02 | 埋め込みモード | ?embed=1 で5項目の点検(2章) |
Must |
| F-S03 | PWA・オフライン起動 | ホーム画面追加、通信なしで起動 | Must |
| F-S04 | 通知 | 実施予定、未是正、期限超過(プレビュー表示で再現) | Must |
| F-S05 | 記事への導線 | 元記事へのリンク、資料ダウンロード、問い合わせ | Must |
| F-S06 | デモリセット | localStorage / IndexedDB / Cache をクリア | Must |
| F-S07 | ガイドツアー | 初回訪問時に「何を試せるか」を3ステップで案内 | Should |
対象外(Won't have)
| 項目 | 理由 |
|---|---|
| データベース・バックエンドAPI | 2章の方針に基づく |
| 本物の認証・SSO | ロール切替で代替 |
| 実際のメール・チャット送信 | プレビュー表示で再現 |
| 電子署名法に準拠した電子署名 | 手書き署名の画像保持までが対象。 法的な電子署名は10章で論点として扱う |
| AIによる異常検知・画像判定 | 対象外。 判定は人が行う。画像認識は MIWAKE の領域 |
| IoTセンサーからの自動データ取得 | 対象外。 11章で論点として扱う |
| 設備の保全計画・予防保全の最適化 | 点検スケジュールまでが対象 |
| 部品在庫・発注管理 | KURA(在庫管理)の領域 |
| 工数・原価管理 | 対象外 |
| 帳票の自由レイアウトエディタ | テンプレートから選ぶ方式に留める(自由レイアウトは単体で数週間) |
| 官公庁への電子申請連携 | 対象外 |
| 多言語UI | 日本語のみ。ただし外国人作業者向けのふりがな表示は Should で対応 |
スコープリスク: 点検表は「既存の紙をそのまま再現したい」が絶対条件になる領域。そして紙のレイアウトは驚くほど自由。 罫線の結合、縦書き、備考欄の位置、押印欄——帳票の再現度が要件の8割を占めることがある。 後付けは大改修になるため、要件定義の最初に「現物の紙を全種類集める」ことが必須。資料PDFの「点検表ヒアリングシート」はこのために作る。
4. ロールとデモ切替
ロール定義
| ロールID | 名称 | 見えるデータ | 特徴的な権限 |
|---|---|---|---|
inspector |
現場担当 | 自分の担当設備の点検記録と是正タスク | 点検の実施、是正の実施報告。記録の修正は不可 |
supervisor |
点検責任者 | 自拠点の全設備 | 上記 + 是正の承認、担当者の割当、未実施のリマインド、記録の修正(履歴付き) |
admin |
管理者 | 全社 | 上記 + テンプレート管理・スケジュール設定・帳票出力・分析・操作ログ・記録の無効化 |
データスコープと権限の要点
| ID | 要件 |
|---|---|
| SCP-01 | データスコープを lib/repo/_scope.ts に集約すること |
| SCP-02 | コンポーネント側で role === 'inspector' のような分岐を書かないこと |
| SCP-03 | 現場担当は他拠点の点検記録を一切取得できないこと |
| SCP-04 | 現場担当は確定した点検記録を修正できないこと。 修正の必要がある場合は責任者へ「修正依頼」を出す導線とすること |
| SCP-05 | 記録の修正・無効化は、権限に関わらず必ず履歴を残すこと(REC-01, REC-05)。権限で履歴の有無が変わらないこと |
SCP-05 が重要。 「管理者なら履歴なしで直せる」設計にすると、改ざん防止が成立しない。権限は「できるか」を決めるが、「履歴を残すか」は決めない。
ロールを跨ぐ体験(必ず動くようにする)
- 現場担当が点検を実施し、1項目をNGにする → 責任者に切替 → 是正タスクが自動生成されており、未対応として並んでいる
- 責任者が是正タスクを担当者に割り当てる → 現場担当に切替 → 自分のタスクに現れている
- 現場担当が是正を実施して完了報告 → 責任者に切替 → 承認待ちに出る。承認するまで「完了」にならない
- 責任者が点検記録を修正する → 現場担当に切替 → 修正後の値が見え、「修正あり」のバッジと元の値が確認できる
- 管理者が点検表に項目を5つ追加 → 現場担当に切替 → 項目が増え、平均タップ数の警告が管理画面に出ている
- 同じ設備の同じ項目を3回連続でNGにする → 管理者に切替 → 再発として検出され、強調表示されている
- 機内モードにして点検を最初から最後まで実施 → 完了できる → オンラインに戻す → 自動同期される
1番と7番が、このデモの核心。 厳しさ②(異常追跡)と、オフラインファーストが体験として成立する瞬間。
デモ切替バー(F-S01)
画面上部に常時表示する固定バー。埋め込みモードでは非表示。
- 現在のロールと氏名・拠点の表示、3ロールの切替
- 現場担当ロール時:2拠点から選択
- オフライン状態と未同期件数(常時表示。OFF-04, OFF-05)
- デモ内の現在日時
- 「デモをリセット」ボタン(確認ダイアログ付き)
- 元記事へ戻るリンク
- 「これはデモです。点検記録は送信されません」の明示
デザイン上の扱い: 点検画面が黒地に近い高コントラストなので、切替バーは明るいグレーの帯にして区別する(他プロジェクトと逆)。
5. 画面一覧とユーザーフロー
全30画面。画面IDはディレクトリ構成と 1:1 で対応させる。
点検を実施する(現場担当)
| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-001 | ホーム | / |
本日の点検予定(大きく)、未同期件数、自分の是正タスク、オフライン状態 |
| SC-010 | 設備の選択 | /inspect |
QR読取ボタン、担当設備の一覧、最近点検した設備 |
| SC-011 | 点検表の選択 | /inspect/[equipmentId] |
その設備に紐づく点検表、前回実施日 |
| SC-012 | 点検の実施(1項目ずつ) | /inspect/[equipmentId]/[sheetId] |
画面の40%以上を占める判定ボタン、進捗、前回値、スワイプで次へ |
| SC-013 | NG時の詳細入力 | 同画面内で展開 | 症状の選択、写真、コメント。NG以外では出さない |
| SC-014 | 一覧モード | ?view=list |
全項目をリストで表示(切替可) |
| SC-015 | 未判定項目の確認 | 完了前 | 未判定の一覧、N/A への一括設定、戻って判定 |
| SC-016 | 署名と完了 | 完了画面 | 手書き署名、実施者確認、完了ボタン |
| SC-017 | 完了結果 | 完了後 | 所要時間・タップ数・紙との比較、NG件数、生成された是正タスク |
| SC-020 | 自分の点検履歴 | /history |
実施した点検の一覧、結果、同期状態 |
| SC-021 | 点検記録の詳細 | /records/[id] |
全項目の結果、写真、署名、修正履歴 |
| SC-030 | 自分の是正タスク | /corrections/mine |
未対応/対応中、期限、経過日数 |
| SC-031 | 是正の実施 | /corrections/[id] |
対応内容、部品、対応前/対応後の写真、完了報告 |
| SC-040 | 通知 | /notifications |
実施予定、是正の割当、期限超過 |
| SC-900 | 埋め込みモード | /?embed=1 |
5項目の点検 + タップ数・所要時間(2章) |
是正を追跡する(責任者)
| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-100 | 是正タスク一覧 | /corrections |
未対応/対応中/承認待ち/完了。期限超過と放置日数の強調 |
| SC-101 | 是正タスクの詳細 | /corrections/[id]/review |
NGの内容、点検記録へのリンク、対応記録、承認/差戻し |
| SC-102 | 担当者の割当 | モーダル | 担当者選択、期限設定、通知プレビュー |
| SC-103 | 再発の検出 | /corrections/recurring |
同一設備・同一項目で繰り返しNGが出ている組み合わせ |
| SC-110 | 点検実施状況 | /status |
設備×日付のグリッド、実施済/未実施/期限超過、リマインド送信 |
| SC-111 | 点検記録の一覧 | /records |
絞り込み(設備・期間・判定・実施者)、全文検索 |
| SC-112 | 点検記録の修正 | /records/[id]/revise |
上書きせず修正履歴を作る。理由必須、元の値を表示 |
| SC-113 | 設備の点検履歴 | /equipments/[id]/history |
その設備の点検・是正の時系列。前回の是正内容 |
管理する(管理者)
| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-200 | ダッシュボード | /admin |
実施率、未是正、期限超過、再発、平均所要時間 |
| SC-210 | 点検表テンプレート管理 | /admin/sheets |
項目の追加・削除・並び替え、セクション、平均タップ数の警告 |
| SC-211 | 項目の編集 | モーダル | 判定種別、必須/任意、写真必須、数値の範囲、前回値の表示 |
| SC-212 | 帳票レイアウト設定 | /admin/sheets/[id]/layout |
紙の点検表の再現(セクション順、列構成、押印欄、備考) |
| SC-213 | 点検表のプレビュー | モーダル | 現場から見える実画面 + 帳票PDF |
| SC-220 | 点検スケジュール | /admin/schedule |
日次/週次/月次/年次、設備別、担当者、期限 |
| SC-221 | 設備マスタ | /admin/equipments |
設備、場所、型式、管理番号、QRコード発行・印刷 |
| SC-230 | 帳票PDF出力 | /admin/export/pdf |
期間・設備の選択、プレビュー、ダウンロード |
| SC-231 | CSVエクスポート | /admin/export/csv |
期間・項目の選択 |
| SC-240 | 分析:NG傾向 | /admin/analytics/ng |
NG発生率、設備別・項目別の異常傾向 |
| SC-241 | 分析:是正リードタイム | /admin/analytics/lead-time |
NG発生から是正完了までの日数、担当者別 |
| SC-242 | 分析:所要時間 | /admin/analytics/duration |
点検表別の平均所要時間・タップ数、紙との比較 |
| SC-250 | 担当者・チーム管理 | /admin/members |
メンバー、権限、担当設備・拠点 |
| SC-251 | 操作ログ | /admin/logs |
修正・承認・無効化の履歴 |
主要フロー
フローA:記事の読者が「速さ」を体験する(最重要)
SEO記事を読んでいる
↓
記事内の埋め込み(SC-900)
┌──────────────────────────────────────────────────────┐
│ 1号機 日常点検 3 / 5 │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 油漏れの有無 │
│ │
│ 前回:異常なし(8/19) │
│ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ │ │ │ │
│ │ OK │ │ NG │ │
│ │ 異常なし │ │ 異常あり │ │
│ │ │ │ │ │
│ └────────────────────┘ └────────────────────┘ │
│ │
│ [ 該当なし ] [ 戻る ] │
└──────────────────────────────────────────────────────┘
↓
「OK」を押す
↓ ★ 判定と同時に、自動で次の項目に進む(TAP-03)
★ ボタンを押した位置から次の項目がスライドしてくる
↓
5項目を通しで判定(うち1つをNGにしてみる)
↓ ★ NG を押すと、その場で症状の選択と写真の欄が開く(SC-013)
完了
↓
┌──────────────────────────────────────────────────────┐
│ 点検を完了しました │
│ │
│ 所要時間 4.6秒 タップ 6回 │
│ │
│ 1項目あたり 0.9秒 / 1.2タップ │
│ │
│ 紙の点検表(レ点を書く場合)の目安:1項目あたり 1.5秒 │
│ → 電子化しても遅くなっていません │
│ │
│ 異常 1件 → 是正タスクを作成しました │
│ │
│ [ 全画面で30項目の点検を試す ] │
└──────────────────────────────────────────────────────┘
↓
「全画面で試す」→ 別タブ
↓
SC-012 で30項目の点検を通しで実施 ── ★ テンポが崩れないことを体感
↓
SC-017 完了結果 ── ★ 30項目でも所要時間が出る
↓
ロール切替「責任者」→ SC-100 是正タスク一覧
↓ ★ たった今のNGが是正タスクとして並んでいる
SC-230 帳票PDF出力 ── ★ 紙と同じレイアウトのPDFが実際にダウンロードできる
↓
資料ダウンロード or 元記事に戻る
フローB:NGが是正完了まで追跡される(厳しさ②の実装を見せる)
【現場担当】SC-012 点検の実施
「ベルトの張り」→ NG
↓ ★ その場で詳細入力が開く(SC-013)
症状:「摩耗」を選択 → 写真1枚 → コメント「亀裂あり、交換要」
↓
点検完了
↓ ★ NG判定から是正タスクが自動生成される(F-C01)
タスク:「1号機 ベルトの張り:摩耗(亀裂あり、交換要)」
状態:未対応 / 期限:未設定
↓
【責任者】SC-100 是正タスク一覧
★ 未対応として並んでいる。点検記録へのリンク付き
↓
SC-102 担当者の割当
担当:田中 / 期限:3日後 → 通知プレビューを確認して割当
↓
【現場担当(田中)】SC-030 自分の是正タスク
★ 割り当てられたタスクが現れている
↓
SC-031 是正の実施
対応内容:「ベルト交換(品番 XX-1234)」
★ 対応前の写真(点検時のもの)が自動で表示される
対応後の写真を撮影 → 完了報告
↓
【責任者】SC-101 是正タスクの詳細
★ 承認待ちに出る。対応前/対応後の写真が並んで表示される
★ 承認するまで「完了」にならない
↓
承認
↓
【次回の点検】SC-012
★ 「ベルトの張り」の項目に、前回の是正内容が表示される
「前回:NG(摩耗)→ ベルト交換済(8/22)」
↓
★ ここで伝わること:
「NGを記録するだけでは意味がない。是正されて、次の点検で確認されて初めて閉じる」
フローC:オフラインで点検を完結する(現場の現実)
【現場担当】機械室に入る(電波なし)
↓
アプリを起動
↓ ★ Service Worker から起動。通信なしで立ち上がる(OFF-01)
★ 上部に「オフライン(点検は通常どおり行えます)」
※ 警告色ではない。これが正常な状態だから(OFF-04)
↓
SC-010 設備の選択 ── ★ 事前にダウンロード済みの設備・点検表が並ぶ
↓
SC-012 点検の実施 ── 30項目を判定
★ 写真も撮れる(IndexedDB に保存。OFF-03)
★ 途中でアプリが落ちても、SC-012 を開き直せば中断地点から再開(F-I08)
↓
SC-016 署名 → 完了
↓ ★ この時点で点検記録は成立している(OFF-02)
★ 「未同期 1件」のバッジが付く
↓
機械室を出る(電波が戻る)
↓ ★ 自動で同期開始。「同期中 1/1」→「同期しました」
★ 未同期バッジが消える
↓
【責任者】SC-110 点検実施状況
★ グリッドが「実施済」に変わっている
↓
★ ここで伝わること:
「点検すべき場所ほど電波がない。だからオフラインは対応ではなく、既定でなければならない」
6. 機能要件
各要件はテスト仕様書の項目と1:1で対応する。「〜できること」の粒度で、判定可能な形で記述している。
6.1 入力体験(このデモの心臓部)
| ID | 要件 | 優先 |
|---|---|---|
| FR-101 | 2値判定(OK/NG)を1タップで完了できること。セレクトボックスを使わないこと(TAP-01) | P1 |
| FR-102 | 判定ボタンが画面の40%以上を占めること(TAP-02, 8章) | P1 |
| FR-103 | 判定後、自動で次の項目に進むこと。 設定でOFFにできること(TAP-03) | P1 |
| FR-104 | 選択肢が3つ以下の項目は、すべてボタンとして並べること(TAP-05) | P1 |
| FR-105 | 数値入力はテンキーが直接開くこと(inputMode="numeric")。範囲外の値は入力時点で警告すること(TAP-04) |
P1 |
| FR-106 | 数値項目に前回値を表示すること。前回との差が閾値を超えたら注意表示すること | P1 |
| FR-107 | NG判定時のみ、詳細入力(症状・写真・コメント)を展開すること。 OK時には出さないこと | P1 |
| FR-108 | 症状は選択式(ボタン)を第一とし、自由記述は補助とすること | P1 |
| FR-109 | 「該当なし(N/A)」を1タップで設定でき、理由を選択できること | P1 |
| FR-110 | スワイプで次/前の項目に移動できること。 ボタンでも移動できること | P2 |
| FR-111 | 進捗(12/30)とプログレスバーを常時表示すること | P1 |
| FR-112 | 項目をスキップでき、未判定として残ること。完了前に一覧で確認できること | P1 |
| FR-113 | 前の項目に戻って判定を変更できること(完了前は自由に修正でき、履歴を残さない) | P1 |
| FR-114 | 一覧モードに切り替えられること(1画面1項目が合わない人向け)(F-I11) | P2 |
| FR-115 | タップ数と所要時間を計測すること(TAP-06)。項目ごとの秒数も記録すること | P1 |
| FR-116 | 完了時に、所要時間・タップ数・1項目あたりの値・紙との比較を表示すること(EMB-05) | P1 |
| FR-117 | 判定タップから次項目の表示までが 100ms以下であること。擬似ディレイを入れないこと | P1 |
| FR-118 | 外国人作業者向けに、項目名にふりがなを表示できること(設定でON/OFF) | P2 |
6.2 写真・署名
| ID | 要件 | 優先 |
|---|---|---|
| FR-201 | カメラで撮影でき、端末内で圧縮(長辺1600px・JPEG品質80)してから保存すること | P1 |
| FR-202 | 写真を IndexedDB に Blob で保存し、オフラインでも閲覧できること(OFF-03) | P1 |
| FR-203 | 項目ごとに写真を必須にできること(テンプレート設定)。NG時のみ必須にもできること | P1 |
| FR-204 | 複数枚(既定3枚まで)添付でき、削除できること | P1 |
| FR-205 | 撮影した写真に撮影日時を記録すること。位置情報は設定でON/OFF(既定OFF) | P1 |
| FR-206 | 写真の上に簡単な注記(矢印・丸)を描けること | P3 |
| FR-207 | 手書き署名を Canvas で入力でき、画像として保持すること | P1 |
| FR-208 | 署名を必須にするかを、点検表ごとに設定できること | P1 |
| FR-209 | ストレージ使用量を表示し、上限接近時に警告すること | P2 |
6.3 オフラインと同期
| ID | 要件 | 優先 |
|---|---|---|
| FR-301 | 点検の開始から完了まで、通信を一切必要としないこと(OFF-01) | P1 |
| FR-302 | 通信状態のチェックで操作をブロックしないこと(OFF-01) | P1 |
| FR-303 | 点検完了をローカル保存の時点で成立させること。「同期中」を完了条件にしないこと(OFF-02) | P1 |
| FR-304 | Service Worker によりオフラインでアプリを起動できること | P1 |
| FR-305 | 設備・点検表の定義を事前にキャッシュし、オフラインで参照できること | P1 |
| FR-306 | オフライン状態を常時表示するが、警告色にしないこと。「点検は通常どおり行えます」を併記すること(OFF-04) | P1 |
| FR-307 | 未同期件数を常時表示すること(OFF-05) | P1 |
| FR-308 | オンライン復帰時に自動同期し、進捗を表示すること。手動同期も提供すること(OFF-05) | P1 |
| FR-309 | 同期に失敗しても記録を失わないこと。 キューに残し、再試行できること(OFF-06) | P1 |
| FR-310 | 点検中にアプリ更新による強制リロードを起こさないこと。 更新は次回起動時に適用すること(OFF-07) | P1 |
| FR-311 | 点検の途中状態を保存し、アプリを閉じても中断地点から再開できること(F-I08) | P1 |
| FR-312 | 中断中の点検を一覧で確認でき、破棄もできること(破棄は理由不要。確定前だから) | P2 |
6.4 是正の追跡(厳しさ②への回答)
| ID | 要件 | 優先 |
|---|---|---|
| FR-401 | NG判定から是正タスクを自動生成すること。 例外なく生成すること(F-C01) | P1 |
| FR-402 | 是正タスクに、元の点検記録・設備・項目・症状・写真・コメントを引き継ぐこと | P1 |
| FR-403 | 是正タスクの状態を「未対応/対応中/承認待ち/完了/対応不要」で管理すること | P1 |
| FR-404 | 「対応不要」には理由の入力を必須とすること(安易に閉じられないようにする) | P1 |
| FR-405 | 担当者と期限を設定でき、担当者に通知されること(プレビュー表示で再現) | P1 |
| FR-406 | 是正の実施記録として、対応内容・部品・対応前/対応後の写真を記録できること | P1 |
| FR-407 | 対応前の写真として、点検時の写真を自動で表示すること | P1 |
| FR-408 | 完了報告 → 承認 の2段階とすること。承認するまで完了にしないこと(F-C05) | P1 |
| FR-409 | 差戻しができ、理由を必須とすること | P1 |
| FR-410 | 期限超過・放置日数が閾値(既定7日)を超えたタスクを強調表示し、通知すること(F-C06) | P1 |
| FR-411 | 同一設備・同一項目で繰り返しNGが出ている組み合わせを検出すること(既定:直近5回中3回以上)(F-C07) | P1 |
| FR-412 | 次回の点検時に、その項目の前回の是正内容を表示すること(F-C08, フローB) | P1 |
| FR-413 | 設備ごとの是正履歴を時系列で確認できること | P1 |
| FR-414 | 是正リードタイム(NG発生から承認完了まで)を算出すること | P2 |
6.5 記録の完全性(厳しさ③への回答)
| ID | 要件 | 優先 |
|---|---|---|
| FR-501 | 確定した点検記録の値を上書きしないこと。 修正は ResultRevision として別レコードで持つこと(REC-01) |
P1 |
| FR-502 | 修正時は理由の入力を必須とし、修正者・日時を記録すること(REC-02) | P1 |
| FR-503 | 元の値を必ず参照できること。 記録詳細と帳票に「修正あり」を表示すること(REC-03) | P1 |
| FR-504 | 点検記録に実施者・実施日時・端末種別・(設定時)位置情報を記録すること(REC-04) | P1 |
| FR-505 | 記録の削除を実装しないこと。 誤作成は「無効化」(理由必須)とし、履歴に残すこと(REC-05) | P1 |
| FR-506 | 点検完了時に記録内容のハッシュを算出して保持すること(REC-06) | P2 |
| FR-507 | 現場担当は確定記録を修正できないこと。 修正依頼を出す導線とすること(SCP-04) | P1 |
| FR-508 | 修正・無効化・承認をすべて操作ログに記録すること。 権限によって履歴の有無が変わらないこと(SCP-05) | P1 |
6.6 点検表テンプレートと帳票
| ID | 要件 | 優先 |
|---|---|---|
| FR-601 | 点検表をセクション(大項目)+ 項目の階層で定義できること | P1 |
| FR-602 | 項目の判定種別として、良否(2値)/3値/数値/単一選択/複数選択/テキスト/写真のみ に対応すること | P1 |
| FR-603 | 項目ごとに、必須/任意、写真必須、N/A許可、前回値の表示、数値の上下限 を設定できること | P1 |
| FR-604 | 項目をドラッグで並び替えられること。キーボードでも操作できること | P1 |
| FR-605 | 1項目あたりの平均タップ数を算出し、テンプレート管理画面に常時表示すること | P1 |
| FR-606 | 平均タップ数が 1.2 を超える場合、警告を表示すること(TAP-07) | P1 |
| FR-607 | 推定所要時間を表示し、項目の追加・削除で即座に更新されること | P1 |
| FR-608 | 現場から見える実画面をプレビューできること | P1 |
| FR-609 | 帳票レイアウトを設定できること:セクションの並び、列構成、押印欄の有無、備考欄の位置、ヘッダー(設備名・管理番号・実施日・実施者) | P1 |
| FR-610 | 既存の紙の点検表を再現できるよう、レイアウトのテンプレートを複数用意すること(横型チェックリスト/縦型帳票/マトリクス型) | P1 |
| FR-611 | 帳票PDFを実際にダウンロードできること。 期間・設備でまとめて出力できること | P1 |
| FR-612 | 帳票に写真を添付ページとして含められること(設定で切替) | P2 |
| FR-613 | 帳票に**「修正あり」の記録を明示すること**(REC-03) | P1 |
| FR-614 | テンプレートを変更しても、過去の記録が当時の構造で表示・出力されること | P1 |
| FR-615 | QRコードを設備ごとに発行し、印刷用シートを出力できること | P2 |
| FR-616 | QRコード読取で、その設備の点検表を直接開けること(カメラは任意。キー入力も可) | P2 |
6.7 スケジュールと実施管理
| ID | 要件 | 優先 |
|---|---|---|
| FR-701 | 点検スケジュールを設定できること:日次/週次/月次/四半期/年次、対象設備、担当者、期限 | P1 |
| FR-702 | 本日の実施予定をホームに大きく表示すること(現場担当が最初に見る情報) | P1 |
| FR-703 | 点検実施状況を、設備×日付のグリッドで表示すること(実施済/未実施/期限超過/対象外) | P1 |
| FR-704 | 未実施・期限超過にリマインドを送れること(プレビュー表示で再現) | P1 |
| FR-705 | 実施率を算出すること(実施済 ÷ 予定) | P1 |
| FR-706 | 記録を設備・期間・判定結果・実施者で絞り込めること。URLクエリに反映すること | P1 |
| FR-707 | 記録の全文検索(コメント・症状・是正内容)ができること | P2 |
| FR-708 | 設備マスタを管理できること(設備名、場所、型式、管理番号、担当、点検表の紐付け) | P1 |
| FR-709 | 設備の点検履歴を時系列で確認できること | P1 |
6.8 分析
| ID | 要件 | 優先 |
|---|---|---|
| FR-801 | ダッシュボードに、実施率/未是正件数/期限超過件数/再発件数/平均所要時間 を表示すること | P1 |
| FR-802 | NG発生率を、設備別・項目別・期間別で表示すること | P1 |
| FR-803 | NGが多い項目のランキングを表示すること(設備の弱点が分かる) | P1 |
| FR-804 | 是正リードタイム(NG発生から承認完了まで)を、担当者別・設備別で表示すること | P1 |
| FR-805 | 再発している組み合わせを一覧表示すること(FR-411) | P1 |
| FR-806 | 点検表別の平均所要時間・平均タップ数を表示し、紙との比較を示すこと(F-A11) | P1 |
| FR-807 | 期間指定(今月/先月/四半期/任意)ができること | P1 |
| FR-808 | CSVエクスポートができること | P2 |
| FR-809 | 所要時間・タップ数を、個人の評価指標として提示しないこと。 点検表ごとの集計に留め、個人別ランキングを作らないこと | P1 |
6.9 デモ基盤
| ID | 要件 | 優先 |
|---|---|---|
| FR-901 | 3ロールをワンクリックで切り替えられること | P1 |
| FR-902 | データスコープを lib/repo/_scope.ts に集約すること(SCP-01, SCP-02) |
P1 |
| FR-903 | 現場担当が他拠点の記録を取得できないこと(SCP-03) | P1 |
| FR-904 | 現場担当が確定記録を修正できないこと(SCP-04, FR-507) | P1 |
| FR-905 | 「デモをリセット」で localStorage / IndexedDB / Cache をクリアすること | P1 |
| FR-906 | 点検記録・是正タスク・修正履歴がリロード後も保持されること | P1 |
| FR-907 | デモ内の現在日時を切替バーに表示すること | P2 |
| FR-908 | ロール権限外の画面では案内画面から切り替えられること。素の404を出さないこと | P1 |
| FR-909 | 権限で不可の操作はボタンを非活性にし、理由をツールチップで示すこと | P1 |
| FR-910 | 元記事へ戻るリンクと資料ダウンロードを常設すること | P1 |
| FR-911 | 初回訪問時に3ステップのガイドツアーを表示し、「今後表示しない」を選べること | P2 |
| FR-912 | 埋め込みモード:5項目の点検を実施でき、タップ数・所要時間・紙との比較を表示し、カメラ・位置情報を呼ばず、アプリ内遷移をしないこと(EMB-04〜07) | P1 |
7. データ設計
型定義(lib/types/)
// ---- 組織・設備 ----
type Site = { id: string; name: string; address: string } // 拠点
type Member = {
id: string
name: string // ★ 架空
siteIds: string[] // 担当拠点。スコープ判定の起点
role: 'inspector' | 'supervisor' | 'admin'
avatarUrl: string
isActive: boolean
}
type Equipment = {
id: string
code: string // 管理番号
name: string // ★ 架空(「1号機 空調機」等)
siteId: string
location: string // 設置場所(「B1 機械室」)
model: string
manufacturer: string
installedAt: string
sheetIds: string[] // 紐づく点検表
qrToken: string // QRコードの値
isActive: boolean
}
// ---- ★★★ 点検表テンプレート ★★★ ----
type JudgeType =
| 'ok_ng' // 良否(2値)★ 1タップ
| 'three_state' // 良/要注意/不良(3値)★ 1タップ
| 'number' // 数値
| 'select' // 単一選択
| 'multiselect' // 複数選択
| 'text' // テキスト
| 'photo_only' // 写真のみ
type SheetItem = {
id: string
sectionId: string
label: string
labelKana?: string // ★ ふりがな(FR-118)
judgeType: JudgeType
// --- 判定の設定 ---
options?: string[] // select / multiselect
numberConfig?: { unit: string; min?: number; max?: number; decimals: number; warnDiffFromPrev?: number }
symptomOptions?: string[] // ★ NG時の症状(ボタンで選ばせる。FR-108)
// --- 入力の要件 ---
isRequired: boolean
photoRule: 'none' | 'optional' | 'required' | 'required_on_ng'
allowNa: boolean
showPreviousValue: boolean // ★ 前回値の表示(FR-106)
helpText?: string
criteria?: string // 判定基準(「油量が下限線以上」)
sortOrder: number
// ★ タップ数の算出に使う(FR-605)
estimatedTaps: number
estimatedSeconds: number
}
type SheetSection = { id: string; name: string; sortOrder: number }
type InspectionSheet = {
id: string
version: number // ★ 過去の記録は当時の構造で表示(FR-614)
name: string
sections: SheetSection[]
items: SheetItem[]
requireSignature: boolean
effectiveFrom: string
// --- 帳票レイアウト(FR-609, FR-610)---
layout: ReportLayout
// --- 算出値(保存しない):itemCount, avgTaps, estimatedTotalSeconds
}
type ReportLayout = {
template: 'horizontal_checklist' | 'vertical_form' | 'matrix'
header: { showEquipmentName: boolean; showCode: boolean; showLocation: boolean; showModel: boolean }
showStampBox: boolean // 押印欄
stampBoxLabels: string[] // 「実施者」「確認者」「承認者」
remarksPosition: 'bottom' | 'right' | 'none'
includePhotoPages: boolean
paperSize: 'A4' | 'A3'
orientation: 'portrait' | 'landscape'
}
// ---- ★★★ 点検記録(確定後は値を上書きしない)★★★ ----
type InspectionStatus = 'in_progress' | 'completed' | 'voided'
type Inspection = {
id: string
equipmentId: string
sheetId: string
sheetVersion: number // ★ 当時の構造で表示(FR-614)
scheduledDate?: string // 予定日(スケジュールから生成された場合)
status: InspectionStatus
// --- 結果 ---
results: InspectionResult[]
// --- 実施情報(REC-04)---
inspectorId: string
startedAt: string
completedAt?: string
device: 'mobile' | 'tablet' | 'pc'
coord?: { lat: number; lng: number } // 設定時のみ
signatureImageId?: string // IndexedDB のキー
// --- 完全性(REC-06)---
contentHash?: string
// --- 無効化(REC-05。削除はしない)---
voidedAt?: string
voidedBy?: string
voidReason?: string
// --- ★ 計測(TAP-06)---
metrics: {
activeSeconds: number
totalTaps: number
itemSeconds: Record<string, number> // itemId → 秒数
interruptionCount: number // 中断回数
}
// --- 同期 ---
syncState: 'local' | 'synced'
createdAt: string
}
type InspectionResult = {
itemId: string
// --- 判定値 ---
judgment?: 'ok' | 'ng' | 'good' | 'caution' | 'bad' | 'na'
numberValue?: number
selectedOptions?: string[]
textValue?: string
// --- NG時の詳細(FR-107)---
symptoms?: string[]
comment?: string
// --- 添付 ---
photoIds: string[] // IndexedDB のキー
// --- N/A の理由 ---
naReason?: string
// --- 計測 ---
taps: number
seconds: number
recordedAt: string
// --- ★ 修正履歴(REC-01)。値は上書きしない ---
revisions: ResultRevision[]
}
// ★ 確定後の修正は、必ずこのレコードで表現する
type ResultRevision = {
id: string
revisedBy: string
revisedAt: string
reason: string // ★ 必須(REC-02)
before: Partial<InspectionResult> // ★ 元の値(REC-03)
after: Partial<InspectionResult>
}
// ---- ★ 是正タスク(NGから必ず生成される)----
type CorrectionStatus = 'open' | 'in_progress' | 'pending_approval' | 'completed' | 'not_required'
type Correction = {
id: string
// --- 発生元(FR-402)---
inspectionId: string
itemId: string
equipmentId: string
itemLabel: string
symptoms: string[]
comment: string
beforePhotoIds: string[] // ★ 点検時の写真(FR-407)
detectedAt: string
detectedBy: string
// --- 割当 ---
assigneeId?: string
dueDate?: string
priority: 'low' | 'normal' | 'high'
// --- 対応(FR-406)---
status: CorrectionStatus
actionTaken?: string
partsUsed?: string
afterPhotoIds: string[]
reportedBy?: string
reportedAt?: string
// --- 承認(FR-408)---
approvedBy?: string
approvedAt?: string
rejectReason?: string
// --- 対応不要(FR-404)---
notRequiredReason?: string
// --- 算出値:daysOpen, isOverdue
}
// ---- スケジュール ----
type Schedule = {
id: string
equipmentId: string
sheetId: string
frequency: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'
weekdays?: number[] // weekly
dayOfMonth?: number // monthly
months?: number[] // quarterly / yearly
assigneeId?: string
dueOffsetDays: number // 予定日から何日以内に実施すべきか
isActive: boolean
}
// ---- 算出値(ストアに保存しない)----
type SheetMetrics = { // FR-605〜607
sheetId: string
itemCount: number
avgTapsPerItem: number // ★ 1.2超で警告(FR-606)
estimatedTotalSeconds: number
hasWarning: boolean
warningReasons: string[] // 「選択項目が多い」「必須テキストが3つある」
}
type DurationStats = { // FR-806
sheetId: string
sheetName: string
itemCount: number
avgSecondsPerItem: number
avgTapsPerItem: number
paperBaselineSecondsPerItem: number // ★ 紙の目安(既定1.5秒)
isFasterThanPaper: boolean
// ★ 個人別は含めない(FR-809)
}
type RecurringNg = { // FR-411
equipmentId: string
itemId: string
itemLabel: string
ngCount: number
recentCount: number // 直近N回中
lastNgAt: string
openCorrectionId?: string
}
type LeadTimeStats = { scope: string; avgDays: number; medianDays: number; overdueCount: number }
type ComplianceStats = { plannedCount: number; completedCount: number; completionRate: number; overdueCount: number }
// ---- 監査・通知 ----
type AuditLog = {
id: string
actorId: string
action: 'revise_result' | 'void_inspection' | 'approve_correction' | 'reject_correction'
| 'update_sheet' | 'update_schedule' | 'export_pdf'
targetType: string
targetId: string
reason?: string // ★ 修正・無効化は理由必須
changes: { field: string; before: unknown; after: unknown }[]
createdAt: string
}
type Notification = {
id: string
targetMemberId: string
kind: 'scheduled_today' | 'not_performed' | 'correction_assigned' | 'correction_overdue'
| 'correction_approved' | 'correction_rejected' | 'recurring_ng'
title: string
body: string
link: string
previewMessage?: { channel: 'email' | 'chat'; subject?: string; body: string } // ★ 送信しない
readAt?: string
createdAt: string
}
タップ数と所要時間の算出(7章の中核)
// lib/metrics/taps.ts — 判定種別ごとの最小タップ数(FR-605)
const MIN_TAPS: Record<JudgeType, number> = {
ok_ng: 1, // ★ 1タップ
three_state: 1, // ★ 1タップ(3つ並べる)
select: 1, // ★ 選択肢3つ以下ならボタンで1タップ(TAP-05)
multiselect: 2, // 選択 + 確定
number: 3, // テンキー入力の平均
text: 4, // キーボード入力の平均(重い)
photo_only: 2, // 撮影 + 確定
}
export function calcSheetMetrics(sheet: InspectionSheet): SheetMetrics {
const items = sheet.items
const totalTaps = items.reduce((sum, it) => {
let taps = MIN_TAPS[it.judgeType]
// 選択肢が4つ以上ある select は、セレクトを開く分のタップが増える
if (it.judgeType === 'select' && (it.options?.length ?? 0) > 3) taps += 1
// 写真必須は撮影分が乗る
if (it.photoRule === 'required') taps += 2
return sum + taps
}, 0)
const avgTaps = items.length ? totalTaps / items.length : 0
const warnings: string[] = []
// ★ 1.2 を超えたら警告(TAP-07, FR-606)
if (avgTaps > 1.2) warnings.push(`1項目あたり ${avgTaps.toFixed(1)} タップ。紙より遅くなる可能性があります`)
const textCount = items.filter(i => i.judgeType === 'text' && i.isRequired).length
if (textCount >= 3) warnings.push(`必須のテキスト項目が ${textCount} 件あります。選択式に変えられませんか`)
const wideSelects = items.filter(i => i.judgeType === 'select' && (i.options?.length ?? 0) > 3).length
if (wideSelects > 0) warnings.push(`選択肢が4つ以上の項目が ${wideSelects} 件あります。3つ以下にすると1タップで済みます`)
return {
sheetId: sheet.id,
itemCount: items.length,
avgTapsPerItem: avgTaps,
estimatedTotalSeconds: items.reduce((s, i) => s + i.estimatedSeconds, 0),
hasWarning: warnings.length > 0,
warningReasons: warnings,
}
}
シードデータ(lib/seed/)
| ファイル | 内容 | 件数 |
|---|---|---|
sites.ts members.ts |
拠点2(本社工場・第二工場)、メンバー(現場6・責任者2・管理者1) | 拠点2 / 9名 |
equipments.ts |
設備(空調機・受変電設備・ポンプ・фフォークリフト・消火設備など。場所を分散) | 48件 |
sheets.ts |
点検表(日常点検30項目/月次点検18項目/年次点検45項目。うち1つは平均タップ数1.2超で警告が出る設計) | 5件 |
schedules.ts |
スケジュール(日次・週次・月次・年次を混在) | 62件 |
inspections.ts |
点検記録(過去6ヶ月分)。所要時間・タップ数の実データを持たせる | 約1,900件 |
corrections.ts |
是正タスク(未対応/対応中/承認待ち/完了/期限超過 を分散) | 約170件 |
revisions.ts |
修正履歴(理由付きで数件。修正ありの記録を作る) | 12件 |
シード作成のルール(品質を左右する)
- 実在企業名・実在製品名を使わない。 架空の設備名・型式で構成する
- 設備名・項目名を「設備A」「項目1」のようなダミーにしない。 実在しそうなものにする(「1号機 空調機(AHU-01)」「Vベルトの張り・摩耗」)。点検表のデモは項目名のリアリティがそのまま説得力になる
- 判定基準(
criteria)を具体的に書く。「油量が下限線以上であること」「異音・異臭がないこと」。ここが空だと点検表として嘘に見える - 所要時間・タップ数の実データを持たせる。 1項目あたり 0.7〜2.5秒の分布。テキスト項目が多い点検表は明確に遅く(FR-806 が意味を持つように)
- NG率を項目ごとに変える。 特定の項目(消耗品系)にNGを集中させる。FR-803 の「NGが多い項目ランキング」に意味のあるパターンが出ること
- 再発している組み合わせを2〜3件作る(同一設備・同一項目で直近5回中3回以上NG)(FR-411 の実演用)
- 未是正のまま長期放置されているタスクを数件作る(期限超過30日超を1件含める)
- 是正リードタイムに幅を持たせる(即日〜3週間)。担当者によって差が出るようにする
- 修正ありの記録を数件作る(理由付き。「数値の入力誤りを訂正」等)
- 無効化された記録を1件作る(「誤った設備で開始したため」)
- 未実施の点検を作る。 特に特定の設備が繰り返し未実施になっている状態(FR-703 のグリッドで見えるように)
- 平均タップ数が1.2を超える点検表を1つ作る(テキスト必須が4件、選択肢6つの項目が3件など)。FR-606 の警告が実際に出ること
- 日付は現在日時からの相対で生成する。固定日付を埋め込まない
- 点検記録はスケジュールの予定日に対応して存在すること(年次点検が毎日並んでいると嘘に見える)
ストアとリポジトリ
lib/
├── types/ seed/
├── inspection/ # ★ 純粋関数のみ
│ ├── flow.ts # 次の項目の決定、未判定の抽出、完了判定
│ ├── validate.ts # 必須・写真必須・数値範囲の検証
│ ├── correction.ts # ★ NG → 是正タスクの生成(FR-401)
│ ├── recurring.ts # ★ 再発の検出(FR-411)
│ ├── revision.ts # ★ 修正履歴の適用(REC-01)
│ └── hash.ts # 記録のハッシュ算出(REC-06)
├── metrics/ # ★ 純粋関数 + タイマー
│ ├── session-timer.ts # 所要時間・タップ数の計測(TAP-06)
│ ├── taps.ts # ★ 点検表の平均タップ数(7章のコード)
│ ├── duration.ts # 所要時間の集計、紙との比較(FR-806)
│ ├── compliance.ts # 実施率(FR-705)
│ └── leadtime.ts # 是正リードタイム(FR-414)
├── report/ # ★ 帳票。純粋関数
│ ├── layout.ts # レイアウトの解決(3テンプレート)
│ └── pdf.tsx # PDF生成(@react-pdf/renderer)
├── offline/
│ ├── queue.ts # 同期キュー(OFF-05, OFF-06)
│ └── cache.ts # 点検表定義のキャッシュ
├── storage/ # IndexedDB(写真・署名)
├── query/ # 絞り込み・検索
├── store/ { session, data, draft, sync, ui }
└── repo/ # ★ コンポーネントが触るのはここだけ
├── _delay.ts
├── _scope.ts # ★ データスコープ(SCP-01)
├── inspections.ts results.ts revisions.ts
├── corrections.ts schedules.ts equipments.ts
├── sheets.ts reports.ts metrics.ts members.ts logs.ts
リポジトリ層の規約(厳守)
- 全メソッドを
asyncで定義する。中身が同期でも例外なく - 戻り値は
{ ok: true; data: T } | { ok: false; error: string }に統一する - コンポーネントから Zustand ストアを直接参照しない。 読み取りも書き込みもリポジトリ経由
- 確定した点検結果の値を上書きするコードを書かない(REC-01)。修正は
revisionsへの追加のみ - 記録を削除するコードを書かない(REC-05)。無効化は
status: 'voided'+ 理由 - NG判定から是正タスクを生成する処理を、
lib/inspection/correction.tsに集約する(FR-401) - スコープ適用(
_scope.ts)を全メソッドが必ず通す lib/inspection/lib/metrics/lib/report/layout.tsは純粋関数のみ。 ストア・DOM に依存させず、new Date()を内部で呼ばない- 派生値(平均タップ数・実施率・リードタイム・再発)はストアに保存せず算出する
- 判定タップ・次項目への遷移・途中保存に擬似ディレイを入れない(FR-117)
- 通信状態を見て操作をブロックするコードを書かない(OFF-01, FR-302)
// lib/inspection/correction.ts — NGから是正タスクを必ず生成する(FR-401)
export function buildCorrections(inspection: Inspection, sheet: InspectionSheet): Correction[] {
return inspection.results
.filter(r => r.judgment === 'ng' || r.judgment === 'bad') // ★ 例外なく生成
.map(r => {
const item = sheet.items.find(i => i.id === r.itemId)!
return {
id: newId(),
inspectionId: inspection.id,
itemId: r.itemId,
equipmentId: inspection.equipmentId,
itemLabel: item.label,
symptoms: r.symptoms ?? [],
comment: r.comment ?? '',
beforePhotoIds: r.photoIds, // ★ 点検時の写真を引き継ぐ(FR-407)
detectedAt: inspection.completedAt!,
detectedBy: inspection.inspectorId,
status: 'open',
priority: 'normal',
afterPhotoIds: [],
}
})
}
8. デザイン要件
アートディレクション
「手袋のまま、見ずに押せる」
TENKEN が使われるのは、機械室、屋上、暗い地下、騒音の中、雨天の屋外。手袋をしていて、片手がふさがっていて、画面をじっと見る余裕がない。
だから TENKEN は1画面に1つのことしかさせない。判定ボタンは画面の40%以上を占め、指の位置を覚えれば画面を見ずに押せる。姉妹プロジェクトの中で、情報量が最も少なく、要素が最も大きい。
- 1画面1項目。 一覧は「切替」で提供する。既定は1項目
- 判定ボタンが主役。 画面の40%以上。OK と NG を左右に大きく
- 高コントラスト。 直射日光でも、暗い機械室でも読める。濃い地に明るい文字
- 安全色を使う。 現場の人が身体で覚えている色(緑=良、黄=注意、赤=不良)
姉妹プロジェクトとの差別化(最重要) FIELDPIN も屋外向けの大型UIだが、FIELDPIN は地図が全画面で明るい背景。 TENKEN は濃い地(#14171C 系)に大きな判定ボタン。地図もリストもない。1画面に1項目だけ。 HIBI は「読み書きする1カラム・明るい・15px本文」。TENKEN は「押すだけ・濃い地・28px以上の文字」。 10本並べたとき、TENKEN だけが「濃い地に巨大なボタン2つ」であることが一目で分かること。
カラートークン
:root {
/* ★ 濃い地。直射日光でも暗所でも読める高コントラスト */
--bg: #14171C; /* 点検画面の背景 */
--bg-raised: #1E232B; /* カード・パネル */
--bg-overlay: #29303A; /* モーダル・詳細入力 */
--fg-high: #F5F7FA; /* 主要テキスト(項目名・ボタンラベル) */
--fg-mid: #A7B0BC; /* 補助テキスト(前回値・基準) */
--fg-low: #6B7480; /* 非活性 */
--line: #333B45;
/* ★ 安全色。現場が身体で覚えている色を使う */
--ok: #1E8E4A; /* 良・OK(緑) */
--ok-press: #166F39;
--caution: #C48A0E; /* 要注意(黄) */
--caution-press:#9C6E0B;
--ng: #C0392B; /* 不良・NG(赤) */
--ng-press: #9A2E23;
--na: #4A5560; /* 該当なし(無彩色) */
/* 状態 */
--st-done: #1E8E4A;
--st-pending: #C48A0E;
--st-overdue: #C0392B;
--st-void: #6B7480;
/* オフライン(★ 警告色にしない。OFF-04) */
--offline-bg: #29303A;
--offline-fg: #A7B0BC;
--sync-pending:#C48A0E;
/* 管理画面(PC)は明るい地に切り替える */
--admin-bg: #F4F5F7;
--admin-panel: #FFFFFF;
--admin-ink: #14171C;
--admin-line: #DCDFE4;
}
配色ルール
- 点検画面は濃い地、管理画面は明るい地。 使う場所と使う人が違うので、意図的に分ける
- 判定の色は安全色から動かさない。 OK=緑、要注意=黄、NG=赤。デザインの都合で変えない
- オフラインを警告色にしない(OFF-04)。オフラインは正常な状態。未同期件数だけを
--sync-pendingで示す - N/A を無彩色にする。 判定ではないことを色で示す
- 押下時に必ず色が変わること(
--*-press)。手袋だと触覚のフィードバックが弱いので、視覚で補う
タイポグラフィ
| 役割 | 書体 | 用途 |
|---|---|---|
| UI全般 | Noto Sans JP 700 / 500 | 既定ウェイトを500以上にする。 濃い地では細い文字が沈む |
| 数値 | Roboto Mono 600(tabular-nums) |
測定値、進捗、所要時間 |
| トークン | 点検画面 | 管理画面 | 用途 |
|---|---|---|---|
| item-label | 28px / 700 | — | 点検項目名。画面の主役 |
| judge-label | 32px / 700 | — | 判定ボタンのラベル(OK / NG) |
| judge-sub | 15px / 500 | — | ボタン内の補足(「異常なし」) |
| criteria | 15px / 500 | — | 判定基準・前回値 |
| progress | 20px / 600 Mono | — | 進捗(12 / 30) |
| page-title | 22px / 700 | 20px / 700 | 画面タイトル |
| body | 16px / 500 | 13px / 400 | 本文(点検画面は16px以上) |
| meta | 13px / 500 | 12px / 400 | 補助情報 |
点検画面の最小文字サイズは 15px。 それ以下を使わない。管理画面は13pxの高密度でよいが、点検画面と混ぜない。
レイアウトとスペーシング
| 項目 | 定義 |
|---|---|
| スペーシング | 8pxベース:8 / 16 / 24 / 32 / 48 / 64(4pxを使わない。 現場UIに細かい余白は不要) |
| 点検画面 | 1カラム、100dvh。 上部に進捗(56px)、中央に項目名、下部60%に判定ボタン |
| 判定ボタン | 画面高の40%以上、幅は画面の半分ずつ。 最小 高さ160px |
| タップ領域 | 判定ボタン以外も最小64×64px(手袋前提。FIELDPINの56pxより大きい) |
| 補助ボタン(該当なし・戻る) | 高さ56px。判定ボタンから十分離す(誤タップ防止に24px以上) |
| セーフエリア | env(safe-area-inset-*) を必ず考慮 |
| 高さ | 100dvh を使う。100vh を使わない |
| 管理画面 | 左サイドバー232px + メイン。テーブル行高38px |
| 角丸 | 8px(判定ボタン・カード)/4px(補助ボタン・バッジ) |
| 影 | 使わない。 明度差で階層を表現する |
主要コンポーネント仕様
| コンポーネント | 仕様 |
|---|---|
| 判定ボタン(最重要) | 画面下部を左右に2分割(3値なら3分割)。judge-label 32px + judge-sub 15px。 押下で --*-press に変化 + 軽い振動(対応端末)。押した瞬間に次項目がスライドイン(TAP-03) |
| 項目名 | 画面中央上、item-label 28px。2行までで収まるよう文字数を制限(テンプレート側で警告) |
| 判定基準・前回値 | 項目名の下、criteria 15px --fg-mid。前回値は「前回:異常なし(8/19)」の形式 |
| 進捗バー | 画面最上部、高さ4px。その下に「12 / 30」(progress 20px Mono) |
| NG詳細入力 | NG押下で下からシートが上がる(--bg-overlay)。症状はボタンで並べる(FR-108)。写真ボタンは大きく。「保存して次へ」1タップで閉じる |
| 数値入力 | 中央に大きな数値表示(32px Mono)+ その下にテンキー(各キー64px以上)。上下限を外れると数値が --ng に変わり、警告文が出る |
| 一覧モード | 全項目をリスト表示。各行に判定ボタン(小)。切替はヘッダーのトグル |
| 未判定の確認 | 完了前。未判定項目をリストで表示。「まとめて該当なしにする」ボタン |
| 署名パッド | Canvas。横向き推奨のヒント。「やり直す」「確定」 |
| 完了結果 | 所要時間(32px Mono)/タップ数/1項目あたり/紙との比較/NG件数と生成された是正タスク |
| オフラインバー | 上部固定、--offline-bg。「オフライン(点検は通常どおり行えます)」。赤くしない |
| 未同期バッジ | ヘッダー右。--sync-pending。件数(Mono) |
| 是正タスクカード | 設備名 + 項目名/症状/経過日数(Mono・大)/期限/担当アバター。期限超過は左端に --overdue の帯 |
| 対応前後の写真 | 左右に並べて表示。ラベル「対応前」「対応後」を明示 |
| 修正履歴の表示 | 記録詳細で、修正された項目に「修正あり」バッジ。展開で元の値 → 修正後の値 + 理由 + 修正者・日時 |
| 実施状況グリッド | 設備×日付。セルは ● 実施済/○ 未実施/△ 期限超過/− 対象外。色ではなく記号で区別(A11Y-05) |
| 平均タップ数の警告 | テンプレート管理画面上部。「1項目あたり 1.6 タップ。紙より遅くなる可能性があります」+ 改善提案(7章のコード参照) |
| 帳票プレビュー | 実際の印刷レイアウト。押印欄・備考欄を含む |
| 空状態 | 「本日の点検予定はありません」/「条件に一致する記録はありません」を出し分け |
| スケルトン | 点検画面は使わない(1項目ずつなので即表示)。管理画面のテーブルのみ |
| デモ切替バー | 明るいグレーの帯(濃い地の点検画面と逆にして区別する) |
インタラクション要件(現場UIの肝)
| ID | 要件 |
|---|---|
| IX-01 | 判定タップから次項目の表示までが 100ms以下。擬似ディレイを入れない(FR-117) |
| IX-02 | 判定ボタンの押下時に、色の変化と(対応端末なら)振動でフィードバックすること |
| IX-03 | 誤タップを防ぐため、判定ボタンと補助ボタンの間に24px以上の間隔を空けること |
| IX-04 | スワイプで次/前に移動できること。 ただし判定していない項目を飛ばす場合は確認しないこと(後で一覧に出るから) |
| IX-05 | 戻って判定を変更できること(完了前は自由。履歴を残さない)(FR-113) |
| IX-06 | 数値入力はテンキーだけで完結すること(Enter で確定して次へ) |
| IX-07 | 画面が消えても点検が失われないこと。 判定ごとに即座にローカル保存すること |
| IX-08 | アプリ更新による強制リロードを、点検中に起こさないこと(OFF-07, FR-310) |
| IX-09 | 是正タスクの承認画面は連続処理できること(A 承認/R 差戻し/→ 次) |
| IX-10 | 破壊的操作(記録の無効化・修正)は確認ダイアログ + 理由の入力を同じ画面で求めること |
| IX-11 | 管理画面の一覧は、絞り込み・並び替えが100ms以下で反映されること |
モーション
| 対象 | duration | 内容 |
|---|---|---|
| 判定ボタンの押下 | 60ms | 色の変化。最速。ここが遅いと全体が遅く感じる |
| 次項目への遷移 | 140ms | 横スライド。押したボタンの側から入ってくる(方向で進行が分かる) |
| NG詳細シートの展開 | 200ms | 下から。cubic-bezier(.32,.72,0,1) |
| 進捗バーの伸長 | 140ms | 遷移と同時 |
| 完了結果の数値 | 500ms | カウントアップ。ここは見せる |
| 同期の進捗 | — | バーではなく件数のカウントダウン |
| トースト | 160ms | 上から(下は判定ボタンがあるため) |
prefers-reduced-motion: reduce 時は、遷移を即時にし、カウントアップを停止する。 ただし押下時の色変化は残す(フィードバックとして必要)。
アクセシビリティ(WCAG 2.1 AA)
| ID | 要件 |
|---|---|
| A11Y-01 | 濃い地でのコントラスト比は通常4.5:1以上、判定ボタンのラベルは7:1以上を確保すること |
| A11Y-02 | 点検画面の最小文字サイズを15pxとすること。 それ以下を使わないこと |
| A11Y-03 | タップ領域は最小64×64px(手袋前提)。判定ボタンは画面の40%以上 |
| A11Y-04 | 判定を色のみで伝えないこと。 ボタンには必ずテキスト(OK/NG、良/要注意/不良)を入れること |
| A11Y-05 | 実施状況グリッドを色のみで伝えないこと。 記号(●○△−)とテキストを併記すること |
| A11Y-06 | 全ての操作をキーボードで完遂できること(PCでの点検も想定 — 事務所での代理入力) |
| A11Y-07 | テンプレート管理のD&Dにキーボード代替を用意すること(FR-604) |
| A11Y-08 | フォーカスリングは3px・オフセット2pxで常時可視。濃い地でも視認できる色にすること |
| A11Y-09 | 判定の記録を aria-live="polite" で通知すること(「OK と記録しました。次:ベルトの張り」)。画面を見ずに操作する人のため |
| A11Y-10 | フォームの各入力に label を関連付け、エラーは aria-describedby と role="alert" で読み上げること |
| A11Y-11 | 数値の範囲外警告を aria-live="assertive" で通知すること |
| A11Y-12 | モーダル・シートはフォーカストラップ + Esc で閉じる + 起動元へフォーカス復帰 |
| A11Y-13 | 項目名にふりがなを表示できること(FR-118)。外国人作業者・漢字が読みにくい人への配慮 |
| A11Y-14 | フォントサイズ200%指定でも、判定ボタンと項目名が操作・読解できること |
ライティング規約
| 原則 | 例 |
|---|---|
| 判定は現場の言葉で | ○「OK / 異常なし」「NG / 異常あり」/×「合格 / 不合格」「true / false」 |
| オフラインを不安にさせない | ○「オフライン(点検は通常どおり行えます)」/×「通信エラー」「オフラインです」 |
| 完了は成立を伝える | ○「点検を完了しました。記録は端末に保存されています」/×「同期待ちです」 |
| 紙との比較は事実として | ○「1項目あたり 0.9秒。紙の目安 1.5秒より速いです」/×「紙より圧倒的に速い!」 |
| NGを責めない | ○「異常を記録しました。是正タスクを作成しました」/×「不合格項目があります」 |
| 修正は理由を求める理由を書く | ○「修正の理由を入力してください。記録の履歴として保存されます」 |
| 未実施は事実だけ | ○「8月20日の点検が未実施です」/×「点検を怠っています」 |
| 警告は改善案を添える | ○「1項目あたり 1.6 タップ。選択肢を3つ以下にすると1タップで済みます」 |
| システム語を使わない | ○「この記録を無効にする」/×「statusをvoidedに更新」 |
9. 技術要件・ディレクトリ構成
技術スタック(固定・勝手に変更しない)
| レイヤ | 技術 | 備考 |
|---|---|---|
| フレームワーク | Next.js 15(App Router)/ TypeScript strict | |
| スタイリング | Tailwind CSS + CSS Variables | 点検画面と管理画面でトークンを切り替える |
| UIコンポーネント | shadcn/ui(Radix UI基盤) | 管理画面が主。点検画面のボタンは自前実装 |
| 状態管理 | Zustand + persist | localStorage に永続化 |
| 判定ボタン・点検フロー | 自前実装 | ライブラリを使わない。 画面の40%を占めるボタンに既製品は合わない |
| PWA / Service Worker | next-pwa(設定は自前で調整) | オフライン起動が必須要件(OFF-01, FR-304) |
| 写真・署名の保存 | IndexedDB(idb) | Blob 保存(OFF-03, FR-202) |
| 画像圧縮 | Canvas API(自前) | ライブラリを足さない |
| 署名 | Canvas API(自前) | signature_pad を入れない(数十行で書ける) |
| 帳票PDF | @react-pdf/renderer(動的import) | 既存の紙のレイアウト再現が要件(FR-609〜611) |
| QRコード読取 | @zxing/browser(動的import・任意) |
キー入力を既定とする |
| QRコード生成 | qrcode(動的import) |
設備QRの印刷用(FR-615) |
| D&D | dnd-kit | テンプレート項目の並び替え。キーボード対応必須 |
| グラフ | Recharts(動的import) | 管理画面の分析のみ |
| フォーム | React Hook Form + Zod | 管理画面のみ。点検画面には使わない(重い) |
| テーブル | TanStack Table v8 | 管理画面のみ |
| CSV | Papa Parse | |
| 日付 | date-fns(ja locale) | タイムゾーンは Asia/Tokyo 固定 |
| アイコン | lucide-react | |
| ホスティング | Vercel(HTTPS必須) | Service Worker・カメラは HTTPS 必須 |
| テスト | Vitest / Playwright / axe-core |
上記以外のライブラリを入れる前に必ず提案し、承認を得ること。特に、点検画面に重いライブラリ(フォームライブラリ・UIキット・アニメーションライブラリ)を持ち込むことは禁止。
点検画面を軽く保つ理由: 判定タップから次項目までを100ms以下に保つ(FR-117)ため、点検画面のバンドルを最小にする。管理画面の重いライブラリ(Recharts / TanStack Table / react-pdf)はすべて動的import で分離し、点検画面に載せない。
ディレクトリ構成
/
├── CLAUDE.md
├── app/
│ ├── layout.tsx # ロール別シェル + デモ切替バー
│ ├── page.tsx # SC-001 ホーム / SC-900(?embed=1)
│ ├── inspect/ # SC-010〜017(★ 点検画面。軽さを最優先)
│ ├── history/ records/ # SC-020, 021
│ ├── corrections/ # SC-030, 031(現場)/ SC-100〜103(責任者)
│ ├── notifications/ # SC-040
│ ├── status/ equipments/ # SC-110, 113
│ ├── admin/ # SC-200〜251
│ └── dev/components/
├── components/
│ ├── judge/ # ★ 最重要。点検画面の中核
│ │ ├── JudgeButtons.tsx # ★ 画面40%以上を占める判定ボタン
│ │ ├── ItemLabel.tsx # 28px の項目名 + 基準 + 前回値
│ │ ├── ProgressHeader.tsx # 進捗バー + 12/30
│ │ ├── NgDetailSheet.tsx # NG時の詳細(下から)
│ │ ├── NumberPad.tsx # ★ 自前テンキー(64px以上のキー)
│ │ ├── SignaturePad.tsx # Canvas
│ │ └── useInspectionFlow.ts # 次項目の決定・即座のローカル保存
│ ├── correction/ # CorrectionCard, BeforeAfterPhotos, ApprovalPanel
│ ├── record/ # RevisionHistory, ResultTable
│ ├── admin/ # StatusGrid, SheetEditor, TapWarning, LayoutEditor
│ ├── offline/ # OfflineBar, SyncBadge, SyncProgress
│ ├── demo/ # RoleSwitcher, ResetButton, GuideTour, BackToArticle
│ └── layout/
├── lib/
│ ├── types/ seed/ store/ repo/
│ ├── inspection/ # ★ 純粋関数
│ ├── metrics/ # ★ 純粋関数 + タイマー
│ ├── report/ # ★ 帳票(layout は純粋関数、pdf は動的import)
│ ├── offline/ storage/ query/ validation/ utils/
├── docs/
│ └── inspection-checklist.md # 資料PDFの元原稿(点検表ヒアリングシート)
├── e2e/
└── public/
コーディング規約
- TypeScript strict。
any禁止。やむを得ない場合はunknown+ 型ガード - ファイル名は kebab-case、コンポーネントは PascalCase
- コンポーネントから Zustand ストアを直接参照しない。必ず
lib/repo/経由 - 確定した点検結果の値を上書きするコードを書かない(REC-01)。修正は
revisionsへの追加のみ - 記録を削除するコードを書かない(REC-05)
- 通信状態を見て操作をブロックするコードを書かない(OFF-01)
- 判定ごとに即座にローカル保存する(IX-07)。デバウンスしない
- 点検画面に重いライブラリを import しない。 管理画面用のライブラリは必ず動的import
lib/inspection/lib/metrics/lib/report/layout.tsは純粋関数のみ。 ストア・DOM に依存させず、new Date()を内部で呼ばない- 派生値(平均タップ数・実施率・リードタイム・再発)をストアに保存しない
- 時刻は分数または ISO 文字列で扱い、文字列の切り貼りをしない
- 機能を追加するとき、1項目あたりのタップ数が増えないことを確認する(TAP-08)
- 高さは
100dvhを使う。100vhを使わない - コミットは1タスクごと。メッセージに要件IDを含める
例:
feat(judge): 判定後の自動遷移とスライド方向を実装 (FR-103)
10. 非機能要件・法令
性能要件
| ID | 要件 | 目標値 |
|---|---|---|
| NFR-01 | 判定タップから次項目の表示まで | 100ms以下 |
| NFR-02 | 点検画面の初期表示(オフライン起動時) | 1.0秒以下 |
| NFR-03 | 判定ごとのローカル保存 | 30ms以下、操作をブロックしないこと |
| NFR-04 | 写真の圧縮・保存 | 1枚1.5秒以下、その間も次の項目に進めること |
| NFR-05 | 点検画面のJSバンドル(gzip後) | 90KB以下(管理画面用ライブラリを含めないこと) |
| NFR-06 | 埋め込みモードの初期JSバンドル(gzip後) | 55KB以下 |
| NFR-07 | 管理画面の初期JSバンドル(gzip後) | 250KB以下(PDF・グラフ・テーブルは動的import) |
| NFR-08 | 帳票PDF生成(30項目・写真10枚) | 3秒以下(進捗表示を出すこと) |
| NFR-09 | 点検記録1,900件の絞り込み・検索 | 150ms以下 |
| NFR-10 | 実施状況グリッド(設備48 × 31日)の描画 | 200ms以下 |
| NFR-11 | LCP(モバイル・オフライン起動) | 1.5秒以下 |
| NFR-12 | INP | 200ms以下 |
| NFR-13 | CLS | 0.1以下。判定ボタンの領域を最初から確保すること |
| NFR-14 | Lighthouse | Performance 95 / Accessibility 95 以上(点検画面) |
| NFR-15 | 対応環境 | iOS Safari 16以降、Android Chrome 最新、PC Chrome / Edge 最新2 |
| NFR-16 | 30項目の点検を10回連続で実施してもメモリが単調増加しないこと | — |
| NFR-17 | IndexedDB の容量上限接近時に警告し、同期済みの写真から削除を促すこと(記録本体は消さない) | — |
セキュリティ・プライバシー
| ID | 要件 |
|---|---|
| SEC-01 | 点検記録・写真・設備名を一切外部に送信しないこと。 localStorage / IndexedDB にのみ保存すること |
| SEC-02 | この事実を、点検画面・デモ切替バーで明示すること |
| SEC-03 | GA4 に点検記録の内容・設備名を送らないこと(EMB-10)。送るのは秒数・タップ数などのメタ情報のみ |
| SEC-04 | ユーザー入力(コメント・是正内容)をサニタイズすること |
| SEC-05 | 「デモをリセット」ですべてのデータが消えることを明示し、実際に消えること |
| SEC-06 | 位置情報を取得する場合、目的を事前に説明し、拒否しても点検できること(既定OFF) |
| SEC-07 | Service Worker のキャッシュに、点検記録の内容を含めないこと(アプリ本体と点検表定義のみ) |
法令・保存要件に関する取り扱い(この領域固有)
点検表は法令や取引先要求で形式・保存期間が決まっていることが多い。 本デモは実装例であり、法令適合の保証をするものではない。
| ID | 要件 |
|---|---|
| LAW-01 | デモ内に「本デモは点検記録の実装例です。法令上の記録要件・保存期間は分野ごとに異なるため、実運用の際は所轄官庁・専門家への確認を推奨します」の注記を、テンプレート管理画面と帳票出力画面に常設すること |
| LAW-02 | 記事内でも同じ注記を明示すること。 特定の法令への適合を断定しないこと |
| LAW-03 | 記録の改ざん防止を実装すること(REC-01〜06)。修正履歴・無効化履歴が残ること |
| LAW-04 | 保存期間の設定を持ち、期間内の記録を削除できないようにすること |
| LAW-05 | 帳票に、実施者・実施日時・確認者・押印欄を含められること(FR-609) |
| LAW-06 | 手書き署名は画像として保持するが、「法的な電子署名ではない」旨を設定画面に明示すること |
実案件への転用時の注意(デモ本体の要件ではない。商談で聞かれた際に説明できるよう記録)
① 分野ごとに記録要件が違う。 消防設備、電気設備、建設機械、車両、食品衛生——それぞれ根拠法令と保存期間が異なる。「点検表アプリ」として汎用に作るのではなく、対象分野の要件を先に確定させる
② 電子化そのものの可否を確認する必要がある。 分野によって、電子的な記録の保存が認められる条件(見読性・完全性・保存性)が定められている場合がある
③ 紙の帳票の再現度が要件の大半を占めることがある。 取引先監査で提出する帳票は、フォーマットが指定されていることが多い。現物の紙を全種類集めることが要件定義の第一歩
④ 改ざん防止は「上書きしない設計」で始まる。 本デモの
ResultRevision(REC-01)がその実装例。実案件ではさらに、サーバー側での署名・タイムスタンプ・アクセス制御が必要になる⑤ 手書き署名画像は、法的な電子署名ではない。 電子署名法に基づく効力が必要な場合は、専用サービスの利用が前提になる
⑥ 保存義務のある記録は、システム更新・事業者変更のときにデータ移行の責任が生じる。 出力可能な形式(PDF・CSV)を常に持たせておくことが、事業継続の観点で重要
本デモはデータをサーバーに送信しないため、①〜⑥の義務は発生しない。「デモである」旨と LAW-01 の注記を画面上に常時明示する
11. 費用設計
記事の主題の半分は「費用」。この章は実装要件であると同時に、記事本文の原稿素材でもある。
点検表の電子化だけの特殊事情:帳票の再現度が費用を決める
| 層 | 内容 | 特徴 |
|---|---|---|
| ① 初期開発費 | 設計・実装・テスト | 帳票レイアウトの再現度で大きく変わる |
| ② 点検表の棚卸し工数 | 現物の紙を全種類集め、項目を構造化する | 見積書に「お客様側作業」として明記しないと必ず揉める |
| ③ 帳票レイアウトの再現工数 | 紙と同じPDFを出す | 点検表の種類 × レイアウトの複雑さ。 ここが最大の変動要因 |
| ④ ハードウェア費 | タブレット、防水ケース、ストラップ、(必要なら)QRラベル | 現場人数 × 拠点 |
| ⑤ 固定運用費 | ホスティング、ドメイン、監視、データベース、保存領域(写真) | 写真がある分、他システムより保存費が乗る |
③を見積もらずに始めると、「アプリはできたが取引先に出せる帳票が出ない」になる。
③の分岐:帳票をどこまで再現するか(最大の費用要因)
| 再現レベル | 内容 | 費用 |
|---|---|---|
| A:標準テンプレート | 用意された3種のレイアウトから選ぶ。項目は自動配置 | 小 |
| B:レイアウト調整 | セクション順、列構成、押印欄、備考欄の位置を設定で調整 | 中 |
| C:紙の完全再現 | 罫線の結合、特殊な枠、社章、独自の並び——紙をそのままPDFで再現 | 大(点検表1種ごとに工数) |
「取引先に提出する帳票」がある場合、ほぼCになる。 そして点検表の種類が10種類あればC×10。
本デモはA+B(3種のテンプレート + 設定による調整)で実装している(FR-610)。Cが必要かどうかは、現物の紙を見なければ判断できない。
記事に書くべき一文: 「点検表の電子化で最初にやるべきは、現物の紙を全種類コピーして並べることです。」 それを見ないと、見積もりができない。
②の分岐:見落とされやすい「点検表の棚卸し」
電子化の依頼者自身が、点検表が何種類あるか把握していないことが多い。
- 部署ごとに独自の点検表がある
- 同じ設備なのに拠点で様式が違う
- 使われていない項目が残っている
- 法令要求と社内独自項目が混在している(分けないと簡略化できない)
この棚卸しをせずに電子化すると、紙の非効率をそのままアプリに移すことになる。 そして項目が多い点検表は、紙より遅くなって使われなくなる(1章 厳しさ①)。
TENKEN がテンプレート管理画面に「平均タップ数の警告と改善提案」を出しているのは、この棚卸しの会議で使うためにある(FR-605, FR-606)。資料PDFの「点検表ヒアリングシート」も、この工数を前倒しするために作る。
①の分岐:既製サービスがあるのに、なぜ自作するのか
点検・現場帳票の電子化サービスは充実している。この問いに答えられない案件は、受けるべきではない。
| 自作が正当化されるケース | 内容 |
|---|---|
| ① 帳票の完全再現が必要 | 取引先提出用の様式が指定されており、既製サービスの帳票では通らない |
| ② 既存システムと繋ぎたい | 設備マスタ・保全管理・在庫と点検を一体化したい |
| ③ 点検表の種類が多く、独自ロジックがある | 前回値との比較判定、条件分岐(この項目がNGなら追加項目が出る)など |
| ④ 人数が多く、人数課金が重い | 数百名規模で、月額×人数が積み上がっている |
| ⑤ 極端な簡略化が必要 | 「1項目1タップ」レベルまで削りたい。既製サービスの汎用画面では無理 |
| ⑥ オフライン要件が厳しい | 地下・山間部・工場内で、既製サービスのオフライン挙動が実用に足りない |
⑤と⑥は、現場系で最も多い動機。 既製サービスは汎用性のために画面が複雑で、現場の人が使わない。
逆に、①〜⑥のどれにも当てはまらないなら、既製サービスを使うべき。
損益分岐の計算式
既製サービスの年間コスト
= 月額単価 × 人数 × 12
+ 初期設定費(点検表の登録代行)
( + オプション:帳票カスタマイズ・API連携・保存容量追加 )
回収年数 = (開発費 + 帳票再現費 + ハード費 + 棚卸し工数)
÷(既製サービス年間コスト − 自作の年間運用費 − 年間保守費)
| 規模 | 現場人数 | 点検表の種類 | 判断 |
|---|---|---|---|
| 小 | 〜20名 | 1〜3種 | 既製サービスで十分 |
| 中 | 30〜100名 | 5〜15種 | ①③⑤⑥に該当するなら検討価値あり |
| 大 | 100名超 | 20種以上 | 人数課金が効いてくる。ただし帳票再現の工数も増える |
④の分岐:端末の選択
| 私物スマホ(BYOD) | 会社支給スマホ | 業務用タブレット | |
|---|---|---|---|
| 初期費用 | ゼロ | 人数分 | 拠点・班ごと |
| 画面サイズ | 小さい(判定ボタンは足りる) | 同じ | 大きい。一覧モードが使える |
| 手袋対応 | 機種次第 | 選べる | 選べる(手袋対応モデル) |
| 防水・耐衝撃 | 弱い | 選べる | 強い |
| BYODの論点 | 発生する(私物利用の同意、通信費、紛失時) | なし | なし |
判断の目安:
- 1人1台で持ち歩く運用 → 会社支給スマホ
- 班で共有して回る運用 → 業務用タブレット1台を持ち回り
- まず試したい → BYODで開始し、定着したら支給に切り替える
TENKEN が「1画面1項目・巨大ボタン」にしているのは、小さい画面でも成立させるため。 端末選択の自由度を、設計で確保している。
⑤の分岐:写真の保存費(見落とされやすい)
点検アプリは写真が積み上がる。 そして保存義務があるので消せない。
年間の写真容量 = 1点検あたりの写真枚数 × 1枚の平均サイズ × 年間点検回数
例:平均2枚 × 400KB × 年間12,000回 = 約 9.6GB/年
→ 5年保存なら 48GB
「写真は撮れるだけ撮らせる」設計にすると、保存費が線形に増える。 だから TENKEN は端末内で圧縮してから保存し(FR-201)、写真必須の項目を絞る設計にしている。
Webで足りるか、ネイティブが必要か
点検は、Webで足りる領域。ただしオフラインの作り込みが条件。
| やりたいこと | Web(PWA) | ネイティブ |
|---|---|---|
| オフラインでの点検実施 | できる(Service Worker + IndexedDB) | できる |
| オフライン起動 | できる(PWA) | できる |
| 写真の撮影・保存 | できる | できる |
| 手書き署名 | できる(Canvas) | できる |
| NFC タグでの設備識別 | 限定的 | できる |
| 実施予定のプッシュ通知 | 限定的 | できる |
| 大量の写真の長期保持 | ブラウザの容量制限あり | できる |
判断の目安: NFC を使う、または実施忘れのプッシュ通知が必須でなければ、Webで十分。通知はチャットツールやメールで代替できる。
デモ自体の運用コストを実測して記事に載せる
| ID | 要件 |
|---|---|
| COST-01 | デモ公開後、月次で「デモ起動数・点検完了数・ホスティング費用」を記録すること |
| COST-02 | seconds_per_item と taps_per_item の分布を集計し、記事に掲載すること。「読者の中央値は1項目あたり何秒・何タップだったか」は、主張を裏付ける最強のデータになる(EMB-09) |
| COST-03 | offline_used の発生率を記録すること(読者がオフラインを試したか) |
| COST-04 | 記録した実測値を記事に掲載し、確認日を明記すること |
| COST-05 | 既製サービス・タブレットの価格は変動するため、この文書に金額を書き込まない。 記事執筆時点で公式ページを確認し、確認日を明記すること |
「このデモを触った読者の点検速度は、1項目あたり中央値 0.9秒でした。紙にレ点を書くのと同じか、それより速い」——この一文が、どんな機能説明よりも強い。
12. 実装タスク
この順に進める。フェーズを飛ばさない。 完了時は [x] に更新する。
Phase 0 — 基盤と計測・判定ロジック(4日)
0-1. 初期化
- Next.js 15 / TypeScript strict / App Router / Tailwind で初期化
- ESLint / Prettier / husky(pre-commit で lint + typecheck)
- 9章のディレクトリ構成を作成
- ローカル開発をHTTPSで行えるようにする(Service Worker・カメラの検証に必須)
0-2. lib/inspection/(純粋関数)
-
flow.ts:次の項目の決定、未判定の抽出、完了判定 -
validate.ts:必須・写真必須・数値範囲の検証 -
correction.ts:NG → 是正タスクの生成(7章のコード参照)(FR-401, FR-402, FR-407) -
recurring.ts:再発の検出(直近N回中M回以上)(FR-411) -
revision.ts:修正履歴の適用。元の値を保持したまま表示用の値を解決する(REC-01, REC-03) -
hash.ts:記録内容のハッシュ算出(REC-06) -
new Date()を内部で呼ばない。現在時刻は引数で受け取る - 単体テストを網羅的に書く。以下を必ず含めること:
- NG判定から是正タスクが例外なく生成されること(最重要)
- 点検時の写真が是正タスクの「対応前」に引き継がれること
- 修正履歴を追加しても、元の値が失われないこと(REC-01)
- 修正が複数回あった場合、履歴が積み上がり、最新の値が解決されること
- 再発の検出が閾値どおりに働くこと
- 未判定項目の抽出、N/A の扱い
- 数値の範囲外判定、前回値との差の警告
0-3. lib/metrics/(このデモの主張を支える)
-
session-timer.ts:所要時間・タップ数の計測(TAP-06) -
taps.ts:点検表の平均タップ数と改善提案(7章のコード参照)(FR-605, FR-606) -
duration.ts:所要時間の集計、紙との比較(FR-806) -
compliance.ts:実施率(FR-705) -
leadtime.ts:是正リードタイム(FR-414) - 単体テストを書く。以下を必ず含めること:
- 判定種別ごとの最小タップ数が正しく算出されること
- 選択肢4つ以上の select でタップ数が増えること
- 写真必須でタップ数が増えること
- 平均タップ数1.2超で警告が出ること、改善提案が付くこと
- 項目の追加・削除で推定所要時間が変わること
- 個人別の集計が返らないこと(FR-809)
0-4. 型とシード
-
lib/types/に7章の型定義をすべて実装 -
lib/seed/の全ファイル - 設備名・項目名・判定基準を、実在しそうな架空のものにする。ダミー文を1件も残さない(最重要)
- 所要時間・タップ数の実データを持たせる。テキスト項目が多い点検表は明確に遅く
- NG率を項目ごとに変える。消耗品系にNGを集中させる
- 再発している組み合わせを2〜3件、長期放置の是正を数件、修正ありの記録を数件、無効化された記録を1件作る
- 平均タップ数1.2超の点検表を1つ作る(FR-606 の警告が実際に出ること)
- 繰り返し未実施になっている設備を作る
- 点検記録はスケジュールの予定日に対応して存在すること
- 日付は現在日時からの相対で生成する
0-5. ストアとリポジトリ
-
lib/store/{session,data,draft,sync,ui}.tsを Zustand + persist で実装 -
lib/repo/_scope.tsを最初に実装する(SCP-01〜05) -
lib/repo/の全モジュール。全メソッド async、Result型、スコープ適用 - 確定結果を上書きするコードを書かない。修正は
revisionsへの追加のみ(REC-01) - 記録を削除するコードを書かない。無効化のみ(REC-05)
- 通信状態を見て操作をブロックするコードを書かない(OFF-01)
- スコープの単体テストをこの時点で書く:
- 現場担当が他拠点の記録を取得できないこと(SCP-03)
- 現場担当が確定記録を修正できないこと(SCP-04)
- 権限に関わらず修正履歴が残ること(SCP-05)
-
lib/storage/:IndexedDB(写真・署名) - 判定タップ・遷移・途中保存に擬似ディレイを入れない(FR-117)
0-6. デザインシステム
-
app/globals.cssに2系統のトークンを定義(点検画面=濃い地/管理画面=明るい地) -
tailwind.config.tsを拡張。8pxベースのスペーシング、タップ領域64px を既定に - フォント(Noto Sans JP 500/700 / Roboto Mono 600)を
next/fontで最適化 -
100dvhとセーフエリアの対応 - 点検画面用コンポーネントを自前実装(shadcn/ui を使わない)
- shadcn/ui は管理画面用に導入し、明るいトークンに合わせて上書き
- 共通コンポーネント:
OfflineBar(警告色にしない)/SyncBadge/EmptyState(2種の出し分け) -
prefers-reduced-motionの対応(押下時の色変化は残す) -
/dev/componentsにコンポーネントカタログを作成
0-7. デモ基盤
-
RoleSwitcher(3ロール + 現場担当の拠点選択)(FR-901) - デモ切替バー(明るいグレーの帯)+ 「これはデモです。点検記録は送信されません」(SEC-02)
- LAW-01 の注記コンポーネント(テンプレート管理画面・帳票出力画面に常設)
- 「デモをリセット」(FR-905)/ロール権限外アクセス時の案内画面(FR-908)
- 元記事リンク・資料ダウンロード(FR-910)
Phase 0 完了チェック
-
lib/inspection/lib/metrics/のテストが全パターン通る - 「NGから是正タスクが例外なく生成される」がテストで検証済み
- 「修正履歴を追加しても元の値が失われない」がテストで検証済み
- 「平均タップ数1.2超で警告と改善提案が出る」がテストで検証済み
- 「現場担当が確定記録を修正できない」がテストで検証済み
-
/dev/componentsで判定ボタンが画面40%以上を占めて表示される
Phase 1 — 点検の実施(5日・最重要フェーズ)
1-1. 判定ボタンと点検フロー
-
JudgeButtons.tsx:画面下部40%以上を占める判定ボタン(TAP-02, FR-102) - 2値/3値に対応。判定ラベル32px + 補足15px(8章)
- 押下で色変化 + 振動(対応端末)(IX-02)
-
ItemLabel.tsx:項目名28px + 判定基準 + 前回値(FR-106) -
ProgressHeader.tsx:進捗バー + 12/30(Mono 20px)(FR-111) -
useInspectionFlow.ts:判定後の自動遷移(TAP-03, FR-103) - 押したボタンの側から次項目がスライドイン(140ms、8章)
- 判定ごとに即座にローカル保存(IX-07, NFR-03)
- 判定タップから次項目表示まで100ms以下を実測(FR-117, NFR-01)
- スワイプでの移動(FR-110, IX-04)/前の項目に戻る(FR-113)
-
aria-liveでの判定通知(A11Y-09)
1-2. 各判定種別
-
NumberPad.tsx:自前テンキー(各キー64px以上)(TAP-04, FR-105) - 範囲外を入力時点で警告。
aria-live="assertive"(FR-105, A11Y-11) - 前回値との差の警告(FR-106)
- 選択肢3つ以下はボタンで並べる(TAP-05, FR-104)
- 選択肢4つ以上のみリスト表示(タップ数が増えることを管理画面で警告)
- テキスト入力(必須は最小限にする設計であることをテンプレート側で警告)
- N/A の1タップ設定と理由選択(FR-109)
1-3. NG詳細・写真・署名
-
NgDetailSheet.tsx:NG押下で下から展開。OK時には出さない(FR-107) - 症状はボタンで並べる(FR-108)
- 写真撮影:Canvas で圧縮(長辺1600px・品質80)→ IndexedDB(FR-201, FR-202)
- 複数枚(3枚まで)、削除(FR-204)
- 写真必須ルール(none / optional / required / required_on_ng)(FR-203)
- 写真の圧縮中も次の項目に進めること(NFR-04)
-
SignaturePad.tsx:Canvas での手書き署名(FR-207) - 署名必須の設定(FR-208)
1-4. 中断・完了
- 点検の途中状態を保存し、中断地点から再開(FR-311, F-I08)
- 中断中の点検一覧と破棄(FR-312)
- SC-015 未判定項目の確認、「まとめてN/A」(FR-112)
- SC-016 署名と完了
- SC-017 完了結果:所要時間・タップ数・1項目あたり・紙との比較(FR-116)
- 完了時に是正タスクを生成(FR-401)
- 記録内容のハッシュ算出(FR-506)
- SC-014 一覧モードへの切替(FR-114)
- SC-010 設備の選択 / SC-011 点検表の選択 / SC-001 ホーム(本日の予定を大きく)(FR-702)
1-5. 埋め込みモード
- SC-900 埋め込みモード:5項目の点検を実際に完了できる(FR-912, EMB-04)
- 完了時にタップ数・所要時間・紙との比較を表示(EMB-05)
- カメラ・位置情報を呼ばない(EMB-06)/アプリ内遷移をしない(EMB-07)
- 初期バンドル 55KB以下を実測(NFR-06)
Phase 1 完了チェック
- フローAが完走する(5項目 → 完了 → タップ数と秒数が出る → 全画面で30項目)
- 1項目1タップで判定できる。セレクトが1つも開かない
- 判定タップから次項目まで100ms以下
- 点検画面のバンドルが90KB以下(NFR-05)
- 画面を消しても点検が失われず、中断地点から再開できる
- 30項目を10回連続実施してもメモリが単調増加しない
Phase 2 — オフラインと同期(3日)
- Service Worker / PWA マニフェスト。オフラインでアプリが起動すること(FR-304, OFF-01)
- 設備・点検表定義の事前キャッシュ(FR-305)
-
OfflineBar:常時表示、警告色にしない、「点検は通常どおり行えます」(FR-306, OFF-04) -
SyncBadge:未同期件数の常時表示(FR-307, OFF-05) -
lib/offline/queue.ts:同期キュー - オンライン復帰時の自動同期 + 手動同期(FR-308)
- 同期失敗時に記録を失わない。キューに残して再試行(FR-309, OFF-06)
- 点検中に強制リロードを起こさない。更新は次回起動時に適用(FR-310, OFF-07, IX-08)
- 通信状態で操作をブロックしないことを、コードレビューで確認(FR-302)
- IndexedDB 容量上限の警告(記録本体は消さず、同期済み写真の削除を促す)(NFR-17)
- Service Worker のキャッシュに点検記録を含めないこと(SEC-07)
Phase 2 完了チェック
- フローCが完走する(機内モードで点検を開始から完了まで → オンライン復帰で自動同期)
- 機内モードでアプリが起動する
- オフライン表示が警告色になっていない
- 同期を失敗させても記録が消えない
Phase 3 — 是正の追跡(3日)
- SC-100 是正タスク一覧(未対応/対応中/承認待ち/完了)(FR-403)
- 期限超過・放置日数超の強調表示と通知(FR-410, F-C06)
- SC-102 担当者の割当・期限設定・通知プレビュー(FR-405)
- SC-030 自分の是正タスク(現場担当)
- SC-031 是正の実施:対応内容・部品・対応前/対応後の写真(FR-406)
- 点検時の写真が「対応前」に自動表示されること(FR-407)
-
BeforeAfterPhotos.tsx:左右に並べてラベルを明示 - SC-101 是正タスクの詳細:承認/差戻し(理由必須)(FR-408, FR-409)
- 連続処理(
A承認/R差戻し/→次)(IX-09) - 「対応不要」の理由必須(FR-404)
- SC-103 再発の検出一覧(FR-411)
- 次回点検時に前回の是正内容を表示(FR-412, フローB)
- SC-113 設備の点検・是正履歴(FR-413)
- SC-020 自分の点検履歴 / SC-021 点検記録の詳細
-
RevisionHistory.tsx:修正ありのバッジと、元の値 → 修正後 + 理由 + 修正者(FR-503)
Phase 3 完了チェック
- フローBが完走する(NG → 是正タスク自動生成 → 割当 → 実施 → 承認 → 次回点検で表示)
- 4章「ロールを跨ぐ体験」の1・2・3・6が動作する
- 承認するまで「完了」にならない
- 再発が検出され、強調表示される
Phase 4 — テンプレート・帳票・スケジュール(4日)
4-1. テンプレート管理
- SC-210 点検表テンプレート管理:セクション + 項目、D&D並び替え + キーボード操作(FR-601, FR-604, A11Y-07)
- SC-211 項目の編集(判定種別・必須・写真ルール・N/A・前回値・数値範囲)(FR-602, FR-603)
-
TapWarning.tsx:平均タップ数を常時表示。1.2超で警告 + 改善提案(FR-605, FR-606) - 項目の追加・削除で推定所要時間が即座に更新されること(FR-607)
- SC-213 現場視点のプレビュー(FR-608)
- テンプレート変更後も、過去の記録が当時の構造で表示されること(FR-614)
- 項目名にふりがな(FR-118, A11Y-13)
4-2. 帳票
- SC-212 帳票レイアウト設定(セクション順・列構成・押印欄・備考欄・ヘッダー)(FR-609)
- 3種のレイアウトテンプレート(横型チェックリスト/縦型帳票/マトリクス型)(FR-610)
-
lib/report/layout.ts:レイアウトの解決(純粋関数) -
lib/report/pdf.tsx:@react-pdf/renderer で生成(動的import) - SC-230 帳票PDF出力。期間・設備でまとめて出力。実際にダウンロードできること(FR-611)
- 写真の添付ページ(FR-612)
- 帳票に「修正あり」を明示(FR-613, REC-03)
- LAW-01 の注記が帳票出力画面に常設されていること
- PDF生成が3秒以下、進捗表示(NFR-08)
4-3. スケジュールと設備
- SC-220 点検スケジュール(日次/週次/月次/四半期/年次、担当者、期限)(FR-701)
- SC-110 点検実施状況グリッド(設備×日付、記号で区別)(FR-703, A11Y-05)
- リマインド送信(プレビュー表示)(FR-704)
- 実施率の算出(FR-705)
- SC-221 設備マスタ(FR-708)/QRコード発行・印刷(FR-615)
- QRコード読取(カメラは任意、キー入力を既定)(FR-616)
- SC-111 点検記録の一覧・絞り込み・全文検索(FR-706, FR-707)
- SC-112 点検記録の修正:上書きせず修正履歴を作る。理由必須(FR-501, FR-502)
- 記録の無効化(理由必須。削除は実装しない)(FR-505)
- SC-251 操作ログ(FR-508)
Phase 4 完了チェック
- 4章「ロールを跨ぐ体験」の4と5が動作する
- 帳票PDFが実際にダウンロードでき、押印欄・備考欄が含まれている
- 平均タップ数1.2超の点検表で警告と改善提案が出る
- 記録を修正しても元の値が残り、帳票に「修正あり」が出る
Phase 5 — 分析・記事統合・仕上げ(3日)
5-1. 分析
- SC-200 ダッシュボード(実施率/未是正/期限超過/再発/平均所要時間)(FR-801)
- SC-240 NG発生率と、NGが多い項目のランキング(FR-802, FR-803)
- SC-241 是正リードタイム(担当者別・設備別)(FR-804)
- SC-103 再発一覧(FR-805)
- SC-242 所要時間分析:点検表別の平均秒数・タップ数、紙との比較(FR-806)
- 個人別ランキングを作らないこと(FR-809)
- 期間指定(FR-807)/CSVエクスポート(FR-808)
- SC-250 担当者・チーム管理
5-2. 記事統合
- 記事側の埋め込みコードを作成し、実際の記事ページで動作確認(EMB-01〜07)
- 埋め込みで5項目の点検が完了でき、秒数とタップ数が出ることを複数ブラウザで確認
- 記事の Core Web Vitals を埋め込み前後で比較検証
- 「全画面で試す」カードの設置
- 資料PDF(点検表ヒアリングシート・損益分岐計算シート)の作成とダウンロード導線
- 元記事リンク・問い合わせ導線
- GA4 イベント設定。
seconds_per_itemtaps_per_itemoffline_usedを必ず含める(EMB-08, EMB-09) - GA4 に点検記録の内容・設備名を送っていないことを確認(EMB-10, SEC-03)
-
?embed=1をnoindexに、デモ本体の title / description / OGP(EMB-11) - 記事内に LAW-02 の注記を明示
5-3. 体験の総点検
- 4章「ロールを跨ぐ体験」の7パターンをすべて手動で確認
- 全30画面を開き、空の画面が1つもないことを確認
- 設備名・項目名・判定基準にダミー文が1件も残っていないことを確認
- 実機(手袋をした状態)で判定ボタンが押せることを確認
- 薄暗い場所と直射日光下での視認性を実機で確認
- ガイドツアーを実装(FR-911)
5-4. アクセシビリティ監査
- axe-core を全画面に実行し、Critical / Serious を0件に
- 判定ボタンのラベルのコントラスト比が7:1以上であることを実測(A11Y-01)
- 点検画面に15px未満の文字が存在しないことを確認(A11Y-02)
- タップ領域64px以上を全画面で確認(A11Y-03)
- 判定・実施状況が色のみで伝えられていないことを確認(A11Y-04, A11Y-05)
- キーボードのみで点検・承認・テンプレート管理を完遂(A11Y-06, A11Y-07)
- スクリーンリーダーで判定の記録が読み上げられることを確認(A11Y-09)
- フォントサイズ200%での確認(A11Y-14)
5-5. パフォーマンス
- 管理画面用ライブラリ(Recharts / TanStack Table / react-pdf / zxing)を動的import に分離
- 点検画面のバンドルが90KB以下、埋め込みが55KB以下を実測(NFR-05, NFR-06)
- 判定タップから次項目まで100ms以下を実測(NFR-01)
- オフライン起動が1.0秒以下を実測(NFR-02)
- 帳票PDF生成3秒以下、記録検索150ms以下を実測(NFR-08, NFR-09)
- 30項目×10回連続でメモリが単調増加しないことを計測(NFR-16)
- Lighthouse で Performance 95 / Accessibility 95(NFR-14)
5-6. 公開と記録
- Playwright で フローA・B・C の E2E(オフラインは Playwright の offline モードで)
- Vitest:
lib/inspectionlib/metricslib/report/layout_scopeのカバレッジ85%以上 -
/dev/componentsを本番で非公開に - Vercel へデプロイ(HTTPS必須)
-
seconds_per_itemtaps_per_itemの分布とoffline_usedの記録を開始(COST-01〜03) - 解説動画(3分)の収録:30項目を通しで点検(テンポの速さ)→ NGで是正タスク生成 → 機内モードで点検完了 → 帳票PDF出力 → 平均タップ数の警告
- 各フェーズのキャプチャを整理し、記事の「方法」パートの素材にまとめる
合計 22営業日(約4.5週間)/1名専任 + レビュー体制。
工数配分の意図: Phase 1(点検の実施)に5日を割いているのは、このアプリの価値が「1項目1タップ・100ms以下」に集約されているから。判定ボタンのサイズ、自動遷移、即座のローカル保存、テンキー、NG詳細の展開——どれ1つ欠けても「紙の方が速い」に戻る。 Phase 4(テンプレート・帳票)に4日を割いているのは、帳票の再現が要件の大半を占める領域だから(11章)。3種のレイアウトテンプレートと設定による調整までを、この工数で作る。
13. 受入基準
- 6章の優先度 P1 の要件がすべて実装され、テストで合格していること
- 5章の主要フローA・B・Cが、エンドツーエンドで完走すること
- 4章「ロールを跨ぐ体験」の7パターンがすべて動作すること
- 2値・3値判定が1タップで完了し、セレクトボックスが1つも使われていないこと
- 判定ボタンが画面高の40%以上を占めていること
- 判定タップから次項目の表示までが100ms以下で、擬似ディレイが入っていないこと
- 判定後に自動で次の項目に進むこと(設定でOFFにできること)
- 完了時に所要時間・タップ数・1項目あたりの値・紙との比較が表示されること
- テンプレート管理に平均タップ数が常時表示され、1.2超で警告と改善提案が出ること
- 点検の開始から完了まで、通信を一切必要としないこと。通信状態で操作がブロックされないこと
- オフラインでアプリが起動でき、機内モードで点検を完了できること
- 点検完了がローカル保存の時点で成立し、「同期中」が完了条件になっていないこと
- オフライン表示が警告色になっていないこと
- 同期に失敗しても記録が失われないこと
- 画面を消しても点検が失われず、中断地点から再開できること
- NG判定から是正タスクが例外なく生成され、点検時の写真が「対応前」に引き継がれること
- 是正が完了報告 → 承認の2段階であり、承認するまで完了にならないこと
- 次回点検時に、その項目の前回の是正内容が表示されること
- 再発(同一設備・同一項目の繰り返しNG)が検出され、強調表示されること
- 確定した点検結果の値が上書きされておらず、修正が履歴として保持され、元の値が参照できること
- 修正時に理由が必須であり、権限に関わらず履歴が残ること
- 記録の削除が実装されておらず、無効化(理由必須)のみであること
- 現場担当が確定記録を修正できず、他拠点の記録を取得できないこと
- 帳票PDFが実際にダウンロードでき、押印欄・備考欄・「修正あり」の表示が含まれること
- テンプレート変更後も、過去の記録が当時の構造で表示・出力されること
- 点検画面のJSバンドルが90KB以下、埋め込みが55KB以下であること
- 点検画面に15px未満の文字が存在せず、タップ領域が64px以上であること
- 判定ボタンのラベルのコントラスト比が7:1以上であること
- 判定・実施状況が色のみで伝えられていないこと
- 所要時間・タップ数の個人別ランキングが、どの画面にも存在しないこと
LAW-01の注記が、テンプレート管理画面と帳票出力画面に常設されていること- 設備名・項目名・判定基準にダミー文が1件も存在しないこと
- 点検記録・写真・設備名が外部に一切送信されていないこと(DevTools と GA4 のイベントで確認)
- 全30画面のいずれにも空の状態がなく、リアルなデータが表示されていること
- 30項目×10回連続実施でメモリが単調増加しないこと
- axe-core で Critical / Serious の指摘が0件であること
- データアクセスがすべて
lib/repo/を経由していること - 姉妹プロジェクトと並べたときに、明確に別のプロダクトとして見えること。特に FIELDPIN と混同されないこと
判断に迷ったときのルール
- 仕様がこの文書にない場合は、実装せずに質問する。 推測で作らない
- 紙より速いことを常に確認する。 機能を足すとき、1項目あたりのタップ数が増えるなら実装しない。これがこのプロジェクトで最も重要なルール
- セレクトボックスを点検画面で使わない。 選択肢は並べる
- 判定ボタンを小さくしない。 画面の40%以上。手袋・片手・見ずに押せること
- 通信状態で操作をブロックしない。 オフラインは正常な状態。警告色にしない
- 点検完了をローカル保存で成立させる。 「同期中」を完了条件にしない
- 確定した結果を上書きしない。 修正は履歴として追加する。権限で履歴の有無が変わらない
- 記録を削除しない。 無効化(理由必須)のみ
- NGから是正タスクを必ず生成する。 例外を作らない。記録するだけで放置される仕組みにしない
- 是正は承認まで完了にしない。 完了報告と承認を分ける
- 点検画面に重いライブラリを持ち込まない。 管理画面用は必ず動的import
- 所要時間・タップ数で人を評価させない。 個人別ランキングを作らない
- 法令適合を断定しない。 LAW-01 の注記を削らない
- 設備名・項目名・判定基準にダミー文を残さない。 点検表のデモは項目名のリアリティが説得力になる
- スコープ外(3章 Won't have)は実装しない。特に帳票の自由レイアウトエディタ・IoT連携・AI判定は範囲外
- 「デモだから」を理由に品質を落とす判断はしない
- FIELDPIN のトークン・コンポーネントをコピーしない。 TENKEN は濃い地・1画面1項目・巨大ボタン