reservation-status

予約状況自動更新システム

眼科クリニックの予約状況を自動判定し、ホームページのバナーに表示するシステムです。

対応予約: 一般予約・視野予約の2種類の予約システムを独立して管理


🔗 クイックリンク集

📱 予約状況チェッカー(メイン)

🖥️ タイムスロット表示(iframe埋め込み用)

一般予約

視野予約

🏷️ バナー表示(ホームページ埋め込み用)

🚪 エントランスディスプレイ

📊 ダッシュボード


📌 主な機能

  1. 予約状況チェッカー(メイン) - スマホ/壁掛けの2モード対応
    • 2本構成タイムラインバー(午前/午後)
    • 白抜きドットで空き枠を可視化
    • 動的ラベル拡張(13時台/18時台の枠がある場合)
  2. Chrome拡張機能(3種類)
    • 予約枠数表示拡張機能 - タスクバーに枠数を数字で表示(一般・視野)
    • ディスプレイ自動リロード拡張機能
  3. Docker自動化システム - Cloud Runで自動実行(タイムスロット抽出)
  4. タイムスロット表示システム - 空き時間枠を詳細に表示(iframe埋め込み用)
  5. GitHub Pagesバナー - ホームページに埋め込み可能なバナー
  6. 曜日・時間対応 - 火曜18:30以降は木曜、水曜は木曜をチェック

🗂️ ディレクトリ構成

reservation-status/
├── timeslot-status-checker.html    # ⭐メイン: 予約状況チェッカー(スマホ/壁掛け2モード)
├── timeslot-banner.html            # バナー: タイムスロット表示(iframe埋め込み用)
├── timeslot-display.html           # 一般予約: 時間枠を抽出して表示
├── timeslot-display-shiya.html     # 視野予約: 時間枠を抽出して表示
├── entrance-display.html           # エントランスディスプレイ(入口用)
├── entrance-display-debug.html     # エントランスディスプレイ(デバッグ版)
├── dashboard.html                  # 予約傾向ダッシュボード(分析・可視化)
├── chrome-extension-timeslot-general/  # Chrome拡張機能(一般予約枠数表示)
├── chrome-extension-timeslot-shiya/    # Chrome拡張機能(視野予約枠数表示)
├── chrome-extension-auto-reload/   # Chrome拡張機能(ディスプレイ自動リロード)
├── chrome-web-store/           # Chrome Web Store公開用ファイル
├── docker-timeslot-checker/    # 一般予約: タイムスロット抽出システム(Cloud Run)
├── docker-timeslot-checker-shiya/  # 視野予約: タイムスロット抽出システム(Cloud Run)
├── docs/                       # ドキュメント
│   └── handover-to-claude-code.md  # システム全体の引き継ぎ資料
└── Downloads/                  # バナー用画像ファイル・設定ファイル

🚀 クイックスタート

Docker自動化(完全自動)【おすすめ】

Cloud Runで自動実行(タイムスロット抽出)


バナーをホームページに埋め込み

タイムスロット版(時間枠表示)

<iframe
  src="https://nekonekoganka.github.io/reservation-status/timeslot-banner.html"
  width="100%"
  height="280"
  frameborder="0"
  scrolling="no"
  style="border:none;">
</iframe>

表示例:

応急措置版(夜間の誤表示対策)

19〜23時台の0〜30分に発生する日付判定問題を回避するため、該当時間帯はバナーを非表示にします。

<div id="reservation-banner-container">
  <iframe
    src="https://nekonekoganka.github.io/reservation-status/timeslot-banner.html"
    width="100%"
    height="280"
    frameborder="0"
    scrolling="no"
    style="border:none;">
  </iframe>
</div>

<script>
function checkAndHideBanner() {
  var now = new Date();
  var hour = now.getHours();
  var minute = now.getMinutes();
  var container = document.getElementById('reservation-banner-container');

  // 19時〜23時台で、0分〜30分の間は非表示
  if (hour >= 19 && hour <= 23 && minute >= 0 && minute <= 30) {
    container.style.display = 'none';
  } else {
    container.style.display = 'block';
  }
}

// ページ読み込み時と1分ごとにチェック
checkAndHideBanner();
setInterval(checkAndHideBanner, 60000);
</script>

📖 ドキュメント


🔧 技術スタック


📊 動作フロー

Cloud Scheduler(定期実行)
    ↓
Cloud Run(Puppeteer)
    ↓ (時間枠抽出)
Cloud Storage(JSON)
    ↓
GitHub Pages(バナー・ディスプレイ・チェッカー)

🏥 クリニック情報

ふじみ野ひかり眼科


