@storyshelf/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +127 -0
  2. package/dist/adapter/auth.d.mts +50 -0
  3. package/dist/adapter/auth.mjs +10 -0
  4. package/dist/adapter/auth.mjs.map +1 -0
  5. package/dist/adapter/capture-runner.d.mts +70 -0
  6. package/dist/adapter/capture-runner.mjs +4 -0
  7. package/dist/adapter/database.d.mts +43 -0
  8. package/dist/adapter/database.mjs +4 -0
  9. package/dist/adapter/git-host/helpers.d.mts +12 -0
  10. package/dist/adapter/git-host/helpers.mjs +29 -0
  11. package/dist/adapter/git-host/helpers.mjs.map +1 -0
  12. package/dist/adapter/git-host.d.mts +46 -0
  13. package/dist/adapter/git-host.mjs +4 -0
  14. package/dist/adapter/storage.d.mts +22 -0
  15. package/dist/adapter/storage.mjs +4 -0
  16. package/dist/adapters/capture-queue.d.mts +49 -0
  17. package/dist/adapters/metadata.d.mts +24 -0
  18. package/dist/adapters/webhook-events.mjs +39 -0
  19. package/dist/adapters/webhook-events.mjs.map +1 -0
  20. package/dist/capture/adapter.d.mts +29 -0
  21. package/dist/capture/adapter.mjs +13 -0
  22. package/dist/capture/adapter.mjs.map +1 -0
  23. package/dist/capture/orchestrator.d.mts +23 -0
  24. package/dist/capture/orchestrator.mjs +103 -0
  25. package/dist/capture/orchestrator.mjs.map +1 -0
  26. package/dist/capture/pipeline.d.mts +41 -0
  27. package/dist/capture/pipeline.mjs +119 -0
  28. package/dist/capture/pipeline.mjs.map +1 -0
  29. package/dist/capture/queue.d.mts +35 -0
  30. package/dist/capture/queue.mjs +96 -0
  31. package/dist/capture/queue.mjs.map +1 -0
  32. package/dist/capture/status-fanout.mjs +66 -0
  33. package/dist/capture/status-fanout.mjs.map +1 -0
  34. package/dist/capture/storybook.d.mts +14 -0
  35. package/dist/capture/storybook.mjs +40 -0
  36. package/dist/capture/storybook.mjs.map +1 -0
  37. package/dist/config.d.mts +68 -0
  38. package/dist/config.mjs +69 -0
  39. package/dist/config.mjs.map +1 -0
  40. package/dist/ddl.d.mts +7 -0
  41. package/dist/ddl.mjs +155 -0
  42. package/dist/ddl.mjs.map +1 -0
  43. package/dist/diff/engine.d.mts +8 -0
  44. package/dist/diff/engine.mjs +79 -0
  45. package/dist/diff/engine.mjs.map +1 -0
  46. package/dist/diff/options.d.mts +40 -0
  47. package/dist/diff/options.mjs +15 -0
  48. package/dist/diff/options.mjs.map +1 -0
  49. package/dist/index.d.mts +48 -0
  50. package/dist/index.mjs +327 -0
  51. package/dist/index.mjs.map +1 -0
  52. package/dist/logger.d.mts +39 -0
  53. package/dist/logger.mjs +35 -0
  54. package/dist/logger.mjs.map +1 -0
  55. package/dist/middleware/csrf.mjs +39 -0
  56. package/dist/middleware/csrf.mjs.map +1 -0
  57. package/dist/middleware/rate-limit.mjs +38 -0
  58. package/dist/middleware/rate-limit.mjs.map +1 -0
  59. package/dist/models/baseline.d.mts +20 -0
  60. package/dist/models/baseline.mjs +89 -0
  61. package/dist/models/baseline.mjs.map +1 -0
  62. package/dist/models/build.d.mts +35 -0
  63. package/dist/models/build.mjs +77 -0
  64. package/dist/models/build.mjs.map +1 -0
  65. package/dist/models/comment.d.mts +20 -0
  66. package/dist/models/comment.mjs +42 -0
  67. package/dist/models/comment.mjs.map +1 -0
  68. package/dist/models/label.d.mts +43 -0
  69. package/dist/models/label.mjs +107 -0
  70. package/dist/models/label.mjs.map +1 -0
  71. package/dist/models/member.d.mts +27 -0
  72. package/dist/models/member.mjs +54 -0
  73. package/dist/models/member.mjs.map +1 -0
  74. package/dist/models/project.d.mts +42 -0
  75. package/dist/models/project.mjs +78 -0
  76. package/dist/models/project.mjs.map +1 -0
  77. package/dist/models/snapshot.d.mts +46 -0
  78. package/dist/models/snapshot.mjs +75 -0
  79. package/dist/models/snapshot.mjs.map +1 -0
  80. package/dist/models/status-config.mjs +61 -0
  81. package/dist/models/status-config.mjs.map +1 -0
  82. package/dist/models/token.d.mts +33 -0
  83. package/dist/models/token.mjs +60 -0
  84. package/dist/models/token.mjs.map +1 -0
  85. package/dist/models/webhook.d.mts +37 -0
  86. package/dist/models/webhook.mjs +67 -0
  87. package/dist/models/webhook.mjs.map +1 -0
  88. package/dist/pages/build-detail.mjs +333 -0
  89. package/dist/pages/build-detail.mjs.map +1 -0
  90. package/dist/pages/build-diff.mjs +437 -0
  91. package/dist/pages/build-diff.mjs.map +1 -0
  92. package/dist/pages/compute-jobs.mjs +196 -0
  93. package/dist/pages/compute-jobs.mjs.map +1 -0
  94. package/dist/pages/login.mjs +77 -0
  95. package/dist/pages/login.mjs.map +1 -0
  96. package/dist/pages/project-builds.mjs +228 -0
  97. package/dist/pages/project-builds.mjs.map +1 -0
  98. package/dist/pages/project-create.mjs +168 -0
  99. package/dist/pages/project-create.mjs.map +1 -0
  100. package/dist/pages/project-settings.mjs +111 -0
  101. package/dist/pages/project-settings.mjs.map +1 -0
  102. package/dist/pages/projects.mjs +178 -0
  103. package/dist/pages/projects.mjs.map +1 -0
  104. package/dist/pages/root.mjs +90 -0
  105. package/dist/pages/root.mjs.map +1 -0
  106. package/dist/pages/settings-general.mjs +191 -0
  107. package/dist/pages/settings-general.mjs.map +1 -0
  108. package/dist/pages/settings-labels.mjs +149 -0
  109. package/dist/pages/settings-labels.mjs.map +1 -0
  110. package/dist/pages/settings-members.mjs +124 -0
  111. package/dist/pages/settings-members.mjs.map +1 -0
  112. package/dist/pages/settings-status.mjs +150 -0
  113. package/dist/pages/settings-status.mjs.map +1 -0
  114. package/dist/pages/settings-tokens.mjs +95 -0
  115. package/dist/pages/settings-tokens.mjs.map +1 -0
  116. package/dist/pages/settings-webhooks.mjs +149 -0
  117. package/dist/pages/settings-webhooks.mjs.map +1 -0
  118. package/dist/retention/purge.d.mts +28 -0
  119. package/dist/retention/purge.mjs +66 -0
  120. package/dist/retention/purge.mjs.map +1 -0
  121. package/dist/routers/admin.mjs +41 -0
  122. package/dist/routers/admin.mjs.map +1 -0
  123. package/dist/routers/assets.mjs +24 -0
  124. package/dist/routers/assets.mjs.map +1 -0
  125. package/dist/routers/auth.mjs +77 -0
  126. package/dist/routers/auth.mjs.map +1 -0
  127. package/dist/routers/builds.handlers.mjs +87 -0
  128. package/dist/routers/builds.handlers.mjs.map +1 -0
  129. package/dist/routers/builds.mjs +162 -0
  130. package/dist/routers/builds.mjs.map +1 -0
  131. package/dist/routers/comments.mjs +89 -0
  132. package/dist/routers/comments.mjs.map +1 -0
  133. package/dist/routers/helpers.mjs +76 -0
  134. package/dist/routers/helpers.mjs.map +1 -0
  135. package/dist/routers/htmx.mjs +18 -0
  136. package/dist/routers/htmx.mjs.map +1 -0
  137. package/dist/routers/labels.mjs +77 -0
  138. package/dist/routers/labels.mjs.map +1 -0
  139. package/dist/routers/media.mjs +54 -0
  140. package/dist/routers/media.mjs.map +1 -0
  141. package/dist/routers/members.mjs +101 -0
  142. package/dist/routers/members.mjs.map +1 -0
  143. package/dist/routers/projects.mjs +121 -0
  144. package/dist/routers/projects.mjs.map +1 -0
  145. package/dist/routers/schemas.mjs +188 -0
  146. package/dist/routers/schemas.mjs.map +1 -0
  147. package/dist/routers/settings.mjs +235 -0
  148. package/dist/routers/settings.mjs.map +1 -0
  149. package/dist/routers/snapshots.mjs +143 -0
  150. package/dist/routers/snapshots.mjs.map +1 -0
  151. package/dist/routers/status-configs.mjs +100 -0
  152. package/dist/routers/status-configs.mjs.map +1 -0
  153. package/dist/routers/tokens.mjs +80 -0
  154. package/dist/routers/tokens.mjs.map +1 -0
  155. package/dist/routers/ui.mjs +142 -0
  156. package/dist/routers/ui.mjs.map +1 -0
  157. package/dist/routers/webhooks.mjs +89 -0
  158. package/dist/routers/webhooks.mjs.map +1 -0
  159. package/dist/schema.d.mts +4372 -0
  160. package/dist/schema.mjs +158 -0
  161. package/dist/schema.mjs.map +1 -0
  162. package/dist/store.mjs +18 -0
  163. package/dist/store.mjs.map +1 -0
  164. package/dist/types.d.mts +18 -0
  165. package/dist/types.mjs +49 -0
  166. package/dist/types.mjs.map +1 -0
  167. package/dist/ui/components.mjs +127 -0
  168. package/dist/ui/components.mjs.map +1 -0
  169. package/dist/ui/document.d.mts +8 -0
  170. package/dist/ui/document.mjs +338 -0
  171. package/dist/ui/document.mjs.map +1 -0
  172. package/dist/ui/styles.mjs +173 -0
  173. package/dist/ui/styles.mjs.map +1 -0
  174. package/dist/ui/theme.mjs +42 -0
  175. package/dist/ui/theme.mjs.map +1 -0
  176. package/dist/urls.d.mts +19 -0
  177. package/dist/urls.mjs +23 -0
  178. package/dist/urls.mjs.map +1 -0
  179. package/dist/utils/encrypt.mjs +49 -0
  180. package/dist/utils/encrypt.mjs.map +1 -0
  181. package/dist/utils/hash.mjs +22 -0
  182. package/dist/utils/hash.mjs.map +1 -0
  183. package/dist/utils/paths.d.mts +11 -0
  184. package/dist/utils/paths.mjs +23 -0
  185. package/dist/utils/paths.mjs.map +1 -0
  186. package/dist/utils/ulid.d.mts +8 -0
  187. package/dist/utils/ulid.mjs +41 -0
  188. package/dist/utils/ulid.mjs.map +1 -0
  189. package/package.json +162 -0
