reservation-status

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

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

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


🔗 クイックリンク集

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

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

一般予約

視野予約

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

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

📊 ダッシュボード

📱 Androidアプリ


📌 主な機能

  1. 予約状況チェッカー(メイン) - スマホ/壁掛けの2モード対応
    • 2本構成タイムラインバー(午前/午後)
    • 白抜きドットで空き枠を可視化
    • 動的ラベル拡張(13時台/18時台の枠がある場合)
  2. Chrome拡張機能(3種類)
    • 予約枠数表示拡張機能 - タスクバーに枠数を数字で表示(一般・視野)
    • ディスプレイ自動リロード拡張機能
  3. Androidアプリ - スマホのステータスバーに空き枠数を常時表示
  4. Docker自動化システム - Cloud Runで自動実行(タイムスロット抽出)
  5. タイムスロット表示システム - 空き時間枠を詳細に表示(iframe埋め込み用)
  6. GitHub Pagesバナー - ホームページに埋め込み可能なバナー
  7. 曜日・時間対応 - 火曜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公開用ファイル
├── android-reservation-status/ # Androidアプリ(ステータスバー表示)
├── docker-timeslot-checker-unified/ # ⭐統合版: 一般・視野両対応(Cloud Run)
├── docker-timeslot-checker/    # 旧一般予約(統合版移行済み)
├── docker-timeslot-checker-shiya/  # 旧視野予約(統合版移行済み)
├── 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(定期実行)
    ↓ ?type=general または ?type=shiya
Cloud Run 統合版(Puppeteer)
    ↓ (時間枠抽出)
Cloud Storage(JSON)
    ├── timeslots.json(一般予約)
    └── timeslots-shiya.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 サービス(統合版・2026年1月15日〜)

種類 サービス名 URL
統合版 reservation-timeslot-checker-unified <YOUR_CLOUD_RUN_URL>

エンドポイント:

Cloud Scheduler ジョブ(統合版・2026年2月19日更新)

種類 ジョブ名 スケジュール 説明
一般予約(午前) timeslot-checker-unified-general-morning */2 7-12 * * 0-2,4-6 7:00-12:59、2分毎(水曜除く)
一般予約(午後) timeslot-checker-unified-general-afternoon */3 13-16 * * 0-2,4-6 13:00-16:59、3分毎(水曜除く)
一般予約(夜間) timeslot-checker-unified-general-offpeak */5 0-6,17-23 * * * 17:00-6:59、5分毎
一般予約(水曜) timeslot-checker-unified-general-wed */10 7-17 * * 3 水曜7:00-17:59、10分毎
視野予約(日中) timeslot-checker-unified-shiya-daytime */10 7-17 * * 0-2,4-6 7:00-17:59、10分毎(水曜除く)
視野予約(夜間) timeslot-checker-unified-shiya-offpeak */10 0-6,18-23 * * * 18:00-6:59、10分毎
視野予約(水曜) timeslot-checker-unified-shiya-wed */10 7-17 * * 3 水曜7:00-17:59、10分毎
日次集計(一般) generate-daily-summary-general 5 0 * * * 毎日0:05
日次集計(視野) generate-daily-summary-shiya 10 0 * * * 毎日0:10
月次集計(一般) monthly-summary-unified 0 1 1 * * 毎月1日1:00
月次集計(視野) monthly-summary-unified-shiya 5 1 1 * * 毎月1日1:05

統合版移行日: 2026年1月15日 コスト最適化: 2026年2月19日設定変更(一般:午前2分/午後3分/夜間5分、視野:終日10分、水曜10分) 年間削減効果: 約5,900円

コスト最適化の詳細: COST_OPTIMIZATION_GUIDE.md

注意: 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に非公開公開する準備が完了しています。


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

2026年1月15日 - Cloud Run統合版によるコスト削減 🆕

💰 2サービスを1サービスに統合

概要: 一般予約と視野予約で別々に稼働していた2つのCloud Runサービスを、1つの統合版サービスに統合しました。これにより、Cold startコストが半減し、月額コストを削減しました。

変更内容:

項目 変更前 変更後
Cloud Runサービス数 2 1
サービス名 timeslot-checker, timeslot-checker-shiya reservation-timeslot-checker-unified
1日あたりコスト 約75円 約35-50円
月額コスト 約2,250円 約1,050-1,500円

統合版の仕組み:

【旧構成】
Cloud Scheduler → Cloud Run(一般) → timeslots.json
Cloud Scheduler → Cloud Run(視野) → timeslots-shiya.json

【統合版】
Cloud Scheduler → Cloud Run(統合) → timeslots.json
                  ?type=general

Cloud Scheduler → Cloud Run(統合) → timeslots-shiya.json
                  ?type=shiya

新しいエンドポイント:

