From bb74335a2fc1b0697c2b7088b010484ade784519 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Thu, 5 Nov 2026 13:30:26 +0900 Subject: events: 月別アーカイブページを追加 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /kodaira-data/events/YYYY-MM/ をデータから自動生成(2026年10月〜2027年3月の6か月分) - 会議とイベントを月ごとに表示し、前月・翌月のリンクを設置 - 行事予定ページ下部に月別アーカイブのリンクを追加 - 日付計算を共通モジュール化し、カレンダーと生成ページで同じ結果になるようにした --- src/lib/schedule-dates.ts | 20 ++++++++++++++++++++ src/lib/schedule-months.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/lib/schedule-months.ts (limited to 'src/lib') diff --git a/src/lib/schedule-dates.ts b/src/lib/schedule-dates.ts index 5f0900b..719a9c6 100644 --- a/src/lib/schedule-dates.ts +++ b/src/lib/schedule-dates.ts @@ -128,3 +128,23 @@ export function parseDates(raw: string): string[] { return [...dates].sort(); } + +/** + * 行事予定1行分の開催日を求める。 + * - 会議:日付セルに加えて、時間・会場セル内の日付も拾う(「毎月1回(11月12日も開催)」など) + * - イベント:時間セルの「(11月4日を除く)」を反映する + * カレンダー(ブラウザ)と月別アーカイブ(ビルド時)で同じ結果になるよう、ここに集約する。 + */ +export function entryDates( + type: 'meeting' | 'event', + dateText: string, + placeText: string, +): string[] { + const dates = new Set(parseDates(dateText)); + if (type === 'meeting') { + for (const key of parseDates(placeText)) dates.add(key); + } else { + applyExclusions(dates, placeText); + } + return [...dates].sort(); +} diff --git a/src/lib/schedule-months.ts b/src/lib/schedule-months.ts new file mode 100644 index 0000000..cd109b7 --- /dev/null +++ b/src/lib/schedule-months.ts @@ -0,0 +1,44 @@ +// 行事予定データを月ごとに集計する(ビルド時専用)。 +// +// 注意: このモジュールは JSON を import するため、ブラウザ用スクリプトからは読み込まないこと。 +// (カレンダー側が必要とする日付計算は src/lib/schedule-dates.ts にある) +import meetings from '@/data/schedule/meetings.json'; +import events from '@/data/schedule/events.json'; +import { entryDates } from './schedule-dates'; + +export type Row = { date: string; title: string; place: string; note: string; source: string }; +export type Item = { type: 'meeting' | 'event'; row: Row }; + +export const items: Item[] = [ + ...(meetings.rows as Row[]).map((row) => ({ type: 'meeting' as const, row })), + ...(events.rows as Row[]).map((row) => ({ type: 'event' as const, row })), +]; + +const datesOf = (item: Item) => entryDates(item.type, item.row.date, item.row.place); + +/** 月ごとの件数(会議・イベントの合計) */ +export const monthCounts: Record = (() => { + const counts: Record = {}; + for (const item of items) { + for (const date of new Set(datesOf(item).map((d) => d.slice(0, 7)))) { + counts[date] = (counts[date] ?? 0) + 1; + } + } + return counts; +})(); + +/** データに存在する月(YYYY-MM、昇順) */ +export const monthKeys: string[] = Object.keys(monthCounts).sort(); + +/** 指定した月に開催される行だけを返す */ +export function rowsInMonth(month: string, type: 'meeting' | 'event'): Row[] { + return items + .filter((item) => item.type === type && datesOf(item).some((d) => d.startsWith(month))) + .map((item) => item.row); +} + +/** 年月の表示(2026-11 → 2026年11月) */ +export function monthLabel(key: string): string { + const [y, m] = key.split('-').map(Number); + return `${y}年${m}月`; +} -- cgit v1.3.1