Events as a content type with a monthly calendar view and iCal feed.
An events section for your site: the owner creates events in Studio (date, time, location, description), visitors browse a monthly calendar or an upcoming-events list, and anyone can subscribe to the whole calendar in their own calendar app via an iCal feed.
Events are a content type. Prelo has no date field type, so start/end are ISO-8601 text fields — they validate with a small check and sort correctly as strings. The frontend renders a month grid from the published events, and a custom route serves a standards-compliant .ics feed generated from the same data.
You are adding events to a Prelo project.
1. **Content type.** In `cms.config.js`:
```js
event: {
label: "Events",
fields: {
start: { type: "text", required: true }, // ISO 8601, e.g. 2026-09-12T19:00
end: { type: "text" }, // optional, same format
location: { type: "text" },
url: { type: "text" }, // tickets / external page
summary: { type: "text" },
body: { type: "richtext" },
},
studio: { columns: ["start", "location"] },
},
```
Add a note in the field labels or project docs that `start`/`end` are `YYYY-MM-DDTHH:mm` local time. If the project has hooks-level validation available (`hooks.beforeSave`), reject saves where `start` doesn't parse with `Date.parse` or `end < start`; otherwise validate at render time and skip invalid events with a console warning.
2. **Data helper.** Server-side `getEvents({from, to})`: fetch published events (`GET /api/event?per_page=200`, paginate), parse dates, filter to the window, sort by `start`. Reuse it for the calendar page, the upcoming list, and the feed.
3. **Calendar page.** An `/events` page:
- Month grid (Mon–Sun columns, or locale per project), navigable prev/next via `?month=2026-09` query param — server-rendered, no state library. Days with events show the event title(s) linking to the event's detail page; the current day is highlighted subtly.
- Below (or on mobile instead of) the grid: an "Upcoming" list of the next 10 events with date, time, location, summary.
- Event detail pages at `/events/{slug}`: title, formatted date/time range, location, rendered body, and the external `url` as a button when set.
4. **iCal feed.** `"GET /events.ics"` in `cms.config.js` `routes:` (public URL: `/api/x/events.ics`):
- Emit `text/calendar` with `VCALENDAR`/`VEVENT` blocks: `UID` = `{id}@{site-host}`, `DTSTAMP` from `updated_at`, `DTSTART`/`DTEND` in UTC basic format (convert from local; if no `end`, default 1 hour), `SUMMARY`, `LOCATION`, `DESCRIPTION` (tag-stripped summary), `URL` (the event's page).
- Fold lines at 75 octets and escape commas/semicolons/newlines per RFC 5545 — calendar apps are strict.
- Include only future events plus the past 30 days.
5. **Subscribe link.** On the `/events` page, a "Subscribe (iCal)" link pointing at the feed URL, with a `webcal://` variant.
6. **Verify.** Create two events in Studio (one with end time, one without); check the month grid and both detail pages; validate the feed by importing it into a calendar app or an ICS validator; confirm draft events appear nowhere.The owner manages events entirely in Studio — publish to list, draft to hide, past events fall off the upcoming list on their own. Subscribers' calendar apps re-fetch the feed automatically.
Instant client-side search across pages and posts, indexed at build time.
Threaded comments with a moderation queue in Studio. No third-party embed.
Contact form with spam protection and email notifications, plus a submissions inbox in Studio.