oficina / calendar Public

Clone
git clone https://amadan.net/oficina/calendar

Plain git — no account needed to clone.

Download

Download this file

1{{define "title"}}{{.Title}}{{end}}
2{{define "content"}}
3<header rst-page-header>
4 <div rst-page-header-titles>
5 <h1>{{.Data.Heading}}</h1>
6 </div>
7</header>
8
9{{with .Data.Error}}
10{{template "callout" dict "Tone" "negative" "Body" . "Alert" true}}
11{{end}}
12
13<form rst-form method="post" action="{{.Data.Action}}">
14 {{if and .Data.Series .Data.Occurrence}}
15 <input type="hidden" name="occurrence" value="{{.Data.Occurrence}}">
16 {{/* Asked BEFORE the fields, not after: which occurrences this
17 applies to changes what every field below means. "Just this one"
18 is first and checked, because an edit that silently changed a
19 year of standups is the most expensive default in the app. */}}
20 {{template "choice-field" dict
21 "Legend" "This repeats. What should this change?"
22 "Name" "scope"
23 "Type" "radio"
24 "Options" (list
25 (dict "Value" "this" "Title" "Just this one"
26 "Desc" "The rest of the series keeps its current time and details."
27 "Checked" true)
28 (dict "Value" "following" "Title" "This one and everything after it"
29 "Desc" "Earlier occurrences are left alone. The series is split in two from here.")
30 (dict "Value" "all" "Title" "Every occurrence"
31 "Desc" "Past ones included."))}}
32 {{end}}
33 {{template "field" dict
34 "ID" "title" "Name" "title" "Label" "What is it?"
35 "Value" .Data.EventTitle "Required" true "Autofocus" true
36 "Error" (if_eq .Data.ErrorField "title" .Data.Error)}}
37
38 {{if .Data.Booked}}
39 {{/* A BOOKED meeting's scheduling belongs to the person who booked
40 it (booking spec B18). The controls are shown disabled, and the
41 stored values ride along as hidden inputs — a disabled control
42 submits nothing, and a title-only save has to submit exactly
43 what is stored to pass the comparison (B25). tz is the EVENT's
44 own, not the host's profile zone. */}}
45 <input type="hidden" name="start" value="{{.Data.StartLocal}}">
46 <input type="hidden" name="end" value="{{.Data.EndLocal}}">
47 <input type="hidden" name="tz" value="{{.Data.TZ}}">
48 <input type="hidden" name="kind" value="{{.Data.Kind}}">
49 <input type="hidden" name="repeat" value="none">
50 {{if .Data.AllDay}}<input type="hidden" name="all_day" value="1">{{end}}
51 <div rst-field cal-booked-lock>
52 <label rst-field-label for="start-locked">Starts</label>
53 <input rst-input id="start-locked" type="datetime-local" value="{{.Data.StartLocal}}" disabled>
54 <label rst-field-label for="end-locked">Ends</label>
55 <input rst-input id="end-locked" type="datetime-local" value="{{.Data.EndLocal}}" disabled>
56 <label rst-field-label for="tz-locked">Zone</label>
57 <input rst-input="short" id="tz-locked" type="text" value="{{.Data.TZ}}" disabled>
58 <label rst-field-label for="kind-locked">Kind</label>
59 <input rst-input="short" id="kind-locked" type="text" value="{{.Data.Kind}}" disabled>
60 <label rst-switch><input type="checkbox" disabled{{if .Data.AllDay}} checked{{end}}><span rst-switch-track aria-hidden="true"></span> All day</label>
61 <label rst-field-label for="repeat-locked">Repeats</label>
62 <input rst-input="short" id="repeat-locked" type="text" value="Never" disabled>
63 <small rst-field-hint>This is a booked meeting{{with .Data.BookedBy}} with {{.}}{{end}}. Its time is changed by the person who booked it, or by cancelling and letting them rebook.</small>
64 {{with (if_eq .Data.ErrorField "start" .Data.Error)}}<small rst-field-error>{{.}}</small>{{end}}
65 </div>
66 {{else}}
67 {{/* Three kinds, one radio group, and the descriptions say what the
68 difference actually is — because "meeting / commitment / note"
69 without them is three words nobody can choose between. They
70 differ in reminder defaults, in whether they count toward the
71 day's booked hours, and in how they draw. */}}
72 {{template "choice-field" dict
73 "Legend" "What kind of thing?"
74 "Name" "kind"
75 "Type" "radio"
76 "Options" (list
77 (dict "Value" "meeting" "Title" "A meeting"
78 "Desc" "Other people are in it. Being late matters."
79 "Checked" (eq .Data.Kind "meeting"))
80 (dict "Value" "commitment" "Title" "A commitment"
81 "Desc" "Time you have promised yourself. It fills the day, but only you are inconvenienced when it moves."
82 "Checked" (eq .Data.Kind "commitment"))
83 (dict "Value" "note" "Title" "A note"
84 "Desc" "True of the day without occupying any of it — a birthday, a deadline, a bin collection."
85 "Checked" (eq .Data.Kind "note")))}}
86
87 {{template "field-check" dict
88 "Name" "all_day" "Value" "1" "Label" "All day" "Checked" .Data.AllDay}}
89
90 {{/* An all-day event's times are DATES, and a datetime-local input
91 refuses a bare date: it renders blank, and the form then cannot
92 be saved without retyping both. So an all-day event gets date
93 inputs. Ticking All day without script cannot swap the input,
94 which is why readEventForm takes either shape.
95 datetime.js enhances both into a picker with natural-language
96 parsing. Without it they are ordinary inputs, which is a
97 working floor. */}}
98 {{if .Data.AllDay}}
99 {{template "field-date" dict
100 "Name" "start" "Label" "Starts" "Value" .Data.StartLocal "Required" true
101 "Error" (if_eq .Data.ErrorField "start" .Data.Error)}}
102 {{template "field-date" dict
103 "Name" "end" "Label" "Ends" "Value" .Data.EndLocal "Required" true
104 "Hint" "The end is exclusive: an all-day event on the 2nd ends on the 3rd."
105 "Error" (if_eq .Data.ErrorField "end" .Data.Error)}}
106 {{else}}
107 {{template "field-datetime" dict
108 "Name" "start" "Label" "Starts" "Value" .Data.StartLocal "Required" true
109 "Error" (if_eq .Data.ErrorField "start" .Data.Error)}}
110 {{template "field-datetime" dict
111 "Name" "end" "Label" "Ends" "Value" .Data.EndLocal "Required" true
112 "Hint" "The end is exclusive: an all-day event on the 2nd ends on the 3rd."
113 "Error" (if_eq .Data.ErrorField "end" .Data.Error)}}
114 {{end}}
115
116 {{template "field-select" dict
117 "ID" "repeat" "Name" "repeat" "Label" "Repeats"
118 "Options" (repeatOptions .Data.StartLocal .Data.TZ .Data.AllDay .Data.Repeat)}}
119
120 {{template "field-date" dict
121 "Name" "repeat_end" "Label" "Repeat until" "Value" .Data.RepeatEnd
122 "Hint" "Leave empty for a series with no end."}}
123 {{end}}
124
125 {{template "field" dict
126 "ID" "location" "Name" "location" "Label" "Where" "Value" .Data.Location}}
127
128 {{template "field-textarea" dict
129 "Name" "notes" "Label" "Notes" "Value" .Data.Notes "Rows" 4}}
130
131 {{template "field-select" dict
132 "ID" "calendar_id" "Name" "calendar_id" "Label" "Calendar"
133 "Options" (calendarOptions .Data.Calendars .Data.CalendarID)}}
134
135 {{template "field-select" dict
136 "ID" "status" "Name" "status" "Label" "Status"
137 "Error" (if_eq .Data.ErrorField "status" .Data.Error)
138 "Options" (list
139 (dict "Value" "confirmed" "Label" "Confirmed" "Selected" (eq .Data.Status "confirmed"))
140 (dict "Value" "tentative" "Label" "Tentative" "Selected" (eq .Data.Status "tentative"))
141 (dict "Value" "cancelled" "Label" "Cancelled" "Selected" (eq .Data.Status "cancelled")))}}
142
143 {{template "form-foot" dict
144 "Submit" .Data.Submit
145 "CancelHref" (eventCancelHref .Data.EventID)
146 "CancelLabel" "Cancel"}}
147</form>
148{{end}}
149