試してみたブログ

AI関連・iPhone/Pixelなどのガジェット・音声入力・サーマルプリンタなど興味をある事をどんどん試してみた際の記録

行きつけのボルダリングジムの課題完登表をWEBアプリとして作る その1

背景

  • ClaudeCodeでいろいろ出来る様になったので、課題完登表を作ってみる
  • 試行錯誤してみる

Claudeで仕様を壁打ちする

  • とりあえず作りたいぼやっとしたものをとりあえず会話する
タブレットで1画面で、ボルダリング課題の一覧表が表示されており、課題をタップしたら何月何日に登ったかが登録される物を作成したい。
また人を上部で簡単に切り替えれる様にしたい。

技術スタックを何にするのが良いか、また何を次に決める必要があるか

仕様を確定させる

了解しました。Claude Codeに渡す詳細仕様をまとめます。

ボルダリング課題管理アプリ 詳細仕様書

1. プロジェクト概要

目的

タブレット/スマホで使えるボルダリング課題の登攀記録管理アプリ

主な利用シーン

  • ジムでタブレットを使って課題一覧を表示
  • 課題を登ったらその場でタップして記録
  • 4人分のデータを切り替えて管理

技術スタック

  • React + TypeScript + Vite
  • Tailwind CSS(レスポンシブ対応)
  • localStorage(データ永続化)
  • デプロイ:Netlify または Vercel

2. データ構造

2.1 マスタデータ

// 壁マスタ
type WallMaster = {
  id: string;
  name: string;  // 例: "A", "B", "屋根"
  order: number; // 表示順
}

// 記号マスタ
type SymbolMaster = {
  id: string;
  name: string;  // 例: "①", "②", "③"
  order: number;
}

// 色マスタ
type ColorMaster = {
  id: string;
  name: string;     // 例: "赤", "青", "黄"
  colorCode?: string; // 表示用カラーコード(オプション)
  order: number;
}

// グレードマスタ
type GradeMaster = {
  id: string;
  name: string;  // 例: "7級", "6級", "5級", "4級"
  order: number; // 難易度順(数字が小さいほど難しい)
}

2.2 課題データ

type Problem = {
  id: string;
  wallId: string;    // 壁マスタID
  symbolId: string;  // 記号マスタID
  colorId: string;   // 色マスタID
  gradeId: string;   // グレードマスタID
  createdAt: string; // ISO8601形式の日付
}

2.3 ユーザーデータ

type User = {
  id: string;
  name: string;  // 例: "太郎", "花子", "次郎", "四郎"
  order: number; // タブ表示順
}

2.4 登攀記録データ

type Ascent = {
  id: string;
  userId: string;    // ユーザーID
  problemId: string; // 課題ID
  date: string;      // 登った日付(YYYY-MM-DD形式)
  createdAt: string; // 記録作成日時
}

2.5 localStorage保存形式

{
  "wallMasters": WallMaster[],
  "symbolMasters": SymbolMaster[],
  "colorMasters": ColorMaster[],
  "gradeMasters": GradeMaster[],
  "users": User[],
  "problems": Problem[],
  "ascents": Ascent[]
}

3. 画面仕様

3.1 メイン画面レイアウト

┌────────────────────────────────────┐
│ [太郎] [花子] [次郎] [四郎]         │ ← ユーザー切り替えタブ
├────────────────────────────────────┤
│                                    │
│ 課題一覧テーブル                    │
│ ┌────┬────┬────┬────┬──────┐  │
│ │壁  │記号│色  │級  │完登  │  │
│ ├────┼────┼────┼────┼──────┤  │
│ │A   │①  │赤  │6級 │✓✓ 2回│← タップで記録追加
│ │A   │②  │青  │5級 │✓ 1回 │  │
│ │B   │①  │緑  │7級 │      │  │
│ │B   │②  │黒  │6級 │✓ 1回 │  │
│ └────┴────┴────┴────┴──────┘  │
│                                    │
├────────────────────────────────────┤
│ [+ 課題追加] [マスタ管理]           │ ← 下部固定ボタン
└────────────────────────────────────┘

3.2 レスポンシブ対応

タブレット(横向き・iPad等) - 1画面で課題一覧全体が見える - テーブル幅: 100%表示 - スクロール不要(または最小限)

スマホ(縦向き) - 縦スクロール可能 - テーブルは横幅いっぱいに表示 - タップ領域は十分な大きさを確保(最低44px四方)


4. 機能仕様

