+21
-2
appview/pages/templates/labels/fragments/label.html
+21
-2
appview/pages/templates/labels/fragments/label.html
···
1
1
{{ define "labels/fragments/label" }}
2
2
{{ $d := .def }}
3
3
{{ $v := .val }}
4
+
{{ $withPrefix := .withPrefix }}
4
5
<span class="flex items-center gap-2 font-normal normal-case rounded py-1 px-2 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm">
5
6
{{ template "repo/fragments/colorBall" (dict "color" $d.GetColor) }}
6
-
{{ $d.Name }}{{ if not $d.ValueType.IsNull }}/{{ template "labelVal" (dict "def" $d "val" $v) }}{{ end }}
7
+
8
+
{{ $lhs := printf "%s" $d.Name }}
9
+
{{ $rhs := "" }}
10
+
11
+
{{ if not $d.ValueType.IsNull }}
12
+
{{ if $d.ValueType.IsDidFormat }}
13
+
{{ $v = resolve $v }}
14
+
{{ end }}
15
+
16
+
{{ if not $withPrefix }}
17
+
{{ $lhs = "" }}
18
+
{{ else }}
19
+
{{ $lhs = printf "%s/" $d.Name }}
20
+
{{ end }}
21
+
22
+
{{ $rhs = printf "%s" $v }}
23
+
{{ end }}
24
+
25
+
{{ printf "%s%s" $lhs $rhs }}
7
26
</span>
8
27
{{ end }}
9
28
···
13
32
{{ $v := .val }}
14
33
15
34
{{ if $d.ValueType.IsDidFormat }}
16
-
{{ resolve $v }}
35
+
{{ resolve $v }}
17
36
{{ else }}
18
37
{{ $v }}
19
38
{{ end }}
-127
appview/pages/templates/repo/fragments/addLabelModal.html
-127
appview/pages/templates/repo/fragments/addLabelModal.html
···
1
-
{{ define "repo/fragments/addLabelModal" }}
2
-
{{ $root := .root }}
3
-
{{ $subject := .subject }}
4
-
{{ $state := .state }}
5
-
{{ with $root }}
6
-
<form
7
-
hx-put="/{{ .RepoInfo.FullName }}/labels/perform"
8
-
hx-on::after-request="this.reset()"
9
-
hx-indicator="#spinner"
10
-
hx-swap="none"
11
-
class="flex flex-col gap-4"
12
-
>
13
-
<p class="text-gray-500 dark:text-gray-400">Add, remove or update labels.</p>
14
-
15
-
<input class="hidden" name="repo" value="{{ .RepoInfo.RepoAt.String }}">
16
-
<input class="hidden" name="subject" value="{{ $subject }}">
17
-
18
-
<div class="flex flex-col gap-2">
19
-
{{ $id := 0 }}
20
-
{{ range $k, $valset := $state.Inner }}
21
-
{{ $d := index $root.LabelDefs $k }}
22
-
{{ range $v, $s := $valset }}
23
-
{{ template "labelCheckbox" (dict "def" $d "key" $k "val" $v "id" $id "isChecked" true) }}
24
-
{{ $id = add $id 1 }}
25
-
{{ end }}
26
-
{{ end }}
27
-
28
-
{{ range $k, $d := $root.LabelDefs }}
29
-
{{ if not ($state.ContainsLabel $k) }}
30
-
{{ template "labelCheckbox" (dict "def" $d "key" $k "val" "" "id" $id "isChecked" false) }}
31
-
{{ $id = add $id 1 }}
32
-
{{ end }}
33
-
{{ else }}
34
-
<span>
35
-
No labels defined yet. You can define custom labels in <a class="underline" href="/{{ .RepoInfo.FullName }}/settings">settings</a>.
36
-
</span>
37
-
{{ end }}
38
-
</div>
39
-
40
-
<div class="flex gap-2 pt-2">
41
-
<button
42
-
type="button"
43
-
popovertarget="add-label-modal"
44
-
popovertargetaction="hide"
45
-
class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
46
-
>
47
-
{{ i "x" "size-4" }} cancel
48
-
</button>
49
-
<button type="submit" class="btn w-1/2 flex items-center">
50
-
<span class="inline-flex gap-2 items-center">{{ i "check" "size-4" }} save</span>
51
-
<span id="spinner" class="group">
52
-
{{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
53
-
</span>
54
-
</button>
55
-
</div>
56
-
<div id="add-label-error" class="text-red-500 dark:text-red-400"></div>
57
-
</form>
58
-
{{ end }}
59
-
{{ end }}
60
-
61
-
{{ define "labelCheckbox" }}
62
-
{{ $key := .key }}
63
-
{{ $val := .val }}
64
-
{{ $def := .def }}
65
-
{{ $id := .id }}
66
-
{{ $isChecked := .isChecked }}
67
-
<div class="grid grid-cols-[auto_1fr_50%] gap-2 items-center cursor-pointer">
68
-
<input type="checkbox" id="op-{{$id}}" name="op-{{$id}}" value="add" {{if $isChecked}}checked{{end}} class="peer">
69
-
<label for="op-{{$id}}" class="flex items-center gap-2 text-base">{{ template "labels/fragments/labelDef" $def }}</label>
70
-
<div class="w-full hidden peer-checked:block">{{ template "valueTypeInput" (dict "valueType" $def.ValueType "value" $val "key" $key) }}</div>
71
-
<input type="hidden" name="operand-key" value="{{ $key }}">
72
-
</div>
73
-
{{ end }}
74
-
75
-
{{ define "valueTypeInput" }}
76
-
{{ $valueType := .valueType }}
77
-
{{ $value := .value }}
78
-
{{ $key := .key }}
79
-
80
-
{{ if $valueType.IsEnumType }}
81
-
{{ template "enumTypeInput" $ }}
82
-
{{ else if $valueType.IsBool }}
83
-
{{ template "boolTypeInput" $ }}
84
-
{{ else if $valueType.IsInt }}
85
-
{{ template "intTypeInput" $ }}
86
-
{{ else if $valueType.IsString }}
87
-
{{ template "stringTypeInput" $ }}
88
-
{{ else if $valueType.IsNull }}
89
-
{{ template "nullTypeInput" $ }}
90
-
{{ end }}
91
-
{{ end }}
92
-
93
-
{{ define "enumTypeInput" }}
94
-
{{ $valueType := .valueType }}
95
-
{{ $value := .value }}
96
-
<select name="operand-val" class="w-full p-1 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
97
-
{{ range $valueType.Enum }}
98
-
<option value="{{.}}" {{ if eq $value . }} selected {{ end }}>{{.}}</option>
99
-
{{ end }}
100
-
</select>
101
-
{{ end }}
102
-
103
-
{{ define "boolTypeInput" }}
104
-
{{ $value := .value }}
105
-
<select name="operand-val" class="w-full p-1 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
106
-
<option value="true" {{ if $value }} selected {{ end }}>true</option>
107
-
<option value="false" {{ if not $value }} selected {{ end }}>false</option>
108
-
</select>
109
-
{{ end }}
110
-
111
-
{{ define "intTypeInput" }}
112
-
{{ $value := .value }}
113
-
<input class="p-1 w-full" type="number" name="operand-val" value="{{$value}}" max="100">
114
-
{{ end }}
115
-
116
-
{{ define "stringTypeInput" }}
117
-
{{ $valueType := .valueType }}
118
-
{{ $value := .value }}
119
-
{{ if $valueType.IsDidFormat }}
120
-
{{ $value = resolve .value }}
121
-
{{ end }}
122
-
<input class="p-1 w-full" type="text" name="operand-val" value="{{$value}}">
123
-
{{ end }}
124
-
125
-
{{ define "nullTypeInput" }}
126
-
<input class="p-1" type="hidden" name="operand-val" value="null">
127
-
{{ end }}
+208
appview/pages/templates/repo/fragments/editLabelPanel.html
+208
appview/pages/templates/repo/fragments/editLabelPanel.html
···
1
+
{{ define "repo/fragments/editLabelPanel" }}
2
+
<form
3
+
id="edit-label-panel"
4
+
hx-put="/{{ .RepoInfo.FullName }}/labels/perform"
5
+
hx-indicator="#spinner"
6
+
hx-disabled-elt="#save-btn,#cancel-btn"
7
+
hx-swap="none"
8
+
class="flex flex-col gap-6"
9
+
>
10
+
<input type="hidden" name="repo" value="{{ .RepoInfo.RepoAt }}">
11
+
<input type="hidden" name="subject" value="{{ .Subject }}">
12
+
{{ template "editBasicLabels" . }}
13
+
{{ template "editKvLabels" . }}
14
+
{{ template "editLabelPanelActions" . }}
15
+
<div id="add-label-error" class="text-red-500 dark:text-red-400"></div>
16
+
</form>
17
+
{{ end }}
18
+
19
+
{{ define "editBasicLabels" }}
20
+
{{ $defs := .Defs }}
21
+
{{ $subject := .Subject }}
22
+
{{ $state := .State }}
23
+
{{ $labelStyle := "flex items-center gap-2 rounded py-1 px-2 border border-gray-200 dark:border-gray-700 text-sm bg-white dark:bg-gray-800 text-black dark:text-white" }}
24
+
<div>
25
+
{{ template "repo/fragments/labelSectionHeaderText" "Labels" }}
26
+
27
+
<div class="flex gap-1 items-center flex-wrap">
28
+
{{ range $k, $d := $defs }}
29
+
{{ $isChecked := $state.ContainsLabel $k }}
30
+
{{ if $d.ValueType.IsNull }}
31
+
{{ $fieldName := $d.AtUri }}
32
+
<label class="{{$labelStyle}}">
33
+
<input type="checkbox" id="{{ $fieldName }}" name="{{ $fieldName }}" value="null" {{if $isChecked}}checked{{end}}>
34
+
{{ template "labels/fragments/labelDef" $d }}
35
+
</label>
36
+
{{ end }}
37
+
{{ else }}
38
+
<p class="text-gray-500 dark:text-gray-400 text-sm py-1">
39
+
No labels defined yet. You can choose default labels or define custom
40
+
labels in <a class="underline" href="/{{ $.RepoInfo.FullName }}/settings">settings</a>.
41
+
</p>
42
+
{{ end }}
43
+
</div>
44
+
</div>
45
+
{{ end }}
46
+
47
+
{{ define "editKvLabels" }}
48
+
{{ $defs := .Defs }}
49
+
{{ $subject := .Subject }}
50
+
{{ $state := .State }}
51
+
{{ $labelStyle := "font-normal normal-case flex items-center gap-2 p-0" }}
52
+
53
+
{{ range $k, $d := $defs }}
54
+
{{ if (not $d.ValueType.IsNull) }}
55
+
{{ $fieldName := $d.AtUri }}
56
+
{{ $valset := $state.GetValSet $k }}
57
+
<div id="label-{{$d.Id}}" class="flex flex-col gap-1">
58
+
{{ template "repo/fragments/labelSectionHeaderText" $d.Name }}
59
+
{{ if (and $d.Multiple $d.ValueType.IsEnum) }}
60
+
<!-- checkbox -->
61
+
{{ range $variant := $d.ValueType.Enum }}
62
+
<label class="{{$labelStyle}}">
63
+
<input type="checkbox" name="{{ $fieldName }}" value="{{$variant}}" {{if $state.ContainsLabelAndVal $k $variant}}checked{{end}}>
64
+
{{ $variant }}
65
+
</label>
66
+
{{ end }}
67
+
{{ else if $d.Multiple }}
68
+
<!-- dynamically growing input fields -->
69
+
{{ range $v, $s := $valset }}
70
+
{{ template "multipleInputField" (dict "def" $d "value" $v "key" $k) }}
71
+
{{ else }}
72
+
{{ template "multipleInputField" (dict "def" $d "value" "" "key" $k) }}
73
+
{{ end }}
74
+
{{ template "addFieldButton" $d }}
75
+
{{ else if $d.ValueType.IsEnum }}
76
+
<!-- radio buttons -->
77
+
{{ $isUsed := $state.ContainsLabel $k }}
78
+
{{ range $variant := $d.ValueType.Enum }}
79
+
<label class="{{$labelStyle}}">
80
+
<input type="radio" name="{{ $fieldName }}" value="{{$variant}}" {{if $state.ContainsLabelAndVal $k $variant}}checked{{end}}>
81
+
{{ $variant }}
82
+
</label>
83
+
{{ end }}
84
+
<label class="{{$labelStyle}}">
85
+
<input type="radio" name="{{ $fieldName }}" value="" {{ if not $isUsed }}checked{{ end }}>
86
+
None
87
+
</label>
88
+
{{ else }}
89
+
<!-- single input field based on value type -->
90
+
{{ range $v, $s := $valset }}
91
+
{{ template "valueTypeInput" (dict "def" $d "value" $v "key" $k) }}
92
+
{{ else }}
93
+
{{ template "valueTypeInput" (dict "def" $d "value" "" "key" $k) }}
94
+
{{ end }}
95
+
{{ end }}
96
+
</div>
97
+
{{ end }}
98
+
{{ end }}
99
+
{{ end }}
100
+
101
+
{{ define "multipleInputField" }}
102
+
<div class="flex gap-1 items-stretch">
103
+
{{ template "valueTypeInput" . }}
104
+
{{ template "removeFieldButton" }}
105
+
</div>
106
+
{{ end }}
107
+
108
+
{{ define "addFieldButton" }}
109
+
<div style="display:none" id="tpl-{{ .Id }}">
110
+
{{ template "multipleInputField" (dict "def" . "value" "" "key" .AtUri.String) }}
111
+
</div>
112
+
<button type="button" onClick="this.insertAdjacentHTML('beforebegin', document.getElementById('tpl-{{ .Id }}').innerHTML)" class="w-full btn flex items-center gap-2">
113
+
{{ i "plus" "size-4" }} add
114
+
</button>
115
+
{{ end }}
116
+
117
+
{{ define "removeFieldButton" }}
118
+
<button type="button" onClick="this.parentElement.remove()" class="btn flex items-center gap-2 text-red-400 dark:text-red-500">
119
+
{{ i "trash-2" "size-4" }}
120
+
</button>
121
+
{{ end }}
122
+
123
+
{{ define "valueTypeInput" }}
124
+
{{ $def := .def }}
125
+
{{ $valueType := $def.ValueType }}
126
+
{{ $value := .value }}
127
+
{{ $key := .key }}
128
+
129
+
{{ if $valueType.IsBool }}
130
+
{{ template "boolTypeInput" $ }}
131
+
{{ else if $valueType.IsInt }}
132
+
{{ template "intTypeInput" $ }}
133
+
{{ else if $valueType.IsString }}
134
+
{{ template "stringTypeInput" $ }}
135
+
{{ else if $valueType.IsNull }}
136
+
{{ template "nullTypeInput" $ }}
137
+
{{ end }}
138
+
{{ end }}
139
+
140
+
{{ define "boolTypeInput" }}
141
+
{{ $def := .def }}
142
+
{{ $fieldName := $def.AtUri }}
143
+
{{ $value := .value }}
144
+
{{ $labelStyle = "font-normal normal-case flex items-center gap-2" }}
145
+
<div class="flex flex-col gap-1">
146
+
<label class="{{$labelStyle}}">
147
+
<input type="radio" name="{{ $fieldName }}" value="true" {{ if not $value }}checked{{ end }}>
148
+
None
149
+
</label>
150
+
<label class="{{$labelStyle}}">
151
+
<input type="radio" name="{{ $fieldName }}" value="true" {{ if not $value }}checked{{ end }}>
152
+
None
153
+
</label>
154
+
<label class="{{$labelStyle}}">
155
+
<input type="radio" name="{{ $fieldName }}" value="true" {{ if not $value }}checked{{ end }}>
156
+
None
157
+
</label>
158
+
</div>
159
+
{{ end }}
160
+
161
+
{{ define "intTypeInput" }}
162
+
{{ $def := .def }}
163
+
{{ $fieldName := $def.AtUri }}
164
+
{{ $value := .value }}
165
+
<input class="p-1 w-full" type="number" name="{{$fieldName}}" value="{{$value}}">
166
+
{{ end }}
167
+
168
+
{{ define "stringTypeInput" }}
169
+
{{ $def := .def }}
170
+
{{ $fieldName := $def.AtUri }}
171
+
{{ $valueType := $def.ValueType }}
172
+
{{ $value := .value }}
173
+
{{ if $valueType.IsDidFormat }}
174
+
{{ $value = trimPrefix (resolve .value) "@" }}
175
+
{{ end }}
176
+
<input class="p-1 w-full" type="text" name="{{$fieldName}}" value="{{$value}}">
177
+
{{ end }}
178
+
179
+
{{ define "nullTypeInput" }}
180
+
{{ $def := .def }}
181
+
{{ $fieldName := $def.AtUri }}
182
+
<input class="p-1" type="hidden" name="{{$fieldName}}" value="null">
183
+
{{ end }}
184
+
185
+
{{ define "editLabelPanelActions" }}
186
+
<div class="flex gap-2 pt-2">
187
+
<button
188
+
id="cancel-btn"
189
+
type="button"
190
+
hx-get="/{{ .RepoInfo.FullName }}/label"
191
+
hx-vals='{"subject": "{{.Subject}}"}'
192
+
hx-swap="outerHTML"
193
+
hx-target="#edit-label-panel"
194
+
class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 group">
195
+
{{ i "x" "size-4" }} cancel
196
+
</button>
197
+
198
+
<button
199
+
id="save-btn"
200
+
type="submit"
201
+
class="btn w-1/2 flex items-center">
202
+
<span class="inline-flex gap-2 items-center">{{ i "check" "size-4" }} save</span>
203
+
<span id="spinner" class="group">
204
+
{{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
205
+
</span>
206
+
</button>
207
+
</div>
208
+
{{ end }}
+43
appview/pages/templates/repo/fragments/labelPanel.html
+43
appview/pages/templates/repo/fragments/labelPanel.html
···
1
+
{{ define "repo/fragments/labelPanel" }}
2
+
<div id="label-panel" class="flex flex-col gap-6">
3
+
{{ template "basicLabels" . }}
4
+
{{ template "kvLabels" . }}
5
+
</div>
6
+
{{ end }}
7
+
8
+
{{ define "basicLabels" }}
9
+
<div>
10
+
{{ template "repo/fragments/labelSectionHeader" (dict "Name" "Labels" "RepoInfo" .RepoInfo "Subject" .Subject) }}
11
+
12
+
{{ $hasLabel := false }}
13
+
<div class="flex gap-1 items-center flex-wrap">
14
+
{{ range $k, $d := .Defs }}
15
+
{{ if (and $d.ValueType.IsNull ($.State.ContainsLabel $k)) }}
16
+
{{ $hasLabel = true }}
17
+
{{ template "labels/fragments/label" (dict "def" $d "val" "") }}
18
+
{{ end }}
19
+
{{ end }}
20
+
21
+
{{ if not $hasLabel }}
22
+
<p class="text-gray-500 dark:text-gray-400 text-sm py-1">None yet.</p>
23
+
{{ end }}
24
+
</div>
25
+
</div>
26
+
{{ end }}
27
+
28
+
{{ define "kvLabels" }}
29
+
{{ range $k, $d := .Defs }}
30
+
{{ if (not $d.ValueType.IsNull) }}
31
+
<div id="label-{{$d.Id}}">
32
+
{{ template "repo/fragments/labelSectionHeader" (dict "Name" $d.Name "RepoInfo" $.RepoInfo "Subject" $.Subject) }}
33
+
<div class="flex gap-1 items-center flex-wrap">
34
+
{{ range $v, $s := $.State.GetValSet $d.AtUri.String }}
35
+
{{ template "labels/fragments/label" (dict "def" $d "val" $v "withPrefix" false) }}
36
+
{{ else }}
37
+
<p class="text-gray-500 dark:text-gray-400 text-sm py-1">None yet.</p>
38
+
{{ end }}
39
+
</div>
40
+
</div>
41
+
{{ end }}
42
+
{{ end }}
43
+
{{ end }}
+16
appview/pages/templates/repo/fragments/labelSectionHeader.html
+16
appview/pages/templates/repo/fragments/labelSectionHeader.html
···
1
+
{{ define "repo/fragments/labelSectionHeader" }}
2
+
3
+
<div class="flex justify-between items-center gap-2">
4
+
{{ template "repo/fragments/labelSectionHeaderText" .Name }}
5
+
{{ if (or .RepoInfo.Roles.IsOwner .RepoInfo.Roles.IsCollaborator) }}
6
+
<a
7
+
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group"
8
+
hx-get="/{{ .RepoInfo.FullName }}/label/edit"
9
+
hx-vals='{"subject": "{{.Subject}}"}'
10
+
hx-swap="outerHTML"
11
+
hx-target="#label-panel">
12
+
{{ i "pencil" "size-3" }}
13
+
</a>
14
+
{{ end }}
15
+
</div>
16
+
{{ end }}
+3
appview/pages/templates/repo/fragments/labelSectionHeaderText.html
+3
appview/pages/templates/repo/fragments/labelSectionHeaderText.html
+135
-83
appview/pages/templates/repo/settings/fragments/addLabelDefModal.html
+135
-83
appview/pages/templates/repo/settings/fragments/addLabelDefModal.html
···
1
1
{{ define "repo/settings/fragments/addLabelDefModal" }}
2
-
<form
3
-
hx-put="/{{ $.RepoInfo.FullName }}/settings/label"
4
-
hx-indicator="#spinner"
5
-
hx-swap="none"
6
-
hx-on::after-request="if(event.detail.successful) this.reset()"
7
-
class="flex flex-col gap-4"
8
-
>
9
-
<p class="text-gray-500 dark:text-gray-400">Labels can have a name and a value. Set the value type to "none" to create a simple label.</p>
2
+
<div class="grid grid-cols-2">
3
+
<input type="radio" name="tab" id="basic-tab" value="basic" class="hidden peer/basic" checked>
4
+
<input type="radio" name="tab" id="kv-tab" value="kv" class="hidden peer/kv">
10
5
11
-
<div class="w-full">
12
-
<label for="name">Name</label>
13
-
<input class="w-full" type="text" id="label-name" name="name" required placeholder="improvement"/>
6
+
<!-- Labels as direct siblings -->
7
+
{{ $base := "py-2 text-sm font-normal normal-case block hover:no-underline text-center cursor-pointer bg-gray-100 dark:bg-gray-800 shadow-inner border border-gray-200 dark:border-gray-700" }}
8
+
<label for="basic-tab" class="{{$base}} peer-checked/basic:bg-white peer-checked/basic:dark:bg-gray-700 peer-checked/basic:shadow-sm rounded-l">
9
+
Basic Labels
10
+
</label>
11
+
<label for="kv-tab" class="{{$base}} peer-checked/kv:bg-white peer-checked/kv:dark:bg-gray-700 peer-checked/kv:shadow-sm rounded-r">
12
+
Key-value Labels
13
+
</label>
14
+
15
+
<!-- Basic Labels Content - direct sibling -->
16
+
<div class="mt-4 hidden peer-checked/basic:block col-span-full">
17
+
{{ template "basicLabelDef" . }}
18
+
</div>
19
+
20
+
<!-- Key-value Labels Content - direct sibling -->
21
+
<div class="mt-4 hidden peer-checked/kv:block col-span-full">
22
+
{{ template "kvLabelDef" . }}
14
23
</div>
15
24
16
-
<!-- Value Type -->
17
-
<div class="w-full">
18
-
<label for="valueType">Value Type</label>
19
-
<select id="value-type" name="valueType" class="w-full p-3 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
20
-
<option value="null" selected>None</option>
21
-
<option value="string">String</option>
22
-
<option value="integer">Integer</option>
23
-
<option value="boolean">Boolean</option>
24
-
</select>
25
-
<details id="constrain-values" class="group hidden">
26
-
<summary class="list-none cursor-pointer flex items-center gap-2 py-2">
27
-
<span class="group-open:hidden inline text-gray-500 dark:text-gray-400">{{ i "square-plus" "w-4 h-4" }}</span>
28
-
<span class="hidden group-open:inline text-gray-500 dark:text-gray-400">{{ i "square-minus" "w-4 h-4" }}</span>
29
-
<span>Constrain values</span>
30
-
</summary>
31
-
<label for="enumValues">Permitted values</label>
32
-
<input type="text" id="enumValues" name="enumValues" placeholder="value1, value2, value3" class="w-full"/>
33
-
<p class="text-sm text-gray-400 dark:text-gray-500 mt-1">Enter comma-separated list of permitted values.</p>
25
+
<div id="add-label-error" class="text-red-500 dark:text-red-400 col-span-full"></div>
26
+
</div>
27
+
{{ end }}
28
+
29
+
{{ define "basicLabelDef" }}
30
+
<form
31
+
hx-put="/{{ $.RepoInfo.FullName }}/settings/label"
32
+
hx-indicator="#spinner"
33
+
hx-swap="none"
34
+
hx-on::after-request="if(event.detail.successful) this.reset()"
35
+
class="flex flex-col space-y-4">
36
+
37
+
<p class="text-gray-500 dark:text-gray-400">These labels can have a name and a color.</p>
38
+
39
+
{{ template "nameInput" . }}
40
+
{{ template "scopeInput" . }}
41
+
{{ template "colorInput" . }}
42
+
43
+
<div class="flex gap-2 pt-2">
44
+
{{ template "cancelButton" . }}
45
+
{{ template "submitButton" . }}
46
+
</div>
47
+
</form>
48
+
{{ end }}
34
49
35
-
<label for="valueFormat">String format</label>
36
-
<select id="valueFormat" name="valueFormat" class="w-full p-3 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
37
-
<option value="any" selected>Any</option>
38
-
<option value="did">DID</option>
39
-
</select>
40
-
<p class="text-sm text-gray-400 dark:text-gray-500 mt-1">Choose a string format.</p>
41
-
</details>
42
-
</div>
50
+
{{ define "kvLabelDef" }}
51
+
<form
52
+
hx-put="/{{ $.RepoInfo.FullName }}/settings/label"
53
+
hx-indicator="#spinner"
54
+
hx-swap="none"
55
+
hx-on::after-request="if(event.detail.successful) this.reset()"
56
+
class="flex flex-col space-y-4">
43
57
44
-
<!-- Scope -->
58
+
<p class="text-gray-500 dark:text-gray-400">
59
+
These labels are more detailed, they can have a key and an associated
60
+
value. You may define additional constraints on label values.
61
+
</p>
62
+
63
+
{{ template "nameInput" . }}
64
+
{{ template "valueInput" . }}
65
+
{{ template "multipleInput" . }}
66
+
{{ template "scopeInput" . }}
67
+
{{ template "colorInput" . }}
68
+
69
+
<div class="flex gap-2 pt-2">
70
+
{{ template "cancelButton" . }}
71
+
{{ template "submitButton" . }}
72
+
</div>
73
+
</form>
74
+
{{ end }}
75
+
76
+
{{ define "nameInput" }}
45
77
<div class="w-full">
46
-
<label for="scope">Scope</label>
47
-
<select id="scope" name="scope" class="w-full p-3 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
48
-
<option value="sh.tangled.repo.issue">Issues</option>
49
-
<option value="sh.tangled.repo.pull">Pull Requests</option>
50
-
</select>
78
+
<label for="name">Name</label>
79
+
<input class="w-full" type="text" id="label-name" name="name" required placeholder="improvement"/>
51
80
</div>
81
+
{{ end }}
52
82
53
-
<!-- Color -->
83
+
{{ define "colorInput" }}
54
84
<div class="w-full">
55
85
<label for="color">Color</label>
56
86
<div class="grid grid-cols-4 grid-rows-2 place-items-center">
···
63
93
{{ end }}
64
94
</div>
65
95
</div>
96
+
{{ end }}
66
97
67
-
<!-- Multiple -->
68
-
<div class="w-full flex flex-wrap gap-2">
69
-
<input type="checkbox" id="multiple" name="multiple" value="true" />
70
-
<span>
71
-
Allow multiple values
72
-
</span>
98
+
{{ define "scopeInput" }}
99
+
<div class="w-full">
100
+
<label>Scope</label>
101
+
<label class="font-normal normal-case flex items-center gap-2 p-0">
102
+
<input type="checkbox" id="issue-scope" name="scope" value="sh.tangled.repo.issue" checked />
103
+
Issues
104
+
</label>
105
+
<label class="font-normal normal-case flex items-center gap-2 p-0">
106
+
<input type="checkbox" id="pulls-scope" name="scope" value="sh.tangled.repo.pull" checked />
107
+
Pull Requests
108
+
</label>
109
+
</div>
110
+
{{ end }}
111
+
112
+
{{ define "valueInput" }}
113
+
<div class="w-full">
114
+
<label for="valueType">Value Type</label>
115
+
<select id="value-type" name="valueType" class="w-full p-3 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
116
+
<option value="string">String</option>
117
+
<option value="integer">Integer</option>
118
+
</select>
119
+
</div>
120
+
121
+
<div class="w-full">
122
+
<label for="enumValues">Permitted values</label>
123
+
<input type="text" id="enumValues" name="enumValues" placeholder="value1, value2, value3" class="w-full"/>
124
+
<p class="text-sm text-gray-400 dark:text-gray-500 mt-1">
125
+
Enter comma-separated list of permitted values, or leave empty to allow any value.
126
+
</p>
73
127
</div>
74
128
75
-
<div class="flex gap-2 pt-2">
76
-
<button
77
-
type="button"
78
-
popovertarget="add-labeldef-modal"
79
-
popovertargetaction="hide"
80
-
class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
81
-
>
82
-
{{ i "x" "size-4" }} cancel
83
-
</button>
84
-
<button type="submit" class="btn w-1/2 flex items-center">
85
-
<span class="inline-flex gap-2 items-center">{{ i "plus" "size-4" }} add</span>
86
-
<span id="spinner" class="group">
87
-
{{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
88
-
</span>
89
-
</button>
129
+
<div class="w-full">
130
+
<label for="valueFormat">String format</label>
131
+
<select id="valueFormat" name="valueFormat" class="w-full p-3 rounded border border-gray-300 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-600">
132
+
<option value="any" selected>Any</option>
133
+
<option value="did">DID</option>
134
+
</select>
90
135
</div>
91
-
<div id="add-label-error" class="text-red-500 dark:text-red-400"></div>
92
-
</form>
136
+
{{ end }}
93
137
94
-
<script>
95
-
document.getElementById('value-type').addEventListener('change', function() {
96
-
const constrainValues = document.getElementById('constrain-values');
97
-
const selectedValue = this.value;
138
+
{{ define "multipleInput" }}
139
+
<div class="w-full flex flex-wrap gap-2">
140
+
<input type="checkbox" id="multiple" name="multiple" value="true" />
141
+
<span>Allow multiple values</span>
142
+
</div>
143
+
{{ end }}
98
144
99
-
if (selectedValue === 'string') {
100
-
constrainValues.classList.remove('hidden');
101
-
} else {
102
-
constrainValues.classList.add('hidden');
103
-
constrainValues.removeAttribute('open');
104
-
document.getElementById('enumValues').value = '';
105
-
}
106
-
});
145
+
{{ define "cancelButton" }}
146
+
<button
147
+
type="button"
148
+
popovertarget="add-labeldef-modal"
149
+
popovertargetaction="hide"
150
+
class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
151
+
>
152
+
{{ i "x" "size-4" }} cancel
153
+
</button>
154
+
{{ end }}
107
155
108
-
function toggleDarkMode() {
109
-
document.documentElement.classList.toggle('dark');
110
-
}
111
-
</script>
156
+
{{ define "submitButton" }}
157
+
<button type="submit" class="btn-create w-1/2 flex items-center">
158
+
<span class="inline-flex gap-2 items-center">{{ i "plus" "size-4" }} add</span>
159
+
<span id="spinner" class="group">
160
+
{{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
161
+
</span>
162
+
</button>
112
163
{{ end }}
113
164
165
+
+6
appview/repo/router.go
+6
appview/repo/router.go
···
64
64
r.Get("/*", rp.RepoCompare)
65
65
})
66
66
67
+
// label panel in issues/pulls/discussions/tasks
68
+
r.Route("/label", func(r chi.Router) {
69
+
r.Get("/", rp.LabelPanel)
70
+
r.Get("/edit", rp.EditLabelPanel)
71
+
})
72
+
67
73
// settings routes, needs auth
68
74
r.Group(func(r chi.Router) {
69
75
r.Use(middleware.AuthMiddleware(rp.oauth))