エンドポイント 説明
/check?type=general 一般予約をチェック
/check?type=shiya 視野予約をチェック
/test?type=general\|shiya 手動テスト
/generate-monthly-summary?type=... 月次集計生成
/ ヘルスチェック

検討したが見送った追加コスト削減案:

効果 見送り理由
メモリ512MBに削減 月¥300-500 Puppeteerが動作しない
視野オフピーク10分→30分 月¥50-100 ダッシュボード修正が必要、効果小
履歴データ自動削除 月¥0.1-0.2 効果が微小

新規作成ファイル:

ファイル 内容
docker-timeslot-checker-unified/server.js 統合版サーバー
docker-timeslot-checker-unified/Dockerfile Docker設定
docker-timeslot-checker-unified/package.json 依存関係
DEPLOY_UNIFIED_SERVICE.md 統合版デプロイ手順書

更新ファイル:


2026年1月12日 - Cloud Runキャッシュ問題の再発と修正

⚠️ 問題の概要

ダッシュボードの「時間枠別 埋まり推移」タイルが正しく動作しなくなった:

🔍 原因

履歴データにslotsフィールドが含まれていなかった

調査結果:

原因は Cloud Buildのキャッシュ問題の再発。1月10日のチェック間隔変更後、Cloud Runが古いコードで動作し続けていた。

🔧 修正内容

Dockerfileにキャッシュ無効化コメントを追加してリデプロイ:

# docker-timeslot-checker/Dockerfile
# docker-timeslot-checker-shiya/Dockerfile
# Force rebuild: 2026-01-12 slots fix

✅ デプロイ結果

サービス リビジョン slots保存
一般予約 timeslot-checker-00010-dvp
視野予約 timeslot-checker-shiya-00004-t5z

📁 修正したファイル

ファイル 修正内容
docker-timeslot-checker/Dockerfile キャッシュ無効化コメント追加
docker-timeslot-checker-shiya/Dockerfile キャッシュ無効化コメント追加

⚠️ チェック間隔変更時のチェックリスト(再発予防)

Cloud Schedulerの実行間隔を変更する際は、以下の手順を必ず実行してください。

📋 変更前の準備

📋 Cloud Scheduler設定変更後

📋 リデプロイ後の確認(重要!)

❌ よくある失敗パターン

  1. Cloud Schedulerだけ変更してCloud Runをリデプロイしない → 古いコードが動作し続け、slotsが保存されない

  2. リデプロイしたがキャッシュ無効化を忘れた → Cloud Buildがキャッシュを使い、古いコードがデプロイされる

  3. 確認せずに作業完了とした → 数日後にダッシュボードが壊れていることに気づく


2026年1月10日〜11日 - Cloud Runデプロイ時のキャッシュ問題と解決

⚠️ 問題の概要

Cloud Schedulerの実行間隔を変更した後、ダッシュボードの以下の機能が動作しなくなりました:

🔍 原因調査の経緯

1. 最初の仮説(間違い): 実行間隔が長すぎる

2. 発見した問題①: 日次サマリーの許容誤差が厳しすぎた

3. 発見した問題②: 日次サマリーにslots配列が含まれていなかった

4. 発見した問題③: 履歴データ自体にslotsが保存されていなかった

💥 最大のトラブル: Cloud Runのキャッシュ問題

現象: コードを修正してCloud Runにデプロイしても、変更が反映されなかった。

再現手順と失敗:

  1. server.js を修正
  2. gcloud run deploy でデプロイ → 新しいリビジョン作成
  3. Cloud Schedulerが実行 → 古いコードが動作
  4. 履歴データを確認 → slotsフィールドがない

原因: Cloud Build(gcloud run deploy --source .)がDockerレイヤーをキャッシュしており、server.jsの変更が検出されなかった。

解決方法:

# Dockerfileに意味のない変更を加えてキャッシュを無効化
echo "# Force rebuild: $(date)" >> Dockerfile

# 再デプロイ
gcloud run deploy timeslot-checker-shiya \
  --source . \
  --region asia-northeast1 \
  --memory 1Gi

確認方法: デバッグログを追加して、実際にコードが更新されたことを確認:

// saveHistoryData関数内
console.log('=== 履歴エントリ保存デバッグ ===');
console.log('data.slots:', JSON.stringify(data.slots));
console.log('historyEntry:', JSON.stringify(historyEntry));

📋 教訓と今後の対策

1. Cloud Runデプロイ時のキャッシュ対策

# 方法1: --no-cache オプション(Cloud Buildに渡す)
gcloud builds submit --tag gcr.io/PROJECT/SERVICE --no-cache
gcloud run deploy SERVICE --image gcr.io/PROJECT/SERVICE

# 方法2: Dockerfileに変更を加える(簡単)
echo "# Rebuild: $(date)" >> Dockerfile
gcloud run deploy SERVICE --source .