4.1 ユーザー切り替え

  • 画面上部にタブ形式で4人分のユーザー名を表示
  • タブをタップでユーザー切り替え
  • 選択中のユーザーは視覚的に区別(背景色変更など)
  • ユーザーごとに完全に独立した記録を表示

4.2 課題一覧表示

  • 壁 → 記号の順でソート表示
  • 各課題は1行で表示:壁・記号・色・級・完登状況
  • 完登列の表示形式:
    • 記録なし: 空白
    • 1回: ✓ 1回
    • 2回以上: ✓✓ 2回(✓の個数 + 回数テキスト)

4.3 登攀記録の追加

操作フロー: 1. ユーザーがログインしている状態で課題の「完登」列をタップ 2. 今日の日付(YYYY-MM-DD形式)で新しいAscentレコードを作成 3. localStorageに保存 4. 画面を即座に更新(✓が増える、回数が増える)

制約: - 同じ課題に同じ日付で複数回記録可能 - オンライン前提のため同期処理は不要

4.4 課題追加機能

モーダルまたは別画面で以下の入力フォーム: - 壁: ドロップダウン(マスタから選択) - 記号: ドロップダウン(マスタから選択) - 色: ドロップダウン(マスタから選択) - 級: ドロップダウン(マスタから選択)

動作: - 保存ボタンで新しいProblemレコードを作成 - 一覧に即座に反映

4.5 マスタ管理機能

別画面またはモーダルで4つのマスタを管理: 1. 壁マスタ(追加・編集・削除) 2. 記号マスタ(追加・編集・削除) 3. 色マスタ(追加・編集・削除) 4. グレードマスタ(追加・編集・削除)

各マスタの操作: - 一覧表示 - 新規追加フォーム - 編集(名前変更、表示順変更) - 削除(使用中の場合は警告)

4.6 初期データ

アプリ初回起動時にデフォルトデータを投入:

// デフォルトユーザー
users: [
  { id: "user1", name: "太郎", order: 1 },
  { id: "user2", name: "花子", order: 2 },
  { id: "user3", name: "次郎", order: 3 },
  { id: "user4", name: "四郎", order: 4 }
]

// デフォルトマスタ(例)
wallMasters: [
  { id: "w1", name: "A", order: 1 },
  { id: "w2", name: "B", order: 2 }
]

symbolMasters: [
  { id: "s1", name: "①", order: 1 },
  { id: "s2", name: "②", order: 2 },
  { id: "s3", name: "③", order: 3 }
]

colorMasters: [
  { id: "c1", name: "赤", order: 1 },
  { id: "c2", name: "青", order: 2 },
  { id: "c3", name: "黄", order: 3 },
  { id: "c4", name: "緑", order: 4 }
]

gradeMasters: [
  { id: "g1", name: "7級", order: 7 },
  { id: "g2", name: "6級", order: 6 },
  { id: "g3", name: "5級", order: 5 },
  { id: "g4", name: "4級", order: 4 }
]

5. UI/UXガイドライン

5.1 カラースキーム

  • シンプルで視認性の高い配色
  • タップ可能な要素は明確に区別
  • 選択中の状態は分かりやすく

5.2 タッチ操作の最適化

  • タップ領域は最低44px × 44px
  • ボタンやセルに十分な余白
  • 誤タップを防ぐための適切な間隔

5.3 表示の優先度

  • 課題一覧が最も重要(画面の大部分を占める)
  • ユーザー切り替えタブは常時表示
  • 課題追加・マスタ管理は下部に配置

6. 将来的な拡張案(今回は実装不要)

  • 日付ごとの記録削除機能
  • 統計表示(完登率、グレード別集計)
  • 複数端末での同期(Supabase等)
  • 課題の「張り替え」対応(アーカイブ機能)
  • エクスポート/インポート機能

7. 開発の進め方

フェーズ1: 基本機能

  1. プロジェクトセットアップ(React + Vite + TypeScript + Tailwind)
  2. データ構造とlocalStorage処理の実装
  3. メイン画面のUI実装(ユーザー切り替え + 課題一覧)
  4. 登攀記録の追加機能

フェーズ2: マスタ管理

  1. 課題追加機能
  2. マスタ管理画面

フェーズ3: 最適化

  1. レスポンシブ対応の調整
  2. デプロイ設定(Netlify/Vercel)

8. 参考情報

  • オンライン前提(オフライン対応は不要)
  • iPadでの利用を主に想定しつつスマホでも使える
  • ジムの課題表のような直感的なUIを目指す

作成されたもの

振り返り

  • 想定していた物とだいぶ異なる形となった
  • ここから細かく修正+仕様を追加していく