HIBI — 要件定義書 兼 Claude Code 実装指示書
版数 1.0(DBレス構成 / 記事埋め込み型デモ)/ 作成日 2026-08-24 / 発行 ソウゾウ合同会社 用途 SEO記事「【デモで試せる】Claude Codeで日報アプリを自作する方法と費用|動画つき資料【2026年】」に組み込む実演デモ
このドキュメントの使い方(Claude Code へ)
これ1枚が、規約・仕様・タスクのすべて。リポジトリ直下に CLAUDE.md として置き、毎回読み込むこと。
- 実装は「12. 実装タスク」の順に進める。フェーズを飛ばさない。
- 各タスクの括弧内は要件ID。着手前に該当章を読むこと。
- 仕様がここに書かれていない場合は、推測で実装せず質問する。
- スコープ外(3章 Won't have)は、思いついても実装しない。
- 「デモだから」を理由に品質を落とす判断はしない。
- この領域だけの特別ルール:入力を増やす機能を提案しない。 日報アプリの失敗はすべて「書くのが面倒」から始まる。機能追加を思いついたら、まず「入力項目を増やさずに実現できるか」を考えること(1章)。
姉妹プロジェクトとの関係 HIREBASE(求人)/ RELATE(顧客管理)/ CASTA(動画配信)/ FIELDPIN(位置情報)/ KANADE(音楽)/ MIWAKE(画像認識)/ KAMIWAZA(美容室予約)/ KURA(在庫管理)/ TOKIWA(勤怠・シフト)に続くシリーズ。並べて「同じテンプレ」と思われた時点で失敗。 **RELATE / KURA / TOKIWA との差別化に注意。**4本とも「業務システム」だが、 RELATE=罫線と等幅数字で表を読ませる(藍・高密度)/ KURA=各行に在庫の水位バー(ティール+アンバー・縦長数字)/ TOKIWA=各行に24時間帯(インディゴ+コーラル・大きな時刻)/ HIBI=1画面1カラムの縦書き文章。テキストが主役。余白が広く、密度は最も低い。(墨+若草) HIBI だけは「読み書きするアプリ」であり、表を読ませない。 8章参照。
目次
- プロジェクト概要
- アーキテクチャ方針
- スコープ定義
- ロールとデモ切替
- 画面一覧とユーザーフロー
- 機能要件
- データ設計
- デザイン要件
- 技術要件・ディレクトリ構成
- 非機能要件
- 費用設計
- 実装タスク
- 受入基準
1. プロジェクト概要
背景と目的
「日報アプリを作りたい」という相談は、実装が最も簡単で、失敗率が最も高いという珍しい組み合わせを持つ。
作るのは簡単だ。フォームがあって、保存されて、一覧で見られればいい。1週間で作れる。
しかし3ヶ月後、誰も書いていない。
この領域には、最初に共有すべき事実が3つある。
事実①:日報アプリは「作れないから失敗する」のではない。続かないから失敗する
失敗の原因は技術ではなく、次の3つに集約される。
| 原因 | 実態 |
|---|---|
| 書くのが面倒 | 項目が多い、毎回ゼロから書く、スマホで書けない |
| 書いても読まれない | 上司が見ていない、反応がない、提出しても何も起きない |
| 何のために書くか分からない | 監視のために書かされている感覚。書く側に利益がない |
日報アプリの要件定義で最初に決めるべきは、画面でも項目でもない。 「誰が、何のために読むのか」である。 読み手が決まらなければ、書く項目も決まらない。
事実②:「日報」は3つの別の目的が混ざっている
多くの日報が破綻するのは、目的の違う情報を1つのフォームに詰め込むから。
| 目的 | 誰が読む | 必要な情報 | 頻度 |
|---|---|---|---|
| ① 報告(何をしたか) | 上司・チーム | 実施内容、進捗、時間 | 日次 |
| ② 相談(困っている) | 上司 | 課題、判断が必要なこと | 発生時 |
| ③ 蓄積(学びを残す) | 本人・後任・全社 | 気づき、ノウハウ、失敗の記録 | 週次でも十分 |
①を毎日、③を週次、②は発生時——本来はこの粒度が違う。 すべてを毎日書かせると、③が形骸化して「特になし」が並ぶ。
HIBI はこの3つを構造として分け、①は最小の手数で、②は目立つように、③は無理をさせない設計にする。
事実③:入力を減らす機能だけが、日報を生き残らせる
だから HIBI の機能はすべて「入力を減らす」か「読まれるようにする」のどちらかに寄与しなければならない。
| 入力を減らす | 読まれるようにする |
|---|---|
| テンプレート/前日のコピー | 一覧での既読・未読 |
| 選択式の項目(自由記述を最小に) | コメント・リアクション |
| 過去の実績からのサジェスト | 未提出者のリマインド |
| 下書きの自動保存 | チームのタイムライン |
| 音声入力の許容 | 週次のまとめ自動生成 |
| 提出はワンタップ | 読み手の返信を促す設計 |
入力項目を増やす機能は、原則として採用しない。 これがこのプロジェクトの設計方針であり、記事の主張でもある。
| 目的 | 内容 |
|---|---|
| 実装力の証明 | テンプレート・下書き・コメント・既読管理・集計・検索・オフラインまで、実運用に耐える構造を見せる |
| 期待値の正常化 | **「日報は作れる。続かないのが問題」**を実物で示す。入力の手数を数字で可視化する |
| 運用設計力の証明 | 提出率・返信率・入力時間をダッシュボードで示す。「運用が回っているか」を測れることを示す |
| 費用判断の材料 | 既製サービス・チャットツールとの比較、自作が正当化される条件を示す |
プロダクト定義
| 項目 | 内容 |
|---|---|
| プロダクト名 | HIBI(日々)※仮称 |
| 一言定義 | 書く手数を最小にし、読まれる仕組みを持たせた日報・週報アプリ |
| 想定業態 | 営業チーム、現場作業(建設・設備)、店舗、士業、開発チーム、教育・研修 |
| 提供形態 | レスポンシブWebアプリ(PWA)。フロントエンド完結(サーバー側の永続化なし) |
| 主戦場 | 書き手はスマホ(現場で、移動中に、就業直前に書かれる)。読み手はPC |
| 想定利用者 | 記事の読者。登録なしで、書き手側と読み手側の両方を体験できる |
プロダクトコンセプト
「書く時間を、90秒に。」
HIBI は日報の入力時間を計測し、画面に表示する。テンプレート、前日のコピー、選択式項目、サジェスト——すべてはその秒数を縮めるためにある。そして書かれた日報には必ず読み手の痕跡(既読・コメント)が残る。書くのが速く、読まれるなら、日報は続く。
デモとしての成功条件
- 入力時間が見える — 日報を書くと「入力 1分12秒」と表示され、平均と比較される
- 手数が減る体験ができる — 「前日をコピー」を押すと、大半が埋まった状態から始まる
- 3つの目的が分かれている — 報告・相談・蓄積が別のブロックとして構造化されている
- 読まれた痕跡が残る — 既読者のアバター、コメント、リアクションが日報に付く
- 運用が測れる — 提出率・返信率・平均入力時間がダッシュボードに出る
- ロールで見え方が変わる — メンバーは自分とチーム、管理者は全社。個人の評価情報は分離
- リセットできる — 誰が触った後でも初期状態に戻せる
2. アーキテクチャ方針
基本方針
バックエンドとデータベースを持たない。すべてブラウザ内で完結させる。
| 一般的な構成 | 本プロジェクト |
|---|---|
| PostgreSQL | シードデータ + ブラウザ内ストア |
| 認証・SSO | デモ用ロール切替(ワンクリック) |
| REST / GraphQL API | ストア上の同期的な操作 |
| メール・Slack・LINE通知 | 送信内容のプレビュー表示で再現 |
| 全文検索(Elasticsearch等) | ブラウザ内の全文検索(自前実装) |
| ファイルストレージ | IndexedDB(画像は端末内) |
| 入力時間の計測 | これは本物。 実際に計測して表示する |
| 週次まとめの自動生成 | これも本物。 ルールベースで実際に組み立てる |
なぜこの構成にするか
- LPデモとして最適 — 訪問者は登録もログインもせずに、全機能を即座に触れる
- 書いた内容が外に出ない — 日報は業務の機密。データを預からないことが訴求になる
- 開発速度 — 認証・APIの実装が不要になり、入力体験と読まれる仕組みの作り込みに全時間を投下できる
- 運用コストゼロ — Vercel の静的配信のみ
入力時間の計測(このプロジェクト固有の中核)
「書く時間を90秒に」を主張するなら、それを測らなければならない。
入力セッションの計測
開始:新規作成 or 編集画面を開いた瞬間
一時停止:タブが非表示になった/30秒以上操作がない
再開:操作が再開された
終了:提出 or 離脱
→ activeSeconds(実際に手を動かしていた秒数)を記録
| ID | 規約 |
|---|---|
| MEA-01 | 入力時間は「アクティブな秒数」を計測すること。 タブを離れていた時間、30秒以上の無操作は含めない |
| MEA-02 | 計測を lib/metrics/input-timer.ts に集約し、コンポーネント側でタイマーを持たないこと |
| MEA-03 | 計測していることを書き手に隠さないこと。 入力中に経過時間を控えめに表示する(監視ではなく、短さの実感のため) |
| MEA-04 | 計測結果を個人の評価に使わせない設計にすること。 管理者画面ではチーム平均と分布のみを表示し、個人別のランキングを作らない(4章 SCP-04) |
| MEA-05 | 計測は下書き保存・提出のたびに累積すること(複数回に分けて書くケースに対応) |
MEA-04 は重要。 入力時間を個人評価に使えるようにすると、日報アプリが監視ツールになり、事実①の「監視のために書かされている感覚」を強化してしまう。測るのは運用改善のためであり、人を測るためではない。
週次まとめの自動生成(入力を減らす機能の核)
事実②で「③蓄積は週次でよい」と書いた。だが週報を別途書かせると入力が増える。だから日報から自動で組み立てる。
週次まとめの生成(ルールベース。AIを使わない)
① その週の日報を収集
② 「実施内容」から、同じタグ・案件のものをグループ化
③ 「進捗」の数値項目を合算
④ 「課題・相談」を未解決のものだけ抽出
⑤ 「気づき」をそのまま列挙
⑥ 未提出日を明示
→ 下書きとして提示し、本人が追記・修正して提出
| ID | 規約 |
|---|---|
| SUM-01 | 週次まとめはルールベースで生成すること。 外部AIを呼ばない(キー管理が不要/データが外に出ない) |
| SUM-02 | 生成結果は必ず下書きとし、本人が確認・修正してから提出すること。 自動提出しない |
| SUM-03 | 生成に使った日報をリンクで辿れるようにすること(根拠の可視化) |
| SUM-04 | 生成できなかった項目を明示すること(「今週は気づきの記入が0件でした」) |
| SUM-05 | 生成ロジックを lib/summary/ に集約し、純粋関数とすること |
差し替え可能性の担保
データアクセスは必ず lib/repo/ のリポジトリ層を経由すること。 コンポーネントからストアを直接触らない。
[ コンポーネント ]
↓ 呼ぶのはこの層だけ
[ lib/repo/*.ts ] ← 全メソッドを async にしておく
↓
[ lib/search/ ] ← 全文検索(純粋関数)
[ lib/summary/ ] ← 週次まとめ生成(純粋関数)
[ lib/metrics/ ] ← 提出率・返信率・入力時間(純粋関数)
[ lib/store/*.ts ] ← Zustand + persist(localStorage)
↓
[ lib/seed/*.ts ] ← 初期データ
リポジトリの各メソッドは、中身が同期処理でも async で定義し、await で呼ぶ。実案件へ転用する際、リポジトリの実装だけをAPI呼び出しに差し替えれば、UI層は一行も変更せずに済む。
記事への組み込み
| 段 | 配置 | 中身 |
|---|---|---|
| ① 記事内インライン | 記事の冒頭〜中盤、<iframe> |
日報の入力フォーム + 入力時間タイマー + 「前日をコピー」ボタン。 実際に打てて、秒数が出る |
| ② 全画面デモ | 「全画面で試す」カード → 別タブ | アプリ全体(タイムライン・コメント・週次まとめ・提出率ダッシュボード) |
記事内で見せるのは「書くのがどれだけ速いか」だけに絞る。 これが事実③そのものであり、スクロール中に体験できる唯一のことになる。
| ID | 要件 |
|---|---|
| EMB-01 | 記事内 iframe は loading="lazy" とし、ビューポート接近まで読み込まないこと |
| EMB-02 | 埋め込みモードのJS初期バンドルは 60KB以下(gzip後)とすること |
| EMB-03 | 埋め込み iframe が記事の LCP 要素にならないこと |
| EMB-04 | 埋め込みモードで実際に入力でき、入力時間が計測されて表示されること(これが主役) |
| EMB-05 | 「前日をコピー」を押すと、大半が埋まった状態になり、残り入力が明示されること(「あと2項目」) |
| EMB-06 | 埋め込みモードで提出はできること。ただしアプリ内遷移はせず、「全画面で続きを見る」を target="_blank" で提示すること |
| EMB-07 | 埋め込みモードでカメラ・位置情報を呼ばないこと |
| EMB-08 | GA4 で計測すること:demo_open / report_submitted(提出した)/copy_yesterday_used(前日コピーを使った)/input_seconds(入力秒数の分布)/comment_posted / summary_generated / role_switch / doc_download / form_click / line_click |
| EMB-09 | input_seconds は最重要指標。 読者が実際に何秒で書けたかの分布は、記事の主張を裏付ける実データになる |
| EMB-10 | 入力された日報の本文を GA4 に送らないこと。 送るのは秒数・項目数などのメタ情報のみ |
| EMB-11 | ?embed=1 のURLは noindex とすること。デモ本体は固有の title / description を持つこと |
永続化の範囲
| 対象 | 挙動 |
|---|---|
| シードデータ(メンバー・チーム・テンプレート・過去3ヶ月の日報・コメント) | 初期投入。テンプレートは編集可能 |
| 日報・下書き・コメント・リアクション・既読・週次まとめ | localStorage に保存。リロードしても残る |
| 入力時間の計測結果 | localStorage に保存 |
| 添付画像 | IndexedDB(Blob)。サーバーには送らない |
| リセット | 「デモをリセット」で localStorage / IndexedDB をクリア |
擬似ディレイ は 150〜400ms。ただし入力・下書き保存・提出・検索は 100ms以下とし、ディレイを入れない(入力でもたつくのは、このアプリで最も致命的な体験)。
3. スコープ定義
書く(メンバー)
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-W01 | 日報の作成・提出 | テンプレートに沿った入力。提出はワンタップ | Must |
| F-W02 | 前日をコピー | 前回の内容を引き継いで開始。変わった部分だけ直す | Must |
| F-W03 | 入力時間の表示 | 入力中の経過時間と、提出後の所要時間・平均との比較 | Must |
| F-W04 | 下書きの自動保存 | 入力停止から2秒後に保存。アプリを閉じても失われない | Must |
| F-W05 | 選択式項目 | 案件・作業種別・進捗ステータスは選択式(自由記述を最小に) | Must |
| F-W06 | サジェスト | 過去の入力から、案件名・作業内容・よく使う語句を候補表示 | Must |
| F-W07 | 課題・相談ブロック | 報告と分離。入れると読み手側で目立つ | Must |
| F-W08 | 気づき・学びブロック | 任意入力。空でも提出できる(無理をさせない) | Must |
| F-W09 | 数値項目 | 訪問件数・作業時間・進捗率など。テンプレートで定義 | Must |
| F-W10 | 画像添付 | 現場写真など。端末内で圧縮 | Should |
| F-W11 | 音声入力の許容 | ブラウザの音声入力を妨げないこと(専用実装はしない) | Should |
| F-W12 | 提出後の編集 | 一定時間内(既定30分)は自分で編集可。以降は編集履歴が残る | Must |
| F-W13 | 未提出のリマインド | 提出時刻を過ぎたら通知(プレビュー表示で再現) | Must |
| F-W14 | オフライン作成 | 圏外でも書けて、復帰時に同期 | Should |
| F-W15 | 週次まとめの生成 | 日報から自動生成した下書きを、確認・修正して提出(2章) | Must |
読む・返す(メンバー・上司)
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-R01 | チームのタイムライン | 日付順のカード。未読が明確。フィルタ(メンバー・案件・課題あり) | Must |
| F-R02 | 日報の詳細 | 本文、数値、画像、既読者、コメント、リアクション | Must |
| F-R03 | コメント | 返信。メンション。書き手に通知 | Must |
| F-R04 | リアクション | 1タップの反応(確認した/いいね/助かった)。コメントより軽い反応の道を用意 | Must |
| F-R05 | 既読管理 | 誰が読んだかを日報に表示。書き手に「読まれた」が見える | Must |
| F-R06 | 課題の一覧 | 「課題・相談」がある日報だけを抽出。未対応/対応中/解決 | Must |
| F-R07 | 全文検索 | 本文・コメント・案件名を横断検索。期間・メンバーで絞り込み | Must |
| F-R08 | 個人のタイムライン | 特定メンバーの日報を時系列で。成長の記録として読む | Should |
| F-R09 | 通知センター | コメント、メンション、未読、未提出 | Must |
| F-R10 | 日報のエクスポート | 期間指定でCSV/Markdown出力 | Should |
運用を測る・整える(管理者)
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-A01 | 運用ダッシュボード | 提出率・返信率・平均入力時間・未読率。運用が回っているかを測る | Must |
| F-A02 | 提出状況 | メンバー×日付のグリッド。提出済/未提出/遅延 | Must |
| F-A03 | 返信率 | 上司ごとの返信率・平均返信時間。読まれていないチームを検出 | Must |
| F-A04 | 入力時間の分布 | チーム平均、分布、テンプレート別。個人別ランキングは作らない(MEA-04) | Must |
| F-A05 | テンプレート管理 | 項目の追加・削除・並び替え・必須設定。項目数と推定入力時間を表示 | Must |
| F-A06 | 提出ルールの設定 | 提出期限、対象曜日、リマインド時刻、編集可能時間 | Must |
| F-A07 | チーム・メンバー管理 | チーム編成、上司の割当、権限 | Must |
| F-A08 | 案件・作業種別マスタ | 選択式項目の選択肢 | Must |
| F-A09 | 集計・レポート | 数値項目の期間集計、案件別、メンバー別 | Should |
| F-A10 | 通知文面の設定 | リマインド・コメント通知の文面 | Should |
| F-A11 | 操作ログ | 誰がいつ何を編集・削除したか | Should |
共通・基盤
| ID | 機能 | 概要 | 優先度 |
|---|---|---|---|
| F-C01 | ロール切替 | メンバー/上司(チームリーダー)/管理者 をワンクリック切替 | Must |
| F-C02 | 埋め込みモード | ?embed=1 で入力フォーム + タイマー(2章) |
Must |
| F-C03 | 記事への導線 | 元記事へのリンク、資料ダウンロード、問い合わせ | Must |
| F-C04 | デモリセット | localStorage / IndexedDB をクリア | Must |
| F-C05 | PWA | ホーム画面追加、オフライン起動 | Should |
| F-C06 | ガイドツアー | 初回訪問時に「何を試せるか」を3ステップで案内 | Should |
対象外(Won't have)
| 項目 | 理由 |
|---|---|
| データベース・バックエンドAPI | 2章の方針に基づく |
| 本物の認証・SSO | ロール切替で代替 |
| 実際のメール・Slack・LINE送信 | プレビュー表示で再現 |
| AIによる日報の自動要約・添削 | 週次まとめはルールベースで実装(SUM-01)。AI連携は11章で費用論点として扱う |
| Slack / Teams / チャットツールとのAPI連携 | 11章で「そもそもチャットで足りるか」の論点として扱う |
| 勤怠・工数管理としての利用 | 日報に工数を書かせるのは入力増になる。 TOKIWA(勤怠)の領域 |
| 承認ワークフロー(多段承認) | 日報に承認は不要。 コメントと既読で足りる。要望が出たら別途見積 |
| 人事評価との連携・評価スコア化 | 意図的に対象外(MEA-04 の方針。監視ツール化を避ける) |
| 個人別の入力時間ランキング | 意図的に対象外(MEA-04) |
| 多言語UI | 日本語のみ |
| ネイティブアプリ | PWAで対応 |
スコープリスク: 日報は「あれも書かせたい」が最も起きやすい領域。項目追加の要望は必ず来る。 そのとき「項目を1つ増やすと、入力時間が何秒増え、提出率が何%下がるか」を議論できる状態を作ることが、このプロジェクトの目的。F-A05 のテンプレート管理画面に「項目数と推定入力時間」を表示させているのはこのため。
4. ロールとデモ切替
ロール定義
| ロールID | 名称 | 見えるデータ | 特徴的な権限 |
|---|---|---|---|
member |
メンバー | 自分の日報と、所属チームの日報 | 作成・提出・編集(時間内)、コメント、リアクション |
leader |
上司(チームリーダー) | 自チーム全員 | 上記 + チームの提出状況・返信率の確認、リマインド送信 |
admin |
管理者 | 全社 | 上記 + テンプレート管理・提出ルール・全社ダッシュボード・操作ログ |
データスコープと項目スコープ
| ID | 要件 |
|---|---|
| SCP-01 | データスコープ(見える行)と項目スコープ(見える列)を、ともに lib/repo/_scope.ts に集約すること |
| SCP-02 | コンポーネント側で role === 'member' のような分岐を書かないこと |
| SCP-03 | メンバーは他チームの日報を一切取得できないこと。 リポジトリが空配列を返すこと |
| SCP-04 | 個人別の入力時間を、本人以外に返さないこと。 上司・管理者にはチーム平均と分布のみを返す(MEA-04) |
| SCP-05 | 「課題・相談」ブロックは、書き手が「上司のみ」に限定できること。 限定された場合、チームの他メンバーには表示しないこと |
SCP-04 と SCP-05 が、このデモの倫理設計。 日報アプリは容易に監視ツールになるため、データの返し方の段階で防ぐ。
ロールを跨ぐ体験(必ず動くようにする)
- メンバーで日報を書いて提出 → 上司に切替 → タイムラインに未読として現れ、課題ありのバッジが付いている
- 上司がコメントする → メンバーに切替 → 通知が来ており、日報に既読者とコメントが付いている
- メンバーが「前日をコピー」で書く → 入力時間が前回より短くなり、「前回より38秒短縮」と表示される
- 上司が返信をサボる → 管理者に切替 → 返信率ダッシュボードでそのチームの返信率が下がっている
- 管理者がテンプレートに項目を3つ追加 → メンバーに切替 → 入力項目が増え、推定入力時間が伸びている
- メンバーが週次まとめを生成 → その週の日報から自動で組み立てられ、根拠の日報にリンクが張られている
- メンバーが「課題・相談」を上司のみに限定 → 別のメンバーに切替 → その部分が表示されていない
3番と5番が、このデモの核心。 事実③(入力を減らす機能だけが生き残らせる)が体験として成立する瞬間。
デモ切替バー(F-C01)
画面上部に常時表示する固定バー。埋め込みモードでは非表示。
- 現在のロールと氏名・チームの表示、3ロールの切替
- メンバーロール時:3名から操作主体を選択(毎日書く人/たまに書かない人/新人 を用意)
- デモ内の現在日時(シードが相対日付のため基準を示す)
- 「デモをリセット」ボタン(確認ダイアログ付き)
- 元記事へ戻るリンク
- 「これはデモです。書いた内容は送信されません」の明示
デザイン上の扱い: プロダクト本体が明るく余白の広い読み書きのUIなので、切替バーは濃い墨色の帯にして明確に区別する。
5. 画面一覧とユーザーフロー
全28画面。画面IDはディレクトリ構成と 1:1 で対応させる。
書く(メンバー)
| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-001 | ホーム | / |
今日の日報(未提出なら大きく促す)、未読件数、コメント通知、今週のまとめ状況 |
| SC-010 | 日報の作成 | /reports/new |
テンプレート項目、入力時間タイマー、前日コピー、下書き自動保存 |
| SC-011 | 日報の編集 | /reports/[id]/edit |
同UI。編集可能時間の残りを表示 |
| SC-012 | 提出完了 | モーダル | 入力時間と平均との比較、次のアクション(チームを読む) |
| SC-013 | 下書き一覧 | /reports/drafts |
書きかけの日報、最終編集日時 |
| SC-020 | 自分の日報一覧 | /reports/mine |
時系列カード、提出状況、既読数、コメント数 |
| SC-021 | 日報の詳細(自分) | /reports/[id] |
本文、数値、画像、既読者のアバター、コメント、リアクション |
| SC-030 | 週次まとめの生成 | /weekly/new |
生成された下書き、根拠の日報へのリンク、追記・修正、提出 |
| SC-031 | 週次まとめ一覧 | /weekly |
過去の週報、提出状況 |
| SC-040 | 通知センター | /notifications |
コメント、メンション、未読、未提出リマインド |
| SC-900 | 埋め込みモード | /?embed=1 |
入力フォーム + タイマー + 前日コピー(2章) |
読む・返す
| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-100 | チームのタイムライン | /team |
日付順カード、未読の明示、フィルタ(メンバー/案件/課題あり/未読のみ) |
| SC-101 | 日報の詳細(他人) | /team/reports/[id] |
本文、課題ブロックの強調、既読者、コメント欄、リアクション |
| SC-102 | 課題の一覧 | /team/issues |
課題・相談がある日報の抽出。未対応/対応中/解決、経過日数 |
| SC-103 | メンバーのタイムライン | /team/members/[id] |
特定メンバーの日報を時系列で。提出率、最近の傾向 |
| SC-110 | 検索 | /search |
全文検索、期間・メンバー・案件で絞り込み、ハイライト表示 |
| SC-120 | エクスポート | /export |
期間指定、CSV/Markdown、プレビュー |
運用を測る・整える(上司・管理者)
| 画面ID | 画面名 | パス | 主要要素 |
|---|---|---|---|
| SC-200 | 運用ダッシュボード | /admin |
提出率・返信率・平均入力時間・未読率の4指標と推移 |
| SC-201 | 提出状況 | /admin/submissions |
メンバー×日付グリッド、提出済/未提出/遅延、リマインド送信 |
| SC-202 | 返信率 | /admin/response |
上司別の返信率・平均返信時間、返信されていない日報の抽出 |
| SC-203 | 入力時間の分布 | /admin/input-time |
チーム平均、ヒストグラム、テンプレート別。個人別ランキングなし |
| SC-210 | テンプレート管理 | /admin/templates |
項目の追加・削除・並び替え・必須設定、項目数と推定入力時間の表示 |
| SC-211 | テンプレートのプレビュー | モーダル | 書き手から見える実画面 |
| SC-220 | 提出ルールの設定 | /admin/settings/rules |
提出期限、対象曜日、リマインド時刻、編集可能時間 |
| SC-221 | チーム・メンバー管理 | /admin/members |
チーム編成、上司の割当、権限 |
| SC-222 | 案件・作業種別マスタ | /admin/masters |
選択式項目の選択肢、並び順 |
| SC-223 | 通知文面の設定 | /admin/settings/notifications |
リマインド・コメント通知の文面 |
| SC-230 | 数値集計レポート | /admin/reports |
数値項目の期間集計、案件別、メンバー別 |
| SC-240 | 操作ログ | /admin/logs |
操作者・日時・対象・内容の検索 |
主要フロー
フローA:記事の読者が「書く速さ」を体験する(最重要)
SEO記事を読んでいる
↓
記事内の埋め込み(SC-900)
┌────────────────────────────────────────────────────────┐
│ 2026-08-24(月)の日報 ⏱ 0:00 │
│ │
│ 案件 [ ▼ 選択してください ] │
│ 作業種別 [ ▼ 選択してください ] │
│ 進捗 [ ○順調 ○やや遅れ ○遅延 ] │
│ 実施内容 [ ] │
│ 課題・相談 [ ] (任意) │
│ 気づき [ ] (任意) │
│ │
│ [ 前日をコピー ] [ 提出する ] │
└────────────────────────────────────────────────────────┘
↓
何か打ち始める
↓ ★ タイマーが動き出す(⏱ 0:07 …)
「前日をコピー」を押す
↓ ★ 案件・作業種別・実施内容の大半が埋まる
★ 「あと2項目で提出できます」と表示される
↓
残りを埋めて「提出する」
↓
┌────────────────────────────────────────────────────────┐
│ 提出しました │
│ │
│ 入力時間 1分12秒 │
│ │
│ 前日をコピーしなかった場合の平均:3分48秒 │
│ → 2分36秒 短縮できました │
│ │
│ [ 全画面でチームの日報を読む ] │
└────────────────────────────────────────────────────────┘
↓
「全画面で試す」→ 別タブ
↓
SC-100 チームのタイムライン ── ★ 8名の日報が並び、未読が明確
↓
SC-101 日報の詳細 ── コメントを書いてみる
↓
ロール切替「管理者」→ SC-203 入力時間の分布
↓ ★ チーム平均 2分14秒。前日コピー利用時は 1分22秒
SC-210 テンプレート管理 ── ★ 項目を3つ追加すると推定入力時間が +45秒 と表示される
↓
資料ダウンロード or 元記事に戻る
フローB:書いたら読まれる(事実①の「読まれない」を潰す)
【メンバー:田中】SC-010 日報の作成
実施内容を書く
★ 「課題・相談」に「A社の見積、値引き幅の判断をお願いしたいです」と書く
★ 公開範囲を「上司のみ」に設定(SCP-05)
↓
提出
↓
【上司】SC-100 チームのタイムライン
★ 田中の日報カードに「未読」と「課題あり」のバッジが付いている
★ 課題ありは他のカードより視覚的に前に出る
↓
SC-101 日報の詳細
★ 「課題・相談」ブロックが強調表示され、上司のみに見えている旨が表示される
↓
コメントを書く:「値引きは8%まで可。それ以上は要相談」
↓
【メンバー:別の人】SC-100 タイムライン
★ 田中の日報は読めるが、「課題・相談」ブロックは表示されない(SCP-05)
↓
【メンバー:田中】SC-001 ホーム
★ 通知:「上司さんがコメントしました」
↓
SC-021 日報の詳細
★ 既読者のアバターが3名分ついている
★ コメントが付いている
★ リアクション「確認した」が2件
↓
★ ここで伝わること:
「書いたら、読まれた痕跡が返ってくる。だから次も書く」
フローC:運用が回っているかを測る
【管理者】SC-200 運用ダッシュボード
提出率 87% (先週 91% ▼)
返信率 42% (先週 58% ▼)★ 悪化
平均入力時間 2分14秒
未読率 31% ★ 高い
↓
★ 「提出率は高いが、返信率が下がり未読が溜まっている」
→ 書かせているのに読んでいない状態
↓
SC-202 返信率
★ 上司別に表示
山田(営業1課) 返信率 71% / 平均返信 4時間
佐藤(営業2課) 返信率 18% / 平均返信 3日 ★ 赤で強調
★ 「返信されていない日報 24件」の一覧へ
↓
SC-201 提出状況 ── メンバー×日付のグリッド
★ 特定の曜日(金曜)に未提出が集中している
★ リマインド時刻が18:00だが、金曜は早く帰るため間に合っていない
↓
SC-220 提出ルールの設定
★ 金曜のリマインド時刻を16:00に変更
↓
SC-210 テンプレート管理
★ 項目数 9個/推定入力時間 3分20秒 と表示されている
★ 「気づき」を必須から任意に変更 → 推定 2分40秒 に
↓
【メンバー】★ 入力項目が減り、提出しやすくなっている
↓
(1週間後)SC-200 ダッシュボード
★ 提出率 87% → 94%、返信率 42% → 65%
↓
★ ここで伝わること:
「日報アプリは作って終わりではない。運用を測って、調整して初めて回る」
6. 機能要件
各要件はテスト仕様書の項目と1:1で対応する。「〜できること」の粒度で、判定可能な形で記述している。
6.1 入力体験(このデモの心臓部)
| ID | 要件 | 優先 |
|---|---|---|
| FR-101 | 入力時間を計測すること。 タブが非表示の時間、30秒以上の無操作は除外すること(MEA-01) | P1 |
| FR-102 | 計測を lib/metrics/input-timer.ts に集約し、コンポーネント側でタイマーを持たないこと(MEA-02) |
P1 |
| FR-103 | 入力中に経過時間を控えめに表示すること(MEA-03)。大きく表示して急かさないこと | P1 |
| FR-104 | 提出完了時に、入力時間とチーム平均・自分の平均との比較を表示すること | P1 |
| FR-105 | 複数回に分けて書いた場合、入力時間を累積すること(MEA-05) | P1 |
| FR-106 | 「前日をコピー」で、前回提出した内容を引き継いで開始できること。 引き継ぐ項目はテンプレートごとに設定できること | P1 |
| FR-107 | 前日コピー後、「あと N項目で提出できます」と残りを明示すること | P1 |
| FR-108 | 下書きを入力停止から2秒後に自動保存すること。 保存状態を画面上で示すこと | P1 |
| FR-109 | アプリを閉じて再訪しても、下書きが復元されること | P1 |
| FR-110 | 選択式項目(案件・作業種別・進捗)を提供し、自由記述を最小にすること | P1 |
| FR-111 | サジェスト:過去の入力から、案件名・作業内容・よく使う語句を候補表示すること | P1 |
| FR-112 | 提出はワンタップとし、確認ダイアログを出さないこと(提出後の編集で担保する) | P1 |
| FR-113 | 必須項目が未入力の場合、提出ボタンの直上に不足項目を列挙すること(押してからエラーを出さない) | P1 |
| FR-114 | 数値項目をテンプレートで定義でき、キーボードがテンキーで開くこと(inputMode="numeric") |
P1 |
| FR-115 | 画像を添付でき、端末内で圧縮(長辺1600px・JPEG品質80)してから保持すること | P2 |
| FR-116 | ブラウザの音声入力を妨げないこと(テキストエリアに独自のキーハンドラを被せない) | P2 |
| FR-117 | 提出後、既定30分間は自分で編集できること。 それ以降の編集は履歴を残すこと | P1 |
| FR-118 | オフラインでも作成・下書き保存・提出ができ、復帰時に同期すること | P2 |
| FR-119 | 入力・下書き保存・提出が 100ms以下で完了すること。擬似ディレイを入れないこと | P1 |
6.2 日報の構造(3つの目的の分離)
| ID | 要件 | 優先 |
|---|---|---|
| FR-201 | 日報を**「報告」「課題・相談」「気づき・学び」の3ブロックで構造化すること**(1章 事実②) | P1 |
| FR-202 | 「課題・相談」は空でも提出でき、入力があると読み手側で強調表示されること | P1 |
| FR-203 | 「気づき・学び」は任意入力とし、空での提出を妨げないこと。 催促の文言を出さないこと | P1 |
| FR-204 | 「課題・相談」の公開範囲を「チーム全体/上司のみ」から選べること(SCP-05) | P1 |
| FR-205 | 「上司のみ」を選んだ場合、チームの他メンバーには当該ブロックを表示しないこと。 リポジトリの段階で除外すること | P1 |
| FR-206 | 「課題・相談」に対応状態(未対応/対応中/解決)を持たせ、読み手側で更新できること | P1 |
| FR-207 | テンプレートの項目種別として、テキスト/長文テキスト/単一選択/複数選択/数値/日付/チェックボックス/画像 に対応すること | P1 |
| FR-208 | 項目ごとに、必須/任意、前日コピーの対象/非対象 を設定できること | P1 |
6.3 週次まとめ(入力を減らす機能の核)
| ID | 要件 | 優先 |
|---|---|---|
| FR-301 | その週の日報から、週次まとめの下書きを自動生成すること(2章 SUM) | P1 |
| FR-302 | 生成はルールベースで行い、外部AIを呼ばないこと(SUM-01) | P1 |
| FR-303 | 生成内容:実施内容のグループ化/数値項目の合算/未解決の課題の抽出/気づきの列挙/未提出日の明示 | P1 |
| FR-304 | 生成結果は必ず下書きとし、本人が確認・修正してから提出すること。自動提出しないこと(SUM-02) | P1 |
| FR-305 | 生成に使った日報へのリンクを表示すること(根拠の可視化)(SUM-03) | P1 |
| FR-306 | 生成できなかった項目を明示すること(「今週は気づきの記入が0件でした」)(SUM-04) | P1 |
| FR-307 | 週次まとめの生成ロジックを lib/summary/ に集約し、純粋関数とすること(SUM-05) |
P1 |
| FR-308 | 週の起算曜日を設定できること | P2 |
| FR-309 | 週次まとめにも既読・コメントが付けられること | P2 |
6.4 読む・返す
| ID | 要件 | 優先 |
|---|---|---|
| FR-401 | チームのタイムラインを日付順のカードで表示し、未読を明確に区別すること | P1 |
| FR-402 | フィルタを提供すること(メンバー/案件/課題あり/未読のみ/期間) | P1 |
| FR-403 | 課題ありの日報を、視覚的に前に出すこと(バッジ + 枠線)。件数をタイムライン上部に表示すること | P1 |
| FR-404 | 既読を記録し、日報詳細に既読者のアバターを表示すること。 書き手からも見えること | P1 |
| FR-405 | 既読は日報を一定時間(既定3秒)表示したときに記録すること。 スクロールで通過しただけで既読にしないこと | P1 |
| FR-406 | コメントを投稿でき、書き手に通知されること(通知はプレビュー表示で再現) | P1 |
| FR-407 | コメントでメンションでき、対象者に通知されること | P2 |
| FR-408 | リアクション(確認した/いいね/助かった)を1タップで付けられること。 コメントより軽い反応の道を用意すること | P1 |
| FR-409 | SC-102 課題の一覧で、課題がある日報だけを抽出し、対応状態と経過日数を表示すること | P1 |
| FR-410 | 未対応のまま一定日数(既定3日)経過した課題を強調表示すること | P1 |
| FR-411 | SC-103 メンバーのタイムラインで、特定メンバーの日報を時系列に表示し、提出率を併記すること | P2 |
| FR-412 | 全文検索:本文・コメント・案件名を横断検索し、ヒット箇所をハイライト表示すること | P1 |
| FR-413 | 検索は期間・メンバー・案件・課題ありで絞り込めること。URLクエリに反映すること | P1 |
| FR-414 | 検索が 100ms以下で完了すること(日報1,500件規模) | P1 |
| FR-415 | 期間指定で CSV/Markdown をエクスポートできること | P2 |
6.5 運用の計測(このデモの差別化要素)
| ID | 要件 | 優先 |
|---|---|---|
| FR-501 | 運用ダッシュボードに4指標を表示すること:提出率/返信率/平均入力時間/未読率。前週比を併記すること | P1 |
| FR-502 | 提出率 = 提出済み日報数 ÷ 提出対象日数(対象曜日・在籍期間を考慮)で算出すること | P1 |
| FR-503 | 返信率 = コメントまたはリアクションが1件以上付いた日報数 ÷ 提出日報数 で算出すること | P1 |
| FR-504 | 未読率 = 未読の日報数 ÷ 閲覧対象日報数 で算出すること | P1 |
| FR-505 | SC-201 提出状況を、メンバー×日付のグリッドで表示すること(提出済/未提出/遅延/対象外) | P1 |
| FR-506 | グリッドから未提出者へのリマインドを送れること(プレビュー表示で再現) | P1 |
| FR-507 | 提出が集中して落ちている曜日・時刻を検出して提示すること(フローC) | P2 |
| FR-508 | SC-202 返信率を上司別に表示し、平均返信時間を併記すること | P1 |
| FR-509 | 返信されていない日報を抽出できること。 経過日数順に並べられること | P1 |
| FR-510 | SC-203 入力時間をチーム平均・ヒストグラム・テンプレート別で表示すること | P1 |
| FR-511 | 個人別の入力時間ランキングを作らないこと。個人別の入力時間を本人以外に返さないこと(MEA-04, SCP-04) | P1 |
| FR-512 | 前日コピーを使った場合と使わなかった場合の入力時間を比較表示すること | P1 |
| FR-513 | 数値項目の期間集計を、案件別・メンバー別で表示すること | P2 |
6.6 テンプレート・設定
| ID | 要件 | 優先 |
|---|---|---|
| FR-601 | テンプレートの項目を追加・削除・並び替えできること(ドラッグ + キーボード操作) | P1 |
| FR-602 | 項目数と推定入力時間をテンプレート管理画面に常時表示すること(3章のスコープリスク対策) | P1 |
| FR-603 | 推定入力時間は、項目種別ごとの実測平均から算出すること(自由記述は長く、選択式は短い) | P1 |
| FR-604 | 項目を追加・削除すると、推定入力時間が即座に更新されること | P1 |
| FR-605 | テンプレートを書き手視点でプレビューできること | P1 |
| FR-606 | テンプレートをチーム別に割り当てられること | P2 |
| FR-607 | テンプレート変更時、過去の日報は当時の構造で表示されること(項目を消しても過去分は消えない) | P1 |
| FR-608 | 提出ルールを設定できること:提出期限、対象曜日、リマインド時刻、編集可能時間 | P1 |
| FR-609 | 曜日ごとにリマインド時刻を変えられること(フローC) | P2 |
| FR-610 | チーム編成・上司の割当・権限を管理できること | P1 |
| FR-611 | 案件・作業種別マスタを管理できること。使用件数を表示し、未使用の選択肢を検出すること | P2 |
| FR-612 | 通知文面(リマインド・コメント通知)を編集でき、差し込み変数を使えること | P2 |
| FR-613 | 日報の編集・削除・テンプレート変更を操作ログに記録すること | P2 |
6.7 デモ基盤
| ID | 要件 | 優先 |
|---|---|---|
| FR-701 | 3ロールをワンクリックで切り替えられること | P1 |
| FR-702 | データスコープと項目スコープを lib/repo/_scope.ts に集約すること(SCP-01, SCP-02) |
P1 |
| FR-703 | メンバーは他チームの日報を一切取得できないこと(SCP-03) | P1 |
| FR-704 | 個人別の入力時間を本人以外に返さないこと(SCP-04) | P1 |
| FR-705 | 「上司のみ」の課題ブロックを、対象外のメンバーに返さないこと(SCP-05) | P1 |
| FR-706 | 「デモをリセット」で localStorage / IndexedDB をクリアすること | P1 |
| FR-707 | 日報・下書き・コメント・既読・入力時間がリロード後も保持されること | P1 |
| FR-708 | デモ内の現在日時を切替バーに表示すること | P2 |
| FR-709 | ロール権限外の画面では案内画面から切り替えられること。素の404を出さないこと | P1 |
| FR-710 | 権限で不可の操作はボタンを非活性にし、理由をツールチップで示すこと | P1 |
| FR-711 | 元記事へ戻るリンクと資料ダウンロードを常設すること | P1 |
| FR-712 | 初回訪問時に3ステップのガイドツアーを表示し、「今後表示しない」を選べること | P2 |
| FR-713 | 埋め込みモード:入力フォーム + タイマー + 前日コピーを表示し、提出はできるがアプリ内遷移をせず、カメラ・位置情報を呼ばないこと(EMB-04〜07) | P1 |
7. データ設計
型定義(lib/types/)
// ---- 組織・メンバー ----
type Team = { id: string; name: string; leaderId: string; templateId: string }
type Member = {
id: string
name: string // ★ 架空
teamId: string
role: 'member' | 'leader' | 'admin'
avatarUrl: string
joinedAt: string
isActive: boolean
}
// ---- テンプレート ----
type FieldType = 'text' | 'textarea' | 'select' | 'multiselect' | 'number' | 'date' | 'checkbox' | 'image'
type TemplateField = {
id: string
label: string
type: FieldType
block: 'report' | 'issue' | 'insight' // ★ 3つの目的(FR-201)
options?: string[] // select / multiselect
masterKey?: 'project' | 'work_type' // マスタから選択肢を取る
unit?: string // number
isRequired: boolean
copyFromPrevious: boolean // ★ 前日コピーの対象(FR-106)
placeholder?: string
helpText?: string
inputMode?: 'text' | 'numeric' // FR-114
sortOrder: number
// ★ 推定入力時間の算出に使う(FR-603)
estimatedSeconds: number
}
type Template = {
id: string
version: number // ★ 過去の日報は当時の構造で表示(FR-607)
name: string
fields: TemplateField[]
teamIds: string[]
effectiveFrom: string
// --- 算出値(保存しない)---
// fieldCount, estimatedTotalSeconds は lib/metrics で算出
}
// ---- ★★★ 日報(このプロジェクトの中核)★★★ ----
type ReportStatus = 'draft' | 'submitted'
type Report = {
id: string
memberId: string
reportDate: string // YYYY-MM-DD
templateId: string
templateVersion: number // ★ 当時の構造で表示するため(FR-607)
status: ReportStatus
// --- 本文(テンプレート項目のID → 値)---
values: Record<string, FieldValue>
// --- ★ 課題・相談の扱い(FR-204〜206)---
issue?: {
hasIssue: boolean
visibility: 'team' | 'leader_only' // ★ SCP-05
resolutionStatus: 'open' | 'in_progress' | 'resolved'
resolvedBy?: string
resolvedAt?: string
}
// --- 添付 ---
imageIds: string[] // IndexedDB のキー
// --- ★ 入力時間の計測(MEA-01, MEA-05)---
inputMetrics: {
activeSeconds: number // 累積のアクティブ入力秒数
sessionCount: number // 何回に分けて書いたか
usedCopyPrevious: boolean // ★ 前日コピーを使ったか(FR-512)
firstOpenedAt: string
submittedAt?: string
}
// --- 提出 ---
submittedAt?: string
isLate: boolean // 提出期限を過ぎたか
// --- 編集履歴(FR-117)---
editHistory: { at: string; by: string; changedFieldIds: string[] }[]
createdAt: string
updatedAt: string
}
type FieldValue = string | number | boolean | string[] | null
// ---- 反応(★ 「読まれた」を成立させる)----
type ReadReceipt = {
id: string
reportId: string
memberId: string
readAt: string // ★ 3秒以上表示で記録(FR-405)
}
type Reaction = {
id: string
reportId: string
memberId: string
kind: 'confirmed' | 'good' | 'helpful' // 確認した / いいね / 助かった
createdAt: string
}
type Comment = {
id: string
reportId: string
memberId: string
body: string
mentionedMemberIds: string[]
createdAt: string
editedAt?: string
}
// ---- 週次まとめ ----
type WeeklySummary = {
id: string
memberId: string
weekStart: string // YYYY-MM-DD
weekEnd: string
status: 'generated' | 'submitted'
// --- 生成結果(SUM)---
generated: {
groupedActivities: { key: string; label: string; items: string[]; sourceReportIds: string[] }[]
numericTotals: { fieldId: string; label: string; total: number; unit?: string }[]
openIssues: { reportId: string; body: string; daysOpen: number }[]
insights: { reportId: string; body: string }[]
missingDates: string[] // ★ 未提出日(SUM-04)
notes: string[] // ★ 生成できなかった項目の説明(SUM-04)
}
// --- 本人の追記・修正 ---
edited: { retrospective: string; nextWeekPlan: string }
submittedAt?: string
generatedAt: string
}
// ---- 設定 ----
type SubmissionRule = {
id: string
teamId?: string // 未指定なら全社
targetWeekdays: number[] // 提出対象の曜日
deadlineMin: number // 提出期限(0:00からの分数。1080 = 18:00)
reminders: { weekday: number; atMin: number }[] // ★ 曜日別のリマインド(FR-609)
editableMinutes: number // 提出後の編集可能時間(既定30)
weekStartsOn: number
}
type MasterItem = {
id: string
key: 'project' | 'work_type'
name: string
isActive: boolean
sortOrder: number
// 算出値:usageCount(FR-611)
}
// ---- 通知 ----
type Notification = {
id: string
targetMemberId: string
kind: 'comment' | 'mention' | 'reaction' | 'unread_reminder' | 'submit_reminder' | 'issue_stale'
title: string
body: string
link: string
previewMessage?: { channel: 'email' | 'slack' | 'line'; subject?: string; body: string } // ★ 送信しない
readAt?: string
createdAt: string
}
// ---- 運用指標(★ 算出値。ストアに保存しない)----
type OpsMetrics = {
scope: { teamId?: string; from: string; to: string }
submissionRate: number // FR-502
responseRate: number // FR-503
unreadRate: number // FR-504
avgInputSeconds: number
// --- 比較 ---
prevSubmissionRate: number
prevResponseRate: number
prevUnreadRate: number
prevAvgInputSeconds: number
}
type SubmissionGrid = {
members: { memberId: string; name: string }[]
dates: string[]
cells: Record<string, 'submitted' | 'late' | 'missing' | 'not_required'> // `${memberId}:${date}`
}
type ResponseByLeader = {
leaderId: string
name: string
responseRate: number
avgResponseHours: number
unrespondedReportIds: string[]
}
type InputTimeDistribution = {
teamAvgSeconds: number
histogram: { fromSec: number; toSec: number; count: number }[]
byTemplate: { templateId: string; name: string; avgSeconds: number; fieldCount: number }[]
withCopyAvgSeconds: number // ★ FR-512
withoutCopyAvgSeconds: number
// ★ 個人別は含めない(MEA-04, SCP-04)
}
// ---- 監査 ----
type AuditLog = {
id: string
actorId: string
action: 'edit_report' | 'delete_report' | 'update_template' | 'update_rule' | 'send_reminder'
targetType: string
targetId: string
changes: { field: string; before: unknown; after: unknown }[]
createdAt: string
}
入力時間の計測(7章の中核)
// lib/metrics/input-timer.ts — コンポーネントはこのフックだけを使う(MEA-02)
const IDLE_THRESHOLD_MS = 30_000
export function createInputTimer() {
let activeMs = 0
let lastTick: number | null = null
const onActivity = () => {
const now = Date.now()
if (lastTick !== null) {
const delta = now - lastTick
// ★ 30秒以上の無操作は加算しない(MEA-01)
if (delta < IDLE_THRESHOLD_MS) activeMs += delta
}
lastTick = now
}
const onHidden = () => { lastTick = null } // ★ タブ非表示中は加算しない
const onVisible = () => { lastTick = Date.now() }
return {
onActivity, onHidden, onVisible,
getActiveSeconds: () => Math.round(activeMs / 1000),
// ★ 既存の累積に足し込む(複数回に分けて書くケース。MEA-05)
merge: (prevSeconds: number) => prevSeconds + Math.round(activeMs / 1000),
}
}
// lib/metrics/estimate.ts — テンプレートの推定入力時間(FR-603)
// 項目種別ごとの実測平均(シードの実データから算出した係数)
const SECONDS_BY_TYPE: Record<FieldType, number> = {
select: 4, multiselect: 8, checkbox: 3, date: 6, number: 8,
text: 15, textarea: 45, image: 20,
}
export function estimateInputSeconds(fields: TemplateField[]): number {
return fields
.filter(f => f.isRequired) // 任意項目は加算しない
.reduce((sum, f) => sum + (f.estimatedSeconds || SECONDS_BY_TYPE[f.type]), 0)
}
シードデータ(lib/seed/)
| ファイル | 内容 | 件数 |
|---|---|---|
teams.ts members.ts |
チーム4(営業2・現場1・本部1)、メンバー(書く頻度に差をつける) | チーム4 / 22名 |
templates.ts |
テンプレート3種(営業用・現場用・開発用)。バージョン2つ持つものを1つ(FR-607 の実演用) | 3件 |
masters.ts |
案件18・作業種別12(未使用の選択肢を2つ混ぜる) | 30件 |
reports.ts |
日報(過去3ヶ月・22名分)。入力時間の実データを持たせる | 約1,500件 |
comments.ts |
コメント(返信率に差をつける。返信の薄い上司を1名作る) | 約620件 |
reactions.ts readReceipts.ts |
リアクション・既読(未読が溜まっている状態を作る) | 約2,800件 |
weeklySummaries.ts |
週次まとめ(提出済み/未生成を混在) | 約120件 |
submissionRules.ts |
提出ルール(金曜のリマインドが遅い設定にしておく。フローC の実演用) | 2件 |
シード作成のルール(品質を左右する)
- 実在企業名・実在人名を使わない。 架空のメンバー名・案件名で構成する
- 氏名を「メンバーA」のようなダミーにしない。 実在しそうな架空名にする
- 日報の本文を、業種の世界観を持つ実在しそうな文章にする。 「本日の業務を行いました」のようなダミーを1件も残さない。ここが手抜きだとデモ全体が死ぬ。日報アプリのデモは本文の質がそのまま説得力になる
- 本文の長さに幅を持たせる。 3行の人、10行の人、箇条書きの人が混在すること
- 入力時間の実データを持たせる。 30秒〜8分の分布。前日コピー利用時は明確に短く(FR-512 が意味を持つように)
- 課題・相談が書かれている日報を1割程度作る。 うち数件を「上司のみ」に設定(SCP-05 の実演用)
- 未対応のまま3日以上経過した課題を数件作る(FR-410 の実演用)
- 返信率に差をつける。 返信の薄い上司を1名作り、返信されていない日報を20件以上残す(フローCの実演用)
- 未読が溜まっている状態を作る(未読率30%程度)
- 未提出の日を作る。 特に金曜に未提出が集中している状態にする(フローCの実演用)
- 「気づき」が空の日報を多く作る。 任意項目が形骸化する実態を再現する(そしてそれが正常であることを示す)
- 提出が遅延した日報を混ぜる(
isLate: true) - 日付は現在日時からの相対で生成する。固定日付を埋め込まない
- 日報は提出対象曜日にのみ存在すること(土日に日報が並んでいると嘘に見える)
ストアとリポジトリ
lib/
├── types/ seed/
├── metrics/ # ★ 純粋関数 + タイマー
│ ├── input-timer.ts # ★ 入力時間の計測(MEA-02)
│ ├── estimate.ts # ★ テンプレートの推定入力時間(FR-603)
│ ├── submission.ts # 提出率(FR-502)
│ ├── response.ts # 返信率・平均返信時間(FR-503, FR-508)
│ ├── unread.ts # 未読率(FR-504)
│ ├── distribution.ts # 入力時間の分布(FR-510, FR-512)
│ └── patterns.ts # 提出が落ちる曜日・時刻の検出(FR-507)
├── summary/ # ★ 週次まとめ生成。純粋関数のみ
│ ├── generate.ts # SUM-05
│ ├── group.ts # 実施内容のグループ化
│ └── totals.ts # 数値項目の合算
├── search/ # ★ 全文検索。純粋関数のみ
│ ├── index.ts # 転置インデックスの構築
│ ├── query.ts # 検索とスコアリング
│ ├── highlight.ts # ヒット箇所のハイライト
│ └── normalize.ts # ひらがな/カタカナ/半角全角の正規化
├── storage/ # IndexedDB(画像)
├── query/ # 絞り込み・ソート
├── store/ { session, data, settings, draft, sync, ui }
└── repo/ # ★ コンポーネントが触るのはここだけ
├── _delay.ts
├── _scope.ts # ★ データスコープ + 項目スコープ(SCP-01〜05)
├── reports.ts drafts.ts comments.ts reactions.ts reads.ts
├── weekly.ts templates.ts masters.ts rules.ts
├── metrics.ts search.ts members.ts notifications.ts logs.ts
リポジトリ層の規約(厳守)
- 全メソッドを
asyncで定義する。中身が同期でも例外なく - 戻り値は
{ ok: true; data: T } | { ok: false; error: string }に統一する - コンポーネントから Zustand ストアを直接参照しない。 読み取りも書き込みもリポジトリ経由
- 入力時間の計測を
lib/metrics/input-timer.tsに集約する。 コンポーネントでsetIntervalを持たない(MEA-02) - 週次まとめの生成を
lib/summary/に集約し、純粋関数とする(SUM-05) lib/metrics/lib/summary/lib/search/は純粋関数のみ。 ストア・DOM に依存させず、new Date()を内部で呼ばない- スコープ適用(
_scope.ts)を全メソッドが必ず通す。 ここが実案件で権限制御に置き換わる箇所 - 個人別の入力時間を本人以外に返さない(SCP-04)
- 「上司のみ」の課題ブロックを、対象外のメンバーに返さない(SCP-05)
- 派生値(提出率・返信率・未読率・推定入力時間)はストアに保存せず算出する
- 入力・下書き保存・提出・検索に擬似ディレイを入れない(FR-119)
// lib/repo/_scope.ts — 「上司のみ」の課題を除外する(SCP-05, FR-205)
export function scopeReport(report: Report, viewer: Member): Report | null {
// 他チームは一切返さない(SCP-03)
if (viewer.role === 'member' && report.teamId !== viewer.teamId) return null
const isSelf = report.memberId === viewer.id
const isLeaderOfWriter = viewer.role !== 'member' && isInScope(viewer, report)
// ★ 「上司のみ」の課題ブロックは、本人と上司以外には含めない
if (report.issue?.visibility === 'leader_only' && !isSelf && !isLeaderOfWriter) {
return { ...report, issue: undefined, values: omitIssueFields(report.values) }
}
// ★ 個人別の入力時間は本人以外に返さない(SCP-04)
if (!isSelf) return { ...report, inputMetrics: undefined as never }
return report
}
8. デザイン要件
アートディレクション
「業務システムの中で、唯一の読み書きするアプリ」
HIBI が扱うのは表ではなく文章である。日報は読まれるために書かれ、読み手は集中して読む。だから HIBI は姉妹プロジェクトの中で最も密度が低く、余白が広く、1カラムでできている。
- 1画面1カラム。 サイドバーに情報を詰めない。読むときは文章だけを見せる
- 本文が主役。 15px・行間1.9。日報の本文が読みやすいことが最優先
- 色はほぼ使わない。 例外は「課題あり」と「未読」の2つだけ
- 入力フォームに枠を描かない。 下線と余白で区切る。「書類に記入する」感覚を作らない
姉妹プロジェクトとの差別化(最重要) RELATE / KURA / TOKIWA は「表を読ませる高密度の業務システム」。HIBI は文章を読み書きさせるアプリで、密度は真逆。 4本を並べたとき、HIBI だけが余白の広い1カラムであることが一目で分かること。 配色も分ける:RELATE=藍/ KURA=ティール+アンバー/ TOKIWA=インディゴ+コーラル/ HIBI=墨+若草(低彩度の緑)。
カラートークン
:root {
/* 紙に近い、わずかに温かい白 */
--paper: #FAFAF8; /* ページ背景 */
--panel: #FFFFFF; /* カード・本文の地 */
--panel-alt: #F2F2EE; /* 区切り・引用 */
--line: #E5E5DF; /* 罫線(極めて控えめに) */
--ink-900: #1C1D1A; /* 本文(純黒にしない。長文を読ませるため) */
--ink-600: #5B5D57; /* 補助テキスト */
--ink-400: #93958D; /* ラベル・非活性 */
--accent: #4A6B3F; /* 主要CTA(若草。低彩度) */
--accent-d: #3A5531;
--accent-50: #EBF0E8;
/* ★ 色を使う例外は2つだけ */
--issue: #A85B2B; /* 課題あり(テラコッタ) */
--issue-bg: #F6EDE6;
--unread: #4A6B3F; /* 未読(若草の点) */
/* 状態(控えめに) */
--st-draft: #93958D;
--st-late: #A8892B;
--st-missing: #A85B2B;
/* 反応 */
--reaction-bg: #F2F2EE;
}
配色ルール
- 色を使う箇所を2つに限定する:「課題あり」と「未読」。 それ以外は無彩色 + 若草のアクセントのみ
- 提出済み・未提出をバッジの色で塗り分けない。 テキストと位置で示す(一覧が色だらけになるのを防ぐ)
- 入力時間の表示に色を使わない。 数字を大きくするだけ。速い/遅いを色で評価しない(MEA-04 の精神)
- チーム・メンバーに固有色を割り当てない。アバターで識別する
タイポグラフィ
| 役割 | 書体 | 用途 |
|---|---|---|
| 本文 | Noto Sans JP 400(line-height: 1.9) |
日報の本文。これが主役 |
| 見出し | Noto Sans JP 700 | 画面タイトル、セクション |
| 日付・数値 | Roboto Mono 500(tabular-nums) |
日付、入力時間、提出率 |
| トークン | サイズ | 用途 |
|---|---|---|
| page-title | 22px / 700 | 画面タイトル |
| card-title | 16px / 700 | 日報カードの見出し(氏名 + 日付) |
| body | 15px / 400・行間1.9 | 日報の本文。他3本(13px)より大きい |
| label | 11px / 700(letter-spacing:.06em) |
項目ラベル |
| meta | 12px / 400 | 提出時刻、既読数 |
| timer | 15px / 500 Mono | 入力中の経過時間(控えめに。MEA-03) |
| input-result | 30px / 500 Mono | 提出完了時の入力時間(ここは大きく) |
本文15px・行間1.9 が HIBI の視覚的な指紋。 RELATE / KURA / TOKIWA はすべて13px の高密度。HIBI だけが「読ませるための組版」を持つ。
レイアウトとスペーシング
| 項目 | 定義 |
|---|---|
| スペーシング | 4pxベース:4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 |
| アプリシェル | 1カラム。 上部にヘッダー(56px)、下部にタブナビ(モバイル)。サイドバーを持たない(管理画面のみ例外) |
| コンテンツ最大幅 | 本文 680px(1行 36〜42文字)。タイムライン 760px。画面幅いっぱいに広げない |
| 日報カード | 上下 24px の余白。カード間は罫線1本のみ(囲まない) |
| 入力フォーム | 項目間 32px。 ラベルは上、入力は下線のみ。枠で囲まない |
| 提出ボタン | モバイルは下部固定(56px高)。セーフエリアを考慮 |
| 角丸 | 4px(ボタン・入力)/2px(バッジ)/999px(アバター・リアクション) |
| 影 | 使わない。 罫線と余白で階層を表現する |
主要コンポーネント仕様
| コンポーネント | 仕様 |
|---|---|
| 入力フォーム(最重要) | 1カラム。ラベル(label)+ 入力(下線のみ)。項目間32px。 選択式はチップ状のボタン群(セレクトボックスを避け、タップ数を減らす) |
| 入力タイマー | 画面右上に控えめに ⏱ 1:12(timer サイズ、--ink-400)。大きくしない、色を付けない、急かさない(MEA-03) |
| 「前日をコピー」ボタン | フォーム上部。押すと埋まった項目に一瞬だけ若草の背景が走る(何が埋まったか分かる)。押下後に「あと2項目」と表示 |
| 残り項目インジケータ | 提出ボタンの直上。「あと2項目:進捗、実施内容」。押してからエラーを出さない(FR-113) |
| 提出完了モーダル | input-result(30px Mono)で入力時間。その下に平均との比較。「短縮できました」を出すが、遅い場合は何も言わない |
| 3ブロックの構造 | 「報告」「課題・相談」「気づき・学び」を罫線と見出しで分ける。課題ブロックには公開範囲のセレクトを併置。気づきには「空でも提出できます」を明示 |
| 日報カード(タイムライン) | アバター + 氏名 + 日付(Mono)/本文の冒頭3行/数値項目のチップ/未読は左端に若草の点/課題ありは --issue-bg の細い帯 + バッジ/既読数・コメント数 |
| 課題ブロック(詳細) | --issue-bg の地 + 左端に --issue の2px帯。対応状態のセレクト。「上司のみに公開」の場合は鍵アイコンとその旨を表示 |
| 既読者の表示 | アバターを重ねて横並び(最大5 + 「他N名」)。書き手側の詳細画面にも同じものを出す(読まれたことが見える) |
| リアクション | 3種のチップ。押すと自分のアバターが小さく付く。コメントより先に、押しやすい位置に置く |
| コメント欄 | 本文の直下。折りたたまない。 投稿ボタンは常時表示(コメントの摩擦を減らす) |
| 週次まとめの生成結果 | セクションごとに「生成された内容」+ 根拠の日報へのリンク(日付のチップ)。生成できなかった項目は --ink-400 で理由を明示 |
| 提出状況グリッド | メンバー×日付。セルは ● 提出/◐ 遅延/○ 未提出/− 対象外。色ではなく記号で区別(A11Y-04) |
| 4指標カード | ラベル(label)/数値(Mono・大)/前週比(符号 + pt)。悪化した指標のみ --issue で符号を色付け |
| 入力時間ヒストグラム | 横軸=秒数帯、縦軸=件数。個人名を出さない(MEA-04)。前日コピーあり/なしの2系列 |
| テンプレート管理 | 項目リスト(D&D並び替え)+ 画面上部に「項目数 9/推定入力時間 3分20秒」を常時表示。 項目を追加すると即座に更新される |
| 空状態 | 「まだ日報がありません」/「条件に一致する日報はありません」を出し分け |
| スケルトン | カード形状。本文3行分の高さを確保してレイアウトシフトを防ぐ |
| デモ切替バー | 濃い墨色の帯。余白の広いアプリ本体と明確に区別する |
インタラクション要件
| ID | 要件 |
|---|---|
| IX-01 | 入力の反応が 100ms以下であること。 文字入力で1フレームでも落ちないこと |
| IX-02 | 下書き保存が入力を妨げないこと(保存中に入力がブロックされない) |
| IX-03 | 提出は1タップ。確認ダイアログを出さない(FR-112) |
| IX-04 | 選択式項目はチップのタップで選択完了(セレクトを開かせない) |
| IX-05 | テキストエリアは内容に応じて自動で高さが伸びること(スクロールバーを出さない) |
| IX-06 | ⌘Enter / Ctrl+Enter で提出、Esc で下書き保存して閉じる |
| IX-07 | タイムラインはキーボードで移動できること(↑↓ カード移動、Enter 詳細、R リアクション) |
| IX-08 | 既読は3秒以上表示で記録する。 スクロールで通過しただけで既読にしない(FR-405) |
| IX-09 | コメント投稿・リアクションは楽観的更新とし、失敗時のみロールバックすること |
| IX-10 | 検索は入力から300msのデバウンス後に実行し、結果が100ms以下で返ること |
| IX-11 | 編集・削除は5秒間「元に戻す」を表示すること |
モーション
| 対象 | duration | 内容 |
|---|---|---|
| 前日コピーの適用 | 400ms | 埋まった項目に若草の背景が一度だけ走る。 何が埋まったか分かること |
| 提出完了 | 300ms | モーダルのフェード + 入力時間のカウントアップ |
| ホバー・フォーカス | 100ms | 色のみ |
| カードの既読化 | 240ms | 左端の点がフェードアウト |
| リアクション | 180ms | アバターが小さくポップして付く |
| コメント投稿 | 200ms | 下から差し込む |
| テキストエリアの伸長 | なし | アニメーションさせない(入力の邪魔になる) |
| トースト | 160ms | 下から |
prefers-reduced-motion: reduce 時は全アニメーションを無効化する。
アクセシビリティ(WCAG 2.1 AA)
| ID | 要件 |
|---|---|
| A11Y-01 | コントラスト比は通常4.5:1以上。本文は7:1を目標とする(長文を読ませるため) |
| A11Y-02 | 全ての操作をキーボードのみで完遂できること |
| A11Y-03 | テンプレート管理のD&Dにキーボード代替を用意すること(FR-601) |
| A11Y-04 | 提出状況を色のみで伝えないこと。 記号(●◐○−)とテキストを併記すること |
| A11Y-05 | 「課題あり」「未読」を色のみで伝えないこと。 アイコンとテキストを併記すること |
| A11Y-06 | フォーカスリングは2px・オフセット2pxで常時可視。outline:none の単独使用禁止 |
| A11Y-07 | フォームの各入力に label を関連付け、エラーは aria-describedby と role="alert" で読み上げること |
| A11Y-08 | 不足項目の案内を aria-live="polite" で通知すること(FR-113) |
| A11Y-09 | 下書きの自動保存を aria-live="polite" で控えめに通知すること |
| A11Y-10 | 提出完了と入力時間を aria-live="polite" で読み上げること |
| A11Y-11 | モーダルはフォーカストラップ + Esc で閉じる + 起動元へフォーカス復帰 |
| A11Y-12 | 入力時間ヒストグラムを表としても読めるようにマークアップすること |
| A11Y-13 | タップ領域は最小44×44px。提出ボタンは56px |
| A11Y-14 | フォントサイズ200%指定でも、入力フォームと日報の本文が読めること |
ライティング規約
| 原則 | 例 |
|---|---|
| 書く負担を減らす言葉で | ○「気づいたことがあれば。空のままでも提出できます」/×「気づき・学びを記入してください(必須)」 |
| 不足は押す前に伝える | ○「あと2項目:進捗、実施内容」/× 提出後に「必須項目が未入力です」 |
| 入力時間を評価しない | ○「入力時間 1分12秒」/×「速いですね!」「時間がかかっています」 |
| 読まれたことを伝える | ○「山田さん、佐藤さん、他1名が読みました」/×「既読 3」 |
| 課題は目立たせるが煽らない | ○「相談があります」/×「緊急」「至急対応」 |
| リマインドは責めない | ○「今日の日報がまだ提出されていません」/×「未提出です。速やかに提出してください」 |
| 空状態を区別する | データ0件:「まだ日報がありません。今日の分から始めましょう」/絞り込み0件:「条件に一致する日報はありません」+ 条件解除 |
| システム語を使わない | ○「この相談は解決しました」/×「statusをresolvedに更新」 |
9. 技術要件・ディレクトリ構成
技術スタック(固定・勝手に変更しない)
| レイヤ | 技術 | 備考 |
|---|---|---|
| フレームワーク | Next.js 15(App Router)/ TypeScript strict | |
| スタイリング | Tailwind CSS + CSS Variables | トークンは CSS 変数で定義 |
| UIコンポーネント | shadcn/ui(Radix UI基盤) | a11y要件を自前実装せずに満たす |
| 状態管理 | Zustand + persist | localStorage に永続化 |
| 入力時間の計測 | 自前実装(lib/metrics/input-timer.ts) |
ライブラリを入れない。数十行 |
| 全文検索 | 自前実装(lib/search/) |
Fuse.js 等を入れない。 転置インデックス + 日本語正規化は自前で書ける。これが記事の技術パートの見せ場になる |
| 週次まとめ生成 | 自前実装(lib/summary/) |
AIを呼ばない(SUM-01) |
| テキストエリア | 自前実装(自動高さ調整) | react-textarea-autosize を入れない(数行で書ける) |
| D&D | dnd-kit | テンプレート項目の並び替え。キーボード対応必須 |
| グラフ | Recharts(動的import) | 運用ダッシュボードのみ |
| フォーム | React Hook Form + Zod | |
| 画像 | IndexedDB(idb)+ Canvas で圧縮 | browser-image-compression を入れない |
| CSV / Markdown | Papa Parse / 自前実装 | |
| 日付 | date-fns(ja locale) | タイムゾーンは Asia/Tokyo 固定 |
| PWA | next-pwa | オフライン作成(FR-118) |
| アイコン | lucide-react | |
| ホスティング | Vercel | |
| テスト | Vitest / Playwright / axe-core |
上記以外のライブラリを入れる前に必ず提案し、承認を得ること。特に全文検索ライブラリ、リッチテキストエディタ、AI SDK の導入は禁止。
リッチテキストエディタを入れない理由: 日報に装飾は不要であり、エディタを入れるとバンドルが数百KB増え、入力の反応が落ちる。プレーンテキスト + 改行 + 箇条書き(
-記法の表示側変換)で足りる。IX-01(入力の反応100ms以下)を守るための判断。
ディレクトリ構成
/
├── CLAUDE.md
├── app/
│ ├── layout.tsx # 1カラムシェル(ヘッダー + タブナビ + デモ切替バー)
│ ├── page.tsx # SC-001 ホーム / SC-900(?embed=1)
│ ├── reports/ # SC-010〜021
│ ├── weekly/ # SC-030, 031
│ ├── notifications/ # SC-040
│ ├── team/ # SC-100〜103
│ ├── search/ export/ # SC-110, 120
│ ├── admin/ # SC-200〜240
│ └── dev/components/
├── components/
│ ├── editor/ # ★ 最重要
│ │ ├── ReportForm.tsx # 1カラムの入力フォーム
│ │ ├── FieldRenderer.tsx # 項目種別ごとの描画
│ │ ├── ChipSelect.tsx # 選択式(セレクトを使わない)
│ │ ├── AutoTextarea.tsx # 自動高さ調整
│ │ ├── InputTimerBadge.tsx # ★ 控えめなタイマー(MEA-03)
│ │ ├── CopyPreviousButton.tsx # ★ 前日コピー
│ │ ├── MissingFieldsHint.tsx # ★ 残り項目(FR-113)
│ │ ├── SubmitResultModal.tsx # ★ 入力時間の結果
│ │ └── useDraftAutosave.ts
│ ├── report/ # ReportCard, IssueBlock, ReadReceipts, ReactionBar, CommentList
│ ├── weekly/ # SummaryDraft, SourceReportLinks
│ ├── ops/ # MetricCard, SubmissionGrid, ResponseTable, InputTimeHistogram
│ ├── template/ # FieldList, EstimateBadge(★ 項目数と推定時間)
│ ├── demo/ # RoleSwitcher, ResetButton, GuideTour, BackToArticle
│ └── layout/
├── lib/
│ ├── types/ seed/ store/ repo/
│ ├── metrics/ # ★ 計測。純粋関数 + タイマー
│ ├── summary/ # ★ 週次まとめ生成。純粋関数
│ ├── search/ # ★ 全文検索。純粋関数
│ ├── storage/ query/ validation/ utils/
├── docs/
│ └── daily-report-checklist.md # 資料PDFの元原稿(日報の目的整理シート)
├── e2e/
└── public/
コーディング規約
- TypeScript strict。
any禁止。やむを得ない場合はunknown+ 型ガード - ファイル名は kebab-case、コンポーネントは PascalCase
- 1ファイル300行を超えたら分割を検討する
- コンポーネントから Zustand ストアを直接参照しない。必ず
lib/repo/経由 - 入力時間の計測を
lib/metrics/input-timer.tsに集約する。 コンポーネントでsetIntervalを持たない(MEA-02) lib/metrics/lib/summary/lib/search/は純粋関数のみ。 ストア・DOM に依存させず、new Date()を内部で呼ばない- 入力の反応速度を最優先する。 入力中に重い処理(検索インデックス更新・集計)を走らせない。下書き保存も入力をブロックしない(IX-02)
- 個人別の入力時間を本人以外に返さない(SCP-04)。この判定をコンポーネントに書かない
- 派生値(提出率・返信率・未読率・推定入力時間)をストアに保存しない
- 入力を増やす機能を提案しない。 機能追加は「入力を減らす」か「読まれるようにする」に寄与すること(1章 事実③)
- コミットは1タスクごと。メッセージに要件IDを含める
例:
feat(editor): 前日コピーで埋まった項目をハイライト (FR-106)
10. 非機能要件
| ID | 要件 | 目標値 |
|---|---|---|
| NFR-01 | 文字入力の反応 | 1フレームも落とさない(100ms以下) |
| NFR-02 | 下書きの自動保存 | 50ms以下、入力をブロックしないこと |
| NFR-03 | 提出から完了表示まで | 200ms以下 |
| NFR-04 | 全文検索(日報1,500件 + コメント620件) | 100ms以下 |
| NFR-05 | 検索インデックスの初期構築 | 300ms以下(初回のみ、非同期で構築し入力をブロックしない) |
| NFR-06 | 週次まとめの生成 | 300ms以下 |
| NFR-07 | 運用指標の算出(22名 × 3ヶ月) | 150ms以下 |
| NFR-08 | 一覧の絞り込み・並び替え | 100ms以下 |
| NFR-09 | タイムラインのスクロール | 60fps維持(仮想スクロール) |
| NFR-10 | LCP(モバイル4G相当) | 2.0秒以下 |
| NFR-11 | INP | 200ms以下 |
| NFR-12 | CLS | 0.1以下。日報カードの本文3行分の高さを最初から確保すること |
| NFR-13 | 埋め込みモードの初期JSバンドル(gzip後) | 60KB以下 |
| NFR-14 | アプリ本体の初期JSバンドル(gzip後) | 180KB以下(グラフは動的import。姉妹プロジェクトより厳しく設定する。入力の速さが命だから) |
| NFR-15 | Lighthouse | Performance 95 / Accessibility 95 以上 |
| NFR-16 | 対応環境 | iOS Safari 16以降、Android Chrome 最新、PC Chrome / Edge / Safari / Firefox 最新2バージョン |
| NFR-17 | localStorage の容量上限に達した場合、警告を表示し、古い月の日報を要約して圧縮すること(データを破損させない) | — |
セキュリティ・プライバシー
| ID | 要件 |
|---|---|
| SEC-01 | 入力された日報の本文・氏名・画像を一切外部に送信しないこと。 localStorage / IndexedDB にのみ保存すること |
| SEC-02 | この事実を、入力画面・デモ切替バーで明示すること |
| SEC-03 | GA4 に日報の本文を送らないこと(EMB-10)。送るのは秒数・項目数などのメタ情報のみ |
| SEC-04 | ユーザー入力(本文・コメント)をサニタイズすること。- の箇条書き変換以外のHTMLを許可しないこと |
| SEC-05 | 「デモをリセット」ですべてのデータが消えることを明示し、実際に消えること |
| SEC-06 | 個人別の入力時間が、本人以外のロールで取得できないことをテストで保証すること(SCP-04) |
運用設計上の注意(この領域固有)
日報アプリは容易に監視ツールになる。 実装の段階でそれを防ぐ。
| ID | 要件 |
|---|---|
| ETH-01 | 個人別の入力時間ランキングを実装しないこと(MEA-04, FR-511) |
| ETH-02 | 入力時間の速い/遅いを色や評価文言で示さないこと |
| ETH-03 | 提出率を個人の評価指標として提示しないこと。 提出状況グリッドは「誰にリマインドするか」のための運用画面であり、評価画面ではない旨を画面上に明記すること |
| ETH-04 | リマインドの文言を責める調子にしないこと(ライティング規約) |
| ETH-05 | 「課題・相談」の公開範囲を書き手が選べること(SCP-05)。書き手の心理的安全を仕様で守る |
| ETH-06 | 位置情報・スクリーンショット・操作ログによる書き手の監視機能を実装しないこと |
実案件への転用時の注意(デモ本体の要件ではない。商談で聞かれた際に説明できるよう記録)
① 日報は「導入」より「運用設計」に工数がかかる。 項目を決める会議、読む担当を決める運用、リマインドの設計。開発費より運用設計の合意形成の方が時間を食う
② 日報の内容は労務トラブルの証拠になりうる。 長時間労働の記録、ハラスメントの記述などが残る。保存期間とアクセス権限の設計が必要
③ 人事評価に使うなら、その旨を事前に明示する必要がある。 「報告のため」と言って集めた情報を評価に使うのは、運用として問題になりやすい
④ 監視色が強いと必ず形骸化する。 「特になし」が並ぶ日報に価値はない。書き手に利益がある設計(自分の記録が残る、相談が返ってくる)が持続の条件
本デモはデータをサーバーに送信しないため、②③の義務は発生しない。「デモである」旨は画面上に常時明示する
11. 費用設計
記事の主題の半分は「費用」。この章は実装要件であると同時に、記事本文の原稿素材でもある。
日報アプリだけの特殊事情:そもそも作らなくていいかもしれない
他のテーマと違い、日報には**「Excelでもチャットでもできる」**という現実がある。この記事で最初に答えるべきは「いくらか」ではなく——
「日報アプリを、そもそも作る必要があるのか」
正直にそう問うことが、結果的に信頼を生む。
まず、作らない選択肢を並べる
| 手段 | 向くケース | 限界 |
|---|---|---|
| チャット(Slack / Teams / LINE WORKS)に投稿 | 10名以下。すぐ始めたい | 流れて消える。 検索性が低い、集計できない、未読管理がない |
| Googleフォーム + スプレッドシート | 20名程度。集計したい | 読み手の体験が悪い。 コメントできない、既読が分からない |
| Notion / Kibela 等のドキュメントツール | 蓄積を重視するチーム | 入力の手数が多い。 集計が弱い |
| 既製の日報SaaS | 30〜100名。標準的な運用 | 月額×人数。項目のカスタマイズに限界 |
| 自作 | 下記の①〜⑤に該当する場合 | 開発費 + 運用設計 + 保守 |
「まずチャットで始めて、限界が来たら考える」が最も合理的なケースが多い。 記事にはこれを明記する。
自作が正当化される5つのケース
| ケース | 内容 |
|---|---|
| ① 数値を集計して業務に使いたい | 訪問件数・施工数・売上見込みを日報から集計し、そのまま管理指標にしたい |
| ② 既存システムと繋ぎたい | 顧客管理・案件管理・勤怠と日報を一体化したい(日報から二重入力をなくす) |
| ③ 業種特有の項目・帳票が必要 | 建設の作業日報、介護の記録、点検記録など、法令や取引先要求で形式が決まっている |
| ④ 人数が多く、人数課金が重い | 100名超で、月額×人数が積み上がっている |
| ⑤ 現場のITリテラシーに合わせた極端な簡略化が必要 | 「3タップで終わる」レベルまで削りたい。既製サービスの画面では無理 |
⑤は見落とされやすいが、現場系の業種で最も多い動機。 既製サービスは汎用性のために項目が多く、現場の人が使わない。
逆に、①〜⑤のどれにも当てはまらないなら、チャットか既製サービスを使うべき。
損益分岐の計算式
既製サービスの年間コスト
= 月額単価 × 人数 × 12
( + オプション:集計機能・API連携 )
回収年数 = (開発費 + 運用設計費)÷(既製サービス年間コスト − 自作の年間運用費 − 年間保守費)
| 規模 | 人数 | 判断 |
|---|---|---|
| 小 | 〜20名 | チャットか無料ツールで十分。 自作は回収できない |
| 中 | 30〜100名 | ①②③⑤に該当するなら検討価値あり |
| 大 | 100名超 | 人数課金が効いてくる。 ただし運用設計の工数も増える |
最も見落とされる費用:運用設計の工数
日報アプリの導入で最も工数を食うのは、実装ではなく「何を書かせるか」の合意形成。
- 項目を決める会議(関係者が増えるほど項目が増える)
- 「誰が読むか」を決める(ここが決まらないと項目が決まらない)
- リマインドと督促の運用を決める
- 既存の日報(紙・Excel・メール)からの移行
そして導入後、必ず項目を増やしたくなる。 そのとき「項目を1つ増やすと入力時間が何秒増え、提出率が何%下がるか」を議論できないと、日報は肥大化して死ぬ。
HIBI がテンプレート管理画面に「項目数と推定入力時間」を常時表示しているのは、この会議のためにある(FR-602)。資料PDFの「日報の目的整理シート」も、この工数を前倒しするために作る。
AI連携は「あとで足す」でよい
「AIで日報を要約したい」「AIが添削してほしい」は必ず出る要望。だが——
| AI連携なし(本デモ) | AI連携あり | |
|---|---|---|
| 週次まとめ | ルールベースで生成できる(SUM-01) | 自然な文章になる |
| 費用 | ゼロ | 1件ごとに課金。日報数 × 人数 × 日数 で積み上がる |
| データの扱い | 外に出ない | 日報の本文を外部APIに送る(業務情報の流出リスクの検討が必要) |
| 実装 | 数十行 | プロキシ(バックエンド)が必要 |
AI要約の月額 = 1件あたりの単価 × 人数 × 月間営業日数
例:50名 × 20日 = 1,000件/月
まずルールベースで作り、必要になったらAIを足す。 この順序が費用とリスクの両面で合理的。そしてルールベースでも「根拠の日報にリンクが張られる」という、AIにはない透明性がある(SUM-03)。
Webで足りるか、ネイティブが必要か
日報は、Webで足りる領域。
| やりたいこと | Web(PWA) | ネイティブ |
|---|---|---|
| 日報の作成・提出 | できる | できる |
| オフラインでの作成 | できる(PWA) | できる |
| 写真の添付 | できる | できる |
| 提出リマインドのプッシュ通知 | 限定的 | できる |
| 音声入力 | できる(OS標準のキーボード) | できる |
判断の目安: リマインドのプッシュ通知を重視するなら、LINE公式アカウントやチャットツールへの通知が現実的な代替(アプリ開発より圧倒的に安い)。日報アプリのためにネイティブアプリを作る必要はほぼない。
デモ自体の運用コストを実測して記事に載せる
| ID | 要件 |
|---|---|
| COST-01 | デモ公開後、月次で「デモ起動数・提出数・ホスティング費用」を記録すること |
| COST-02 | input_seconds の分布を集計し、記事に掲載すること。「読者の中央値は何秒だったか」は、主張を裏付ける最強のデータになる(EMB-09) |
| COST-03 | copy_yesterday_used の利用率と、利用時/非利用時の入力時間差を記録すること |
| COST-04 | 記録した実測値を記事に掲載し、確認日を明記すること |
| COST-05 | 既製日報サービス・AI APIの価格は変動するため、この文書に金額を書き込まない。 記事執筆時点で公式ページを確認し、確認日を明記すること |
「このデモを触った読者の入力時間の中央値は 1分34秒でした」——この一文が、どんな機能説明よりも強い。
12. 実装タスク
この順に進める。フェーズを飛ばさない。 完了時は [x] に更新する。
Phase 0 — 基盤と計測エンジン(4日)
0-1. 初期化
- Next.js 15 / TypeScript strict / App Router / Tailwind で初期化
- ESLint / Prettier / husky(pre-commit で lint + typecheck)
- 9章のディレクトリ構成を作成
0-2. lib/metrics/(このプロジェクトの心臓部)
-
input-timer.ts:入力時間の計測(7章のコード参照)(MEA-01, MEA-02) -
estimate.ts:テンプレートの推定入力時間(FR-603) -
submission.ts:提出率(対象曜日・在籍期間を考慮)(FR-502) -
response.ts:返信率・平均返信時間(FR-503, FR-508) -
unread.ts:未読率(FR-504) -
distribution.ts:入力時間の分布、前日コピーあり/なしの比較(FR-510, FR-512) -
patterns.ts:提出が落ちる曜日・時刻の検出(FR-507) -
new Date()を内部で呼ばない。現在時刻は引数で受け取る - 単体テストを網羅的に書く。以下を必ず含めること:
- タブ非表示中の時間が入力時間に加算されないこと(最重要)
- 30秒以上の無操作が加算されないこと
- 複数セッションに分けた場合、累積されること(MEA-05)
- 推定入力時間が、項目の追加・削除で正しく変わること
- 任意項目が推定入力時間に加算されないこと
- 提出率が対象曜日と在籍期間を正しく考慮すること
- 返信率がコメントとリアクションの両方を数えること
- 前日コピーあり/なしの入力時間差が正しく算出されること
0-3. lib/search/(自前実装。記事の技術パートの見せ場)
-
normalize.ts:ひらがな/カタカナ/半角全角の正規化 -
index.ts:転置インデックスの構築(非同期・入力をブロックしない) -
query.ts:検索とスコアリング -
highlight.ts:ヒット箇所のハイライト - 単体テストを書く。表記ゆれが吸収されること
- 日報1,500件 + コメント620件で検索が 100ms以下をベンチマークで確認(NFR-04)
0-4. lib/summary/(週次まとめ生成。純粋関数)
-
group.ts:実施内容のグループ化(タグ・案件で束ねる) -
totals.ts:数値項目の合算 -
generate.ts:週次まとめの生成(SUM-01〜05, FR-303) - 未解決の課題だけを抽出すること/未提出日を明示すること/生成できなかった項目を明示すること
- 根拠の日報IDを必ず含めること(SUM-03)
- 単体テストを書く。日報が0件の週でも壊れないこと
0-5. 型とシード
-
lib/types/に7章の型定義をすべて実装 -
lib/seed/の全ファイル - 日報の本文を、業種の世界観を持つ実在しそうな文章にする。ダミー文を1件も残さない(最重要)
- 入力時間の実データを持たせる。前日コピー利用時は明確に短く
- 課題・相談を1割程度、うち数件を「上司のみ」に設定
- 未対応3日以上の課題、返信の薄い上司、未読の溜まり、金曜の未提出集中を作る
- 「気づき」が空の日報を多く作る(任意項目の実態を再現)
- テンプレートのバージョンを2つ持つものを1つ作る(FR-607 の実演用)
- 日報は提出対象曜日にのみ存在すること(土日に並べない)
- 日付は現在日時からの相対で生成する
0-6. ストアとリポジトリ
-
lib/store/{session,data,settings,draft,sync,ui}.tsを Zustand + persist で実装 -
lib/repo/_scope.tsを最初に実装する(7章のコード参照)(SCP-01〜05) -
lib/repo/の全モジュール。全メソッド async、Result型、スコープ適用 - スコープの単体テストをこの時点で書く:
- メンバーが他チームの日報を取得できないこと(SCP-03)
- 個人別の入力時間が本人以外に返らないこと(SCP-04, SEC-06)
- 「上司のみ」の課題ブロックが、対象外のメンバーに返らないこと(SCP-05)
- 入力・下書き保存・提出・検索に擬似ディレイを入れない(FR-119)
0-7. デザインシステム
-
app/globals.cssにカラートークンを CSS 変数で定義(色を使うのは「課題あり」と「未読」の2つだけ) -
tailwind.config.tsから CSS 変数を参照するよう theme を拡張 - フォント(Noto Sans JP / Roboto Mono)を
next/fontで最適化 - 本文 15px・行間1.9・最大幅680px のタイポグラフィを定義(8章)
- 1カラムのアプリシェル(ヘッダー + タブナビ。サイドバーを持たない)
- shadcn/ui を導入し、トークンに合わせて上書き
- 共通コンポーネント:
AutoTextarea(自動高さ、アニメーションなし)/ChipSelect(セレクトを使わない)/EmptyState(2種の出し分け)/Skeleton(本文3行分の高さを確保) -
prefers-reduced-motionの対応 -
/dev/componentsにコンポーネントカタログを作成
0-8. デモ基盤
-
RoleSwitcher(3ロール + メンバーの3ペルソナ選択)(FR-701) - デモ切替バー(濃い墨色の帯)+ 「これはデモです。書いた内容は送信されません」(SEC-02)
- 「デモをリセット」(FR-706)/ロール権限外アクセス時の案内画面(FR-709)
- 元記事リンク・資料ダウンロード(FR-711)
Phase 0 完了チェック
-
lib/metrics/のテストが全パターン通る - 「タブを離れた時間・30秒無操作が入力時間に入らない」がテストで検証済み
- 「個人別の入力時間が本人以外に返らない」がテストで検証済み
- 「上司のみの課題が対象外メンバーに返らない」がテストで検証済み
- 検索が100ms以下
-
/dev/componentsで本文の組版(15px・行間1.9・680px)が正しく表示される
Phase 1 — 入力体験(4日・最重要フェーズ)
1-1. 入力フォーム
-
ReportForm.tsx:1カラム、項目間32px、枠で囲まない(8章) -
FieldRenderer.tsx:8種の項目種別に対応(FR-207) -
ChipSelect.tsx:選択式をチップのタップで完了(セレクトを開かせない)(IX-04) -
AutoTextarea.tsx:自動高さ調整。伸長をアニメーションさせない(IX-05) - 数値項目は
inputMode="numeric"(FR-114) - 3ブロック(報告/課題・相談/気づき)の構造(FR-201)
- 「気づき」に「空のままでも提出できます」を明示(FR-203)
- 課題ブロックに公開範囲のセレクトを併置(FR-204)
- ブラウザの音声入力を妨げないこと(独自キーハンドラを被せない)(FR-116)
1-2. 入力時間の計測と表示
-
InputTimerBadge.tsx:画面右上に控えめに表示。大きくしない、色を付けない(MEA-03, FR-103) -
useDraftAutosave.ts:入力停止から2秒後に保存。入力をブロックしない(FR-108, IX-02) - 保存状態の表示、
aria-liveでの控えめな通知(A11Y-09) - アプリを閉じて再訪しても下書きが復元されること(FR-109)
- 入力時間の累積(複数セッション)(FR-105)
1-3. 前日コピーと残り項目
-
CopyPreviousButton.tsx:前回内容の引き継ぎ(FR-106) - 埋まった項目に若草の背景が一度だけ走るアニメーション(400ms)
-
MissingFieldsHint.tsx:提出ボタン直上に「あと N項目:〜」(FR-107, FR-113) -
aria-live="polite"での通知(A11Y-08) -
ChipSelectへのサジェスト(過去の入力から候補)(FR-111)
1-4. 提出
- 提出はワンタップ。確認ダイアログなし(FR-112, IX-03)
-
⌘Enterで提出、Escで下書き保存して閉じる(IX-06) -
SubmitResultModal.tsx:入力時間(30px Mono)+ 平均との比較(FR-104) - 短縮できた場合は伝え、遅い場合は何も言わない(ライティング規約)
- 提出後30分の編集可能(FR-117)/編集履歴(FR-117)
- SC-013 下書き一覧/SC-001 ホーム
- 画像添付(Canvas で圧縮、IndexedDB)(FR-115)
1-5. 埋め込みモード
- SC-900 埋め込みモード:入力フォーム + タイマー + 前日コピー(FR-713, 2章)
- 実際に入力でき、入力時間が計測され、提出後に秒数が表示されること(EMB-04)
- 前日コピーで「あと2項目」が出ること(EMB-05)
- 提出はできるがアプリ内遷移をしない。「全画面で続きを見る」を
target="_blank"(EMB-06) - カメラ・位置情報を呼ばない(EMB-07)
- 初期バンドル 60KB以下を実測(NFR-13)
Phase 1 完了チェック
- フローAが完走する(埋め込みで書く → 前日コピー → 提出 → 入力時間が出る)
- 文字入力が1フレームも落ちない(NFR-01)
- 下書き保存が入力をブロックしない(IX-02)
- 前日コピーで入力時間が明確に短くなる
- タブを離れて戻っても入力時間が正しい
Phase 2 — 読む・返す(4日)
2-1. タイムライン
- SC-100 チームのタイムライン:日付順カード、1カラム760px(FR-401)
-
ReportCard.tsx:未読は左端に若草の点/課題ありは細い帯 + バッジ(FR-403, A11Y-05) - フィルタ(メンバー/案件/課題あり/未読のみ/期間)(FR-402)
- 仮想スクロール、60fps(NFR-09)
- キーボード操作(
↑↓EnterR)(IX-07)
2-2. 詳細と反応
- SC-101 / SC-021 日報の詳細(本文680px、行間1.9)
-
IssueBlock.tsx:課題ブロックの強調 + 対応状態のセレクト + 「上司のみ」の鍵表示(FR-206) -
ReadReceipts.tsx:既読者のアバターを重ねて表示。書き手側にも出す(FR-404) - 既読は3秒以上表示で記録(FR-405, IX-08)
-
ReactionBar.tsx:3種のチップ。コメントより先に、押しやすい位置(FR-408) -
CommentList.tsx:折りたたまない。投稿ボタンは常時表示(FR-406) - メンション(FR-407)/楽観的更新(IX-09)
- SC-040 通知センター(通知はプレビュー表示)
2-3. 課題と検索
- SC-102 課題の一覧:対応状態と経過日数(FR-409)
- 未対応3日以上の強調(FR-410)
- SC-103 メンバーのタイムライン(FR-411)
- SC-110 検索:ハイライト表示、300msデバウンス、100ms以下(FR-412〜414, IX-10)
- 絞り込みのURLクエリ反映(FR-413)
- SC-120 エクスポート(CSV/Markdown)(FR-415)
Phase 2 完了チェック
- フローBが完走する(課題を書く → 上司が読む → コメント → 書き手に既読とコメントが見える)
- 4章「ロールを跨ぐ体験」の1・2・7が動作する
- 「上司のみ」の課題が、対象外メンバーの画面に表示されない
- 検索が100ms以下でハイライト付きで返る
Phase 3 — 週次まとめ(2日)
- SC-030 週次まとめの生成画面
- 生成された下書きをセクションごとに表示(実施内容/数値合計/未解決の課題/気づき/未提出日)
-
SourceReportLinks.tsx:根拠の日報へのリンク(日付チップ)(SUM-03, FR-305) - 生成できなかった項目を
--ink-400で明示(SUM-04, FR-306) - 本人の追記欄(振り返り/来週の予定)
- 必ず下書き。自動提出しない(SUM-02, FR-304)
- SC-031 週次まとめ一覧
- 週次まとめへの既読・コメント(FR-309)
- 週の起算曜日の設定(FR-308)
Phase 3 完了チェック
- 4章「ロールを跨ぐ体験」の6が動作する
- 日報が0件の週でも生成が壊れない
- 根拠の日報にリンクが張られている
Phase 4 — 運用の計測とテンプレート管理(4日)
4-1. 運用ダッシュボード
- SC-200 4指標カード(提出率・返信率・平均入力時間・未読率)+ 前週比(FR-501)
- 悪化した指標のみ符号を色付け(8章)
- SC-201 提出状況グリッド:メンバー×日付、記号で区別(FR-505, A11Y-04)
- リマインド送信(プレビュー表示)(FR-506)
- 提出が落ちる曜日・時刻の検出と提示(FR-507)
- 「これは評価画面ではなく運用画面です」の旨を明記(ETH-03)
- SC-202 返信率:上司別、平均返信時間、返信されていない日報の抽出(FR-508, FR-509)
- SC-203 入力時間の分布:チーム平均、ヒストグラム、テンプレート別、前日コピー比較(FR-510, FR-512)
- 個人別ランキングを作らない。表としても読めるようにする(FR-511, A11Y-12)
- SC-230 数値集計レポート(FR-513)
4-2. テンプレートと設定
- SC-210 テンプレート管理:項目の追加・削除・D&D並び替え + キーボード操作(FR-601, A11Y-03)
-
EstimateBadge.tsx:画面上部に「項目数 N/推定入力時間 M分S秒」を常時表示(FR-602) - 項目を追加・削除すると即座に更新されること(FR-604)
- SC-211 書き手視点のプレビュー(FR-605)
- チーム別のテンプレート割当(FR-606)
- テンプレート変更後も、過去の日報が当時の構造で表示されること(FR-607)
- SC-220 提出ルール:期限・対象曜日・曜日別リマインド時刻・編集可能時間(FR-608, FR-609)
- SC-221 チーム・メンバー管理(FR-610)
- SC-222 案件・作業種別マスタ(使用件数と未使用の検出)(FR-611)
- SC-223 通知文面の設定(FR-612)
- SC-240 操作ログ(FR-613)
Phase 4 完了チェック
- フローCが完走する(返信率の悪化に気づく → 未提出の曜日集中を発見 → リマインド時刻とテンプレートを調整)
- 4章「ロールを跨ぐ体験」の4と5が動作する
- テンプレートに項目を追加すると推定入力時間が伸びる
- 個人別の入力時間ランキングがどこにも存在しない
Phase 5 — オフライン・記事統合・仕上げ(3日)
5-1. オフライン
- Service Worker / PWA マニフェスト/オフライン検知とバナー
- オフラインでの作成・下書き保存・提出と未同期バッジ(FR-118)
- オンライン復帰時の自動同期
- localStorage 上限時の圧縮処理(NFR-17)
5-2. 記事統合
- 記事側の埋め込みコードを作成し、実際の記事ページで動作確認(EMB-01〜07)
- 埋め込みで「実際に書けて、秒数が出る」ことを複数ブラウザで確認
- 記事の Core Web Vitals を埋め込み前後で比較検証
- 「全画面で試す」カードの設置
- 資料PDF(日報の目的整理シート・損益分岐計算シート)の作成とダウンロード導線
- 元記事リンク・問い合わせ導線
- GA4 イベント設定。
input_secondsとcopy_yesterday_usedを必ず含める(EMB-08, EMB-09) - GA4 に日報の本文を送っていないことを確認(EMB-10, SEC-03)
-
?embed=1をnoindexに、デモ本体の title / description / OGP(EMB-11)
5-3. 体験の総点検
- 4章「ロールを跨ぐ体験」の7パターンをすべて手動で確認
- 全28画面を開き、空の画面が1つもないことを確認
- 日報の本文にダミー文が1件も残っていないことを確認
- 空状態(データ0件/絞り込み0件)の出し分けを確認
- ETH-01〜06 の倫理要件をすべて確認(個人別ランキングがない、評価に使えない、監視機能がない)
- ガイドツアーを実装(FR-712)
5-4. アクセシビリティ監査
- axe-core を全画面に実行し、Critical / Serious を0件に
- 本文のコントラスト比が7:1以上であることを実測(A11Y-01)
- 「課題あり」「未読」「提出状況」が色のみで伝えられていないことを確認(A11Y-04, A11Y-05)
- キーボードのみで全画面・入力・タイムライン・テンプレート管理を完遂
- スクリーンリーダーで不足項目の案内と提出完了が読み上げられることを確認
- フォントサイズ200%での確認(A11Y-14)
5-5. パフォーマンス
- グラフを動的import に切り出し、初期バンドルを180KB以下に(NFR-14)
- 文字入力で1フレームも落ちないことを実測(NFR-01)
- 提出200ms以下、検索100ms以下、週次まとめ生成300ms以下を実測(NFR-03, NFR-04, NFR-06)
- タイムラインのスクロール60fpsを実測(NFR-09)
- Lighthouse で Performance 95 / Accessibility 95(NFR-15)
5-6. 公開と記録
- Playwright で フローA・B・C の E2E
- Vitest:
lib/metricslib/summarylib/search_scopeのカバレッジ85%以上 -
/dev/componentsを本番で非公開に - Vercel へデプロイ
-
input_secondsの分布とcopy_yesterday_usedの利用率の記録を開始(COST-01〜03) - 解説動画(3分)の収録:前日コピーで書く → 入力時間1分12秒 → 上司が読んでコメント → 既読が見える → テンプレートに項目追加で推定時間が伸びる → 返信率ダッシュボード
- 各フェーズのキャプチャを整理し、記事の「方法」パートの素材にまとめる
合計 21営業日(約4週間)/1名専任 + レビュー体制。
工数配分の意図: Phase 1(入力体験)に4日を割いているのは、このアプリの価値が入力の速さに集約されているから。前日コピー、チップ選択、下書き自動保存、残り項目の提示、入力時間の計測——どれ1つ欠けても「書くのが面倒」に戻る。 Phase 4(運用の計測)に4日を割いているのは、「日報アプリは作って終わりではない」という記事の主張を、機能として証明する部分だから。提出率・返信率・推定入力時間が測れることが、他の日報アプリとの差になる。
13. 受入基準
- 6章の優先度 P1 の要件がすべて実装され、テストで合格していること
- 5章の主要フローA・B・Cが、エンドツーエンドで完走すること
- 4章「ロールを跨ぐ体験」の7パターンがすべて動作すること
- 文字入力で1フレームも落ちず、下書き保存が入力をブロックしないこと
- 入力時間が計測され、タブ非表示中と30秒以上の無操作が加算されていないこと
- 複数回に分けて書いた場合、入力時間が累積されること
- 「前日をコピー」で大半の項目が埋まり、「あと N項目」が明示されること
- 提出後に入力時間が表示され、平均との比較が出ること。遅い場合に評価的な文言が出ないこと
- 提出がワンタップで、確認ダイアログが出ないこと。不足項目が押す前に提示されること
- 日報が「報告/課題・相談/気づき」の3ブロックで構造化されていること
- 「気づき」が空でも提出でき、催促の文言が出ないこと
- 「課題・相談」の公開範囲を書き手が選べ、「上司のみ」の場合に対象外メンバーへ返らないこと(リポジトリの段階で除外)
- 既読が3秒以上の表示で記録され、書き手側にも既読者が表示されること
- 課題ありの日報が視覚的に前に出て、未対応3日以上が強調されること
- 週次まとめが日報から自動生成され、根拠の日報にリンクが張られ、必ず下書きであること
- 生成できなかった項目が明示されること
- 全文検索が100ms以下で、ヒット箇所がハイライトされること(自前実装であること)
- 運用ダッシュボードに提出率・返信率・平均入力時間・未読率の4指標と前週比が出ること
- テンプレート管理に「項目数と推定入力時間」が常時表示され、項目の追加・削除で即座に更新されること
- 個人別の入力時間ランキングが、どの画面にも存在しないこと。個人別の入力時間が本人以外に返らないこと
- 提出状況グリッドに「評価画面ではない」旨が明記されていること
- メンバーが他チームの日報を一切取得できないこと
- テンプレート変更後も、過去の日報が当時の構造で表示されること
- 初期バンドルが180KB以下、Lighthouse Performance 95以上であること
- 日報の本文にダミー文(「本日の業務を行いました」等)が1件も存在しないこと
- 日報の本文・氏名・画像が外部に一切送信されていないこと(DevTools と GA4 のイベントで確認)
- 全28画面のいずれにも空の状態がなく、リアルなデータが表示されていること
- 本文のコントラスト比が7:1以上であること
- 「課題あり」「未読」「提出状況」が色のみで伝えられていないこと
- axe-core で Critical / Serious の指摘が0件であること
- データアクセスがすべて
lib/repo/を経由し、入力時間の計測がlib/metrics/input-timer.tsに集約されていること - RELATE / KURA / TOKIWA と並べたときに、明確に別のプロダクトとして見えること(1カラム・広い余白・15px本文が効いていること)
判断に迷ったときのルール
- 仕様がこの文書にない場合は、実装せずに質問する。 推測で作らない
- 入力を増やす機能を提案しない。 機能追加は「入力を減らす」か「読まれるようにする」に寄与すること。これがこのプロジェクトで最も重要なルール
- 入力の反応速度を最優先する。 入力中に重い処理を走らせない。下書き保存も入力をブロックしない
- 入力時間で人を評価させない。 個人別ランキングを作らない。本人以外に個人別の値を返さない(ETH-01, SCP-04)
- 提出率を評価指標として提示しない。 運用画面であることを明記する(ETH-03)
- リマインドで責めない。 文言を厳しくしない(ETH-04)
- 書き手の心理的安全を仕様で守る。「課題・相談」の公開範囲を書き手が選べること(ETH-05)
- 監視機能を実装しない。 位置情報・スクリーンショット・操作の記録による書き手の監視はしない(ETH-06)
- 週次まとめはルールベースで、必ず下書き。 AIを呼ばない、自動提出しない
- リッチテキストエディタを入れない。 バンドルと入力速度を守る
- 全文検索を自前で書く。 ライブラリを入れない。これが記事の技術パートの見せ場
- 日報の本文にダミー文を残さない。 このデモは本文の質がそのまま説得力になる
- スコープ外(3章 Won't have)は実装しない。特に承認ワークフロー・工数管理・AI添削は入力増につながる
- 「デモだから」を理由に品質を落とす判断はしない
- RELATE / KURA / TOKIWA のトークン・コンポーネントをコピーしない。 HIBI だけが1カラム・広い余白・15px本文