@@ -0,0 +1,191 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
5
+ //#region src/pages/settings-general.tsx
6
+ function renderSettingsGeneral(project, formState, isAdmin) {
7
+ const errors = formState?.errors ?? {};
8
+ return /* @__PURE__ */ jsxs("div", {
9
+ class: "grid",
10
+ style: "max-width: 720px;",
11
+ children: [
12
+ formState?.globalError ? /* @__PURE__ */ jsx("div", {
13
+ class: "alert alert--danger",
14
+ role: "alert",
15
+ children: formState.globalError
16
+ }) : null,
17
+ /* @__PURE__ */ jsxs("div", {
18
+ class: "card card--padded",
19
+ children: [/* @__PURE__ */ jsx("h2", {
20
+ style: "margin:0 0 .5rem;",
21
+ children: "General"
22
+ }), /* @__PURE__ */ jsxs("form", {
23
+ method: "post",
24
+ action: `/projects/${project.slug}/settings`,
25
+ "hx-post": `/projects/${project.slug}/settings`,
26
+ "hx-target": "body",
27
+ "hx-swap": "outerHTML",
28
+ children: [
29
+ /* @__PURE__ */ jsxs("div", {
30
+ class: "field",
31
+ children: [
32
+ /* @__PURE__ */ jsx("label", {
33
+ class: "field__label",
34
+ for: "name",
35
+ children: "Name"
36
+ }),
37
+ /* @__PURE__ */ jsx("input", {
38
+ class: `field__input ${errors["name"] ? "field__input--error" : ""}`,
39
+ id: "name",
40
+ name: "name",
41
+ value: project.name
42
+ }),
43
+ errors["name"] ? /* @__PURE__ */ jsx("p", {
44
+ class: "field__error",
45
+ role: "alert",
46
+ children: errors["name"]
47
+ }) : null
48
+ ]
49
+ }),
50
+ /* @__PURE__ */ jsxs("div", {
51
+ class: "field",
52
+ children: [/* @__PURE__ */ jsx("label", {
53
+ class: "field__label",
54
+ for: "gitRepository",
55
+ children: "Git repository"
56
+ }), /* @__PURE__ */ jsx("input", {
57
+ class: "field__input",
58
+ id: "gitRepository",
59
+ name: "gitRepository",
60
+ value: project.gitRepository ?? "",
61
+ placeholder: "owner/repo"
62
+ })]
63
+ }),
64
+ /* @__PURE__ */ jsxs("div", {
65
+ class: "field",
66
+ children: [/* @__PURE__ */ jsx("label", {
67
+ class: "field__label",
68
+ for: "gitDefaultBranch",
69
+ children: "Default branch"
70
+ }), /* @__PURE__ */ jsx("input", {
71
+ class: "field__input",
72
+ id: "gitDefaultBranch",
73
+ name: "gitDefaultBranch",
74
+ value: project.gitDefaultBranch
75
+ })]
76
+ }),
77
+ /* @__PURE__ */ jsxs("div", {
78
+ class: "grid grid--2",
79
+ children: [/* @__PURE__ */ jsxs("div", {
80
+ class: "field",
81
+ children: [
82
+ /* @__PURE__ */ jsx("label", {
83
+ class: "field__label",
84
+ for: "pixelThreshold",
85
+ children: "Pixel threshold"
86
+ }),
87
+ /* @__PURE__ */ jsx("input", {
88
+ class: "field__input",
89
+ id: "pixelThreshold",
90
+ name: "pixelThreshold",
91
+ type: "number",
92
+ step: "0.01",
93
+ min: "0",
94
+ max: "1",
95
+ value: String(project.pixelThreshold)
96
+ }),
97
+ /* @__PURE__ */ jsx("p", {
98
+ class: "field__hint",
99
+ children: "Per-pixel color distance 0–1"
100
+ })
101
+ ]
102
+ }), /* @__PURE__ */ jsxs("div", {
103
+ class: "field",
104
+ children: [
105
+ /* @__PURE__ */ jsx("label", {
106
+ class: "field__label",
107
+ for: "maxDiffRatio",
108
+ children: "Max diff ratio"
109
+ }),
110
+ /* @__PURE__ */ jsx("input", {
111
+ class: "field__input",
112
+ id: "maxDiffRatio",
113
+ name: "maxDiffRatio",
114
+ type: "number",
115
+ step: "0.001",
116
+ min: "0",
117
+ max: "1",
118
+ value: String(project.maxDiffRatio)
119
+ }),
120
+ /* @__PURE__ */ jsx("p", {
121
+ class: "field__hint",
122
+ children: "Allowed diff ratio 0–1"
123
+ })
124
+ ]
125
+ })]
126
+ }),
127
+ /* @__PURE__ */ jsxs("div", {
128
+ class: "field",
129
+ children: [
130
+ /* @__PURE__ */ jsx("label", {
131
+ class: "field__label",
132
+ for: "publicBranchRegex",
133
+ children: "Public branch regex"
134
+ }),
135
+ /* @__PURE__ */ jsx("input", {
136
+ class: "field__input",
137
+ id: "publicBranchRegex",
138
+ name: "publicBranchRegex",
139
+ value: project.publicBranchRegex ?? "",
140
+ placeholder: "^main$"
141
+ }),
142
+ /* @__PURE__ */ jsx("p", {
143
+ class: "field__hint",
144
+ children: "Branches matching this regex are publicly viewable."
145
+ })
146
+ ]
147
+ }),
148
+ isAdmin ? /* @__PURE__ */ jsx("button", {
149
+ class: "btn btn--primary",
150
+ type: "submit",
151
+ children: "Save changes"
152
+ }) : /* @__PURE__ */ jsx("p", {
153
+ class: "field__hint",
154
+ children: "You need admin access to edit settings."
155
+ })
156
+ ]
157
+ })]
158
+ }),
159
+ isAdmin ? /* @__PURE__ */ jsxs("div", {
160
+ class: "card card--padded",
161
+ style: "border-color: var(--status-rejected);",
162
+ children: [
163
+ /* @__PURE__ */ jsx("h3", {
164
+ style: "margin:0 0 .4rem; color: var(--status-rejected);",
165
+ children: "Danger zone"
166
+ }),
167
+ /* @__PURE__ */ jsx("p", {
168
+ class: "field__hint",
169
+ children: "Deleting a project removes all builds, snapshots, baselines and tokens. This cannot be undone."
170
+ }),
171
+ /* @__PURE__ */ jsx("form", {
172
+ method: "post",
173
+ action: `/projects/${project.slug}/delete`,
174
+ "hx-post": `/projects/${project.slug}/delete`,
175
+ "hx-target": "body",
176
+ "hx-confirm": "Delete this project? This cannot be undone.",
177
+ children: /* @__PURE__ */ jsx("button", {
178
+ class: "btn btn--danger",
179
+ type: "submit",
180
+ children: "Delete project"
181
+ })
182
+ })
183
+ ]
184
+ }) : null
185
+ ]
186
+ });
187
+ }
188
+ //#endregion
189
+ export { renderSettingsGeneral };
190
+
191
+ //# sourceMappingURL=settings-general.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-general.mjs","names":[],"sources":["../../src/pages/settings-general.tsx"],"sourcesContent":["import type { Project } from \"../schema.ts\";\n\nexport interface SettingsFormState {\n errors?: Record<string, string>;\n globalError?: string;\n}\n\nexport function renderSettingsGeneral(project: Project, formState: SettingsFormState | undefined, isAdmin: boolean): unknown {\n const errors = formState?.errors ?? {};\n return (\n <div class=\"grid\" style=\"max-width: 720px;\">\n {formState?.globalError ? (\n <div class=\"alert alert--danger\" role=\"alert\">\n {formState.globalError}\n </div>\n ) : null}\n <div class=\"card card--padded\">\n <h2 style=\"margin:0 0 .5rem;\">General</h2>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/settings`}\n hx-post={`/projects/${project.slug}/settings`}\n hx-target=\"body\"\n hx-swap=\"outerHTML\"\n >\n <div class=\"field\">\n <label class=\"field__label\" for=\"name\">\n Name\n </label>\n <input class={`field__input ${errors[\"name\"] ? \"field__input--error\" : \"\"}`} id=\"name\" name=\"name\" value={project.name} />\n {errors[\"name\"] ? (\n <p class=\"field__error\" role=\"alert\">\n {errors[\"name\"]}\n </p>\n ) : null}\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"gitRepository\">\n Git repository\n </label>\n <input class=\"field__input\" id=\"gitRepository\" name=\"gitRepository\" value={project.gitRepository ?? \"\"} placeholder=\"owner/repo\" />\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"gitDefaultBranch\">\n Default branch\n </label>\n <input class=\"field__input\" id=\"gitDefaultBranch\" name=\"gitDefaultBranch\" value={project.gitDefaultBranch} />\n </div>\n <div class=\"grid grid--2\">\n <div class=\"field\">\n <label class=\"field__label\" for=\"pixelThreshold\">\n Pixel threshold\n </label>\n <input class=\"field__input\" id=\"pixelThreshold\" name=\"pixelThreshold\" type=\"number\" step=\"0.01\" min=\"0\" max=\"1\" value={String(project.pixelThreshold)} />\n <p class=\"field__hint\">Per-pixel color distance 0–1</p>\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"maxDiffRatio\">\n Max diff ratio\n </label>\n <input class=\"field__input\" id=\"maxDiffRatio\" name=\"maxDiffRatio\" type=\"number\" step=\"0.001\" min=\"0\" max=\"1\" value={String(project.maxDiffRatio)} />\n <p class=\"field__hint\">Allowed diff ratio 0–1</p>\n </div>\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"publicBranchRegex\">\n Public branch regex\n </label>\n <input class=\"field__input\" id=\"publicBranchRegex\" name=\"publicBranchRegex\" value={project.publicBranchRegex ?? \"\"} placeholder=\"^main$\" />\n <p class=\"field__hint\">Branches matching this regex are publicly viewable.</p>\n </div>\n {isAdmin ? (\n <button class=\"btn btn--primary\" type=\"submit\">\n Save changes\n </button>\n ) : (\n <p class=\"field__hint\">You need admin access to edit settings.</p>\n )}\n </form>\n </div>\n\n {isAdmin ? (\n <div class=\"card card--padded\" style=\"border-color: var(--status-rejected);\">\n <h3 style=\"margin:0 0 .4rem; color: var(--status-rejected);\">Danger zone</h3>\n <p class=\"field__hint\">Deleting a project removes all builds, snapshots, baselines and tokens. This cannot be undone.</p>\n <form\n method=\"post\"\n action={`/projects/${project.slug}/delete`}\n hx-post={`/projects/${project.slug}/delete`}\n hx-target=\"body\"\n hx-confirm=\"Delete this project? This cannot be undone.\"\n >\n <button class=\"btn btn--danger\" type=\"submit\">\n Delete project\n </button>\n </form>\n </div>\n ) : null}\n </div>\n );\n}"],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { Badge } from "../ui/components.mjs";
5
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
6
+ //#region src/pages/settings-labels.tsx
7
+ function renderSettingsLabels(project, labelTypes, isAdmin) {
8
+ return /* @__PURE__ */ jsxs("div", {
9
+ class: "grid",
10
+ style: "max-width: 880px;",
11
+ children: [/* @__PURE__ */ jsxs("div", {
12
+ class: "card card--padded",
13
+ children: [
14
+ /* @__PURE__ */ jsx("h2", {
15
+ style: "margin:0 0 .3rem;",
16
+ children: "Label types"
17
+ }),
18
+ /* @__PURE__ */ jsx("p", {
19
+ class: "field__hint",
20
+ children: "Labels attach typed values to builds (e.g. pr=123, jira=ABC-123). Values link out via the template."
21
+ }),
22
+ /* @__PURE__ */ jsx("div", {
23
+ class: "table-wrap",
24
+ style: "margin-top:.75rem;",
25
+ children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
26
+ /* @__PURE__ */ jsx("th", { children: "Key" }),
27
+ /* @__PURE__ */ jsx("th", { children: "Name" }),
28
+ /* @__PURE__ */ jsx("th", { children: "Template" }),
29
+ /* @__PURE__ */ jsx("th", {})
30
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: labelTypes.map((labelType) => /* @__PURE__ */ jsxs("tr", { children: [
31
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Badge, {
32
+ tone: "neutral",
33
+ children: labelType.key
34
+ }) }),
35
+ /* @__PURE__ */ jsx("td", { children: labelType.name }),
36
+ /* @__PURE__ */ jsx("td", {
37
+ style: "max-width:32ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;",
38
+ children: labelType.linkTemplate ?? "—"
39
+ }),
40
+ /* @__PURE__ */ jsx("td", { children: isAdmin && labelType.key !== "persistent" && labelType.key !== "branch" ? /* @__PURE__ */ jsx("form", {
41
+ method: "post",
42
+ action: `/projects/${project.slug}/settings/labels/${labelType.key}/delete`,
43
+ "hx-post": `/projects/${project.slug}/settings/labels/${labelType.key}/delete`,
44
+ "hx-target": "body",
45
+ children: /* @__PURE__ */ jsx("button", {
46
+ class: "btn btn--ghost",
47
+ type: "submit",
48
+ "aria-label": `Delete ${labelType.key}`,
49
+ children: "Delete"
50
+ })
51
+ }) : /* @__PURE__ */ jsx("span", {
52
+ class: "field__hint",
53
+ children: "built-in"
54
+ }) })
55
+ ] }, labelType.id)) })] })
56
+ }),
57
+ labelTypes.length === 0 ? /* @__PURE__ */ jsx("p", {
58
+ class: "field__hint",
59
+ style: "margin-top:.5rem;",
60
+ children: "No label types configured."
61
+ }) : null
62
+ ]
63
+ }), isAdmin ? /* @__PURE__ */ jsxs("div", {
64
+ class: "card card--padded",
65
+ children: [/* @__PURE__ */ jsx("h3", {
66
+ style: "margin:0 0 .5rem;",
67
+ children: "Create label type"
68
+ }), /* @__PURE__ */ jsxs("form", {
69
+ method: "post",
70
+ action: `/projects/${project.slug}/settings/labels`,
71
+ "hx-post": `/projects/${project.slug}/settings/labels`,
72
+ "hx-target": "body",
73
+ children: [
74
+ /* @__PURE__ */ jsxs("div", {
75
+ class: "grid grid--2",
76
+ children: [/* @__PURE__ */ jsxs("div", {
77
+ class: "field",
78
+ children: [
79
+ /* @__PURE__ */ jsx("label", {
80
+ class: "field__label",
81
+ for: "key",
82
+ children: "Key"
83
+ }),
84
+ /* @__PURE__ */ jsx("input", {
85
+ class: "field__input",
86
+ id: "key",
87
+ name: "key",
88
+ required: true,
89
+ placeholder: "jira",
90
+ pattern: "^[a-z0-9_-]+$"
91
+ }),
92
+ /* @__PURE__ */ jsx("p", {
93
+ class: "field__hint",
94
+ children: "Lowercase, no spaces."
95
+ })
96
+ ]
97
+ }), /* @__PURE__ */ jsxs("div", {
98
+ class: "field",
99
+ children: [/* @__PURE__ */ jsx("label", {
100
+ class: "field__label",
101
+ for: "labelName",
102
+ children: "Name"
103
+ }), /* @__PURE__ */ jsx("input", {
104
+ class: "field__input",
105
+ id: "labelName",
106
+ name: "labelName",
107
+ required: true,
108
+ placeholder: "Jira issue"
109
+ })]
110
+ })]
111
+ }),
112
+ /* @__PURE__ */ jsxs("div", {
113
+ class: "field",
114
+ children: [
115
+ /* @__PURE__ */ jsx("label", {
116
+ class: "field__label",
117
+ for: "linkTemplate",
118
+ children: "Link template"
119
+ }),
120
+ /* @__PURE__ */ jsx("input", {
121
+ class: "field__input",
122
+ id: "linkTemplate",
123
+ name: "linkTemplate",
124
+ placeholder: "https://jira.example.com/browse/{value}"
125
+ }),
126
+ /* @__PURE__ */ jsxs("p", {
127
+ class: "field__hint",
128
+ children: [
129
+ "Use ",
130
+ "{value}",
131
+ " placeholder. Optional."
132
+ ]
133
+ })
134
+ ]
135
+ }),
136
+ /* @__PURE__ */ jsx("button", {
137
+ class: "btn btn--primary",
138
+ type: "submit",
139
+ children: "Add label type"
140
+ })
141
+ ]
142
+ })]
143
+ }) : null]
144
+ });
145
+ }
146
+ //#endregion
147
+ export { renderSettingsLabels };
148
+
149
+ //# sourceMappingURL=settings-labels.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-labels.mjs","names":[],"sources":["../../src/pages/settings-labels.tsx"],"sourcesContent":["import type { LabelType, Project } from \"../schema.ts\";\nimport { Badge } from \"../ui/components.tsx\";\n\nexport function renderSettingsLabels(project: Project, labelTypes: LabelType[], isAdmin: boolean): unknown {\n return (\n <div class=\"grid\" style=\"max-width: 880px;\">\n <div class=\"card card--padded\">\n <h2 style=\"margin:0 0 .3rem;\">Label types</h2>\n <p class=\"field__hint\">Labels attach typed values to builds (e.g. pr=123, jira=ABC-123). Values link out via the template.</p>\n <div class=\"table-wrap\" style=\"margin-top:.75rem;\">\n <table>\n <thead>\n <tr>\n <th>Key</th>\n <th>Name</th>\n <th>Template</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n {labelTypes.map(\n // eslint-disable-next-line promise-function-async -- JSX.Element includes Promise<HtmlEscapedString>\n (labelType) => (\n <tr key={labelType.id}>\n <td>\n <Badge tone=\"neutral\">{labelType.key}</Badge>\n </td>\n <td>{labelType.name}</td>\n <td style=\"max-width:32ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;\">{labelType.linkTemplate ?? \"—\"}</td>\n <td>\n {isAdmin && labelType.key !== \"persistent\" && labelType.key !== \"branch\" ? (\n <form method=\"post\" action={`/projects/${project.slug}/settings/labels/${labelType.key}/delete`} hx-post={`/projects/${project.slug}/settings/labels/${labelType.key}/delete`} hx-target=\"body\">\n <button class=\"btn btn--ghost\" type=\"submit\" aria-label={`Delete ${labelType.key}`}>\n Delete\n </button>\n </form>\n ) : (\n <span class=\"field__hint\">built-in</span>\n )}\n </td>\n </tr>\n ),\n )}\n </tbody>\n </table>\n </div>\n {labelTypes.length === 0 ? <p class=\"field__hint\" style=\"margin-top:.5rem;\">No label types configured.</p> : null}\n </div>\n\n {isAdmin ? (\n <div class=\"card card--padded\">\n <h3 style=\"margin:0 0 .5rem;\">Create label type</h3>\n <form method=\"post\" action={`/projects/${project.slug}/settings/labels`} hx-post={`/projects/${project.slug}/settings/labels`} hx-target=\"body\">\n <div class=\"grid grid--2\">\n <div class=\"field\">\n <label class=\"field__label\" for=\"key\">\n Key\n </label>\n <input class=\"field__input\" id=\"key\" name=\"key\" required placeholder=\"jira\" pattern=\"^[a-z0-9_-]+$\" />\n <p class=\"field__hint\">Lowercase, no spaces.</p>\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"labelName\">\n Name\n </label>\n <input class=\"field__input\" id=\"labelName\" name=\"labelName\" required placeholder=\"Jira issue\" />\n </div>\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"linkTemplate\">\n Link template\n </label>\n <input class=\"field__input\" id=\"linkTemplate\" name=\"linkTemplate\" placeholder=\"https://jira.example.com/browse/{value}\" />\n <p class=\"field__hint\">\n Use {\"{value}\"} placeholder. Optional.\n </p>\n </div>\n <button class=\"btn btn--primary\" type=\"submit\">\n Add label type\n </button>\n </form>\n </div>\n ) : null}\n </div>\n );\n}"],"mappings":";;;;;;AAGA,SAAgB,qBAAqB,SAAkB,YAAyB,SAA2B;CACzG,OACE,qBAAC,OAAD;EAAK,OAAM;EAAO,OAAM;EAAxB,UAAA,CACE,qBAAC,OAAD;GAAK,OAAM;GAAX,UAAA;IACE,oBAAC,MAAD;KAAI,OAAM;KAAoB,UAAA;IAAe,CAAA;IAC7C,oBAAC,KAAD;KAAG,OAAM;KAAc,UAAA;IAAsG,CAAA;IAC7H,oBAAC,OAAD;KAAK,OAAM;KAAa,OAAM;KAC5B,UAAA,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,SAAD,EAAA,UACE,qBAAC,MAAD,EAAA,UAAA;MACE,oBAAC,MAAD,EAAA,UAAI,MAAO,CAAA;MACX,oBAAC,MAAD,EAAA,UAAI,OAAQ,CAAA;MACZ,oBAAC,MAAD,EAAA,UAAI,WAAY,CAAA;MAChB,oBAAC,MAAD,CAAQ,CAAA;KACN,EAAA,CAAA,EACC,CAAA,GACP,oBAAC,SAAD,EAAA,UACG,WAAW,KAET,cACC,qBAAC,MAAD,EAAA,UAAA;MACE,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD;OAAO,MAAK;OAAW,UAAA,UAAU;MAAW,CAAA,EAC1C,CAAA;MACJ,oBAAC,MAAD,EAAA,UAAK,UAAU,KAAS,CAAA;MACxB,oBAAC,MAAD;OAAI,OAAM;OAAgF,UAAA,UAAU,gBAAgB;MAAQ,CAAA;MAC5H,oBAAC,MAAD,EAAA,UACG,WAAW,UAAU,QAAQ,gBAAgB,UAAU,QAAQ,WAC9D,oBAAC,QAAD;OAAM,QAAO;OAAO,QAAQ,aAAa,QAAQ,KAAK,mBAAmB,UAAU,IAAI;OAAU,WAAS,aAAa,QAAQ,KAAK,mBAAmB,UAAU,IAAI;OAAU,aAAU;OACvL,UAAA,oBAAC,UAAD;QAAQ,OAAM;QAAiB,MAAK;QAAS,cAAY,UAAU,UAAU;QAAO,UAAA;OAE5E,CAAA;MACJ,CAAA,IAEN,oBAAC,QAAD;OAAM,OAAM;OAAc,UAAA;MAAc,CAAA,EAExC,CAAA;KACF,EAAA,GAjBK,UAAU,EAiBf,CAER,EACK,CAAA,CACF,EAAA,CAAA;IACJ,CAAA;IACJ,WAAW,WAAW,IAAI,oBAAC,KAAD;KAAG,OAAM;KAAc,OAAM;KAAoB,UAAA;IAA6B,CAAA,IAAI;GAC1G;EAEJ,CAAA,GAAA,UACC,qBAAC,OAAD;GAAK,OAAM;GAAX,UAAA,CACE,oBAAC,MAAD;IAAI,OAAM;IAAoB,UAAA;GAAqB,CAAA,GACnD,qBAAC,QAAD;IAAM,QAAO;IAAO,QAAQ,aAAa,QAAQ,KAAK;IAAmB,WAAS,aAAa,QAAQ,KAAK;IAAmB,aAAU;IAAzI,UAAA;KACE,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA,CACE,qBAAC,OAAD;OAAK,OAAM;OAAX,UAAA;QACE,oBAAC,SAAD;SAAO,OAAM;SAAe,KAAI;SAAM,UAAA;QAE/B,CAAA;QACP,oBAAC,SAAD;SAAO,OAAM;SAAe,IAAG;SAAM,MAAK;SAAM,UAAA;SAAS,aAAY;SAAO,SAAQ;QAAiB,CAAA;QACrG,oBAAC,KAAD;SAAG,OAAM;SAAc,UAAA;QAAwB,CAAA;OAC5C;MACL,CAAA,GAAA,qBAAC,OAAD;OAAK,OAAM;OAAX,UAAA,CACE,oBAAC,SAAD;QAAO,OAAM;QAAe,KAAI;QAAY,UAAA;OAErC,CAAA,GACP,oBAAC,SAAD;QAAO,OAAM;QAAe,IAAG;QAAY,MAAK;QAAY,UAAA;QAAS,aAAY;OAAc,CAAA,CAC5F;MACF,CAAA,CAAA;;KACL,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA;OACE,oBAAC,SAAD;QAAO,OAAM;QAAe,KAAI;QAAe,UAAA;OAExC,CAAA;OACP,oBAAC,SAAD;QAAO,OAAM;QAAe,IAAG;QAAe,MAAK;QAAe,aAAY;OAA2C,CAAA;OACzH,qBAAC,KAAD;QAAG,OAAM;QAAT,UAAA;SAAuB;SAChB;SAAU;QACd;;MACA;;KACL,oBAAC,UAAD;MAAQ,OAAM;MAAmB,MAAK;MAAS,UAAA;KAEvC,CAAA;IACJ;GACH,CAAA,CAAA;EACH,CAAA,IAAA,IACD;;AAET"}
@@ -0,0 +1,124 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { Badge } from "../ui/components.mjs";
5
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
6
+ //#region src/pages/settings-members.tsx
7
+ function renderSettingsMembers(project, members, isAdmin) {
8
+ return /* @__PURE__ */ jsxs("div", {
9
+ class: "grid",
10
+ style: "max-width: 880px;",
11
+ children: [/* @__PURE__ */ jsxs("div", {
12
+ class: "card card--padded",
13
+ children: [
14
+ /* @__PURE__ */ jsx("h2", {
15
+ style: "margin:0 0 .3rem;",
16
+ children: "Members"
17
+ }),
18
+ /* @__PURE__ */ jsx("p", {
19
+ class: "field__hint",
20
+ children: "Project members and their roles. Site admins have implicit admin access."
21
+ }),
22
+ /* @__PURE__ */ jsx("div", {
23
+ class: "table-wrap",
24
+ style: "margin-top:.75rem;",
25
+ children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
26
+ /* @__PURE__ */ jsx("th", { children: "User" }),
27
+ /* @__PURE__ */ jsx("th", { children: "Role" }),
28
+ /* @__PURE__ */ jsx("th", { children: "Since" }),
29
+ /* @__PURE__ */ jsx("th", {})
30
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: members.map((member) => /* @__PURE__ */ jsxs("tr", { children: [
31
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("code", { children: member.userId }) }),
32
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Badge, {
33
+ tone: member.role === "admin" ? "danger" : member.role === "viewer" ? "neutral" : "info",
34
+ children: member.role
35
+ }) }),
36
+ /* @__PURE__ */ jsx("td", { children: new Date(member.createdAt).toLocaleDateString() }),
37
+ /* @__PURE__ */ jsx("td", { children: isAdmin ? /* @__PURE__ */ jsx("form", {
38
+ method: "post",
39
+ action: `/projects/${project.slug}/settings/members/${member.userId}/remove`,
40
+ "hx-post": `/projects/${project.slug}/settings/members/${member.userId}/remove`,
41
+ "hx-target": "body",
42
+ children: /* @__PURE__ */ jsx("button", {
43
+ class: "btn btn--ghost",
44
+ type: "submit",
45
+ children: "Remove"
46
+ })
47
+ }) : null })
48
+ ] }, member.id)) })] })
49
+ }),
50
+ members.length === 0 ? /* @__PURE__ */ jsx("p", {
51
+ class: "field__hint",
52
+ style: "margin-top:.5rem;",
53
+ children: "No members yet."
54
+ }) : null
55
+ ]
56
+ }), isAdmin ? /* @__PURE__ */ jsxs("div", {
57
+ class: "card card--padded",
58
+ children: [/* @__PURE__ */ jsx("h3", {
59
+ style: "margin:0 0 .5rem;",
60
+ children: "Add member"
61
+ }), /* @__PURE__ */ jsxs("form", {
62
+ method: "post",
63
+ action: `/projects/${project.slug}/settings/members`,
64
+ "hx-post": `/projects/${project.slug}/settings/members`,
65
+ "hx-target": "body",
66
+ children: [
67
+ /* @__PURE__ */ jsxs("div", {
68
+ class: "field",
69
+ children: [/* @__PURE__ */ jsx("label", {
70
+ class: "field__label",
71
+ for: "userId",
72
+ children: "User ID"
73
+ }), /* @__PURE__ */ jsx("input", {
74
+ class: "field__input",
75
+ id: "userId",
76
+ name: "userId",
77
+ required: true,
78
+ placeholder: "user_..."
79
+ })]
80
+ }),
81
+ /* @__PURE__ */ jsxs("div", {
82
+ class: "field",
83
+ children: [/* @__PURE__ */ jsx("label", {
84
+ class: "field__label",
85
+ for: "role",
86
+ children: "Role"
87
+ }), /* @__PURE__ */ jsxs("select", {
88
+ class: "field__input",
89
+ id: "role",
90
+ name: "role",
91
+ children: [
92
+ /* @__PURE__ */ jsx("option", {
93
+ value: "viewer",
94
+ children: "viewer"
95
+ }),
96
+ /* @__PURE__ */ jsx("option", {
97
+ value: "developer",
98
+ children: "developer"
99
+ }),
100
+ /* @__PURE__ */ jsx("option", {
101
+ value: "approver",
102
+ children: "approver"
103
+ }),
104
+ /* @__PURE__ */ jsx("option", {
105
+ value: "admin",
106
+ children: "admin"
107
+ })
108
+ ]
109
+ })]
110
+ }),
111
+ /* @__PURE__ */ jsx("button", {
112
+ class: "btn btn--primary",
113
+ type: "submit",
114
+ children: "Add member"
115
+ })
116
+ ]
117
+ })]
118
+ }) : null]
119
+ });
120
+ }
121
+ //#endregion
122
+ export { renderSettingsMembers };
123
+
124
+ //# sourceMappingURL=settings-members.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-members.mjs","names":[],"sources":["../../src/pages/settings-members.tsx"],"sourcesContent":["import type { Project } from \"../schema.ts\";\nimport { Badge } from \"../ui/components.tsx\";\n\nexport interface SettingsMember {\n id: string;\n projectId: string;\n userId: string;\n role: string;\n createdAt: string;\n}\n\nexport function renderSettingsMembers(project: Project, members: SettingsMember[], isAdmin: boolean): unknown {\n return (\n <div class=\"grid\" style=\"max-width: 880px;\">\n <div class=\"card card--padded\">\n <h2 style=\"margin:0 0 .3rem;\">Members</h2>\n <p class=\"field__hint\">Project members and their roles. Site admins have implicit admin access.</p>\n <div class=\"table-wrap\" style=\"margin-top:.75rem;\">\n <table>\n <thead>\n <tr>\n <th>User</th>\n <th>Role</th>\n <th>Since</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n {members.map(\n // eslint-disable-next-line promise-function-async -- JSX.Element includes Promise<HtmlEscapedString>\n (member) => (\n <tr key={member.id}>\n <td>\n <code>{member.userId}</code>\n </td>\n <td>\n <Badge tone={member.role === \"admin\" ? \"danger\" : member.role === \"viewer\" ? \"neutral\" : \"info\"}>{member.role}</Badge>\n </td>\n <td>{new Date(member.createdAt).toLocaleDateString()}</td>\n <td>\n {isAdmin ? (\n <form method=\"post\" action={`/projects/${project.slug}/settings/members/${member.userId}/remove`} hx-post={`/projects/${project.slug}/settings/members/${member.userId}/remove`} hx-target=\"body\">\n <button class=\"btn btn--ghost\" type=\"submit\">\n Remove\n </button>\n </form>\n ) : null}\n </td>\n </tr>\n ),\n )}\n </tbody>\n </table>\n </div>\n {members.length === 0 ? <p class=\"field__hint\" style=\"margin-top:.5rem;\">No members yet.</p> : null}\n </div>\n\n {isAdmin ? (\n <div class=\"card card--padded\">\n <h3 style=\"margin:0 0 .5rem;\">Add member</h3>\n <form method=\"post\" action={`/projects/${project.slug}/settings/members`} hx-post={`/projects/${project.slug}/settings/members`} hx-target=\"body\">\n <div class=\"field\">\n <label class=\"field__label\" for=\"userId\">\n User ID\n </label>\n <input class=\"field__input\" id=\"userId\" name=\"userId\" required placeholder=\"user_...\" />\n </div>\n <div class=\"field\">\n <label class=\"field__label\" for=\"role\">\n Role\n </label>\n <select class=\"field__input\" id=\"role\" name=\"role\">\n <option value=\"viewer\">viewer</option>\n <option value=\"developer\">developer</option>\n <option value=\"approver\">approver</option>\n <option value=\"admin\">admin</option>\n </select>\n </div>\n <button class=\"btn btn--primary\" type=\"submit\">\n Add member\n </button>\n </form>\n </div>\n ) : null}\n </div>\n );\n}"],"mappings":";;;;;;AAWA,SAAgB,sBAAsB,SAAkB,SAA2B,SAA2B;CAC5G,OACE,qBAAC,OAAD;EAAK,OAAM;EAAO,OAAM;EAAxB,UAAA,CACE,qBAAC,OAAD;GAAK,OAAM;GAAX,UAAA;IACE,oBAAC,MAAD;KAAI,OAAM;KAAoB,UAAA;IAAW,CAAA;IACzC,oBAAC,KAAD;KAAG,OAAM;KAAc,UAAA;IAA2E,CAAA;IAClG,oBAAC,OAAD;KAAK,OAAM;KAAa,OAAM;KAC5B,UAAA,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,SAAD,EAAA,UACE,qBAAC,MAAD,EAAA,UAAA;MACE,oBAAC,MAAD,EAAA,UAAI,OAAQ,CAAA;MACZ,oBAAC,MAAD,EAAA,UAAI,OAAQ,CAAA;MACZ,oBAAC,MAAD,EAAA,UAAI,QAAS,CAAA;MACb,oBAAC,MAAD,CAAQ,CAAA;KACN,EAAA,CAAA,EACC,CAAA,GACP,oBAAC,SAAD,EAAA,UACG,QAAQ,KAEN,WACC,qBAAC,MAAD,EAAA,UAAA;MACE,oBAAC,MAAD,EAAA,UACE,oBAAC,QAAD,EAAA,UAAO,OAAO,OAAa,CAAA,EACzB,CAAA;MACJ,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD;OAAO,MAAM,OAAO,SAAS,UAAU,WAAW,OAAO,SAAS,WAAW,YAAY;OAAS,UAAA,OAAO;MAAY,CAAA,EACnH,CAAA;MACJ,oBAAC,MAAD,EAAA,UAAK,IAAI,KAAK,OAAO,SAAS,CAAC,CAAC,mBAAmB,EAAM,CAAA;MACzD,oBAAC,MAAD,EAAA,UACG,UACC,oBAAC,QAAD;OAAM,QAAO;OAAO,QAAQ,aAAa,QAAQ,KAAK,oBAAoB,OAAO,OAAO;OAAU,WAAS,aAAa,QAAQ,KAAK,oBAAoB,OAAO,OAAO;OAAU,aAAU;OACzL,UAAA,oBAAC,UAAD;QAAQ,OAAM;QAAiB,MAAK;QAAS,UAAA;OAErC,CAAA;MACJ,CAAA,IACJ,KACF,CAAA;KACF,EAAA,GAjBK,OAAO,EAiBZ,CAER,EACK,CAAA,CACF,EAAA,CAAA;IACJ,CAAA;IACJ,QAAQ,WAAW,IAAI,oBAAC,KAAD;KAAG,OAAM;KAAc,OAAM;KAAoB,UAAA;IAAkB,CAAA,IAAI;GAC5F;EAEJ,CAAA,GAAA,UACC,qBAAC,OAAD;GAAK,OAAM;GAAX,UAAA,CACE,oBAAC,MAAD;IAAI,OAAM;IAAoB,UAAA;GAAc,CAAA,GAC5C,qBAAC,QAAD;IAAM,QAAO;IAAO,QAAQ,aAAa,QAAQ,KAAK;IAAoB,WAAS,aAAa,QAAQ,KAAK;IAAoB,aAAU;IAA3I,UAAA;KACE,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA,CACE,oBAAC,SAAD;OAAO,OAAM;OAAe,KAAI;OAAS,UAAA;MAElC,CAAA,GACP,oBAAC,SAAD;OAAO,OAAM;OAAe,IAAG;OAAS,MAAK;OAAS,UAAA;OAAS,aAAY;MAAY,CAAA,CACpF;;KACL,qBAAC,OAAD;MAAK,OAAM;MAAX,UAAA,CACE,oBAAC,SAAD;OAAO,OAAM;OAAe,KAAI;OAAO,UAAA;MAEhC,CAAA,GACP,qBAAC,UAAD;OAAQ,OAAM;OAAe,IAAG;OAAO,MAAK;OAA5C,UAAA;QACE,oBAAC,UAAD;SAAQ,OAAM;SAAS,UAAA;QAAc,CAAA;QACrC,oBAAC,UAAD;SAAQ,OAAM;SAAY,UAAA;QAAiB,CAAA;QAC3C,oBAAC,UAAD;SAAQ,OAAM;SAAW,UAAA;QAAgB,CAAA;QACzC,oBAAC,UAAD;SAAQ,OAAM;SAAQ,UAAA;QAAa,CAAA;OAC7B;MACL,CAAA,CAAA;;KACL,oBAAC,UAAD;MAAQ,OAAM;MAAmB,MAAK;MAAS,UAAA;KAEvC,CAAA;IACJ;GACH,CAAA,CAAA;EACH,CAAA,IAAA,IACD;;AAET"}
@@ -0,0 +1,150 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { Badge, Field, SelectField, TextareaField } from "../ui/components.mjs";
5
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
6
+ //#region src/pages/settings-status.tsx
7
+ function providerOptions(providers) {
8
+ return providers.map((provider) => ({
9
+ value: provider.metadata.kind,
10
+ label: provider.metadata.name
11
+ }));
12
+ }
13
+ const StatusConfigRow = ({ config, provider, project, isAdmin }) => {
14
+ const rendered = JSON.stringify(config.config);
15
+ return /* @__PURE__ */ jsxs("tr", { children: [
16
+ /* @__PURE__ */ jsx("td", { children: provider?.metadata.name ?? config.provider }),
17
+ /* @__PURE__ */ jsx("td", {
18
+ style: "max-width:32ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;",
19
+ title: rendered,
20
+ children: /* @__PURE__ */ jsx("code", { children: rendered })
21
+ }),
22
+ /* @__PURE__ */ jsx("td", { children: config.hasToken ? /* @__PURE__ */ jsx(Badge, {
23
+ tone: "success",
24
+ children: "configured"
25
+ }) : /* @__PURE__ */ jsx(Badge, {
26
+ tone: "danger",
27
+ children: "missing"
28
+ }) }),
29
+ /* @__PURE__ */ jsx("td", { children: new Date(config.createdAt).toLocaleDateString() }),
30
+ /* @__PURE__ */ jsx("td", { children: isAdmin ? /* @__PURE__ */ jsx("form", {
31
+ method: "post",
32
+ action: `/projects/${project.slug}/settings/status/${config.id}/delete`,
33
+ "hx-post": `/projects/${project.slug}/settings/status/${config.id}/delete`,
34
+ "hx-target": "body",
35
+ children: /* @__PURE__ */ jsx("button", {
36
+ class: "btn btn--ghost",
37
+ type: "submit",
38
+ children: "Delete"
39
+ })
40
+ }) : null })
41
+ ] }, config.id);
42
+ };
43
+ const StatusCreateCard = ({ project, providers, formState }) => {
44
+ return /* @__PURE__ */ jsxs("div", {
45
+ class: "card card--padded",
46
+ children: [/* @__PURE__ */ jsx("h3", {
47
+ style: "margin:0 0 .5rem;",
48
+ children: "Add git provider"
49
+ }), /* @__PURE__ */ jsxs("form", {
50
+ method: "post",
51
+ action: `/projects/${project.slug}/settings/status`,
52
+ "hx-post": `/projects/${project.slug}/settings/status`,
53
+ "hx-target": "body",
54
+ children: [
55
+ /* @__PURE__ */ jsx(SelectField, {
56
+ label: "Provider",
57
+ name: "provider",
58
+ options: providerOptions(providers),
59
+ hint: "Integration that posts commit statuses for this project."
60
+ }),
61
+ /* @__PURE__ */ jsx(Field, {
62
+ label: "Token",
63
+ name: "token",
64
+ type: "password",
65
+ required: true,
66
+ placeholder: "ghp_…",
67
+ error: formState?.errors?.["token"],
68
+ hint: "Scoped token for the provider (e.g. a GitHub PAT with repo:status)."
69
+ }),
70
+ /* @__PURE__ */ jsx(TextareaField, {
71
+ label: "Config",
72
+ name: "config",
73
+ rows: 6,
74
+ placeholder: "{\"owner\":\"my-org\",\"repo\":\"my-repo\"}",
75
+ error: formState?.errors?.["config"],
76
+ hint: "JSON configuration for the provider. See the provider documentation for its fields."
77
+ }),
78
+ /* @__PURE__ */ jsx("button", {
79
+ class: "btn btn--primary",
80
+ type: "submit",
81
+ children: "Add git provider"
82
+ })
83
+ ]
84
+ })]
85
+ });
86
+ };
87
+ function renderCreateSection(project, providers, formState) {
88
+ if (providers.length === 0) return /* @__PURE__ */ jsx("p", {
89
+ class: "field__hint",
90
+ children: "No git providers registered on this server."
91
+ });
92
+ return /* @__PURE__ */ jsx(StatusCreateCard, {
93
+ project,
94
+ providers,
95
+ formState
96
+ });
97
+ }
98
+ function renderSettingsStatus(project, statusConfigs, providers, isAdmin, formState) {
99
+ const byProvider = new Map(providers.map((provider) => [provider.metadata.kind, provider]));
100
+ return /* @__PURE__ */ jsxs("div", {
101
+ class: "grid",
102
+ style: "max-width: 880px;",
103
+ children: [
104
+ formState?.globalError ? /* @__PURE__ */ jsx("div", {
105
+ class: "alert alert--danger",
106
+ role: "alert",
107
+ children: formState.globalError
108
+ }) : null,
109
+ /* @__PURE__ */ jsxs("div", {
110
+ class: "card card--padded",
111
+ children: [
112
+ /* @__PURE__ */ jsx("h2", {
113
+ style: "margin:0 0 .3rem;",
114
+ children: "Git status"
115
+ }),
116
+ /* @__PURE__ */ jsx("p", {
117
+ class: "field__hint",
118
+ children: "Post commit statuses to a git provider so visual tests show up in your PR checks. Status is reported for each configured provider: pending while capturing, success on approval, failure on rejection or capture errors."
119
+ }),
120
+ /* @__PURE__ */ jsx("div", {
121
+ class: "table-wrap",
122
+ style: "margin-top:.75rem;",
123
+ children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
124
+ /* @__PURE__ */ jsx("th", { children: "Provider" }),
125
+ /* @__PURE__ */ jsx("th", { children: "Config" }),
126
+ /* @__PURE__ */ jsx("th", { children: "Token" }),
127
+ /* @__PURE__ */ jsx("th", { children: "Created" }),
128
+ /* @__PURE__ */ jsx("th", {})
129
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: statusConfigs.map((config) => /* @__PURE__ */ jsx(StatusConfigRow, {
130
+ config,
131
+ provider: byProvider.get(config.provider),
132
+ project,
133
+ isAdmin
134
+ }, config.id)) })] })
135
+ }),
136
+ statusConfigs.length === 0 ? /* @__PURE__ */ jsx("p", {
137
+ class: "field__hint",
138
+ style: "margin-top:.5rem;",
139
+ children: "No git providers configured for this project."
140
+ }) : null
141
+ ]
142
+ }),
143
+ isAdmin ? renderCreateSection(project, providers, formState) : null
144
+ ]
145
+ });
146
+ }
147
+ //#endregion
148
+ export { renderSettingsStatus };
149
+
150
+ //# sourceMappingURL=settings-status.mjs.map