using System.Text.Json.Serialization; namespace Elternbeirat.Contracts; /// /// A calendar entry of the Elternbeirat, as stored in PocketBase. /// /// /// Event lists are ordered by . Unset optional text fields are /// , never . /// public record Event { /// /// Gets the PocketBase record id. /// /// /// : every stored record has an id. Modeling it as /// required states which fields a record must carry, independent of the store, /// so a future data source has to supply them too. /// [JsonPropertyName("id")] public required string Id { get; init; } /// /// Gets the start of the event as Europe/Berlin wall-clock time. /// /// /// PocketBase stores the value in UTC; it is converted to Berlin local time on /// deserialization, with daylight saving applied. An all-day event uses 00:00 /// as the time. /// : an event without a start has no place on the /// calendar, and the field is required in PocketBase. /// [JsonPropertyName("start")] [JsonConverter(typeof(LocalDateTimeConverter))] public required DateTime Start { get; init; } /// /// Gets the optional end of the event as Europe/Berlin wall-clock time. /// /// /// The end time, or if the event has no end. /// [JsonPropertyName("end")] [JsonConverter(typeof(NullableLocalDateTimeConverter))] public DateTime? End { get; init; } /// /// Gets the event name, e.g. "Elternbeiratssitzung". /// /// /// : an event without a name is incomplete, and the /// field is required in PocketBase. /// [JsonPropertyName("title")] public required string Title { get; init; } /// /// Gets the optional location, e.g. "Aula". /// /// /// The location, or if none is set. /// [JsonPropertyName("location")] public string Location { get; init; } = string.Empty; /// /// Gets the optional note, e.g. "Anmeldung erforderlich". /// /// /// The note, or if none is set. /// [JsonPropertyName("note")] public string Note { get; init; } = string.Empty; /// /// Gets a value indicating whether the event has a wall-clock time, as opposed /// to being an all-day event. /// /// /// PocketBase has no all-day flag; the convention is that a start of 00:00 /// means all-day. This is the single definition of that rule -- formatting and /// the calendar feed both read it here instead of re-deriving it. /// [JsonIgnore] public bool HasTime => Start.TimeOfDay != TimeSpan.Zero; }