# 方法3: package.jsonのバージョンを更新
# version: "1.0.0" → "1.0.1"

2. デプロイ後の確認手順

# 1. 手動でエンドポイントを呼び出す
curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  https://SERVICE-URL/check

# 2. Cloud Runログでデバッグ出力を確認
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=SERVICE AND textPayload:デバッグ" --limit=5

# 3. Cloud Storageで保存されたデータを確認
gsutil cat gs://BUCKET/history/TYPE/DATE.json | tail -5

3. 間隔変更時のダッシュボード対応

📁 修正したファイル

ファイル 修正内容
docker-timeslot-checker/server.js saveHistoryDataslots追加、日次サマリー許容誤差10分、デバッグログ
docker-timeslot-checker-shiya/server.js 同上
dashboard.html デバッグログ追加(調査用)

⏱️ 影響と復旧時間


2026年1月10日〜11日 - Cloud Scheduler設定変更実施

✅ コスト最適化の本番適用

実施内容: Cloud Schedulerの実行頻度を時間帯・サービス別に最適化し、月額費用を削減しました。

最終設定:

サービス 時間帯 変更前 変更後
一般予約 7:00〜17:59 1分間隔 1分間隔(維持)
一般予約 18:00〜6:59 1分間隔 5分間隔
視野予約 7:00〜17:59 1分間隔 3分間隔
視野予約 18:00〜6:59 1分間隔 10分間隔

Cloud Run URL(正しいURL):

作成したジョブ(4つ):

削除したジョブ(2つ):

期待される効果:

動作確認:

修正履歴:

備考: 問題発生時は COST_OPTIMIZATION_GUIDE.md の「元に戻す場合」セクションを参照して復旧可能。


2026年1月9日 - Cloud Runコスト最適化

💰 実行頻度の時間帯別最適化

概要: Cloud Runの実行頻度を時間帯・サービス別に最適化し、月額費用を大幅に削減しました。

変更内容:

サービス 時間帯 変更前 変更後
一般予約 7:00〜17:59 1分間隔 1分間隔(維持)
一般予約 18:00〜翌6:59 1分間隔 5分間隔
視野予約 7:00〜17:59 1分間隔 3分間隔
視野予約 18:00〜翌6:59 1分間隔 10分間隔

効果:

項目 変更前 変更後
実行回数/日 2,880回 1,114回(61%削減)
月額費用 ¥4,500〜5,400 ¥1,700〜2,100
年間削減額 - 約¥34,000

背景:

設定手順: COST_OPTIMIZATION_GUIDE.md

変更ファイル:


2026年1月2日 - ダッシュボード大幅改善とスマホ版最適化

📊 時間枠別埋まり推移ヒートマップの実装

概要: 各予約枠がいつ埋まったかを視覚的に確認できるヒートマップを実装しました。当初は3Dグラフ(Plotly.js)で実装しましたが、見やすさを優先して2Dヒートマップに変更しました。

ヒートマップの仕様:

       10:00  10:15  10:30  ...  17:45
19:00  □     □     □           □      ← 前日19時時点
21:00  □     □     □           □
...
09:00  ■     □     ■           □
11:00  ■     ■     ■           ■      ← 当日11時時点
  ↓時刻の経過(2時間刻み)

実装のポイント:

// 2時間刻みのターゲット時刻
const targetHours = ['19:00', '21:00', '23:00', '01:00', '03:00', '05:00',
                     '07:00', '09:00', '11:00', '13:00', '15:00', '17:00'];

// 各ターゲット時刻に最も近いエントリを取得
targetHours.forEach(targetTime => {
    // 日をまたぐ場合の調整(18:30開始なので19時以降は同日扱い)
    if (entryMinutes < 18 * 60) entryMinutes += 24 * 60;
    if (targetMinutes < 18 * 60) targetMinutes += 24 * 60;
});

変更履歴:

  1. 3Dサーフェスチャート(Plotly.js)で実装
  2. 見づらいとのフィードバックで2Dヒートマップに変更
  3. 縦軸を1分刻みから2時間刻みに変更
  4. 横幅いっぱいに表示するようレイアウト変更

🎨 タイムラインバーの2時間以内色変わり機能

概要: タイムラインバーで、2時間以内に状態が変わった枠にアニメーションを適用する機能を実装しました。

機能:

CSSアニメーション:

