aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-11-05 14:58:06 +0900
committerYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-11-05 14:58:06 +0900
commite7b60d5ef8c8cf5471a62498fd0a88f61cd2812e (patch)
tree7a7033fc634baa1d7695dd52e90961e98cfc5e16
parent3e02a6a05c6a5b26fbb19fa91be9afbc6b12877d (diff)
events: メインは前月以降、アーカイブは前々月以前に分ける
メインページの一覧とカレンダーを前月以降に広げ、前々月以前の月だけを アーカイブとして生成するようにした。該当が無いときはアーカイブ欄に その旨を表示する。月が進めば自動でページが増える。 RSSと旧URLの転送先も、存在しない月ページを指さないよう合わせた。
-rw-r--r--src/components/ScheduleInteractive.astro6
-rw-r--r--src/components/ScheduleMonthLinks.astro62
-rw-r--r--src/components/ScheduleTable.astro15
-rw-r--r--src/content/docs/kodaira/events.mdx8
-rw-r--r--src/lib/schedule-dates.ts18
-rw-r--r--src/lib/schedule-months.ts16
-rw-r--r--src/pages/kodaira-data/events/[...path].astro13
-rw-r--r--src/pages/kodaira/events/[period].astro29
-rw-r--r--src/pages/kodaira/events/feed.xml.ts2
-rw-r--r--src/styles/custom.css9
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);