From ff9bafe77a5e0dd84b613bb2aae0bf2a9cb84359 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Thu, 5 Nov 2026 13:40:32 +0900 Subject: events: 年度別アーカイブとRSSフィードを追加 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 月別ページに加えて、4月〜翌3月の年度ページ(/kodaira/events/r8/)を 追加した。月別リンクは年度ごとにまとめ、今後の予定はfeed.xmlで 配信する。 --- src/pages/kodaira/events/[month].astro | 72 ------------------- src/pages/kodaira/events/[period].astro | 124 ++++++++++++++++++++++++++++++++ src/pages/kodaira/events/feed.xml.ts | 79 ++++++++++++++++++++ 3 files changed, 203 insertions(+), 72 deletions(-) delete mode 100644 src/pages/kodaira/events/[month].astro create mode 100644 src/pages/kodaira/events/[period].astro create mode 100644 src/pages/kodaira/events/feed.xml.ts (limited to 'src/pages/kodaira/events') diff --git a/src/pages/kodaira/events/[month].astro b/src/pages/kodaira/events/[month].astro deleted file mode 100644 index 88ce61f..0000000 --- a/src/pages/kodaira/events/[month].astro +++ /dev/null @@ -1,72 +0,0 @@ ---- -// 月別アーカイブ(/kodaira/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}/`; ---- - - -

← 小平市のイベント・行事予定カレンダー(今後)に戻る

- -

- {label}の小平市のイベントと、審議会等・市議会の開催予定です。開催日が過ぎたものも記録として掲載しています。 - 変更になることがありますので、各問い合わせ先へご確認ください。 -

- - { - meetingRows.length > 0 && ( - <> -

審議会等・市議会の開催予定

-
- -
- - ) - } - - { - eventRows.length > 0 && ( - <> -

イベント

-
- -
- - ) - } - - -
diff --git a/src/pages/kodaira/events/[period].astro b/src/pages/kodaira/events/[period].astro new file mode 100644 index 0000000..7ef4599 --- /dev/null +++ b/src/pages/kodaira/events/[period].astro @@ -0,0 +1,124 @@ +--- +// 月別・年度別アーカイブ(/kodaira/events/2026-11/、/kodaira/events/r8/ など)。 +// 行事予定のデータ(src/data/schedule/*.json)から、その期間の予定だけを静的に生成する。 +import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +import ScheduleTable from '@/components/ScheduleTable.astro'; +import { + fiscalYearKeys, + fiscalYearLabel, + fiscalYearOf, + monthCounts, + monthKeys, + monthLabel, + monthsInFiscalYear, + rowsInFiscalYear, + rowsInMonth, +} from '@/lib/schedule-months'; +import { SCHEDULE_BASE_PATH } from '@/data/schedule/config'; + +export function getStaticPaths() { + return [ + ...monthKeys.map((key, i) => ({ + params: { period: key }, + props: { + kind: 'month' as const, + key, + prev: monthKeys[i - 1] ?? null, + next: monthKeys[i + 1] ?? null, + }, + })), + ...fiscalYearKeys.map((key, i) => ({ + params: { period: key }, + props: { + kind: 'fiscal' as const, + key, + prev: fiscalYearKeys[i - 1] ?? null, + next: fiscalYearKeys[i + 1] ?? null, + }, + })), + ]; +} + +const { kind, key, prev, next } = Astro.props as { + kind: 'month' | 'fiscal'; + key: string; + prev: string | null; + next: string | null; +}; + +const label = kind === 'fiscal' ? fiscalYearLabel(key) : monthLabel(key); +const periodLabel = (period: string) => + kind === 'fiscal' ? fiscalYearLabel(period) : monthLabel(period); + +const meetingRows = + kind === 'fiscal' ? rowsInFiscalYear(key, 'meeting') : rowsInMonth(key, 'meeting'); +const eventRows = + kind === 'fiscal' ? rowsInFiscalYear(key, 'event') : rowsInMonth(key, 'event'); + +// 年度ページでは、その年度の月別ページへの一覧も並べる。 +const months = kind === 'fiscal' ? monthsInFiscalYear(key) : []; + +const url = (period: string) => `${SCHEDULE_BASE_PATH}${period}/`; +--- + + +