/* 2時間以内に埋まった枠のパルス */
@keyframes pulse-general {
    0%, 100% { background: #EE3333; }
    50% { background: #FF6666; }
}

/* キャンセル発生時のグロー */
@keyframes glow-general {
    0%, 100% { box-shadow: 0 0 5px 2px rgba(204, 102, 0, 0.6); }
    50% { box-shadow: 0 0 12px 4px rgba(204, 102, 0, 0.9); }
}

バグ修正: スマホ版で2時間以内色変わり機能が動作していなかった問題を修正。

// 修正前(バグ)
renderTimeline('general', generalData.slots || []);

// 修正後
renderDotTimeline('general', 'Am', AM_SLOTS, generalData.slots || []);
renderDotTimeline('general', 'Pm', PM_SLOTS, generalData.slots || []);

📱 スマホ版レイアウトの最適化

概要: スマホ版ダッシュボードの表示順序を最適化し、重要な情報を優先表示するようにしました。

表示順序(CSS flexbox order使用):

.container > header { order: 1; }
.container > .current-status-section { order: 2; }  /* 現在の空き状況 */
.container > .weekly-progress-chart { order: 3; }   /* 直近1週間の推移 */
.container > #slotFillProgressSection { order: 4; } /* 時間枠別埋まり推移 */
.container > .controls { order: 5; }                /* コントロール */
.container > .daily-progress-chart { order: 6; }    /* 曜日別平均 */

改善点:


⚡ スマホ版読み込み速度の改善

問題: スマホでダッシュボードを開いた際、折れ線グラフが表示されなかったり、古いキャッシュデータが表示されることがあった。

原因: 複数のAPIリクエストが同時に発生し、帯域幅を奪い合っていた。

修正内容:

async function loadCurrentStatus() {
    // === STEP 1: 現在の空き枠を最優先で取得・表示 ===
    const [generalData, shiyaData] = await Promise.all([...]);

    // まずアニメーションなしで即座に表示
    updateCurrentStatusCard('general', generalData);
    updateCurrentStatusCard('shiya', shiyaData);

    // === STEP 2: 履歴データを非同期で取得(待たない) ===
    Promise.all([fetchTodayHistory('general'), fetchTodayHistory('shiya')])
        .then(([generalHistory, shiyaHistory]) => {
            // 履歴取得後にアニメーションを適用
            analyzeSlotFillTimes(generalHistory, 'general');
            renderDotTimeline('general', 'Am', AM_SLOTS, generalData.slots);
        });
}

効果:


🏷️ PC版凡例の日本語表記改善

概要: PC版のタイムライン凡例をより分かりやすい表記に変更しました。

変更内容: | 変更前 | 変更後 | |—|—| | 空き | 予約可能 | | 2h以内埋 | 最近埋まった(2h以内) | | 以前埋 | 以前から満枠 | | 2h以内空 | キャンセル発生(2h以内) |


🔌 Chrome拡張機能のアニメーション対応

概要: Chrome拡張機能(一般予約・視野予約)のポップアップにもダッシュボードと同じアニメーション機能を実装しました。

実装内容:

ローディング遅延の修正:

// 修正前(1秒の遅延が発生)
await fetchTodayHistory();
renderTimeline();

// 修正後(即座に表示、アニメーションは後から適用)
renderTimeline();  // まず表示
fetchTodayHistory().then(() => {
    analyzeSlotFillTimes(history);
    renderTimeline();  // アニメーション付きで再描画
});

📁 変更ファイル

ファイル 変更内容
dashboard.html ヒートマップ実装、レイアウト順序変更、凡例改善、バグ修正
chrome-extension-timeslot-general/popup.html アニメーションCSS追加
chrome-extension-timeslot-general/popup.js 履歴取得・アニメーション適用ロジック
chrome-extension-timeslot-shiya/popup.html アニメーションCSS追加
chrome-extension-timeslot-shiya/popup.js 履歴取得・アニメーション適用ロジック

⚠️ 実装時の注意点

1. 日をまたぐ時刻計算

// 18:30開始のデータなので、19時以降は同日、18時未満は翌日扱い
if (entryMinutes < 18 * 60) entryMinutes += 24 * 60;

2. スロット名のマッピング

// 13:00 → 12:55, 18:00 → 17:55 の変換が必要
if (slot === '13:00') return '12:55';
if (slot === '18:00') return '17:55';

3. 履歴データの非同期取得 履歴データ取得をawaitすると、UIの表示が遅延する。Promise.then()で非同期処理し、まずUIを表示してからアニメーションを適用する。

4. CSS flexbox orderの優先順位 より具体的なセレクタ(IDセレクタ)を後に記述することで、クラスセレクタよりも優先される。

.container > .chart-card { order: 9; }           /* 一般的なカード */
.container > #slotFillProgressSection { order: 4; }  /* 特定のセクション */

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="<YOUR_CLOUD_RUN_URL_GENERAL>/check"

# 視野予約
gcloud scheduler jobs update http reservation-timeslot-checker-shiya-job \
  --location=asia-northeast1 \
  --uri="<YOUR_CLOUD_RUN_URL_SHIYA>/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でお知らせください。