diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/ScheduleInteractive.astro | 6 | ||||
| -rw-r--r-- | src/components/ScheduleMonthLinks.astro | 62 | ||||
| -rw-r--r-- | src/components/ScheduleTable.astro | 15 | ||||
| -rw-r--r-- | src/content/docs/kodaira/events.mdx | 8 | ||||
| -rw-r--r-- | src/lib/schedule-dates.ts | 18 | ||||
| -rw-r--r-- | src/lib/schedule-months.ts | 16 | ||||
| -rw-r--r-- | src/pages/kodaira-data/events/[...path].astro | 13 | ||||
| -rw-r--r-- | src/pages/kodaira/events/[period].astro | 29 | ||||
| -rw-r--r-- | src/pages/kodaira/events/feed.xml.ts | 2 | ||||
| -rw-r--r-- | src/styles/custom.css | 9 |
10 files changed, 122 insertions, 56 deletions
diff --git a/src/components/ScheduleInteractive.astro b/src/components/ScheduleInteractive.astro index 30569db..f0bd73c 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, entryDates } from '@/lib/schedule-dates'; + import { parseDates, applyExclusions, toKey, keyToDate, entryDates, mainStartMonth } from '@/lib/schedule-dates'; const CATEGORY_DEFS = [ { icon: '👶', label: '子育て' }, @@ -100,9 +100,9 @@ const todayKey = toKey(today.getFullYear(), today.getMonth() + 1, today.getDate()); const todayMonth = todayKey.slice(0, 7); // 今日の月もカレンダーで移動できるように含める。 - // メインページの一覧は今月以降に絞っているので、カレンダーも過去の月には戻らない。 + // メインページの一覧は前月以降に絞っているので、カレンダーも前々月以前には戻らない。 const months = [...new Set([...dateKeys.map((k) => k.slice(0, 7)), todayMonth])] - .filter((month) => month >= todayMonth) + .filter((month) => month >= mainStartMonth()) .sort(); // 開催日が今日より前のものは「終了」と表示する。 diff --git a/src/components/ScheduleMonthLinks.astro b/src/components/ScheduleMonthLinks.astro index adf3b32..61f1ae1 100644 --- a/src/components/ScheduleMonthLinks.astro +++ b/src/components/ScheduleMonthLinks.astro @@ -1,40 +1,52 @@ --- // 月別・年度別アーカイブのリンク一覧。行事予定ページの下部に置く。 +// アーカイブは前々月以前の月だけ(前月以降はメインページに掲載している)。 import { - fiscalYearKeys, + archiveFiscalYearKeys, + archiveMonthKeys, fiscalYearLabel, fiscalYearOf, monthCounts, - monthKeys, monthLabel, } from '@/lib/schedule-months'; import { scheduleUrl } from '@/data/schedule/config'; -const groups = fiscalYearKeys.map((fy) => ({ +const groups = [...new Set(archiveMonthKeys.map((month) => fiscalYearOf(month)))].map((fy) => ({ key: fy, label: fiscalYearLabel(fy), - months: monthKeys.filter((month) => fiscalYearOf(month) === fy), + hasPage: archiveFiscalYearKeys.includes(fy), + months: archiveMonthKeys.filter((month) => fiscalYearOf(month) === fy), })); --- -<div class="schedule-archive"> - { - groups.map((group) => ( - <section class="schedule-archive-year"> - <h3 class="schedule-archive-year-title"> - <a href={scheduleUrl(`${group.key}/`)}>{group.label}の予定</a> - </h3> - <ul class="schedule-archive-months"> - {group.months.map((key) => ( - <li> - <a href={scheduleUrl(`${key}/`)}> - <span>{monthLabel(key)}</span> - <span class="schedule-archive-count">{monthCounts[key]}件</span> - </a> - </li> - ))} - </ul> - </section> - )) - } -</div> +{ + groups.length === 0 ? ( + <p class="schedule-archive-empty"> + 前々月より前に終了した予定はまだありません。月が進むと、ここに過去の予定が表示されます。 + </p> + ) : ( + <div class="schedule-archive"> + {groups.map((group) => ( + <section class="schedule-archive-year"> + <h3 class="schedule-archive-year-title"> + {group.hasPage ? ( + <a href={scheduleUrl(`${group.key}/`)}>{group.label}の予定</a> + ) : ( + group.label + )} + </h3> + <ul class="schedule-archive-months"> + {group.months.map((key) => ( + <li> + <a href={scheduleUrl(`${key}/`)}> + <span>{monthLabel(key)}</span> + <span class="schedule-archive-count">{monthCounts[key]}件</span> + </a> + </li> + ))} + </ul> + </section> + ))} + </div> + ) +} diff --git a/src/components/ScheduleTable.astro b/src/components/ScheduleTable.astro index e4c59a9..dcf210d 100644 --- a/src/components/ScheduleTable.astro +++ b/src/components/ScheduleTable.astro @@ -3,7 +3,7 @@ // セルの中身は HTML(リンクを含む)としてそのまま出力する。 import meetings from '@/data/schedule/meetings.json'; import events from '@/data/schedule/events.json'; -import { entryDates } from '@/lib/schedule-dates'; +import { entryDates, mainStartMonth } from '@/lib/schedule-dates'; type Row = { date: string; title: string; place: string; note: string; source: string }; type TableData = { columns: string[]; rows: Row[] }; @@ -12,18 +12,17 @@ const { variant, rows, scope = 'all', -} = Astro.props as { variant: 'meeting' | 'event'; rows?: Row[]; scope?: 'all' | 'current' }; +} = Astro.props as { variant: 'meeting' | 'event'; rows?: Row[]; scope?: 'all' | 'main' }; const base: TableData = (variant === 'meeting' ? meetings : events) as TableData; // rows を渡された場合はその行だけを表示する(月別・年度別アーカイブ用) let list = rows ?? base.rows; -// scope="current" のときは、今月以降に開催される予定だけを表示する。 -// 終了した予定は月別アーカイブに移し、メインページが年々肥大化しないようにする。 -if (scope === 'current') { - const now = new Date(); - const monthStart = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`; +// scope="main" のときは、前月以降に開催される予定だけを表示する。 +// 前々月以前の終了分はアーカイブに移し、メインページが年々肥大化しないようにする。 +if (scope === 'main') { + const start = `${mainStartMonth()}-01`; list = list.filter((row) => - entryDates(variant, row.date, row.place).some((date) => date >= monthStart), + entryDates(variant, row.date, row.place).some((date) => date >= start), ); } diff --git a/src/content/docs/kodaira/events.mdx b/src/content/docs/kodaira/events.mdx index 9475848..4cf5462 100644 --- a/src/content/docs/kodaira/events.mdx +++ b/src/content/docs/kodaira/events.mdx @@ -55,7 +55,7 @@ import ScheduleMonthLinks from '../../../components/ScheduleMonthLinks.astro'; 位置は、住所をもとにしたおおよその位置です。場所を1点に絞れない予定は地図に表示していません(一覧表には掲載しています)。 -次の「今後のイベント」と「審議会等・市議会の開催予定」は、すべて上のカレンダー・一覧表・地図に反映されています。一覧表は今月以降の予定を掲載し、終了した予定は月別アーカイブに移しています。 +次の「今後のイベント」と「審議会等・市議会の開催予定」は、すべて上のカレンダー・一覧表・地図に反映されています。一覧表は前月以降の予定を掲載し、前々月より前に終了した予定は月別アーカイブに移しています。 ## 今後のイベント @@ -69,7 +69,7 @@ import ScheduleMonthLinks from '../../../components/ScheduleMonthLinks.astro'; <div id="events-table" class="schedule-table"> -<ScheduleTable variant="event" scope="current" /> +<ScheduleTable variant="event" scope="main" /> </div> @@ -87,7 +87,7 @@ import ScheduleMonthLinks from '../../../components/ScheduleMonthLinks.astro'; <div id="meetings-table" class="schedule-table schedule-table--date-nowrap"> -<ScheduleTable variant="meeting" scope="current" /> +<ScheduleTable variant="meeting" scope="main" /> </div> @@ -97,7 +97,7 @@ import ScheduleMonthLinks from '../../../components/ScheduleMonthLinks.astro'; ## 月別のアーカイブ -終了した予定は、月ごと・年度ごとのページに移しています。それぞれの期間の予定を見られます。 +前々月より前に終了した予定を、月ごと・年度ごとにまとめています。 <ScheduleMonthLinks /> diff --git a/src/lib/schedule-dates.ts b/src/lib/schedule-dates.ts index 719a9c6..eaa4a3a 100644 --- a/src/lib/schedule-dates.ts +++ b/src/lib/schedule-dates.ts @@ -148,3 +148,21 @@ export function entryDates( } return [...dates].sort(); } + +const monthKeyOf = (date: Date) => `${date.getFullYear()}-${pad2(date.getMonth() + 1)}`; + +/** + * メインページに載せる範囲の開始月(前月)。例: 2026-11-05 → 2026-10 + * 前月分も残すことで、終わったばかりの予定をメインページで確認できる。 + */ +export function mainStartMonth(now = new Date()): string { + return monthKeyOf(new Date(now.getFullYear(), now.getMonth() - 1, 1)); +} + +/** + * アーカイブ(月別・年度別ページ)に移す範囲の終わり月(前々月)。 + * 例: 2026-11-05 → 2026-09。前月以前はメインページに残す。 + */ +export function archiveEndMonth(now = new Date()): string { + return monthKeyOf(new Date(now.getFullYear(), now.getMonth() - 2, 1)); +} diff --git a/src/lib/schedule-months.ts b/src/lib/schedule-months.ts index 829fef5..bff86a2 100644 --- a/src/lib/schedule-months.ts +++ b/src/lib/schedule-months.ts @@ -4,7 +4,7 @@ // (カレンダー側が必要とする日付計算は src/lib/schedule-dates.ts にある) import meetings from '@/data/schedule/meetings.json'; import events from '@/data/schedule/events.json'; -import { entryDates } from './schedule-dates'; +import { archiveEndMonth, 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 }; @@ -76,3 +76,17 @@ export function rowsInMonths(months: string[], type: 'meeting' | 'event'): Row[] export function rowsInFiscalYear(fy: string, type: 'meeting' | 'event'): Row[] { return rowsInMonths(monthsInFiscalYear(fy), type); } + +/** + * アーカイブ(月別・年度別ページ)に移す月。前々月以前。 + * 前月と当月以降はメインページに残すので、ここには含めない。 + */ +export const archiveMonthKeys: string[] = monthKeys.filter( + (month) => month <= archiveEndMonth(), +); + +/** アーカイブに移す年度(その年度の最後の月が前々月以前のもの) */ +export const archiveFiscalYearKeys: string[] = fiscalYearKeys.filter((fy) => { + const months = monthsInFiscalYear(fy); + return months.length > 0 && months[months.length - 1] <= archiveEndMonth(); +}); diff --git a/src/pages/kodaira-data/events/[...path].astro b/src/pages/kodaira-data/events/[...path].astro index e09cc30..0eb438b 100644 --- a/src/pages/kodaira-data/events/[...path].astro +++ b/src/pages/kodaira-data/events/[...path].astro @@ -2,12 +2,21 @@ // 旧URL(/kodaira-data/events/2026-11/ など)から新しいURLへの転送。 // 本番のApacheは public/.htaccess が301で返す。astro dev / preview では // .htaccess が効かないため、ここで同じ転送を用意しておく。 -import { fiscalYearKeys, monthKeys } from '@/lib/schedule-months'; +import { + archiveFiscalYearKeys, + archiveMonthKeys, + fiscalYearKeys, + monthKeys, +} from '@/lib/schedule-months'; +import { SCHEDULE_BASE_PATH } from '@/data/schedule/config'; export function getStaticPaths() { return [...monthKeys, ...fiscalYearKeys].map((path) => ({ params: { path } })); } +// アーカイブにある期間はそのページへ、まだアーカイブに移していない期間は +// メインページ(前月以降を掲載)へ送る。 const { path } = Astro.params; -return Astro.redirect(`/kodaira/events/${path}/`, 301); +const archived = [...archiveMonthKeys, ...archiveFiscalYearKeys].includes(path ?? ''); +return Astro.redirect(archived ? `${SCHEDULE_BASE_PATH}${path}/` : SCHEDULE_BASE_PATH, 301); --- diff --git a/src/pages/kodaira/events/[period].astro b/src/pages/kodaira/events/[period].astro index 701d9e3..f2d03df 100644 --- a/src/pages/kodaira/events/[period].astro +++ b/src/pages/kodaira/events/[period].astro @@ -1,14 +1,14 @@ --- -// 月別・年度別アーカイブ(/kodaira/events/2026-11/、/kodaira/events/r8/ など)。 -// 行事予定のデータ(src/data/schedule/*.json)から、その期間の予定だけを静的に生成する。 +// 月別・年度別アーカイブ(/kodaira/events/2026-09/、/kodaira/events/r7/ など)。 +// 前々月以前の予定だけを静的ページにする(前月以降はメインページに載せる)。 import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; import ScheduleTable from '@/components/ScheduleTable.astro'; import { - fiscalYearKeys, + archiveFiscalYearKeys, + archiveMonthKeys, fiscalYearLabel, fiscalYearOf, monthCounts, - monthKeys, monthLabel, monthsInFiscalYear, rowsInFiscalYear, @@ -18,22 +18,22 @@ import { SCHEDULE_BASE_PATH } from '@/data/schedule/config'; export function getStaticPaths() { return [ - ...monthKeys.map((key, i) => ({ + ...archiveMonthKeys.map((key, i) => ({ params: { period: key }, props: { kind: 'month' as const, key, - prev: monthKeys[i - 1] ?? null, - next: monthKeys[i + 1] ?? null, + prev: archiveMonthKeys[i - 1] ?? null, + next: archiveMonthKeys[i + 1] ?? null, }, })), - ...fiscalYearKeys.map((key, i) => ({ + ...archiveFiscalYearKeys.map((key, i) => ({ params: { period: key }, props: { kind: 'fiscal' as const, key, - prev: fiscalYearKeys[i - 1] ?? null, - next: fiscalYearKeys[i + 1] ?? null, + prev: archiveFiscalYearKeys[i - 1] ?? null, + next: archiveFiscalYearKeys[i + 1] ?? null, }, })), ]; @@ -58,6 +58,11 @@ const eventRows = // 年度ページでは、その年度の月別ページへの一覧も並べる。 const months = kind === 'fiscal' ? monthsInFiscalYear(key) : []; +// 月別ページから年度ページに戻れるのは、その年度のページが存在するときだけ。 +const fiscalYearForKey = kind === 'month' ? fiscalYearOf(key) : null; +const hasFiscalPage = + fiscalYearForKey !== null && archiveFiscalYearKeys.includes(fiscalYearForKey); + const url = (period: string) => `${SCHEDULE_BASE_PATH}${period}/`; // 期間がすべて過去か、すべて未来か、今日を含むかで説明文を変える。 @@ -133,9 +138,9 @@ const description = isAllPast } { - kind === 'month' && ( + kind === 'month' && hasFiscalPage && fiscalYearForKey && ( <p class="schedule-fiscal-link"> - <a href={url(fiscalYearOf(key))}>{fiscalYearLabel(fiscalYearOf(key))}の予定をまとめて見る</a> + <a href={url(fiscalYearForKey)}>{fiscalYearLabel(fiscalYearForKey)}の予定をまとめて見る</a> </p> ) } diff --git a/src/pages/kodaira/events/feed.xml.ts b/src/pages/kodaira/events/feed.xml.ts index 860f5f2..a76b775 100644 --- a/src/pages/kodaira/events/feed.xml.ts +++ b/src/pages/kodaira/events/feed.xml.ts @@ -46,7 +46,7 @@ const itemXml = ({ item: { type, row }, upcoming }: (typeof entries)[number]) => 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 link = `${SITE_URL}${SCHEDULE_BASE_PATH}`; const guid = `${SITE_URL}${SCHEDULE_BASE_PATH}#${type}-${upcoming[0]}-${encodeURIComponent(title)}`; return ` <item> diff --git a/src/styles/custom.css b/src/styles/custom.css index e100625..0c2c4a6 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -2259,6 +2259,15 @@ td.sanpi-open .sanpi-title { gap: 0.9rem; margin: 1rem 0 1.4rem; } +.schedule-archive-empty { + margin: 0.8rem 0 1.2rem; + padding: 0.8rem 1rem; + border: 1px dashed var(--sl-color-gray-5); + border-radius: 0.6rem; + color: var(--sl-color-gray-3); + font-size: 0.9rem; + line-height: 1.7; +} .schedule-archive-year { padding: 0.9rem 1rem 1rem; border: 1px solid var(--sl-color-gray-5); |