⚙️ 設定

Cloud Storage

const BUCKET_NAME = 'reservation-timeslots-fujiminohikari';
const FILE_NAME_GENERAL = 'timeslots.json';
const FILE_NAME_SHIYA = 'timeslots-shiya.json';

予約ページURL

一般予約: https://ckreserve.com/clinic/fujiminohikari-ganka/fujimino
視野予約: https://ckreserve.com/clinic/fujiminohikari-ganka/fujiminohikari

Cloud Run サービス

種類 サービス名 URL
一般予約 timeslot-checker https://timeslot-checker-224924651996.asia-northeast1.run.app
視野予約 timeslot-checker-shiya https://timeslot-checker-shiya-224924651996.asia-northeast1.run.app

Cloud Scheduler ジョブ

種類 ジョブ名 呼び出し先
一般予約 reservation-timeslot-checker-job /check エンドポイント
視野予約 reservation-timeslot-checker-shiya-job /check エンドポイント

注意: Schedulerのジョブ名とCloud Runのサービス名が異なるため、デプロイ後はSchedulerの向き先URLも確認が必要です。

デプロイ手順

# 一般予約
cd docker-timeslot-checker
gcloud run deploy timeslot-checker \
  --source . \
  --region asia-northeast1 \
  --memory 1Gi

# 視野予約
cd docker-timeslot-checker-shiya
gcloud run deploy timeslot-checker-shiya \
  --source . \
  --region asia-northeast1 \
  --memory 1Gi

デプロイ後の確認:

# 履歴ファイルが作成されているか確認
gsutil ls gs://reservation-timeslots-fujiminohikari/history/general/
gsutil ls gs://reservation-timeslots-fujiminohikari/history/shiya/

📅 日付判定ロジック


🤝 開発者向け

Claude Codeでの開発

このリポジトリはClaude Codeでの開発を想定しています。

引き継ぎ資料: docs/handover-to-claude-code.md


🏪 Chrome Web Store(非公開公開)

このChrome拡張機能は、Chrome Web Storeに非公開公開する準備が完了しています。


🆕 最近のアップデート(技術的な改善履歴)

2025年12月8日 - Cloud Run再デプロイと履歴保存機能の有効化 🆕

🔧 Cloud Storage URL修正

概要: entrance-display.htmldashboard.html のCloud Storage URLが古いバケット名を参照していた問題を修正しました。

修正内容: | ファイル | 修正前 | 修正後 | |—|—|—| | entrance-display.html | fujimino-ophthalmology-reservations | reservation-timeslots-fujiminohikari | | dashboard.html | fujimino-ophthalmology-reservations | reservation-timeslots-fujiminohikari |

🚀 Cloud Run再デプロイ

概要: 履歴保存機能を有効化するため、Cloud Runサービスを再デプロイしました。

デプロイしたサービス:

⚠️ Cloud Scheduler向き先修正

問題: Cloud Schedulerが古いサービス名を呼び出していたため、履歴データが保存されていませんでした。

種類 Schedulerが呼んでいたサービス 実際のサービス名
一般予約 reservation-timeslot-checker timeslot-checker
視野予約 reservation-timeslot-checker-shiya timeslot-checker-shiya

修正後のScheduler設定:

# 一般予約
gcloud scheduler jobs update http reservation-timeslot-checker-job \
  --location=asia-northeast1 \
  --uri="https://timeslot-checker-224924651996.asia-northeast1.run.app/check"

# 視野予約
gcloud scheduler jobs update http reservation-timeslot-checker-shiya-job \
  --location=asia-northeast1 \
  --uri="https://timeslot-checker-shiya-224924651996.asia-northeast1.run.app/check"

📊 履歴保存機能の有効化

概要: ダッシュボード用の履歴データ保存が開始されました。

保存先:

gs://reservation-timeslots-fujiminohikari/
├── history/
│   ├── general/2025-12-08.json  ✅ 作成確認済み
│   └── shiya/2025-12-08.json    ✅ 作成確認済み

効果:


2025年12月7日 - タイムラインバー2本構成と白抜きドット

📊 タイムラインバーの大幅改善

概要: 予約状況チェッカーとChrome拡張機能のタイムラインバーを、より見やすい2本構成(午前/午後)に変更し、ドットを白抜きスタイルに統一しました。

1. 2本構成タイムラインバー

午前バー: 10:00〜13:00(13時台の枠があれば14:00まで拡張)
午後バー: 15:00〜18:00(18時台の枠があれば18:30まで拡張)

2. 白抜きドット

background: white;
border: 3px solid [テーマカラー];

3. 適用範囲

変更ファイル:


