@@ -366,6 +366,8 @@ PATCH `/api/spot-types/[id]`は`series_styles`・`categories`を保存前に検
366366
367367選び方は** カレンダー** (` components/VisitDateCalendar.tsx ` )。絞り込みモーダルの訪問日セクションには** 選択中の日(期間)を出すボタン** だけを置き、押すと** カレンダーを別モーダル(z-[ 60] 。絞り込みモーダルのz-50の上)で開く** —— 絞り込みモーダルにカレンダーを直に置くと、他の条件を見るのに毎回その分スクロールすることになるため。** カレンダーは日を選んでも閉じない** (期間は2回のタップで決まるので、1回目で閉じると期間を選べない)。選択はその場で反映されるので、閉じる操作は「閉じる」だけでよい。
368368
369+ ** よく使う期間はカレンダーを開かずに選べる。** 訪問日セクションとカレンダーの両方に** 「今日」「過去1年」「表示しない」** を並べてある(` MapView ` の` visitDateOptions ` )。「過去1年」は** 1年前の同じ日〜今日** の期間で、カレンダーだと開始月まで12回さかのぼって2回タップすることになるため置いた。** 1年前の日付は` Date ` に年だけ引かせる** ので、2月29日は3月1日に送られる(閏年でない年に2月29日は無い)——絞り込みの範囲としては1日の差なので、月末を特別扱いはしない。
370+
369371**訪問記録のある日には日付の下に緑の点**を打つ(`visitDateSet`。どの日に記録があるか分からないまま総当たりで選ぶことになるのを避けるため。**他のスポット種別への訪問も数える** — 経路が種別をまたぐようになったので、その日を落とすと辿れない)。1回目のタップで開始日、2回目で終了日。既に期間が決まっている状態でのタップは新しい開始日として選び直し(範囲を狭めるのにリセットを挟まずに済む)、開始日より前をタップしたときはその日を開始日にして元の開始日を終了日にする(前方向にも伸ばせる)。同じ日を2回タップしても単日のまま。「今日」「表示しない」はよく使うので、カレンダーの中と絞り込みモーダルの両方に置く。既定値は今日。かつてはセレクト(今日 / 表示しない / 訪問のある日の一覧)だったが、期間指定とデータのある日の可視化のためカレンダーに変えた。「表示しない」(`null`)は`saveFilters`が文字列`"none"`で保存し、`loadSavedFilters`は`"none"`のときだけ`null`=表示しないにする。**「今日」(値が`todayKey()`と一致)は具体的な日付ではなく文字列`"today"`で保存し、読み込み時にその日の今日へ解決する**(日付のまま保存すると翌日に開いたとき前日が選ばれた状態で復元されてしまうため)。日付はその日、それ以外(旧仕様の絞り込みだった頃の`null`・キー欠落・不正値)は今日に倒すため、**既存ユーザーも初回から今日の経路が出る**(`todayKey`/`defaultMapFilters`)。終了日(`visitedDateTo`)は**「今日」のような相対表現を持たず具体的な日付でだけ保存する**(終了日だけ動くと期間の長さが日をまたぐたびに変わってしまうため)。カレンダーで日を選んだとき(`handleSelectVisitDate`)は、対象日(期間)をセットしたうえで**その経路全体が画面に収まるよう`fitBounds`する**(1地点だけならmaxZoomまで寄る。経路が0件・「表示しない」のときは地図を動かさない)。開始日を`null`にするときは終了日も一緒に落とす(残っていると次に日を選んだとき意図しない期間になる)。ユーザーが明示的に選んだときだけ移動し、マウント時の既定(今日)の復元では移動しない。日付キーへの変換は`toVisitDateKey`(`visits.visited_on`はtimestamptzでUTC文字列のため、**必ずローカル時刻で日を切る** — UTCのまま切ると日本時間の朝9時前の訪問が前日になる)。
370372
371373訪問日が選ばれているとき、`MapView`は**その日の訪問記録を訪問時刻の昇順に矢印で結んだ「訪問順の経路」**を描く(`buildVisitPathsByDay`)。**期間を指定したときは日ごとに別の線にし、日をまたいでスポットを結ばない** —— 宿へ帰って翌朝また出る間の移動は実際には辿っていないので、繋ぐと1日の道のりが読めなくなるため。**線・詳細・Google マップの経路検索のいずれも日ごとに別のルートとして扱う**(フィーチャに`pathDate`を持たせ、タップした線の日ぶんだけを詳細に出す)。かつては期間をまたいで1本に繋いでいた。ルートCSVのルートと同じ`spot-routes`ソース・同じ線/矢印レイヤーに載せるだけなので描画コードは共用で、色だけ`VISIT_PATH_COLOR`(緑`#16a34a`=訪問済みピンの塗りと同じ)にしてルートと区別する。同じスポットへの再訪はそのまま複数回経由地として現れる(行って戻る線になる)が、連続する同じスポットへの訪問は長さ0の線分になり矢印の向きが定まらないためまとめる。日時不明の訪問は除外する。**別のスポット種別のスポットも経路に含める**(訪問予定リストと同じ扱い) — 本体種別で解決できないスポットは`api.spots.get`で座標だけ補完する(`pathExtraSpots`/`pathSpotById`。訪問予定リストと共用の仕組み)。かつては表示中の種別の訪問だけを繋いでいたが、同じ日に別の種別のスポットも回っていると経路がそこで途切れていた。**経路上のスポットでもピンは絞り込みに従う**(下記「ピンの表示と線の表示を分ける」)。
@@ -602,6 +604,25 @@ CSVインポートは差分更新で、`AdminView`側が事前読み込み済み
602604 2か所に書くと、片方だけ直したときに保存できる写真が食い違う
603605- ** エクスポート(ZIP)にも入れる** (CSVの「追記」列と、元の記録の写真の後ろに続く写真)。
604606 入れないと、追記に付けた写真だけが持ち出せない
607+ - ** 拡大表示でも1つながりに扱う** (下記)
608+
609+ ** 写真の拡大表示は自前のジェスチャで動かす** (` components/PhotoLightbox.tsx ` )。
610+ アプリ全体でページのピンチズームを切ってある(` app/layout.tsx ` の` userScalable: false ` 。
611+ 地図表示中にピンチが地図へ取られると元に戻せなくなるため)ので、** ブラウザ任せの拡大ができない**
612+ —— 二本指の距離を自分で見て拡大率を持ち、` touch-action: none ` で既定の動作を止める。
613+ 同じ理由でホイールは** ` ctrl ` 付き(トラックパッドのピンチ)だけ** を拡大に使う。
614+
615+ - ** 指1本か2本かで操作を分ける。** 2本=ピンチ(** 中点を軸にする** ので、つまんだ場所が寄る)、
616+ 1本かつ等倍=横スワイプで前後の写真、1本かつ拡大中=パン。
617+ ** 拡大中に左右スワイプを切り替えに使わない** —— 端を見に行く操作と区別が付かないため
618+ - ** 渡すのは訪問記録1件ぶんの写真** (` visitPhotoGroup ` )。** 記録本体の写真のあとに
619+ 追記の写真を書いた順** で並べ、画面の並びとそろえる(追記は同じ訪問記録への書き足しなので、
620+ 写真も1つながりとして回れる)。サムネイルをタップした位置がそのまま開く位置になる
621+ - ** 写真をまたぐと拡大は解ける。** 前の写真の拡大率のまま次に移ると、
622+ 「なぜか一部しか見えない」状態で始まることになる
623+ - ** 閉じるのは等倍のタップと×とEsc。** 拡大中のタップでは閉じない(パンの途中で
624+ 閉じてしまうため)。タップは** ダブルタップの判定ぶん待ってから** 効かせる
625+ (1回目のタップで閉じると拡大に届かない)
605626
606627** 訪問記録フォームは「保存ボタンが画面内に収まる」高さを保つ** (モーダルは` max-h-[85dvh] ` でスクロールできるが、記録するたびにスクロールさせるのは日常操作として重い)。そのため** 説明文は畳んで置く** : 未訪問記録の説明は見出し横の` HelpTip ` (` anchored ` 表示。下記)、口コミは既定で畳んだ入力欄にしてある。** ` HelpTip ` のボタンは` label ` の外に置くこと** (中に入れると押したときにチェックボックスまで切り替わる)。口コミは** 畳んでも入力済みの本文はそのまま投稿する** (` AddSpotModal ` の「訪問を記録」と違い、開閉は意思表示ではない)ため、畳んだ見出しに「入力済み」と添えて分かるようにしてある。
607628
@@ -716,6 +737,21 @@ CSVインポートは差分更新で、`AdminView`側が事前読み込み済み
716737 250ms以上の間隔** で投げる(無料の公開APIなので自分で間隔を空ける)。
717738 予報の範囲外の日は** 投げる前に「予報なし」で返す** (範囲外は上流が400を返し、
718739 同じリクエストに乗せた地点すべてが巻き添えになるため)
740+ - ** 予定日の前後1週間から天気の良い日を探せる** (` components/PlanWeatherFinder.tsx ` )。
741+ 予定を立てたあとに雨予報になったとき、近い日にずらせるかを同じ画面で確かめるためのもの。
742+ ` /api/weather ` は** 日付1つ(` date ` )と期間(` start ` /` end ` )の2通り** で引けて、
743+ 期間のほうは` data.dates ` と` data.byPoint ` (地点×日)を返す。** 上流も期間をそのまま受け取れる**
744+ ので15日ぶんでも呼び出しは1回で、** キャッシュは日ごと** なのでどちらの引き方でも共有される。
745+ ` data ` の中に入れて返すのは、api-clientが` data ` キーの中身だけを取り出すため(` fetchAndParse ` )
746+ - ** 1日ぶんを1行にたたむときは、いちばん悪い地点に合わせる** (` summarizeDay ` )。
747+ 旅程は** 1か所でも降れば雨具が要る** ので、平均で均すと「全体としては晴れ」に見えてしまう。
748+ 良し悪しの3段階(` weatherGrade ` )は** 降るかどうか** を境目にし、くもり・霧と
749+ 「晴れているが降水確率50%以上」は言い切らずに間へ置く
750+ - ** 予報が無い日を空欄にしない。** 予報が出るのは先15日ほどまでなので、遠い予定では
751+ 前後1週間のほとんどが範囲外になる。黙って飛ばすと「候補が無い=悪い日」と読めるため、
752+ 「予報なし」と書いて** おすすめの判定からも外す** (` known === 0 ` )
753+ - ** 日を選ぶと旅程の長さを保ったままずらす** (3日間の旅程なら3日間のまま動く)。
754+ 更新は並び替えと同じくPATCHで、** 経由スポットを丸ごと置き換える仕様なので基本情報も送り直す**
719755- ** 「このリストだけを地図で表示」** は` /[type]/map?planList=<id> ` へ遷移し、地図側がそのリストを
720756 経路の対象(` filters.planListId ` )に選び「これだけを表示」(` isolate: "plan" ` )にして、経路全体が
721757 入るよう移動する。** 適用は一度だけ** (` appliedPlanListRef ` )で、以後は普通の絞り込みと同じ扱い
0 commit comments