ハコヅミを作る #25:行ったキャンプは実績の日付で出し、おすすめ度と感想を残す

前回(#24)は画面の枠でした。今回は、キャンプの中身です。

  • 一覧のカードを、広い幅でも2列にする。実績期間が入っていれば、実績の日付を出す。日付の書き方をそろえる(#116)
  • キャンプに、おすすめ度と感想を足す。編集画面を「行く前」「行った後」に分ける(#115)

ハコヅミのキャンプは、予定期間と実績期間の両方を持っています(#15)。ただ、これまで一覧に出していたのは予定期間だけでした。


実績が入っていれば、実績の日付を出す

雨で1日ずらした、2泊の予定が1泊になった、ということはよくあります。そのとき、一覧には予定の日付のまま残ります。

そこで、一覧に出す期間を次のように決めました。

  • 実績期間が入っていれば、実績期間
  • 入っていなければ、予定期間

この決め方は、src/lib/camps.ts の displayPeriod() に切り出して、ユニットテストしています。

// 一覧やキャンプの画面に出す期間(#116)。実績期間が入っていれば実績、なければ予定期間
export function displayPeriod(camp: CampPeriods): DisplayPeriod | null {
	if (camp.actualStartDate) {
		return { start: camp.actualStartDate, end: camp.actualEndDate, actual: true };
	}
	if (camp.plannedStartDate) {
		return { start: camp.plannedStartDate, end: camp.plannedEndDate, actual: false };
	}
	return null;
}

この期間を、カードとリストの日付、リストの左の月日の札、年ごとの区切り(#23)、一覧の並び順の、すべてに使います。並び順だけはサーバーの SQL で決めているので、同じ決め方を coalesce(actual_start_date, planned_start_date) で書いています。

「実績」の印は、リストにだけ付ける

予定のままのキャンプと見分けられるよう、実績の日付の前に「実績」の小さな印を付けました。

スマホの幅の画面。左はキャンプの一覧のリスト表示で、9/23 の予定を 9/22 に行ったキャンプは、月日の札が「9月 22」になり、日付の前に「実績」の印が付いている。キャンプ場の名前の下に★4つ。右はそのキャンプの画面で、見出しが「2026/9/22(火)・日帰り ファミリー・日帰り」、キャンプ情報に予定期間・実績期間・キャンプ場・おすすめ度・感想が並ぶ

左:一覧のリスト表示(9/23 の予定を 9/22 に行ったキャンプは、実績の日付と「実績」の印)/右:そのキャンプの画面

カードには、印を付けていません。#23で測ったとおり、幅 375px のカードの日付の行は、いちばん長い「11/21(土)〜11/23(月)・2泊」で、残りが数 px しかありません。印の2文字は入らないので、Issue に書いておいた「入りきらないなら、リストだけ」に従いました。

年をまたいで、年の見出しが変わる

実績で年が変わることもあります。12/30 からの予定を、年明けの 1/2 にずらした、という場合です。

このキャンプは、予定の年(2046年)ではなく、実績の年(2047年)の見出しの下に出ます。E2E では、このずれをわざと作って、見出しの下に出る年と、並び順が「予定の順なら逆になる」組み合わせで確かめています。

広い幅でも、カードは2列

広い幅のキャンプの一覧のカード表示。正方形のカードが2列で並び、1枚が大きい。日付は実績があれば実績(9/22(火)・日帰り、8/15(土)〜8/16(日)・1泊)

広い幅でも2列(ギアの一覧は3列のまま)

正方形のカード(#23)を広い幅で3列にすると、1枚が小さく、キャンプ場の画像も小さくなります。キャンプは件数が少ないので、どの幅でも2列にしました。ギアの一覧は、件数が多いので3列のままです。

画面を撮って見つけた、「1」と「泊」の間の折り返し

キャンプの画面の日付も、一覧と同じ「2026/10/10(土)〜10/11(日)・1泊」の書き方にしました。今までは「2026-10-10〜2026-10-11」でした。

実装のあと、スマホの幅で画面を撮って確かめると、キャンプ情報の実績期間が「…〜1/3(木)・1」と「泊」で2行に割れていました。

一覧のリスト表示では、リストを作り直したとき(#106)から、「〜」のあとか「・」の前でしか折り返さない書き方にしていました。それを部品(PeriodText.svelte)にまとめ、キャンプ情報でも使うようにしています。E2E では、日付と泊数のまとまりが、それぞれ1行に収まっていることを確かめています。


おすすめ度と感想を、メモから分ける

キャンプには、メモが1つあるだけでした。#15のころは「感想専用の欄は作らず、メモで兼ねる」と決めていました。

使ってみると、行く前の覚え書き(「薪は現地で買い足す」など)と、行った後の感想が、同じ欄に混ざります。そこで、メモはそのまま残し、次の2つを足しました(#115)。

  • おすすめ度:★5つ。ギア(#13)と同じ入力欄を使う
  • 感想:複数行の入力欄。1000 文字まで

テーブルは camps に rating と impression の2列を足すだけです。マイグレーションは ALTER TABLE ... ADD の2行で、今のキャンプはそのまま残ります。

編集画面を「行く前」「行った後」に分ける

予定と実績は、入れる時期が違います。編集画面の入力欄も、時期で2つのカードに分けることにしました。

見出しの言葉は、画像を見てから決めたいと頼み、4つの案を並べてもらいました。

キャンプ情報の編集画面の見出しの見比べ。案Aは「行く前に入れるもの」「行った後に入れるもの」、案Bは「行く前」「行った後」、案Cは「予定」「実績」、案Dは「計画」「記録」。どれも2つのカードに分かれ、下のカードに実績期間・おすすめ度(★)・感想が入っている

見出しの見比べ(左から、案A:行く前に入れるもの/行った後に入れるもの、案B:行く前/行った後、案C:予定/実績、案D:計画/記録)

案C の「予定」「実績」は短くて分かりやすいのですが、「予定」のカードにキャンプ場や画像も入るので、少しずれます。案A の「行く前に入れるもの」「行った後に入れるもの」は長いものの、そのカードに何を入れるのかが、そのまま分かります。

実装後の編集画面の下のカード「行った後に入れるもの」。実績期間の開始日と終了日、おすすめ度の★(4つ)と「未設定に戻す」、感想の入力欄が並び、その下に保存とキャンセルのボタン

「行った後に入れるもの」のカード。保存のボタンは、2つのカードの下に1つ

キャンプの登録画面は、今までどおり行く前の欄だけで、見出しも付けていません。

改行の前に、空白が出る

感想を入れて保存し、キャンプの画面に改行のまま出ることを確かめる E2E を書いたところ、落ちました。画面の文字を読むと、改行の前に、余分な空白が入っています。

原因は、ブラウザが送る改行でした。複数行の入力欄(textarea)の改行は、フォームで送るときに CR+LF(\r\n)になります。そのまま保存すると、画面で改行をそのまま出す設定(white-space: pre-wrap)のとき、CR が空白のように見えていました。

フォームの値を読むところで、改行を LF(\n)にそろえるようにしています。

// ブラウザは複数行の入力欄(メモ・感想)の改行を CR+LF で送るので、LF にそろえる
return typeof value === 'string' ? value.replace(/\r\n?/g, '\n').trim() : '';

メモにも同じように効きます。これまでのメモは CR+LF のまま残っていますが、表示は変わりません。


おわりに

  • 予定と実績の両方を持つなら、出すほうを1つの関数で決め、並び順や年の区切りにも同じ決め方を使う
  • 幅の狭いところに入らない印は、無理に入れず、入るところにだけ付ける
  • 折り返しは、画面を撮って見るまで分からない。うまくいった書き方は部品にして使い回す
  • 見出しの言葉は、画面に当てて並べて決める
  • textarea の改行は CR+LF で届く。保存する前にそろえる

次回は、過去のキャンプからキャンプ場の情報を引用する機能と、本番を独自ドメインで開けるようにした話です。ここで、ステップ1 がひと区切りになります。

← トップページに戻る