2025年12月7日 - 旧スプレッドシートシステムのファイル削除

概要: 旧スプレッドシート経由の予約判定システムで使用していたファイルを完全に削除しました。

削除したHTMLファイル(9件):

削除したその他ファイル(6件):

効果: 15ファイル、17,171行を削減


2025年12月6日 - 予約状況チェッカー2モード構成

概要: 予約状況チェッカーを、スマホモード(800px以下)と壁掛けモード(801px以上)の2モード構成に簡素化しました。これにより、旧来の全画面ディスプレイ(display.html等)は不要になりました。

スマホモード(800px以下):

壁掛けモード(801px以上):


2025年12月6日 - 旧システム削除とバナーUI改善

🗑️ 旧システムの完全削除

概要: タイムスロット抽出システムへの移行完了に伴い、旧システム(スプレッドシート経由の判定システム)を完全に削除しました。

削除したCloud Runサービス:

削除したCloud Schedulerジョブ:

削除したディレクトリ(39ファイル、5,690行):

効果:

🎨 タイムスロットバナーのUI改善

概要: timeslot-banner.htmlのUIを大幅に改善し、より見やすく使いやすいデザインに更新しました。

1. 時間枠の個別ピル表示

修正前: 10:00・11:00・14:00…
修正後: [10:00] [11:00] [14:00] ほか

2. 日付形式の変更

修正前: 本日(土)
修正後: 本日(12/6)

3. モバイル表示の改善

変更ファイル:


2025年12月5日 - タイムゾーン問題の修正とCloud Run運用改善

🕐 18:30以降の時間判定が正しく動作しない問題を修正

問題: 一般予約のタイムスロット取得システムで、18:30以降でも「本日」の予約枠を参照してしまう問題が発生していました。視野予約は正常に動作していました。

症状:

原因:

  1. 古いリビジョンがCloud Runに残っていた - 複数のリビジョンが存在し、一部のリクエストが古いコード(タイムゾーン修正前)に当たっていた
  2. タイムゾーン処理の環境依存 - process.env.TZtoLocaleString がCloud Run環境で不安定だった
  3. メモリ不足 - 512MBではPuppeteerがメモリ不足でクラッシュし、インスタンスの再起動が発生していた

修正内容:

1. 日本時間取得関数をUTCオフセット方式に変更

function getJapanTime() {
  const now = new Date();

  // 日本時間はUTC+9時間(環境に依存しない確実な方法)
  const japanOffset = 9 * 60 * 60 * 1000;
  const japanTime = new Date(now.getTime() + japanOffset);

  // UTCメソッドを使って日本時間の各値を取得
  const year = japanTime.getUTCFullYear();
  const month = japanTime.getUTCMonth() + 1;
  const date = japanTime.getUTCDate();
  const hour = japanTime.getUTCHours();
  const minute = japanTime.getUTCMinutes();

  return { year, month, date, dayOfWeek, hour, minute };
}

2. Cloud Runのリビジョン整理

3. メモリを1GBに増加

gcloud run deploy timeslot-checker \
  --memory 1Gi \
  --region asia-northeast1

変更ファイル:

🔑 Google Chrome GPGキーエラーの修正

問題: gcloud builds submit 実行時に以下のエラーが発生:

E: The repository 'https://dl-ssl.google.com/linux/chrome/deb stable InRelease' is not signed.

原因: Puppeteerイメージ(ghcr.io/puppeteer/puppeteer:22.0.0)内のGoogle Chrome GPGキーが古くなっていた。

修正: Dockerfileに以下を追加:

# Google Chrome GPGキーを更新(古いキーが期限切れになる問題を回避)
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg && \
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list

🖼️ Chrome拡張機能アイコンに枠線を追加

概要: タイムスロット表示用のChrome拡張機能(一般予約・視野予約)のアイコンに、視認性向上のための枠線を追加しました。

変更内容:

実装コード:

const borderWidth = Math.max(1, Math.round(size * 0.08));
ctx.strokeStyle = themeColor;
ctx.lineWidth = borderWidth;
ctx.strokeRect(borderWidth / 2, borderWidth / 2, size - borderWidth, size - borderWidth);

変更ファイル:


🛠️ トラブルシューティング:Cloud Run運用のポイント

リビジョン管理

Cloud Runは複数のリビジョン(バージョン)を保持できます。古いリビジョンが残っていると、一部のリクエストが古いコードに当たる可能性があります。

確認方法:

gcloud run revisions list --service timeslot-checker --region asia-northeast1

最新リビジョンに100%トラフィックを割り当て:

gcloud run services update-traffic timeslot-checker --to-latest --region asia-northeast1

