A Calendar page beside Reminders: a month view of interviews and the other dated things of a search #204

Closed
opened 2026-09-13 08:39:26 +00:00 by tiagoagueda · 0 comments
Owner

The change

A Calendar page, as a companion to Reminders: a full calendar view, month view by
default
, showing job interviews and the other dated things of a search. What populates
it beyond interviews is decided later; this issue is the view.

What Postulo has today

Everything dated is a list, and the lists are in different places:

  • Interviews (Interview, applications/models.py:507): a start, an end, a place,
    a kind, the people, an outcome, and a calendar uid. Listed soonest-first by
    InterviewListView (views.py:514, past on request), reached from the dashboard
    rather than the navigation, and exported as iCalendar — one .ics per interview and one
    feed for all of them (applications/ical.py, interview_calendar at urls.py:35),
    written by hand in UTC. The dav plugin pushes them to a CalDAV calendar called Postulo.
  • Reminders (Reminder, :415): a sentence, a moment it is due, done or not,
    notified or not. A navigation item of its own (navigation.py:76) and a list.
  • Dated things with no page of their own: an application's deadline, a posting's
    closes_at, and — once #202 exists — an employment office's appointments and its
    declaration cadence.

Nothing draws a month. A person asking "what is on this week" opens two lists and reads
dates.

What a fix has to settle

  • A page and a route — applications:calendar or a calendar namespace — with a
    navigation item beside Reminders, which means a NavItem in navigation.py, a
    place in HIDEABLE so it can be hidden like the rest, and a line in
    tests/e2e/test_accessibility.py's walk so test_page_coverage.py sees it visited.
  • Month first, without scripts. Postulo vendors two scripts (static/js/vendor:
    htmx and zxcvbn) and its content-security policy allows no inline script and no CDN
    (base.html:24-25, Accessibility in the wiki). A month grid is a table the server can
    render — seven columns, a row per week, the days of the month in the person's time zone
    (core/middleware.py:32 activates Profile.time_zone) — with previous and next as
    links carrying ?month=2026-09, so it is bookmarkable and works with nothing loaded. A
    calendar library is a decision to take deliberately if week or day views want one, not
    a starting point.
  • What an event is, for the page. A shape the page draws — a moment or a span, a
    title, a link, a kind for colour and words — that interviews and reminders fill now
    and the rest can fill later without the page changing. The report has this pattern
    already (reports.Evidence). Interviews are spans (starts_at to ends_at);
    reminders are moments; a deadline is a day. A reminder that is done is drawn as done,
    not dropped.
  • The other views: week and day, and an agenda list which is what a phone wants —
    a month grid at 390 pixels is thirty-one cells of nothing. Month first; the others are
    the same events in another shape, and the request says month by default rather than
    month only.
  • Where the day's items go on a cell. A cell with three interviews and two reminders
    cannot hold five sentences; a count and the first one, with the day opening the agenda
    for that day, is the usual answer. Decide it rather than truncate.
  • The iCalendar feed already exists for interviews; a page that shows reminders
    and deadlines makes "everything in this calendar as a feed" the obvious next ask, and
    ical.py is where it would go — later.
  • The wiki: Tracking applications covers interviews and reminders (:290-300);
    the calendar gets its section there, and the Getting started table of pages a row.

Not in this issue: what populates the calendar beyond interviews and reminders, creating
events from the calendar, or syncing anything.

## The change A **Calendar** page, as a companion to Reminders: a full calendar view, **month view by default**, showing job interviews and the other dated things of a search. What populates it beyond interviews is decided later; this issue is the view. ## What Postulo has today Everything dated is a list, and the lists are in different places: - **Interviews** (`Interview`, `applications/models.py:507`): a start, an end, a place, a kind, the people, an outcome, and a calendar `uid`. Listed soonest-first by `InterviewListView` (`views.py:514`, past on request), reached from the dashboard rather than the navigation, and exported as iCalendar — one `.ics` per interview and one feed for all of them (`applications/ical.py`, `interview_calendar` at `urls.py:35`), written by hand in UTC. The dav plugin pushes them to a CalDAV calendar called *Postulo*. - **Reminders** (`Reminder`, `:415`): a sentence, a moment it is due, done or not, notified or not. A navigation item of its own (`navigation.py:76`) and a list. - **Dated things with no page of their own**: an application's `deadline`, a posting's `closes_at`, and — once #202 exists — an employment office's appointments and its declaration cadence. Nothing draws a month. A person asking "what is on this week" opens two lists and reads dates. ## What a fix has to settle - **A page and a route** — `applications:calendar` or a `calendar` namespace — with a navigation item beside *Reminders*, which means a `NavItem` in `navigation.py`, a place in `HIDEABLE` so it can be hidden like the rest, and a line in `tests/e2e/test_accessibility.py`'s walk so `test_page_coverage.py` sees it visited. - **Month first, without scripts.** Postulo vendors two scripts (`static/js/vendor`: htmx and zxcvbn) and its content-security policy allows no inline script and no CDN (`base.html:24-25`, *Accessibility* in the wiki). A month grid is a table the server can render — seven columns, a row per week, the days of the month in the person's time zone (`core/middleware.py:32` activates `Profile.time_zone`) — with *previous* and *next* as links carrying `?month=2026-09`, so it is bookmarkable and works with nothing loaded. A calendar library is a decision to take deliberately if week or day views want one, not a starting point. - **What an event is, for the page.** A shape the page draws — a moment or a span, a title, a link, a kind for colour and words — that interviews and reminders fill now and the rest can fill later without the page changing. The report has this pattern already (`reports.Evidence`). Interviews are spans (`starts_at` to `ends_at`); reminders are moments; a deadline is a day. A reminder that is done is drawn as done, not dropped. - **The other views**: week and day, and an *agenda* list which is what a phone wants — a month grid at 390 pixels is thirty-one cells of nothing. Month first; the others are the same events in another shape, and the request says month by default rather than month only. - **Where the day's items go on a cell.** A cell with three interviews and two reminders cannot hold five sentences; a count and the first one, with the day opening the agenda for that day, is the usual answer. Decide it rather than truncate. - **The iCalendar feed already exists for interviews**; a page that shows reminders and deadlines makes "everything in this calendar as a feed" the obvious next ask, and `ical.py` is where it would go — later. - **The wiki**: *Tracking applications* covers interviews and reminders (`:290-300`); the calendar gets its section there, and the *Getting started* table of pages a row. Not in this issue: what populates the calendar beyond interviews and reminders, creating events from the calendar, or syncing anything.
tiagoagueda added this to the 0.3.0 milestone 2026-09-13 08:39:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#204
No description provided.