diff --git a/src/compositions/Blocks/EventsBlock.tsx b/src/compositions/Blocks/EventsBlock.tsx
index 7b2f2e7..eff867e 100644
--- a/src/compositions/Blocks/EventsBlock.tsx
+++ b/src/compositions/Blocks/EventsBlock.tsx
@@ -32,7 +32,7 @@ export async function EventsBlock({
diff --git a/src/fetch/eventOccurrences.ts b/src/fetch/eventOccurrences.ts
index 8d31ac4..c536189 100644
--- a/src/fetch/eventOccurrences.ts
+++ b/src/fetch/eventOccurrences.ts
@@ -1,7 +1,10 @@
import { getPayload, PaginatedDocs } from 'payload'
+import { unstable_cache } from 'next/cache'
import config from '@/payload.config'
import { EventOccurrence } from '@/payload-types'
+const CACHE_TTL = 300 // 5 minutes
+
type ListArgs = {
parishId?: string
groupId?: string
@@ -12,10 +15,66 @@ type ListArgs = {
toDate?: Date
}
+// Resolved args use ISO strings so they serialize into a stable cache key.
+type ResolvedArgs = {
+ parishId?: string
+ groupId?: string
+ eventId?: string
+ limit: number
+ page: number
+ fromDate?: string
+ toDate?: string
+}
+
+// Round down to the cache window so a default `new Date()` (millisecond
+// precision) produces a stable key for the duration of the TTL.
+function roundDownToTtl(date: Date): Date {
+ const ms = CACHE_TTL * 1000
+ return new Date(Math.floor(date.getTime() / ms) * ms)
+}
+
/**
* Fetch upcoming event occurrences, joined to their parent event. Always
* filters to occurrences whose parent event is published.
*/
+const getUpcomingOccurrences = unstable_cache(
+ async (args: ResolvedArgs): Promise> => {
+ const { parishId, groupId, eventId, limit, page, fromDate, toDate } = args
+
+ const query: any = {
+ and: [
+ { date: { greater_than_equal: fromDate } },
+ { 'event._status': { equals: 'published' } },
+ ],
+ }
+
+ if (toDate) {
+ query.and.push({ date: { less_than: toDate } })
+ }
+ if (eventId) {
+ query.and.push({ event: { equals: eventId } })
+ }
+ if (parishId) {
+ query.and.push({ 'event.parish': { equals: parishId } })
+ }
+ if (groupId) {
+ query.and.push({ 'event.group': { equals: groupId } })
+ }
+
+ const payload = await getPayload({ config })
+ return payload.find({
+ collection: 'eventOccurrence',
+ sort: 'date',
+ where: query,
+ depth: 2,
+ limit,
+ page,
+ }) as Promise>
+ },
+ ['fetchUpcomingOccurrences'],
+ { revalidate: CACHE_TTL },
+)
+
export async function fetchUpcomingOccurrences(
args?: ListArgs,
): Promise> {
@@ -29,41 +88,59 @@ export async function fetchUpcomingOccurrences(
toDate,
} = args || {}
- const query: any = {
- and: [
- { date: { greater_than_equal: fromDate.toISOString() } },
- { 'event._status': { equals: 'published' } },
- ],
- }
-
- if (toDate) {
- query.and.push({ date: { less_than: toDate.toISOString() } })
- }
- if (eventId) {
- query.and.push({ event: { equals: eventId } })
- }
- if (parishId) {
- query.and.push({ 'event.parish': { equals: parishId } })
- }
- if (groupId) {
- query.and.push({ 'event.group': { equals: groupId } })
- }
-
- const payload = await getPayload({ config })
- return payload.find({
- collection: 'eventOccurrence',
- sort: 'date',
- where: query,
- depth: 2,
+ return getUpcomingOccurrences({
+ parishId,
+ groupId,
+ eventId,
limit,
page,
- }) as Promise>
+ fromDate: roundDownToTtl(fromDate).toISOString(),
+ toDate: toDate?.toISOString(),
+ })
}
/**
* Fetch past event occurrences (most recent first), joined to their parent
* event. Always filters to occurrences whose parent event is published.
*/
+const getPastOccurrences = unstable_cache(
+ async (args: ResolvedArgs): Promise> => {
+ const { parishId, groupId, eventId, limit, page, fromDate, toDate } = args
+
+ const query: any = {
+ and: [
+ { date: { less_than: toDate } },
+ { 'event._status': { equals: 'published' } },
+ ],
+ }
+
+ if (fromDate) {
+ query.and.push({ date: { greater_than_equal: fromDate } })
+ }
+ if (eventId) {
+ query.and.push({ event: { equals: eventId } })
+ }
+ if (parishId) {
+ query.and.push({ 'event.parish': { equals: parishId } })
+ }
+ if (groupId) {
+ query.and.push({ 'event.group': { equals: groupId } })
+ }
+
+ const payload = await getPayload({ config })
+ return payload.find({
+ collection: 'eventOccurrence',
+ sort: '-date',
+ where: query,
+ depth: 2,
+ limit,
+ page,
+ }) as Promise>
+ },
+ ['fetchPastOccurrences'],
+ { revalidate: CACHE_TTL },
+)
+
export async function fetchPastOccurrences(
args?: ListArgs,
): Promise> {
@@ -77,35 +154,15 @@ export async function fetchPastOccurrences(
toDate = new Date(),
} = args || {}
- const query: any = {
- and: [
- { date: { less_than: toDate.toISOString() } },
- { 'event._status': { equals: 'published' } },
- ],
- }
-
- if (fromDate) {
- query.and.push({ date: { greater_than_equal: fromDate.toISOString() } })
- }
- if (eventId) {
- query.and.push({ event: { equals: eventId } })
- }
- if (parishId) {
- query.and.push({ 'event.parish': { equals: parishId } })
- }
- if (groupId) {
- query.and.push({ 'event.group': { equals: groupId } })
- }
-
- const payload = await getPayload({ config })
- return payload.find({
- collection: 'eventOccurrence',
- sort: '-date',
- where: query,
- depth: 2,
+ return getPastOccurrences({
+ parishId,
+ groupId,
+ eventId,
limit,
page,
- }) as Promise>
+ fromDate: fromDate?.toISOString(),
+ toDate: roundDownToTtl(toDate).toISOString(),
+ })
}
/**