デプロイ後の推奨手順:

# 1. ビルド
gcloud builds submit --tag gcr.io/$(gcloud config get-value project)/timeslot-checker

# 2. デプロイ(メモリ1GB指定)
gcloud run deploy timeslot-checker \
  --image gcr.io/$(gcloud config get-value project)/timeslot-checker \
  --region asia-northeast1 \
  --platform managed \
  --memory 1Gi

# 3. トラフィックを最新に
gcloud run services update-traffic timeslot-checker --to-latest --region asia-northeast1

メモリ設定

Puppeteerを使用するサービスは、512MBでは不足する場合があります。

推奨設定:

タイムゾーン

Cloud Run環境でのタイムゾーン処理は環境依存のため、以下の方式を推奨:

推奨: UTCオフセット方式

const japanOffset = 9 * 60 * 60 * 1000;
const japanTime = new Date(now.getTime() + japanOffset);

非推奨: 環境変数依存

process.env.TZ = 'Asia/Tokyo'; // 効果が不安定

2025年12月5日 - 日付表示の統一と時間枠バナーの追加

📅 統一的な日付表示の実装

概要: Chrome拡張機能と予約状況チェッカーに、日付を含む統一的な表示を実装しました。一般予約と視野予約で表示が異なる問題を解決し、「明日(12/6)」のように日付を明示することでユーザーにわかりやすくなりました。

実装した機能:

1. 日付付き表示

修正前: "次の診療日 残り5枠" または "明日 ✕ 満枠"
修正後: "次の診療日(12/9) 残り5枠" または "明日(12/6) ✕ 満枠"

2. 表示の統一ロジック

function calculateDisplayTextWithDate(targetDate) {
  // dateフィールドから本日/明日/次の診療日を自動判定
  if (targetDate === currentDate)  "本日(MM/DD)"
  else if (targetDate === currentDate + 1)  "明日(MM/DD)"
  else  "次の診療日(MM/DD)"
}

3. 問題の解決

修正前の問題:

一般予約: date=6, displayText="次の診療日"
視野予約: date=6, displayText="明日"
→ 同じ日付なのに異なる表示!

修正後:

一般予約: "明日(12/6)"
視野予約: "明日(12/6)"
→ dateフィールドから計算するので統一表示!

変更ファイル:

改善効果:

  1. ✅ 一般と視野で表示が統一される
  2. ✅ 日付が明確になり、ユーザーにわかりやすい
  3. ✅ JSONのdisplayTextの不統一が自動修正される
  4. ✅ 月またぎも自動対応(12月31日→1月1日など)

🏷️ タイムスロットバナーの追加

概要: iframe埋め込み用のタイムスロット版バナーを新規作成しました。具体的な時間枠を表示することで、より詳細な予約情報を提供できます。

新規作成ファイル:

主な機能:

表示内容(空き枠がある場合):

✅ 明日の予約
10:00・11:00・14:00…
に 空きがございます
クリックして予約ページへ
更新:2025/12/05 14:30

表示内容(満枠の場合):

😔 本日(12/5)の予約は 満枠です
誠に恐れ入りますが、次の診療日以降で ご予約ください
クリックして 予約ページへ
更新:2025/12/05 14:30

技術仕様:

iframe埋め込みコード:

<iframe
  src="https://nekonekoganka.github.io/reservation-status/timeslot-banner.html"
  width="100%"
  height="280"
  frameborder="0"
  scrolling="no"
  style="border:none;">
</iframe>

改善効果:

  1. ✅ 具体的な時間枠を表示できる
  2. ✅ 日付表示で対象日が明確
  3. ✅ 従来版と同じサイズで置き換え可能
  4. ✅ Cloud Storageから最新データを取得

2025年12月5日 - 時間枠表示システムの大幅拡張

🎯 タイムスロット対応の予約状況チェッカーとChrome拡張機能

概要: 時間枠(タイムスロット)を数字で表示する新しいシステムを実装しました。○/×だけでなく、「残り何枠」かを具体的に表示し、詳細な空き時間リストも確認できます。

新規作成ファイル:

1. 時間枠対応の予約状況チェッカー

2. Chrome拡張機能(枠数表示版)

3. タイムスロット表示のデバッグパネル

主な機能:

1. 予約状況チェッカー(timeslot-status-checker.html)

2. Chrome拡張機能(枠数表示版)

表示方式:

アイコンクリック時:

データソース:

3. デバッグパネル(タイムスロット表示用)

機能:

使い方:

1. Dキーを押してデバッグパネルを開く
2. 「表示する時間枠の数」を入力(1〜20)
3. 「💾 保存」ボタンをクリック
4. 即座に表示が更新される

