眼科クリニックの予約状況を自動判定し、ホームページのバナーに表示するシステムです。
対応予約: 一般予約・視野予約の2種類の予約システムを独立して管理
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/ # バナー用画像ファイル・設定ファイル
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(バナー・ディスプレイ・チェッカー)
const BUCKET_NAME = 'reservation-timeslots-fujiminohikari';
const FILE_NAME_GENERAL = 'timeslots.json';
const FILE_NAME_SHIYA = 'timeslots-shiya.json';
一般予約: https://ckreserve.com/clinic/fujiminohikari-ganka/fujimino
視野予約: https://ckreserve.com/clinic/fujiminohikari-ganka/fujiminohikari
| 種類 | サービス名 | URL |
|---|---|---|
| 統合版 | reservation-timeslot-checker-unified |
<YOUR_CLOUD_RUN_URL> |
エンドポイント:
/check?type=general/check?type=shiya| 種類 | ジョブ名 | スケジュール | 説明 |
|---|---|---|---|
| 一般予約(午前) | 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での開発を想定しています。
引き継ぎ資料: docs/handover-to-claude-code.md
このChrome拡張機能は、Chrome Web Storeに非公開公開する準備が完了しています。
概要: 一般予約と視野予約で別々に稼働していた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
?type=general / ?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 |
統合版デプロイ手順書 |
更新ファイル:
DEPLOY_GUIDE.md - 統合版デプロイ手順を追加ダッシュボードの「時間枠別 埋まり推移」タイルが正しく動作しなくなった:
履歴データにslotsフィールドが含まれていなかった
調査結果:
slotsあり ✅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の実行間隔を変更する際は、以下の手順を必ず実行してください。
slotsフィールドが含まれていることを確認
curl "https://storage.googleapis.com/reservation-timeslots-fujiminohikari/history/general/$(date +%Y-%m-%d).json" | tail -c 300
# Dockerfileに変更を加えてキャッシュ無効化
echo "# Force rebuild: $(date +%Y-%m-%d)" >> docker-timeslot-checker/Dockerfile
echo "# Force rebuild: $(date +%Y-%m-%d)" >> docker-timeslot-checker-shiya/Dockerfile
# 一般予約
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
curl "https://storage.googleapis.com/reservation-timeslots-fujiminohikari/history/general/$(date +%Y-%m-%d).json" | tail -c 500
"slots": [...] が含まれていることを確認Cloud Schedulerだけ変更してCloud Runをリデプロイしない
→ 古いコードが動作し続け、slotsが保存されない
リデプロイしたがキャッシュ無効化を忘れた → Cloud Buildがキャッシュを使い、古いコードがデプロイされる
確認せずに作業完了とした → 数日後にダッシュボードが壊れていることに気づく
Cloud Schedulerの実行間隔を変更した後、ダッシュボードの以下の機能が動作しなくなりました:
1. 最初の仮説(間違い): 実行間隔が長すぎる
2. 発見した問題①: 日次サマリーの許容誤差が厳しすぎた
3. 発見した問題②: 日次サマリーにslots配列が含まれていなかった
slots配列が必要slotsを含める処理がなかったslots: closestEntry.slots || [] を追加4. 発見した問題③: 履歴データ自体にslotsが保存されていなかった
saveHistoryData で slots フィールドを保存していなかったslots: data.slots || [] を履歴エントリに追加現象: コードを修正してCloud Runにデプロイしても、変更が反映されなかった。
再現手順と失敗:
server.js を修正gcloud run deploy でデプロイ → 新しいリビジョン作成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 |
saveHistoryDataにslots追加、日次サマリー許容誤差10分、デバッグログ |
docker-timeslot-checker-shiya/server.js |
同上 |
dashboard.html |
デバッグログ追加(調査用) |
slots保存を確認実施内容: 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):
<YOUR_CLOUD_RUN_URL_GENERAL>/check<YOUR_CLOUD_RUN_URL_SHIYA>/check作成したジョブ(4つ):
reservation-timeslot-checker-job-peak(一般予約・1分毎・7-17時)reservation-timeslot-checker-job-offpeak(一般予約・5分毎・18-6時)reservation-timeslot-checker-shiya-job-peak(視野予約・3分毎・7-17時)reservation-timeslot-checker-shiya-job-offpeak(視野予約・10分毎・18-6時)削除したジョブ(2つ):
reservation-timeslot-checker-jobreservation-timeslot-checker-shiya-job期待される効果:
動作確認:
修正履歴:
備考: 問題発生時は COST_OPTIMIZATION_GUIDE.md の「元に戻す場合」セクションを参照して復旧可能。
概要: 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
変更ファイル:
COST_OPTIMIZATION_GUIDE.md - 新規作成(設定変更手順書)docker-timeslot-checker/README.md - 料金目安を更新README.md - Cloud Scheduler設定を更新概要: 各予約枠がいつ埋まったかを視覚的に確認できるヒートマップを実装しました。当初は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;
});
変更履歴:
概要: タイムラインバーで、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() 関数を呼んでいたrenderDotTimeline() 関数を呼ぶよう変更// 修正前(バグ)
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版のタイムライン凡例をより分かりやすい表記に変更しました。
変更内容: | 変更前 | 変更後 | |—|—| | 空き | 予約可能 | | 2h以内埋 | 最近埋まった(2h以内) | | 以前埋 | 以前から満枠 | | 2h以内空 | キャンセル発生(2h以内) |
概要: 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; } /* 特定のセクション */
概要:
entrance-display.html と dashboard.html のCloud Storage URLが古いバケット名を参照していた問題を修正しました。
修正内容:
| ファイル | 修正前 | 修正後 |
|—|—|—|
| entrance-display.html | fujimino-ophthalmology-reservations | reservation-timeslots-fujiminohikari |
| dashboard.html | fujimino-ophthalmology-reservations | reservation-timeslots-fujiminohikari |
概要: 履歴保存機能を有効化するため、Cloud Runサービスを再デプロイしました。
デプロイしたサービス:
timeslot-checker (一般予約) - リビジョン: timeslot-checker-00007-qmftimeslot-checker-shiya (視野予約) - リビジョン: timeslot-checker-shiya-00001-vrk問題: 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 ✅ 作成確認済み
効果:
概要: 予約状況チェッカーと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. 適用範囲
timeslot-status-checker.html - メインの予約状況チェッカー変更ファイル:
timeslot-status-checker.htmlchrome-extension-timeslot-general/popup.html, popup.jschrome-extension-timeslot-shiya/popup.html, popup.js概要: 旧スプレッドシート経由の予約判定システムで使用していたファイルを完全に削除しました。
削除したHTMLファイル(9件):
index.html, shiya.html - 旧バナーmobile-status.html - 旧モバイルチェッカーdisplay.html, display-shiya.html, display-combined.html - 旧全画面ディスプレイdisplay-test.html, display-test-shiya.html, display-test-combined.html - 旧デバッグページ削除したその他ファイル(6件):
Downloads/20251109.txt - 旧GASコードDownloads/display-preset.json - 旧デバッグページ用プリセットdocs/delete-old-rows-guide.md, docs/delete-old-rows-shiya-guide.md - 旧スプレッドシート用ガイドdocs/implementation-guide-v5.md - 旧v5システム実装ガイド効果: 15ファイル、17,171行を削減
概要: 予約状況チェッカーを、スマホモード(800px以下)と壁掛けモード(801px以上)の2モード構成に簡素化しました。これにより、旧来の全画面ディスプレイ(display.html等)は不要になりました。
スマホモード(800px以下):
壁掛けモード(801px以上):
概要: タイムスロット抽出システムへの移行完了に伴い、旧システム(スプレッドシート経由の判定システム)を完全に削除しました。
削除したCloud Runサービス:
reservation-checker - 旧一般予約チェッカーreservation-checker-shiya - 旧視野予約チェッカー削除したCloud Schedulerジョブ:
check-reservation - 旧一般予約の定期実行check-reservation-shiya - 旧視野予約の定期実行削除したディレクトリ(39ファイル、5,690行):
bookmarklet/ - ブックマークレット版チェッカーdocker-automation/ - 旧一般予約Docker自動化docker-automation-shiya/ - 旧視野予約Docker自動化chrome-extension/ - 旧Chrome拡張機能(ステータス表示)chrome-extension-status-indicator/ - 旧ステータス表示拡張機能chrome-extension-general-indicator/ - 旧一般予約インジケーターchrome-extension-shiya-indicator/ - 旧視野予約インジケーターgas/ - Google Apps Script効果:
概要:
timeslot-banner.htmlのUIを大幅に改善し、より見やすく使いやすいデザインに更新しました。
1. 時間枠の個別ピル表示
修正前: 10:00・11:00・14:00…
修正後: [10:00] [11:00] [14:00] ほか
2. 日付形式の変更
修正前: 本日(土)
修正後: 本日(12/6)
3. モバイル表示の改善
calc(100% - 10px)で画面いっぱいに表示align-items: center)変更ファイル:
timeslot-banner.html - ピルデザイン、日付形式、モバイル対応問題: 一般予約のタイムスロット取得システムで、18:30以降でも「本日」の予約枠を参照してしまう問題が発生していました。視野予約は正常に動作していました。
症状:
原因:
process.env.TZ や toLocaleString がCloud Run環境で不安定だった修正内容:
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
変更ファイル:
docker-timeslot-checker/server.js - getJapanTime()関数を追加docker-timeslot-checker-shiya/server.js - 同様の修正docker-timeslot-checker/Dockerfile - GPGキー修正を追加docker-timeslot-checker-shiya/Dockerfile - 同様の修正問題:
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拡張機能(一般予約・視野予約)のアイコンに、視認性向上のための枠線を追加しました。
変更内容:
実装コード:
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);
変更ファイル:
chrome-extension-timeslot-general/background.jschrome-extension-timeslot-shiya/background.jsCloud 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では不足する場合があります。
推奨設定:
--memory 1Gi)Memory limit of 512 MiB exceededCloud Run環境でのタイムゾーン処理は環境依存のため、以下の方式を推奨:
推奨: UTCオフセット方式
const japanOffset = 9 * 60 * 60 * 1000;
const japanTime = new Date(now.getTime() + japanOffset);
非推奨: 環境変数依存
process.env.TZ = 'Asia/Tokyo'; // 効果が不安定
概要: 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フィールドから計算するので統一表示!
変更ファイル:
chrome-extension-timeslot-general/popup.js - 日付計算ロジック追加chrome-extension-timeslot-shiya/popup.js - 同様の修正timeslot-status-checker.html - 一般・視野両方に適用改善効果:
概要: iframe埋め込み用のタイムスロット版バナーを新規作成しました。具体的な時間枠を表示することで、より詳細な予約情報を提供できます。
新規作成ファイル:
timeslot-banner.html - タイムスロット版iframeバナー主な機能:
表示内容(空き枠がある場合):
✅ 明日の予約
10:00・11:00・14:00…
に 空きがございます
クリックして予約ページへ
更新:2025/12/05 14:30
表示内容(満枠の場合):
😔 本日(12/5)の予約は 満枠です
誠に恐れ入りますが、次の診療日以降で ご予約ください
クリックして 予約ページへ
更新:2025/12/05 14:30
技術仕様:
timeslots.json)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. 時間枠対応の予約状況チェッカー
timeslot-status-checker.html - 一般予約と視野予約の枠数と詳細を表示2. Chrome拡張機能(枠数表示版)
chrome-extension-timeslot-general/ - 一般予約の枠数を表示
chrome-extension-timeslot-shiya/ - 視野予約の枠数を表示
3. タイムスロット表示のデバッグパネル
timeslot-display.html - デバッグパネル追加(Dキーで開閉)timeslot-display-shiya.html - デバッグパネル追加(Dキーで開閉)主な機能:
1. 予約状況チェッカー(timeslot-status-checker.html)
2. Chrome拡張機能(枠数表示版)
表示方式:
アイコンクリック時:
データソース:
timeslots.jsontimeslots-shiya.json3. デバッグパネル(タイムスロット表示用)
機能:
使い方:
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');
}
timeslot-status-checker.html - 新規作成(578行)chrome-extension-timeslot-general/ - 新規ディレクトリ
chrome-extension-timeslot-shiya/ - 新規ディレクトリ
timeslot-display.html - デバッグパネル追加(+200行)timeslot-display-shiya.html - デバッグパネル追加(+200行)README.md - リンク集追加、新システムの説明追加概要: デバッグページに背景点滅機能を実装し、通行人の注目を集めるための効果的なアニメーション機能を追加しました。背景のみを点滅させる技術により、テキストやアイコンの視認性を保ちながら目を引く演出が可能になりました。
更新ファイル:
display-test.html - 背景点滅機能の実装と改善display-test-shiya.html - 同機能の実装display-test-combined.html - 同機能の実装実装された機能:
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,
}
通行人を呼び込みたい時:
営業時間中の控えめ表示:
display-test.html - 背景点滅機能の実装(約200行追加)display-test-shiya.html - 同機能の実装display-test-combined.html - 同機能の実装README.md - 背景点滅機能のドキュメント追加[以降、既存の更新履歴が続きます…]
このプロジェクトは私的利用を目的としています。
システムに関する質問や問題は、Issuesでお知らせください。