diff options
| author | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-11-05 13:30:26 +0900 |
|---|---|---|
| committer | Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> | 2026-11-05 13:30:26 +0900 |
| commit | bb74335a2fc1b0697c2b7088b010484ade784519 (patch) | |
| tree | 39f8033b3d19af3f1376a222725bae6de816c98e /src | |
| parent | 248d64c096930fa031ce48ddaa4cdb30681918fa (diff) | |
events: 月別アーカイブページを追加
- /kodaira-data/events/YYYY-MM/ をデータから自動生成(2026年10月〜2027年3月の6か月分)
- 会議とイベントを月ごとに表示し、前月・翌月のリンクを設置
- 行事予定ページ下部に月別アーカイブのリンクを追加
- 日付計算を共通モジュール化し、カレンダーと生成ページで同じ結果になるようにした
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/ScheduleInteractive.astro | 14 | ||||
| -rw-r--r-- | src/components/ScheduleMonthLinks.astro | 16 | ||||
| -rw-r--r-- | src/components/ScheduleTable.astro | 6 | ||||
| -rw-r--r-- | src/content/docs/kodaira-data/events.mdx | 7 | ||||
| -rw-r--r-- | src/data/updates.json | 14 | ||||
| -rw-r--r-- | src/lib/schedule-dates.ts | 20 | ||||
| -rw-r--r-- | src/lib/schedule-months.ts | 44 | ||||
| -rw-r--r-- | src/pages/kodaira-data/events/[month].astro | 72 | ||||
| -rw-r--r-- | src/styles/custom.css | 23 |
9 files changed, 195 insertions, 21 deletions
diff --git a/src/components/ScheduleInteractive.astro b/src/components/ScheduleInteractive.astro index 3dd6048..424b453 100644 --- a/src/components/ScheduleInteractive.astro +++ b/src/components/ScheduleInteractive.astro @@ -1,7 +1,7 @@ <script> // URLは src/data/schedule/config.ts で一元管理している(ページ移転時はそこだけ変える) import { scheduleAbsoluteUrl } from '@/data/schedule/config'; - import { parseDates, applyExclusions, toKey, keyToDate } from '@/lib/schedule-dates'; + import { parseDates, applyExclusions, toKey, keyToDate, entryDates } from '@/lib/schedule-dates'; const CATEGORY_DEFS = [ { icon: '👶', label: '子育て' }, @@ -63,17 +63,7 @@ } row.dataset.category = category; - const dates = new Set(parseDates(dateText)); - // 審議会の「毎月1回(11月12日、…も開催)」のように、 - // 時間・会場欄に追加の開催日が書かれている場合がある。 - if (type === 'meeting') { - for (const key of parseDates(timeText)) dates.add(key); - } - // イベントの「(11月4日を除く)」を反映する。 - if (type === 'event') { - // 時間欄の「(11月4日を除く)」を日付セル側の集合に反映する。 - applyExclusions(dates, timeText); - } + const dates = new Set(entryDates(type, dateText, timeText)); entries.push({ type, diff --git a/src/components/ScheduleMonthLinks.astro b/src/components/ScheduleMonthLinks.astro new file mode 100644 index 0000000..8628998 --- /dev/null +++ b/src/components/ScheduleMonthLinks.astro @@ -0,0 +1,16 @@ +--- +// 月別アーカイブのリンク一覧。行事予定ページの下部に置く。 +import { monthCounts, monthKeys, monthLabel } from '@/lib/schedule-months'; +import { scheduleUrl } from '@/data/schedule/config'; +--- + +<ul class="schedule-month-archive"> + { + monthKeys.map((key) => ( + <li> + <a href={scheduleUrl(`${key}/`)}>{monthLabel(key)}の予定</a> + <span class="schedule-month-count">{monthCounts[key]}件</span> + </li> + )) + } +</ul> diff --git a/src/components/ScheduleTable.astro b/src/components/ScheduleTable.astro index 2f4b370..ff13827 100644 --- a/src/components/ScheduleTable.astro +++ b/src/components/ScheduleTable.astro @@ -7,8 +7,10 @@ import events from '@/data/schedule/events.json'; type Row = { date: string; title: string; place: string; note: string; source: string }; type TableData = { columns: string[]; rows: Row[] }; -const { variant } = Astro.props as { variant: 'meeting' | 'event' }; -const data: TableData = (variant === 'meeting' ? meetings : events) as TableData; +const { variant, rows } = Astro.props as { variant: 'meeting' | 'event'; rows?: Row[] }; +const base: TableData = (variant === 'meeting' ? meetings : events) as TableData; +// rows を渡された場合はその行だけを表示する(月別アーカイブ用) +const data: TableData = rows ? { columns: base.columns, rows } : base; --- <table> diff --git a/src/content/docs/kodaira-data/events.mdx b/src/content/docs/kodaira-data/events.mdx index a97f7c1..3455427 100644 --- a/src/content/docs/kodaira-data/events.mdx +++ b/src/content/docs/kodaira-data/events.mdx @@ -8,6 +8,7 @@ import ScheduleInteractive from '../../../components/ScheduleInteractive.astro'; import ScheduleMap from '../../../components/ScheduleMap.astro'; import ScheduleSeo from '../../../components/ScheduleSeo.astro'; import ScheduleTable from '../../../components/ScheduleTable.astro'; +import ScheduleMonthLinks from '../../../components/ScheduleMonthLinks.astro'; <ScheduleSeo /> @@ -87,6 +88,12 @@ import ScheduleTable from '../../../components/ScheduleTable.astro'; {/* <!-- textlint-enable --> */} +## 月別のアーカイブ + +過去の月の予定は、月ごとのページで見られます。 + +<ScheduleMonthLinks /> + ## ご注意 イベントの内容・日程は変更になることがあります。最新の情報は、各問い合わせ先または市公式Xでご確認ください。 diff --git a/src/data/updates.json b/src/data/updates.json index 8a245b4..64cd370 100644 --- a/src/data/updates.json +++ b/src/data/updates.json @@ -1,5 +1,12 @@ [ { + "title": "小平市ホームページのアーカイブ(暫定)と市の案内", + "url": "/kodaira-data/hp-archive/", + "group": "", + "date": "2026-11-05", + "kind": "update" + }, + { "title": "行政不服審査会の問題追及の軌跡", "url": "/gyosei-fufuku-sinsa/", "group": "", @@ -21,13 +28,6 @@ "kind": "update" }, { - "title": "小平市ホームページのアーカイブ(暫定)と市の案内", - "url": "/kodaira-data/hp-archive/", - "group": "", - "date": "2026-11-04", - "kind": "update" - }, - { "title": "こだいら 公園さんぽマップ", "url": "/kodaira-data/parks/", "group": "", 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<string, number> = (() => { + const counts: Record<string, number> = {}; + 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}月`; +} diff --git a/src/pages/kodaira-data/events/[month].astro b/src/pages/kodaira-data/events/[month].astro new file mode 100644 index 0000000..1969cee --- /dev/null +++ b/src/pages/kodaira-data/events/[month].astro @@ -0,0 +1,72 @@ +--- +// 月別アーカイブ(/kodaira-data/events/2026-11/ など)。 +// 行事予定のデータ(src/data/schedule/*.json)から、その月の予定だけを静的に生成する。 +import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +import ScheduleTable from '@/components/ScheduleTable.astro'; +import { monthKeys, monthLabel, rowsInMonth } from '@/lib/schedule-months'; +import { SCHEDULE_BASE_PATH } from '@/data/schedule/config'; + +export function getStaticPaths() { + return monthKeys.map((month, i) => ({ + params: { month }, + props: { + month, + prev: monthKeys[i - 1] ?? null, + next: monthKeys[i + 1] ?? null, + }, + })); +} + +const { month, prev, next } = Astro.props as { + month: string; + prev: string | null; + next: string | null; +}; + +const label = monthLabel(month); +const meetingRows = rowsInMonth(month, 'meeting'); +const eventRows = rowsInMonth(month, 'event'); + +const url = (key: string) => `${SCHEDULE_BASE_PATH}${key}/`; +--- + +<StarlightPage + frontmatter={{ + title: `小平市のイベント・行事予定(${label})`, + description: `${label}の小平市のイベントと、審議会・市議会の開催予定の一覧です。日程・会場・申込期限を確認できます。`, + }} +> + <p><a href={SCHEDULE_BASE_PATH}>← 小平市のイベント・行事予定カレンダー(今後)に戻る</a></p> + + <p> + {label}の小平市のイベントと、審議会等・市議会の開催予定です。開催日が過ぎたものも記録として掲載しています。 + 変更になることがありますので、各問い合わせ先へご確認ください。 + </p> + + { + meetingRows.length > 0 && ( + <> + <h2>審議会等・市議会の開催予定</h2> + <div class="schedule-table schedule-table--date-nowrap"> + <ScheduleTable variant="meeting" rows={meetingRows} /> + </div> + </> + ) + } + + { + eventRows.length > 0 && ( + <> + <h2>イベント</h2> + <div class="schedule-table"> + <ScheduleTable variant="event" rows={eventRows} /> + </div> + </> + ) + } + + <nav class="schedule-month-nav" aria-label="月の移動"> + {prev ? <a href={url(prev)}>← {monthLabel(prev)}</a> : <span />} + {next ? <a href={url(next)}>{monthLabel(next)} →</a> : <span />} + </nav> +</StarlightPage> diff --git a/src/styles/custom.css b/src/styles/custom.css index 8a27700..82b5c9f 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -2254,6 +2254,29 @@ td.sanpi-open .sanpi-title { font-size: 0.88rem; line-height: 1.7; } +.schedule-month-archive { + display: flex; + flex-wrap: wrap; + gap: 0.4rem 1.2rem; + margin: 0.6rem 0 1.2rem; + padding: 0; + list-style: none; +} +.schedule-month-archive li { + display: flex; + align-items: baseline; + gap: 0.35rem; +} +.schedule-month-count { + color: var(--sl-color-gray-3); + font-size: 0.8rem; +} +.schedule-month-nav { + display: flex; + justify-content: space-between; + gap: 1rem; + margin: 1.5rem 0 0.5rem; +} .schedule-updated { margin: 0.2rem 0 1rem; color: var(--sl-color-gray-3); |