技術実装:

タイムスロット表示ロジック:

// デバッグ設定から表示する時間枠の数を取得
const settings = loadDebugSettings();
const maxSlots = settings.maxSlots; // デフォルト: 3

// 時間枠を「・」で区切って表示
let timeslotsText;
if (data.slots.length > maxSlots) {
    timeslotsText = data.slots.slice(0, maxSlots).join('') + '';
} else {
    timeslotsText = data.slots.join('');
}

Chrome拡張機能のアイコン生成:

// Canvas APIで枠数を太字の白い数字で表示
if (slotsCount > 0) {
    const fontSize = slotsCount >= 10 ? size * 0.5 : size * 0.6;
    ctx.font = `bold ${fontSize}px sans-serif`;
    ctx.fillStyle = 'white';
    ctx.fillText(slotsCount.toString(), size / 2, size / 2);
}

予約状況チェッカーの折りたたみ式リスト:

// カードタップで詳細表示/非表示を切り替え
function toggleTimeslots(type) {
    const container = document.getElementById(`timeslots-${type}`);
    container.classList.toggle('expanded');
}

📈 改善効果

  1. 具体的な情報表示: ○/×だけでなく「残り何枠」かを数字で表示
  2. 詳細な時間確認: タップやクリックで空き時間の詳細リストを確認可能
  3. 柔軟なカスタマイズ: デバッグパネルで表示する時間枠の数を自由に調整
  4. モバイル対応: スマホ・タブレットでも使いやすいUI
  5. 既存システムとの統合: Cloud Storageを通じてタイムスロット抽出システムと連携

📁 変更ファイル


2025年11月27日 - 背景点滅機能の実装と速度調整の改善

💡 通行人訴求に最適化された背景点滅システム

概要: デバッグページに背景点滅機能を実装し、通行人の注目を集めるための効果的なアニメーション機能を追加しました。背景のみを点滅させる技術により、テキストやアイコンの視認性を保ちながら目を引く演出が可能になりました。

更新ファイル:

実装された機能:

1. 背景のみを点滅させる仕組み

実装コード:

.display-container::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    pointer-events: none;
    opacity: 0;
    z-index: 1;
}
.display-container > * {
    position: relative;
    z-index: 2;
}

2. 3つの点滅モード

3. 2つの点滅パターン

4. 速度調整機能の段階的改善

第1段階(コミット c71dd13):

第2段階(コミット a71e305):

スライダー/数値入力切り替え:

function toggleSpeedInputMode() {
    customizeState.bgBlinkSpeedInputMode = !customizeState.bgBlinkSpeedInputMode;
    if (customizeState.bgBlinkSpeedInputMode) {
        // 数値入力モード
        sliderContainer.style.display = 'none';
        inputContainer.style.display = 'block';
    } else {
        // スライダーモード
        sliderContainer.style.display = 'block';
        inputContainer.style.display = 'none';
    }
}

5. 強度調整

6. 動的スタイル注入

function applyBgBlinkEffect() {
    const style = document.createElement('style');
    style.id = 'bgBlinkStyle';
    style.textContent = `
        #displayContainer::before {
            background: ${overlayColor};
            animation: ${animationName} ${duration} ease-in-out infinite;
        }
    `;
    document.head.appendChild(style);
}

アニメーション1タブのUI:

カスタマイズ状態の保存:

customizeState: {
    bgBlinkEnabled: false,
    bgBlinkMode: 'brighten',  // 'brighten', 'darken', 'flash-white'
    bgBlinkPattern: 'pulse',  // 'pulse', 'flash'
    bgBlinkSpeed: 1.5,  // 0.3〜5.0秒
    bgBlinkSpeedInputMode: false,  // false: スライダー, true: 数値入力
    bgBlinkIntensity: 0.3,
}

📈 改善効果

  1. 通行人訴求力の向上: 白フラッシュモードで遠くからでも注目を集める
  2. 柔軟な調整: 状況に応じて速度・強度・パターンを細かく調整可能
  3. 視認性の維持: 背景のみ点滅するため、テキストやアイコンは常に読みやすい
  4. 直感的なUI: スライダーと数値入力を切り替えて、好みの方法で設定
  5. 設定の永続化: localStorageに保存され、リロード後も設定を保持

🎯 推奨設定

通行人を呼び込みたい時:

営業時間中の控えめ表示:

📁 変更ファイル


[以降、既存の更新履歴が続きます…]


📄 ライセンス

このプロジェクトは私的利用を目的としています。


📞 お問い合わせ

システムに関する質問や問題は、Issuesでお知らせください。