ハコヅミを作る #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 に行ったキャンプは、実績の日付と「実績」の印)/右:そのキャンプの画面
カードには、印を付けていません。#23で測ったとおり、幅 375px のカードの日付の行は、いちばん長い「11/21(土)〜11/23(月)・2泊」で、残りが数 px しかありません。印の2文字は入らないので、Issue に書いておいた「入りきらないなら、リストだけ」に従いました。
年をまたいで、年の見出しが変わる
実績で年が変わることもあります。12/30 からの予定を、年明けの 1/2 にずらした、という場合です。
このキャンプは、予定の年(2046年)ではなく、実績の年(2047年)の見出しの下に出ます。E2E では、このずれをわざと作って、見出しの下に出る年と、並び順が「予定の順なら逆になる」組み合わせで確かめています。
広い幅でも、カードは2列

広い幅でも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:計画/記録)
案C の「予定」「実績」は短くて分かりやすいのですが、「予定」のカードにキャンプ場や画像も入るので、少しずれます。案A の「行く前に入れるもの」「行った後に入れるもの」は長いものの、そのカードに何を入れるのかが、そのまま分かります。

「行った後に入れるもの」のカード。保存のボタンは、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 がひと区切りになります。