Skip to content

Modules (NXM)

Calendar sources

A package shows its dated records in the nexxus calendar, next to holidays, vacation and the person's own appointments. How the calendar section of the manifest works, who sees which entries, and how moving an entry writes back to the record.

For
Developers
Time
15 min

Many records have a date that people plan around: a deadline, an appointment with a patient, a delivery. nexxus has one calendar for that, under StartCalendar. A package does not build its own. It declares calendar sources in its manifest: which records of which entity, by which date fields, for whom, in which colour, and whether people may move them. nexxus reads the records through the regular record search and writes a move through the regular record update, so the package’s rights, validation and events apply as for any edit.

  1. Manifest

    The package names entity, date fields, title, people and colours in calendar.

  2. Record search

    nexxus asks for the records in the shown days, with the reader's rights.

  3. Calendar

    Shows them beside holidays, vacation and appointments, by Mine or All.

  4. Record update

    A drag writes the new date back, with the record's validation and the updated event.

The package brings no calendar code. Everything runs through the record API it already has.

calendar is a top-level list. Each entry is one source with its own chip in the calendar.

"calendar": [
{
"key": "fristen",
"entity": "frist",
"label": { "de": "Fristen", "en": "Deadlines" },
"icon": "event_upcoming",
"start": "faellig_am",
"title": "titel",
"subtitle": ["akte", "typ"],
"people": ["zustaendig"],
"colorField": "typ",
"colors": { "notfrist": "red", "frist": "amber", "vorfrist": "teal" },
"filter": { "status": "offen" },
"move": false
}
]
FieldRequiredMeaning
keyyesName of the source, unique in the list. A lower-case letter, then lower-case letters, digits, - and _.
entityyesAn entity of the package.
labelyesThe chip’s text, German and English, optionally Polish.
iconnoA Material Symbols name. Default: the entity’s icon.
startyesA field of type date (an all-day entry) or datetime (an entry with times), indexed: true.
endnoA field of the same type as start, indexed. Without it a date entry lasts one day and a datetime entry 30 minutes. An end before the start counts as missing.
titleyesA field of type string, text, enum or ref. An enum shows its label, a ref the display value of its target. Empty: the record’s title.
subtitlenoFields shown smaller under the title, joined with ” · ”. Any type but json and geo.
peoplenoref fields with target: "core:user". They decide when a record counts as the reader’s own, see below.
colorFieldnoAn enum field that colours each entry.
colorsnoWith colorField: value key to a colour. The first one is also the colour of the source’s chip.
colornoWithout colorField: the colour of the whole source.
filternoA fixed filter on indexed fields: a value, or a list meaning “one of”. The calendar never shows records outside it.
movenotrue lets people with write access move the entries. Default false.

The colours are tokens, not hex values, so they work in the light and the dark theme: blue, teal, green, lime, amber, orange, red, pink, purple, slate. A value without an entry in colors, and a source with neither color nor colors, get a token derived from the value or the source key. The derivation is stable: the same key gives the same colour on every server.

Keep legal deadlines and other dates that must not shift by accident at move: false. People still change them on the record page, where the package’s own rules apply.

A source appears for a person when the package’s installation is active and the person may read the entity (the permission <module>.<entity>.read). Records carry no rights of their own, so whoever may read the entity sees all of its records that pass the source’s filter. The calendar itself needs the permission calendar.use, which every role with employee rights has.

The reader chooses between Mine and All:

  • Mine keeps the records where any of the people fields holds the reader. A record with two people fields (responsible and deputy, say) shows for both.
  • All shows every record the reader may read. With people chosen in the people filter, the calendar keeps the records where a people field holds one of them.
  • A source without people shows only under All without a people filter, because none of its records belongs to anyone.

Dates: a date is a day without a time zone; an all-day entry’s end is included. A datetime is a point in time and shows in the tenant’s time zone (the system setting), so an appointment at 09:30 in Berlin shows at 09:30 for everyone in the tenant. A move writes it back with that zone’s offset.

nexxus asks for the records that start in the days on screen and, with an end, those that began earlier and still run. It reads at most 2000 records per source and request, 200 at a time. A source cut off there is named on the page: there are more entries than the calendar shows at once. A shorter period or a people filter shows them all. If your source can have that many records in a month, add a filter or people.

A click on an entry opens the record page, /x/<module>/<entity>/<id>.

With move: true, people who may write the entity (<module>.<entity>.write) drag entries to another day or time, and drag the lower edge of an entry with times to change its end. The AI Assistant moves entries the same way, after the person confirms its card.

nexxus then writes the record through the regular update:

  • A move writes start and, when the record has an end, end with the same length. A resize writes only end and needs an end field.
  • Validation runs as on the record page. A readonly field cannot be a moved field; the package check refuses that combination.
  • The record keeps its history, and the package’s service receives <entity>.updated like for any edit.
  • The request carries the start the person saw. If the record starts elsewhere by now, nexxus answers 409 with the entry as it is now, and the calendar shows that state with the message Der Eintrag wurde inzwischen geändert, er beginnt jetzt …

Without move: true, or without the write permission, the entry cannot be dragged, and the server refuses a move with 403.

validatePackage and the upload refuse a package with one message per finding when:

  • entity does not exist, or a named field is not a field of it, or a key appears twice.
  • start is not date or datetime, or is not indexed. end has another type than start, is not indexed, or is the start field.
  • move is true and start or end is readonly.
  • title is not string, text, enum or ref; a subtitle field is json or geo, or appears twice.
  • A people field is not a ref to core:user, or appears twice.
  • colorField is not an enum, a key in colors is not one of its values, colors comes without colorField, or color with it.
  • A filter field is not indexed, has no value, or a value does not fit its type.

The section is new in the platform. A nexxus version without it refuses the manifest because of the unknown field, so set platform to a range that starts at the version that brings the calendar.