@orange-soft/strapi-deployment-trigger 1.0.1 → 1.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.
@@ -0,0 +1,666 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const admin = require("@strapi/strapi/admin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const react = require("react");
7
+ const reactIntl = require("react-intl");
8
+ const designSystem = require("@strapi/design-system");
9
+ const icons = require("@strapi/icons");
10
+ const index = require("./index-DGatQB-9.js");
11
+ const getTranslation = (id) => `${index.PLUGIN_ID}.${id}`;
12
+ const HomePage = () => {
13
+ const { formatMessage } = reactIntl.useIntl();
14
+ const { get, post } = admin.useFetchClient();
15
+ const [status, setStatus] = react.useState(null);
16
+ const [loading, setLoading] = react.useState(true);
17
+ const [deployingTargetId, setDeployingTargetId] = react.useState(null);
18
+ const [notification, setNotification] = react.useState(null);
19
+ react.useEffect(() => {
20
+ fetchStatus();
21
+ }, []);
22
+ const fetchStatus = async () => {
23
+ try {
24
+ const { data } = await get(`/${index.PLUGIN_ID}/status`);
25
+ setStatus(data.data);
26
+ } catch (error) {
27
+ console.error("Failed to fetch status:", error);
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ };
32
+ const handleDeploy = async (targetId, targetName) => {
33
+ setDeployingTargetId(targetId);
34
+ setNotification(null);
35
+ try {
36
+ const { data } = await post(`/${index.PLUGIN_ID}/trigger`, { targetId });
37
+ setNotification({
38
+ type: "success",
39
+ message: `${targetName}: ${data.data?.message || "Deployment triggered successfully!"}`,
40
+ actionsUrl: data.data?.actionsUrl
41
+ });
42
+ } catch (error) {
43
+ setNotification({
44
+ type: "danger",
45
+ message: `${targetName}: ${error?.response?.data?.error?.message || error.message || "Deployment failed"}`
46
+ });
47
+ } finally {
48
+ setDeployingTargetId(null);
49
+ }
50
+ };
51
+ if (loading) {
52
+ return /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Root, { children: [
53
+ /* @__PURE__ */ jsxRuntime.jsx(
54
+ admin.Layouts.Header,
55
+ {
56
+ title: formatMessage({ id: getTranslation("plugin.name") }),
57
+ subtitle: "Loading..."
58
+ }
59
+ ),
60
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading..." }) }) })
61
+ ] });
62
+ }
63
+ const settings = status?.settings || {};
64
+ const parsed = status?.parsed || {};
65
+ const isConfigured = status?.configured;
66
+ const hasToken = status?.hasToken;
67
+ const targets = settings.targets || [];
68
+ return /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Root, { children: [
69
+ /* @__PURE__ */ jsxRuntime.jsx(
70
+ admin.Layouts.Header,
71
+ {
72
+ title: formatMessage({ id: getTranslation("plugin.name") }),
73
+ subtitle: "Trigger GitHub Actions deployment",
74
+ secondaryAction: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/plugins/${index.PLUGIN_ID}/settings`, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Cog, {}), children: "Settings" }) })
75
+ }
76
+ ),
77
+ /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Content, { children: [
78
+ notification && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
79
+ designSystem.Alert,
80
+ {
81
+ closeLabel: "Close",
82
+ title: notification.message,
83
+ variant: notification.type,
84
+ onClose: () => setNotification(null),
85
+ children: notification.actionsUrl && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", children: [
86
+ "Check the deployment status at",
87
+ " ",
88
+ /* @__PURE__ */ jsxRuntime.jsx(
89
+ designSystem.Typography,
90
+ {
91
+ variant: "pi",
92
+ tag: "a",
93
+ href: notification.actionsUrl,
94
+ target: "_blank",
95
+ rel: "noopener noreferrer",
96
+ textColor: "primary600",
97
+ children: "GitHub Actions"
98
+ }
99
+ )
100
+ ] })
101
+ }
102
+ ) }),
103
+ !hasToken && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { title: "Token Missing", variant: "danger", children: "GitHub Personal Access Token is not configured. Please add it in Settings." }) }),
104
+ !settings.repoUrl && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { title: "Configuration Required", variant: "warning", children: "Please configure your GitHub repository in the Settings page before triggering deployments." }) }),
105
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
106
+ /* @__PURE__ */ jsxRuntime.jsx(
107
+ designSystem.Box,
108
+ {
109
+ background: "neutral0",
110
+ hasRadius: true,
111
+ shadow: "filterShadow",
112
+ paddingTop: 6,
113
+ paddingBottom: 6,
114
+ paddingLeft: 7,
115
+ paddingRight: 7,
116
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
117
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: "Repository" }),
118
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { display: "grid", gridTemplateColumns: "120px 1fr", gap: "12px 16px", alignItems: "center" }, children: [
119
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Repository" }),
120
+ parsed.owner && parsed.repo ? /* @__PURE__ */ jsxRuntime.jsxs(
121
+ designSystem.Typography,
122
+ {
123
+ variant: "pi",
124
+ tag: "a",
125
+ href: settings.repoUrl,
126
+ target: "_blank",
127
+ rel: "noopener noreferrer",
128
+ textColor: "primary600",
129
+ style: { textDecoration: "none" },
130
+ children: [
131
+ parsed.owner,
132
+ "/",
133
+ parsed.repo
134
+ ]
135
+ }
136
+ ) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "Not configured" }),
137
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "GitHub Token" }),
138
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: hasToken ? "success600" : "danger600", children: hasToken ? "Configured" : "Missing" })
139
+ ] })
140
+ ] })
141
+ }
142
+ ),
143
+ /* @__PURE__ */ jsxRuntime.jsx(
144
+ designSystem.Box,
145
+ {
146
+ background: "neutral0",
147
+ hasRadius: true,
148
+ shadow: "filterShadow",
149
+ paddingTop: 6,
150
+ paddingBottom: 6,
151
+ paddingLeft: 7,
152
+ paddingRight: 7,
153
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
154
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: "Deployment Targets" }),
155
+ targets.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { children: [
156
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
157
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Name" }) }),
158
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Workflow" }) }),
159
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Branch" }) }),
160
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Action" }) })
161
+ ] }) }),
162
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: targets.map((target) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
163
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: target.name }) }),
164
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: target.workflow }) }),
165
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: target.branch }) }),
166
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
167
+ designSystem.Button,
168
+ {
169
+ onClick: () => handleDeploy(target.id, target.name),
170
+ loading: deployingTargetId === target.id,
171
+ disabled: !isConfigured || deployingTargetId !== null,
172
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Rocket, {}),
173
+ size: "S",
174
+ children: deployingTargetId === target.id ? "Triggering..." : "Trigger"
175
+ }
176
+ ) })
177
+ ] }, target.id)) })
178
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", justifyContent: "center", gap: 3, padding: 6, children: [
179
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", textAlign: "center", children: "No deployment targets configured" }),
180
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/plugins/${index.PLUGIN_ID}/settings`, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "default", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Cog, {}), children: "Add Targets in Settings" }) })
181
+ ] })
182
+ ] })
183
+ }
184
+ ),
185
+ !isConfigured && targets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
186
+ designSystem.Box,
187
+ {
188
+ background: "neutral0",
189
+ hasRadius: true,
190
+ shadow: "filterShadow",
191
+ paddingTop: 6,
192
+ paddingBottom: 6,
193
+ paddingLeft: 7,
194
+ paddingRight: 7,
195
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", justifyContent: "center", gap: 3, children: [
196
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", textColor: "neutral600", textAlign: "center", children: "Setup Incomplete" }),
197
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", textAlign: "center", children: "Please ensure repository URL and GitHub token are configured in Settings." }),
198
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/plugins/${index.PLUGIN_ID}/settings`, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "default", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Cog, {}), children: "Go to Settings" }) }) })
199
+ ] })
200
+ }
201
+ )
202
+ ] })
203
+ ] })
204
+ ] });
205
+ };
206
+ const TOKEN_PATTERN = /^github_pat_[a-zA-Z0-9_]+$/;
207
+ const REPO_URL_PATTERN = /^https:\/\/github\.com\/[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+\/?$/;
208
+ const WORKFLOW_PATTERN = /^[a-zA-Z0-9_.-]+\.ya?ml$/;
209
+ const validateToken = (value) => {
210
+ if (!value) return null;
211
+ if (!TOKEN_PATTERN.test(value)) {
212
+ return 'Token must start with "github_pat_" followed by alphanumeric characters';
213
+ }
214
+ return null;
215
+ };
216
+ const validateRepoUrl = (value) => {
217
+ if (!value) return "Repository URL is required";
218
+ if (!REPO_URL_PATTERN.test(value)) {
219
+ return "Must be a valid GitHub URL (e.g., https://github.com/owner/repo)";
220
+ }
221
+ return null;
222
+ };
223
+ const validateWorkflow = (value) => {
224
+ if (!value) return "Workflow file is required";
225
+ if (!WORKFLOW_PATTERN.test(value)) {
226
+ return "Workflow file must end with .yml or .yaml";
227
+ }
228
+ return null;
229
+ };
230
+ const SettingsPage = () => {
231
+ const { get, put, post, del } = admin.useFetchClient();
232
+ const [settings, setSettings] = react.useState({
233
+ githubToken: "",
234
+ repoUrl: "",
235
+ targets: []
236
+ });
237
+ const [errors, setErrors] = react.useState({});
238
+ const [hasExistingToken, setHasExistingToken] = react.useState(false);
239
+ const [maskedToken, setMaskedToken] = react.useState(null);
240
+ const [loading, setLoading] = react.useState(true);
241
+ const [saving, setSaving] = react.useState(false);
242
+ const [notification, setNotification] = react.useState(null);
243
+ const [editingTarget, setEditingTarget] = react.useState(null);
244
+ const [targetForm, setTargetForm] = react.useState({ name: "", workflow: "", branch: "" });
245
+ const [targetErrors, setTargetErrors] = react.useState({});
246
+ const [showAddForm, setShowAddForm] = react.useState(false);
247
+ const [deleteDialogOpen, setDeleteDialogOpen] = react.useState(false);
248
+ const [targetToDelete, setTargetToDelete] = react.useState(null);
249
+ react.useEffect(() => {
250
+ fetchSettings();
251
+ }, []);
252
+ const fetchSettings = async () => {
253
+ setLoading(true);
254
+ try {
255
+ const { data } = await get(`/${index.PLUGIN_ID}/settings`);
256
+ const fetchedSettings = data.data || {};
257
+ setSettings({
258
+ githubToken: "",
259
+ repoUrl: fetchedSettings.repoUrl || "",
260
+ targets: fetchedSettings.targets || []
261
+ });
262
+ setHasExistingToken(fetchedSettings.hasToken || false);
263
+ setMaskedToken(fetchedSettings.maskedToken || null);
264
+ } catch (error) {
265
+ console.error("Error fetching settings:", error);
266
+ setNotification({ type: "danger", message: "Failed to load settings" });
267
+ }
268
+ setLoading(false);
269
+ };
270
+ const handleSaveSettings = async () => {
271
+ const newErrors = {};
272
+ const repoError = validateRepoUrl(settings.repoUrl);
273
+ if (repoError) newErrors.repoUrl = repoError;
274
+ const tokenError = validateToken(settings.githubToken);
275
+ if (tokenError && settings.githubToken) newErrors.githubToken = tokenError;
276
+ setErrors(newErrors);
277
+ if (Object.keys(newErrors).length > 0) {
278
+ setNotification({ type: "warning", message: "Please fix the validation errors" });
279
+ return;
280
+ }
281
+ setSaving(true);
282
+ try {
283
+ const dataToSave = {
284
+ repoUrl: settings.repoUrl,
285
+ targets: settings.targets
286
+ };
287
+ if (settings.githubToken) {
288
+ dataToSave.githubToken = settings.githubToken;
289
+ }
290
+ const { data } = await put(`/${index.PLUGIN_ID}/settings`, { data: dataToSave });
291
+ if (settings.githubToken) {
292
+ setHasExistingToken(true);
293
+ setMaskedToken(data.data?.maskedToken);
294
+ }
295
+ setSettings((prev) => ({ ...prev, githubToken: "" }));
296
+ setNotification({ type: "success", message: "Settings saved successfully" });
297
+ } catch (error) {
298
+ console.error("Error saving settings:", error);
299
+ setNotification({ type: "danger", message: "Failed to save settings" });
300
+ }
301
+ setSaving(false);
302
+ };
303
+ const handleChange = (field) => (e) => {
304
+ const value = e.target.value;
305
+ setSettings((prev) => ({ ...prev, [field]: value }));
306
+ if (errors[field]) {
307
+ setErrors((prev) => ({ ...prev, [field]: null }));
308
+ }
309
+ };
310
+ const resetTargetForm = () => {
311
+ setTargetForm({ name: "", workflow: "deploy.yml", branch: "master" });
312
+ setTargetErrors({});
313
+ setEditingTarget(null);
314
+ setShowAddForm(false);
315
+ };
316
+ const validateTargetForm = () => {
317
+ const newErrors = {};
318
+ if (!targetForm.name.trim()) newErrors.name = "Name is required";
319
+ const workflowError = validateWorkflow(targetForm.workflow);
320
+ if (workflowError) newErrors.workflow = workflowError;
321
+ if (!targetForm.branch.trim()) newErrors.branch = "Branch is required";
322
+ setTargetErrors(newErrors);
323
+ return Object.keys(newErrors).length === 0;
324
+ };
325
+ const handleAddTarget = async () => {
326
+ if (!validateTargetForm()) return;
327
+ try {
328
+ const { data } = await post(`/${index.PLUGIN_ID}/targets`, { data: targetForm });
329
+ setSettings((prev) => ({
330
+ ...prev,
331
+ targets: [...prev.targets, data.data]
332
+ }));
333
+ resetTargetForm();
334
+ setNotification({ type: "success", message: "Target added successfully" });
335
+ } catch (error) {
336
+ console.error("Error adding target:", error);
337
+ setNotification({ type: "danger", message: "Failed to add target" });
338
+ }
339
+ };
340
+ const handleEditTarget = (target) => {
341
+ setEditingTarget(target.id);
342
+ setTargetForm({ name: target.name, workflow: target.workflow, branch: target.branch });
343
+ setShowAddForm(false);
344
+ };
345
+ const handleUpdateTarget = async () => {
346
+ if (!validateTargetForm()) return;
347
+ try {
348
+ const { data } = await put(`/${index.PLUGIN_ID}/targets/${editingTarget}`, { data: targetForm });
349
+ setSettings((prev) => ({
350
+ ...prev,
351
+ targets: prev.targets.map((t) => t.id === editingTarget ? data.data : t)
352
+ }));
353
+ resetTargetForm();
354
+ setNotification({ type: "success", message: "Target updated successfully" });
355
+ } catch (error) {
356
+ console.error("Error updating target:", error);
357
+ setNotification({ type: "danger", message: "Failed to update target" });
358
+ }
359
+ };
360
+ const handleDeleteTarget = async () => {
361
+ if (!targetToDelete) return;
362
+ try {
363
+ await del(`/${index.PLUGIN_ID}/targets/${targetToDelete}`);
364
+ setSettings((prev) => ({
365
+ ...prev,
366
+ targets: prev.targets.filter((t) => t.id !== targetToDelete)
367
+ }));
368
+ setDeleteDialogOpen(false);
369
+ setTargetToDelete(null);
370
+ setNotification({ type: "success", message: "Target deleted successfully" });
371
+ } catch (error) {
372
+ console.error("Error deleting target:", error);
373
+ setNotification({ type: "danger", message: "Failed to delete target" });
374
+ }
375
+ };
376
+ const handleTargetFormChange = (field) => (e) => {
377
+ const value = e.target.value;
378
+ setTargetForm((prev) => ({ ...prev, [field]: value }));
379
+ if (targetErrors[field]) {
380
+ setTargetErrors((prev) => ({ ...prev, [field]: null }));
381
+ }
382
+ };
383
+ if (loading) {
384
+ return /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Root, { children: [
385
+ /* @__PURE__ */ jsxRuntime.jsx(
386
+ admin.Layouts.Header,
387
+ {
388
+ title: "Settings",
389
+ subtitle: "Configure GitHub Actions deployment",
390
+ navigationAction: /* @__PURE__ */ jsxRuntime.jsx(admin.BackButton, { fallback: `/plugins/${index.PLUGIN_ID}` })
391
+ }
392
+ ),
393
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading settings..." }) }) })
394
+ ] });
395
+ }
396
+ const isValid = settings.repoUrl && !errors.repoUrl && !errors.githubToken;
397
+ return /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Root, { children: [
398
+ /* @__PURE__ */ jsxRuntime.jsx(
399
+ admin.Layouts.Header,
400
+ {
401
+ title: "Settings",
402
+ subtitle: "Configure GitHub Actions deployment",
403
+ navigationAction: /* @__PURE__ */ jsxRuntime.jsx(admin.BackButton, { fallback: `/plugins/${index.PLUGIN_ID}` }),
404
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(
405
+ designSystem.Button,
406
+ {
407
+ onClick: handleSaveSettings,
408
+ loading: saving,
409
+ disabled: loading || !isValid,
410
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
411
+ size: "L",
412
+ children: "Save Settings"
413
+ }
414
+ )
415
+ }
416
+ ),
417
+ /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Content, { children: [
418
+ notification && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
419
+ designSystem.Alert,
420
+ {
421
+ closeLabel: "Close",
422
+ title: notification.message,
423
+ variant: notification.type,
424
+ onClose: () => setNotification(null)
425
+ }
426
+ ) }),
427
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
428
+ /* @__PURE__ */ jsxRuntime.jsx(
429
+ designSystem.Box,
430
+ {
431
+ background: "neutral0",
432
+ hasRadius: true,
433
+ shadow: "filterShadow",
434
+ paddingTop: 6,
435
+ paddingBottom: 6,
436
+ paddingLeft: 7,
437
+ paddingRight: 7,
438
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
439
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: "Repository" }),
440
+ /* @__PURE__ */ jsxRuntime.jsxs(
441
+ designSystem.Field.Root,
442
+ {
443
+ name: "repoUrl",
444
+ required: true,
445
+ error: errors.repoUrl,
446
+ hint: "Copy the URL from your browser when viewing the repository",
447
+ children: [
448
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Repository URL" }),
449
+ /* @__PURE__ */ jsxRuntime.jsx(
450
+ designSystem.Field.Input,
451
+ {
452
+ placeholder: "https://github.com/{owner}/{repo}",
453
+ value: settings.repoUrl,
454
+ onChange: handleChange("repoUrl")
455
+ }
456
+ ),
457
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
458
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
459
+ ]
460
+ }
461
+ )
462
+ ] })
463
+ }
464
+ ),
465
+ /* @__PURE__ */ jsxRuntime.jsx(
466
+ designSystem.Box,
467
+ {
468
+ background: "neutral0",
469
+ hasRadius: true,
470
+ shadow: "filterShadow",
471
+ paddingTop: 6,
472
+ paddingBottom: 6,
473
+ paddingLeft: 7,
474
+ paddingRight: 7,
475
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
476
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
477
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: "Deployment Targets" }),
478
+ !showAddForm && !editingTarget && /* @__PURE__ */ jsxRuntime.jsx(
479
+ designSystem.Button,
480
+ {
481
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
482
+ onClick: () => {
483
+ setShowAddForm(true);
484
+ setTargetForm({ name: "", workflow: "deploy.yml", branch: "master" });
485
+ },
486
+ size: "S",
487
+ children: "Add Target"
488
+ }
489
+ )
490
+ ] }),
491
+ (showAddForm || editingTarget) && /* @__PURE__ */ jsxRuntime.jsx(
492
+ designSystem.Box,
493
+ {
494
+ background: "neutral100",
495
+ padding: 4,
496
+ hasRadius: true,
497
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 4, children: [
498
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: editingTarget ? "Edit Target" : "Add New Target" }),
499
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, children: [
500
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "targetName", required: true, error: targetErrors.name, children: [
501
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Name" }),
502
+ /* @__PURE__ */ jsxRuntime.jsx(
503
+ designSystem.Field.Input,
504
+ {
505
+ placeholder: "e.g., Production",
506
+ value: targetForm.name,
507
+ onChange: handleTargetFormChange("name")
508
+ }
509
+ ),
510
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
511
+ ] }) }),
512
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "targetWorkflow", required: true, error: targetErrors.workflow, children: [
513
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Workflow File" }),
514
+ /* @__PURE__ */ jsxRuntime.jsx(
515
+ designSystem.Field.Input,
516
+ {
517
+ placeholder: "deploy.yml",
518
+ value: targetForm.workflow,
519
+ onChange: handleTargetFormChange("workflow")
520
+ }
521
+ ),
522
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
523
+ ] }) }),
524
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "targetBranch", required: true, error: targetErrors.branch, children: [
525
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Branch" }),
526
+ /* @__PURE__ */ jsxRuntime.jsx(
527
+ designSystem.Field.Input,
528
+ {
529
+ placeholder: "main",
530
+ value: targetForm.branch,
531
+ onChange: handleTargetFormChange("branch")
532
+ }
533
+ ),
534
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
535
+ ] }) })
536
+ ] }),
537
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, justifyContent: "flex-end", children: [
538
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: resetTargetForm, children: "Cancel" }),
539
+ /* @__PURE__ */ jsxRuntime.jsx(
540
+ designSystem.Button,
541
+ {
542
+ onClick: editingTarget ? handleUpdateTarget : handleAddTarget,
543
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
544
+ children: editingTarget ? "Update" : "Add"
545
+ }
546
+ )
547
+ ] })
548
+ ] })
549
+ }
550
+ ),
551
+ settings.targets.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { children: [
552
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
553
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Name" }) }),
554
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Workflow" }) }),
555
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Branch" }) }),
556
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Actions" }) })
557
+ ] }) }),
558
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: settings.targets.map((target) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
559
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: target.name }) }),
560
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: target.workflow }) }),
561
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: target.branch }) }),
562
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
563
+ /* @__PURE__ */ jsxRuntime.jsx(
564
+ designSystem.IconButton,
565
+ {
566
+ onClick: () => handleEditTarget(target),
567
+ label: "Edit",
568
+ variant: "ghost",
569
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
570
+ }
571
+ ),
572
+ /* @__PURE__ */ jsxRuntime.jsx(
573
+ designSystem.IconButton,
574
+ {
575
+ onClick: () => {
576
+ setTargetToDelete(target.id);
577
+ setDeleteDialogOpen(true);
578
+ },
579
+ label: "Delete",
580
+ variant: "ghost",
581
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
582
+ }
583
+ )
584
+ ] }) })
585
+ ] }, target.id)) })
586
+ ] }) : !showAddForm && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: 'No deployment targets configured. Click "Add Target" to create one.' })
587
+ ] })
588
+ }
589
+ ),
590
+ /* @__PURE__ */ jsxRuntime.jsx(
591
+ designSystem.Box,
592
+ {
593
+ background: "neutral0",
594
+ hasRadius: true,
595
+ shadow: "filterShadow",
596
+ paddingTop: 6,
597
+ paddingBottom: 6,
598
+ paddingLeft: 7,
599
+ paddingRight: 7,
600
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
601
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: "Authentication" }),
602
+ /* @__PURE__ */ jsxRuntime.jsxs(
603
+ designSystem.Field.Root,
604
+ {
605
+ name: "githubToken",
606
+ required: true,
607
+ error: errors.githubToken,
608
+ hint: !hasExistingToken || !maskedToken ? "Create a fine-grained token with Actions (Read and write) permission" : void 0,
609
+ children: [
610
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "GitHub Personal Access Token" }),
611
+ /* @__PURE__ */ jsxRuntime.jsx(
612
+ designSystem.Field.Input,
613
+ {
614
+ type: "password",
615
+ placeholder: hasExistingToken ? "••••••••••••••••" : "github_pat_xxxxxxxxxxxx",
616
+ value: settings.githubToken,
617
+ onChange: handleChange("githubToken")
618
+ }
619
+ ),
620
+ hasExistingToken && maskedToken ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
621
+ "Existing token:",
622
+ " ",
623
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "success600", tag: "span", children: maskedToken }),
624
+ ". Leave empty to keep existing, or enter new to replace."
625
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
626
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
627
+ ]
628
+ }
629
+ ),
630
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingTop: 2, children: [
631
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "How to get a GitHub Token:" }),
632
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", tag: "ol", textColor: "neutral600", children: [
633
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: "Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens" }),
634
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: 'Click "Generate new token"' }),
635
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: "Set token name, expiration, and select the target repository" }),
636
+ /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
637
+ 'Under "Repository permissions", set ',
638
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Actions" }),
639
+ ' to "Read and write"'
640
+ ] }),
641
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: 'Click "Generate token" and paste it above' })
642
+ ] }) })
643
+ ] })
644
+ ] })
645
+ }
646
+ )
647
+ ] }),
648
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
649
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: "Delete Target" }),
650
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: "Are you sure you want to delete this deployment target? This action cannot be undone." }),
651
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
652
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: "Cancel" }) }),
653
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "danger-light", onClick: handleDeleteTarget, children: "Delete" }) })
654
+ ] })
655
+ ] }) })
656
+ ] })
657
+ ] });
658
+ };
659
+ const App = () => {
660
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
661
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
662
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "settings", element: /* @__PURE__ */ jsxRuntime.jsx(SettingsPage, {}) }),
663
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
664
+ ] });
665
+ };
666
+ exports.App = App;