← 小平市のイベント・行事予定カレンダー(今後)に戻る

+ +

+ {label}の小平市のイベントと、審議会等・市議会の開催予定です。開催日が過ぎたものも記録として掲載しています。 + 変更になることがありますので、各問い合わせ先へご確認ください。 +

+ + { + kind === 'fiscal' && ( + + ) + } + + { + meetingRows.length > 0 && ( + <> +

審議会等・市議会の開催予定

+
+ +
+ + ) + } + + { + eventRows.length > 0 && ( + <> +

イベント

+
+ +
+ + ) + } + + { + kind === 'month' && ( + + ) + } + + +
diff --git a/src/pages/kodaira/events/feed.xml.ts b/src/pages/kodaira/events/feed.xml.ts new file mode 100644 index 0000000..860f5f2 --- /dev/null +++ b/src/pages/kodaira/events/feed.xml.ts @@ -0,0 +1,79 @@ +// これからの予定のRSSフィード(/kodaira/events/feed.xml)。 +// ビルド時点の日付以降に開催される予定を、開催日の早い順に最大60件出力する。 +import type { APIRoute } from 'astro'; +import { items } from '@/lib/schedule-months'; +import { entryDates } from '@/lib/schedule-dates'; +import { SCHEDULE_BASE_PATH, SITE_URL } from '@/data/schedule/config'; + +const stripTags = (value: string) => + value + .replace(/<[^>]*>/g, '') + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/\s+/g, ' ') + .trim(); + +const escapeXml = (value: string) => + value + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + +const pad = (n: number) => String(n).padStart(2, '0'); +const now = new Date(); +const todayKey = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`; + +const toRfc822 = (date: string) => new Date(`${date}T00:00:00+09:00`).toUTCString(); + +const entries = items + .map((item) => { + const dates = entryDates(item.type, item.row.date, item.row.place); + return { item, upcoming: dates.filter((date) => date >= todayKey) }; + }) + .filter(({ upcoming }) => upcoming.length > 0) + .sort((a, b) => a.upcoming[0].localeCompare(b.upcoming[0])) + .slice(0, 60); + +const itemXml = ({ item: { type, row }, upcoming }: (typeof entries)[number]) => { + const title = stripTags(row.title); + const description = [stripTags(row.place), stripTags(row.note)].filter(Boolean).join('/'); + const datesLabel = upcoming + .map((date) => `${Number(date.slice(5, 7))}月${Number(date.slice(8, 10))}日`) + .join('、'); + const link = `${SITE_URL}${SCHEDULE_BASE_PATH}${upcoming[0].slice(0, 7)}/`; + const guid = `${SITE_URL}${SCHEDULE_BASE_PATH}#${type}-${upcoming[0]}-${encodeURIComponent(title)}`; + + return ` + ${escapeXml(title)} + ${escapeXml(link)} + ${escapeXml(guid)} + ${type === 'meeting' ? '審議会等・市議会' : 'イベント'} + ${toRfc822(upcoming[0])} + ${escapeXml(`開催日:${datesLabel}${description ? `/${description}` : ''}`)} + `; +}; + +export const GET: APIRoute = () => { + const xml = ` + + + 小平市のイベント・行事予定(審議会・市議会の日程も掲載) + ${SITE_URL}${SCHEDULE_BASE_PATH} + 小平市のイベントと、審議会等・市議会の開催予定です。市報こだいら・市公式X・LoGoフォーム・市議会の行事予定表などをもとにまとめています。 + ja + ${now.toUTCString()} +${entries.map(itemXml).join('\n')} + + +`; + + return new Response(xml, { + headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' }, + }); +}; -- cgit v1.3.1