@orange-soft/strapi-deployment-trigger 1.0.1 → 1.1.2

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