@orange-soft/strapi-deployment-trigger 1.0.0 → 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.
- package/README.md +2 -10
- package/admin/src/pages/HomePage.jsx +109 -61
- package/admin/src/pages/SettingsPage.jsx +270 -97
- package/dist/_chunks/App-CuSCtdH7.js +666 -0
- package/dist/_chunks/App-vakyp6FE.mjs +666 -0
- package/dist/_chunks/{index-C18aSW5z.mjs → index-CZWWYGR3.mjs} +1 -1
- package/dist/_chunks/{index-CqpMwL_C.js → index-DGatQB-9.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +160 -38
- package/dist/server/index.mjs +160 -38
- package/package.json +1 -1
- package/server/src/controllers/controller.js +58 -3
- package/server/src/routes/admin/index.js +25 -0
- package/server/src/services/service.js +113 -43
- package/dist/_chunks/App-3JntxPYv.js +0 -520
- package/dist/_chunks/App-C0Byi5W1.mjs +0 -520
|
@@ -8,9 +8,17 @@ import {
|
|
|
8
8
|
Alert,
|
|
9
9
|
Loader,
|
|
10
10
|
Field,
|
|
11
|
+
Table,
|
|
12
|
+
Thead,
|
|
13
|
+
Tbody,
|
|
14
|
+
Tr,
|
|
15
|
+
Th,
|
|
16
|
+
Td,
|
|
17
|
+
IconButton,
|
|
18
|
+
Dialog,
|
|
11
19
|
Grid,
|
|
12
20
|
} from '@strapi/design-system';
|
|
13
|
-
import { Check } from '@strapi/icons';
|
|
21
|
+
import { Check, Plus, Pencil, Trash } from '@strapi/icons';
|
|
14
22
|
|
|
15
23
|
import { PLUGIN_ID } from '../pluginId';
|
|
16
24
|
|
|
@@ -20,7 +28,7 @@ const REPO_URL_PATTERN = /^https:\/\/github\.com\/[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-
|
|
|
20
28
|
const WORKFLOW_PATTERN = /^[a-zA-Z0-9_.-]+\.ya?ml$/;
|
|
21
29
|
|
|
22
30
|
const validateToken = (value) => {
|
|
23
|
-
if (!value) return null;
|
|
31
|
+
if (!value) return null;
|
|
24
32
|
if (!TOKEN_PATTERN.test(value)) {
|
|
25
33
|
return 'Token must start with "github_pat_" followed by alphanumeric characters';
|
|
26
34
|
}
|
|
@@ -36,7 +44,7 @@ const validateRepoUrl = (value) => {
|
|
|
36
44
|
};
|
|
37
45
|
|
|
38
46
|
const validateWorkflow = (value) => {
|
|
39
|
-
if (!value) return
|
|
47
|
+
if (!value) return 'Workflow file is required';
|
|
40
48
|
if (!WORKFLOW_PATTERN.test(value)) {
|
|
41
49
|
return 'Workflow file must end with .yml or .yaml';
|
|
42
50
|
}
|
|
@@ -44,12 +52,11 @@ const validateWorkflow = (value) => {
|
|
|
44
52
|
};
|
|
45
53
|
|
|
46
54
|
const SettingsPage = () => {
|
|
47
|
-
const { get, put } = useFetchClient();
|
|
55
|
+
const { get, put, post, del } = useFetchClient();
|
|
48
56
|
const [settings, setSettings] = useState({
|
|
49
57
|
githubToken: '',
|
|
50
58
|
repoUrl: '',
|
|
51
|
-
|
|
52
|
-
branch: '',
|
|
59
|
+
targets: [],
|
|
53
60
|
});
|
|
54
61
|
const [errors, setErrors] = useState({});
|
|
55
62
|
const [hasExistingToken, setHasExistingToken] = useState(false);
|
|
@@ -58,6 +65,14 @@ const SettingsPage = () => {
|
|
|
58
65
|
const [saving, setSaving] = useState(false);
|
|
59
66
|
const [notification, setNotification] = useState(null);
|
|
60
67
|
|
|
68
|
+
// Target form state
|
|
69
|
+
const [editingTarget, setEditingTarget] = useState(null);
|
|
70
|
+
const [targetForm, setTargetForm] = useState({ name: '', workflow: '', branch: '' });
|
|
71
|
+
const [targetErrors, setTargetErrors] = useState({});
|
|
72
|
+
const [showAddForm, setShowAddForm] = useState(false);
|
|
73
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
74
|
+
const [targetToDelete, setTargetToDelete] = useState(null);
|
|
75
|
+
|
|
61
76
|
useEffect(() => {
|
|
62
77
|
fetchSettings();
|
|
63
78
|
}, []);
|
|
@@ -70,70 +85,52 @@ const SettingsPage = () => {
|
|
|
70
85
|
setSettings({
|
|
71
86
|
githubToken: '',
|
|
72
87
|
repoUrl: fetchedSettings.repoUrl || '',
|
|
73
|
-
|
|
74
|
-
branch: fetchedSettings.branch || '',
|
|
88
|
+
targets: fetchedSettings.targets || [],
|
|
75
89
|
});
|
|
76
90
|
setHasExistingToken(fetchedSettings.hasToken || false);
|
|
77
91
|
setMaskedToken(fetchedSettings.maskedToken || null);
|
|
78
92
|
} catch (error) {
|
|
79
93
|
console.error('Error fetching settings:', error);
|
|
80
|
-
setNotification({
|
|
81
|
-
type: 'danger',
|
|
82
|
-
message: 'Failed to load settings',
|
|
83
|
-
});
|
|
94
|
+
setNotification({ type: 'danger', message: 'Failed to load settings' });
|
|
84
95
|
}
|
|
85
96
|
setLoading(false);
|
|
86
97
|
};
|
|
87
98
|
|
|
88
|
-
const
|
|
99
|
+
const handleSaveSettings = async () => {
|
|
89
100
|
const newErrors = {};
|
|
90
|
-
|
|
91
|
-
const tokenError = validateToken(settings.githubToken);
|
|
92
|
-
if (tokenError && settings.githubToken) newErrors.githubToken = tokenError;
|
|
93
|
-
|
|
94
101
|
const repoError = validateRepoUrl(settings.repoUrl);
|
|
95
102
|
if (repoError) newErrors.repoUrl = repoError;
|
|
96
103
|
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
104
|
+
const tokenError = validateToken(settings.githubToken);
|
|
105
|
+
if (tokenError && settings.githubToken) newErrors.githubToken = tokenError;
|
|
99
106
|
|
|
100
107
|
setErrors(newErrors);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const handleSave = async () => {
|
|
105
|
-
if (!validateAll()) {
|
|
106
|
-
setNotification({
|
|
107
|
-
type: 'warning',
|
|
108
|
-
message: 'Please fix the validation errors before saving',
|
|
109
|
-
});
|
|
108
|
+
if (Object.keys(newErrors).length > 0) {
|
|
109
|
+
setNotification({ type: 'warning', message: 'Please fix the validation errors' });
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
setSaving(true);
|
|
114
114
|
try {
|
|
115
|
-
const dataToSave = {
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const dataToSave = {
|
|
116
|
+
repoUrl: settings.repoUrl,
|
|
117
|
+
targets: settings.targets,
|
|
118
|
+
};
|
|
119
|
+
if (settings.githubToken) {
|
|
120
|
+
dataToSave.githubToken = settings.githubToken;
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
await put(`/${PLUGIN_ID}/settings`, { data: dataToSave });
|
|
123
|
+
const { data } = await put(`/${PLUGIN_ID}/settings`, { data: dataToSave });
|
|
121
124
|
|
|
122
125
|
if (settings.githubToken) {
|
|
123
126
|
setHasExistingToken(true);
|
|
127
|
+
setMaskedToken(data.data?.maskedToken);
|
|
124
128
|
}
|
|
125
129
|
setSettings(prev => ({ ...prev, githubToken: '' }));
|
|
126
|
-
|
|
127
|
-
setNotification({
|
|
128
|
-
type: 'success',
|
|
129
|
-
message: 'Settings saved successfully',
|
|
130
|
-
});
|
|
130
|
+
setNotification({ type: 'success', message: 'Settings saved successfully' });
|
|
131
131
|
} catch (error) {
|
|
132
132
|
console.error('Error saving settings:', error);
|
|
133
|
-
setNotification({
|
|
134
|
-
type: 'danger',
|
|
135
|
-
message: 'Failed to save settings',
|
|
136
|
-
});
|
|
133
|
+
setNotification({ type: 'danger', message: 'Failed to save settings' });
|
|
137
134
|
}
|
|
138
135
|
setSaving(false);
|
|
139
136
|
};
|
|
@@ -141,17 +138,92 @@ const SettingsPage = () => {
|
|
|
141
138
|
const handleChange = (field) => (e) => {
|
|
142
139
|
const value = e.target.value;
|
|
143
140
|
setSettings(prev => ({ ...prev, [field]: value }));
|
|
144
|
-
|
|
145
|
-
// Clear error when user starts typing
|
|
146
141
|
if (errors[field]) {
|
|
147
142
|
setErrors(prev => ({ ...prev, [field]: null }));
|
|
148
143
|
}
|
|
149
144
|
};
|
|
150
145
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
// Target management
|
|
147
|
+
const resetTargetForm = () => {
|
|
148
|
+
setTargetForm({ name: '', workflow: 'deploy.yml', branch: 'master' });
|
|
149
|
+
setTargetErrors({});
|
|
150
|
+
setEditingTarget(null);
|
|
151
|
+
setShowAddForm(false);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const validateTargetForm = () => {
|
|
155
|
+
const newErrors = {};
|
|
156
|
+
if (!targetForm.name.trim()) newErrors.name = 'Name is required';
|
|
157
|
+
const workflowError = validateWorkflow(targetForm.workflow);
|
|
158
|
+
if (workflowError) newErrors.workflow = workflowError;
|
|
159
|
+
if (!targetForm.branch.trim()) newErrors.branch = 'Branch is required';
|
|
160
|
+
setTargetErrors(newErrors);
|
|
161
|
+
return Object.keys(newErrors).length === 0;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const handleAddTarget = async () => {
|
|
165
|
+
if (!validateTargetForm()) return;
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const { data } = await post(`/${PLUGIN_ID}/targets`, { data: targetForm });
|
|
169
|
+
setSettings(prev => ({
|
|
170
|
+
...prev,
|
|
171
|
+
targets: [...prev.targets, data.data],
|
|
172
|
+
}));
|
|
173
|
+
resetTargetForm();
|
|
174
|
+
setNotification({ type: 'success', message: 'Target added successfully' });
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('Error adding target:', error);
|
|
177
|
+
setNotification({ type: 'danger', message: 'Failed to add target' });
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleEditTarget = (target) => {
|
|
182
|
+
setEditingTarget(target.id);
|
|
183
|
+
setTargetForm({ name: target.name, workflow: target.workflow, branch: target.branch });
|
|
184
|
+
setShowAddForm(false);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const handleUpdateTarget = async () => {
|
|
188
|
+
if (!validateTargetForm()) return;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const { data } = await put(`/${PLUGIN_ID}/targets/${editingTarget}`, { data: targetForm });
|
|
192
|
+
setSettings(prev => ({
|
|
193
|
+
...prev,
|
|
194
|
+
targets: prev.targets.map(t => t.id === editingTarget ? data.data : t),
|
|
195
|
+
}));
|
|
196
|
+
resetTargetForm();
|
|
197
|
+
setNotification({ type: 'success', message: 'Target updated successfully' });
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error('Error updating target:', error);
|
|
200
|
+
setNotification({ type: 'danger', message: 'Failed to update target' });
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const handleDeleteTarget = async () => {
|
|
205
|
+
if (!targetToDelete) return;
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await del(`/${PLUGIN_ID}/targets/${targetToDelete}`);
|
|
209
|
+
setSettings(prev => ({
|
|
210
|
+
...prev,
|
|
211
|
+
targets: prev.targets.filter(t => t.id !== targetToDelete),
|
|
212
|
+
}));
|
|
213
|
+
setDeleteDialogOpen(false);
|
|
214
|
+
setTargetToDelete(null);
|
|
215
|
+
setNotification({ type: 'success', message: 'Target deleted successfully' });
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error('Error deleting target:', error);
|
|
218
|
+
setNotification({ type: 'danger', message: 'Failed to delete target' });
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const handleTargetFormChange = (field) => (e) => {
|
|
223
|
+
const value = e.target.value;
|
|
224
|
+
setTargetForm(prev => ({ ...prev, [field]: value }));
|
|
225
|
+
if (targetErrors[field]) {
|
|
226
|
+
setTargetErrors(prev => ({ ...prev, [field]: null }));
|
|
155
227
|
}
|
|
156
228
|
};
|
|
157
229
|
|
|
@@ -172,7 +244,7 @@ const SettingsPage = () => {
|
|
|
172
244
|
);
|
|
173
245
|
}
|
|
174
246
|
|
|
175
|
-
const isValid = settings.repoUrl && !errors.repoUrl && !errors.githubToken
|
|
247
|
+
const isValid = settings.repoUrl && !errors.repoUrl && !errors.githubToken;
|
|
176
248
|
|
|
177
249
|
return (
|
|
178
250
|
<Layouts.Root>
|
|
@@ -182,7 +254,7 @@ const SettingsPage = () => {
|
|
|
182
254
|
navigationAction={<BackButton fallback={`/plugins/${PLUGIN_ID}`} />}
|
|
183
255
|
primaryAction={
|
|
184
256
|
<Button
|
|
185
|
-
onClick={
|
|
257
|
+
onClick={handleSaveSettings}
|
|
186
258
|
loading={saving}
|
|
187
259
|
disabled={loading || !isValid}
|
|
188
260
|
startIcon={<Check />}
|
|
@@ -231,7 +303,6 @@ const SettingsPage = () => {
|
|
|
231
303
|
placeholder="https://github.com/{owner}/{repo}"
|
|
232
304
|
value={settings.repoUrl}
|
|
233
305
|
onChange={handleChange('repoUrl')}
|
|
234
|
-
onBlur={handleBlur('repoUrl', validateRepoUrl)}
|
|
235
306
|
/>
|
|
236
307
|
<Field.Hint />
|
|
237
308
|
<Field.Error />
|
|
@@ -239,7 +310,7 @@ const SettingsPage = () => {
|
|
|
239
310
|
</Flex>
|
|
240
311
|
</Box>
|
|
241
312
|
|
|
242
|
-
{/*
|
|
313
|
+
{/* Deployment Targets Section */}
|
|
243
314
|
<Box
|
|
244
315
|
background="neutral0"
|
|
245
316
|
hasRadius
|
|
@@ -250,46 +321,134 @@ const SettingsPage = () => {
|
|
|
250
321
|
paddingRight={7}
|
|
251
322
|
>
|
|
252
323
|
<Flex direction="column" alignItems="stretch" gap={4}>
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
<Field.Label>Workflow File</Field.Label>
|
|
266
|
-
<Field.Input
|
|
267
|
-
placeholder="deploy.yml"
|
|
268
|
-
value={settings.workflow}
|
|
269
|
-
onChange={handleChange('workflow')}
|
|
270
|
-
onBlur={handleBlur('workflow', validateWorkflow)}
|
|
271
|
-
/>
|
|
272
|
-
<Field.Hint />
|
|
273
|
-
<Field.Error />
|
|
274
|
-
</Field.Root>
|
|
275
|
-
</Grid.Item>
|
|
276
|
-
|
|
277
|
-
<Grid.Item col={6} s={12}>
|
|
278
|
-
<Field.Root
|
|
279
|
-
name="branch"
|
|
280
|
-
required
|
|
281
|
-
hint="Branch to trigger the workflow on"
|
|
324
|
+
<Flex justifyContent="space-between" alignItems="center">
|
|
325
|
+
<Typography variant="delta" tag="h2">
|
|
326
|
+
Deployment Targets
|
|
327
|
+
</Typography>
|
|
328
|
+
{!showAddForm && !editingTarget && (
|
|
329
|
+
<Button
|
|
330
|
+
startIcon={<Plus />}
|
|
331
|
+
onClick={() => {
|
|
332
|
+
setShowAddForm(true);
|
|
333
|
+
setTargetForm({ name: '', workflow: 'deploy.yml', branch: 'master' });
|
|
334
|
+
}}
|
|
335
|
+
size="S"
|
|
282
336
|
>
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
337
|
+
Add Target
|
|
338
|
+
</Button>
|
|
339
|
+
)}
|
|
340
|
+
</Flex>
|
|
341
|
+
|
|
342
|
+
{/* Add/Edit Form */}
|
|
343
|
+
{(showAddForm || editingTarget) && (
|
|
344
|
+
<Box
|
|
345
|
+
background="neutral100"
|
|
346
|
+
padding={4}
|
|
347
|
+
hasRadius
|
|
348
|
+
>
|
|
349
|
+
<Flex direction="column" gap={4}>
|
|
350
|
+
<Typography variant="omega" fontWeight="bold">
|
|
351
|
+
{editingTarget ? 'Edit Target' : 'Add New Target'}
|
|
352
|
+
</Typography>
|
|
353
|
+
<Grid.Root gap={4}>
|
|
354
|
+
<Grid.Item col={4} s={12}>
|
|
355
|
+
<Field.Root name="targetName" required error={targetErrors.name}>
|
|
356
|
+
<Field.Label>Name</Field.Label>
|
|
357
|
+
<Field.Input
|
|
358
|
+
placeholder="e.g., Production"
|
|
359
|
+
value={targetForm.name}
|
|
360
|
+
onChange={handleTargetFormChange('name')}
|
|
361
|
+
/>
|
|
362
|
+
<Field.Error />
|
|
363
|
+
</Field.Root>
|
|
364
|
+
</Grid.Item>
|
|
365
|
+
<Grid.Item col={4} s={12}>
|
|
366
|
+
<Field.Root name="targetWorkflow" required error={targetErrors.workflow}>
|
|
367
|
+
<Field.Label>Workflow File</Field.Label>
|
|
368
|
+
<Field.Input
|
|
369
|
+
placeholder="deploy.yml"
|
|
370
|
+
value={targetForm.workflow}
|
|
371
|
+
onChange={handleTargetFormChange('workflow')}
|
|
372
|
+
/>
|
|
373
|
+
<Field.Error />
|
|
374
|
+
</Field.Root>
|
|
375
|
+
</Grid.Item>
|
|
376
|
+
<Grid.Item col={4} s={12}>
|
|
377
|
+
<Field.Root name="targetBranch" required error={targetErrors.branch}>
|
|
378
|
+
<Field.Label>Branch</Field.Label>
|
|
379
|
+
<Field.Input
|
|
380
|
+
placeholder="main"
|
|
381
|
+
value={targetForm.branch}
|
|
382
|
+
onChange={handleTargetFormChange('branch')}
|
|
383
|
+
/>
|
|
384
|
+
<Field.Error />
|
|
385
|
+
</Field.Root>
|
|
386
|
+
</Grid.Item>
|
|
387
|
+
</Grid.Root>
|
|
388
|
+
<Flex gap={2} justifyContent="flex-end">
|
|
389
|
+
<Button variant="tertiary" onClick={resetTargetForm}>
|
|
390
|
+
Cancel
|
|
391
|
+
</Button>
|
|
392
|
+
<Button
|
|
393
|
+
onClick={editingTarget ? handleUpdateTarget : handleAddTarget}
|
|
394
|
+
startIcon={<Check />}
|
|
395
|
+
>
|
|
396
|
+
{editingTarget ? 'Update' : 'Add'}
|
|
397
|
+
</Button>
|
|
398
|
+
</Flex>
|
|
399
|
+
</Flex>
|
|
400
|
+
</Box>
|
|
401
|
+
)}
|
|
402
|
+
|
|
403
|
+
{/* Targets Table */}
|
|
404
|
+
{settings.targets.length > 0 ? (
|
|
405
|
+
<Table>
|
|
406
|
+
<Thead>
|
|
407
|
+
<Tr>
|
|
408
|
+
<Th><Typography variant="sigma">Name</Typography></Th>
|
|
409
|
+
<Th><Typography variant="sigma">Workflow</Typography></Th>
|
|
410
|
+
<Th><Typography variant="sigma">Branch</Typography></Th>
|
|
411
|
+
<Th><Typography variant="sigma">Actions</Typography></Th>
|
|
412
|
+
</Tr>
|
|
413
|
+
</Thead>
|
|
414
|
+
<Tbody>
|
|
415
|
+
{settings.targets.map((target) => (
|
|
416
|
+
<Tr key={target.id}>
|
|
417
|
+
<Td><Typography variant="omega">{target.name}</Typography></Td>
|
|
418
|
+
<Td><Typography variant="omega">{target.workflow}</Typography></Td>
|
|
419
|
+
<Td><Typography variant="omega">{target.branch}</Typography></Td>
|
|
420
|
+
<Td>
|
|
421
|
+
<Flex gap={1}>
|
|
422
|
+
<IconButton
|
|
423
|
+
onClick={() => handleEditTarget(target)}
|
|
424
|
+
label="Edit"
|
|
425
|
+
variant="ghost"
|
|
426
|
+
>
|
|
427
|
+
<Pencil />
|
|
428
|
+
</IconButton>
|
|
429
|
+
<IconButton
|
|
430
|
+
onClick={() => {
|
|
431
|
+
setTargetToDelete(target.id);
|
|
432
|
+
setDeleteDialogOpen(true);
|
|
433
|
+
}}
|
|
434
|
+
label="Delete"
|
|
435
|
+
variant="ghost"
|
|
436
|
+
>
|
|
437
|
+
<Trash />
|
|
438
|
+
</IconButton>
|
|
439
|
+
</Flex>
|
|
440
|
+
</Td>
|
|
441
|
+
</Tr>
|
|
442
|
+
))}
|
|
443
|
+
</Tbody>
|
|
444
|
+
</Table>
|
|
445
|
+
) : (
|
|
446
|
+
!showAddForm && (
|
|
447
|
+
<Typography variant="pi" textColor="neutral600">
|
|
448
|
+
No deployment targets configured. Click "Add Target" to create one.
|
|
449
|
+
</Typography>
|
|
450
|
+
)
|
|
451
|
+
)}
|
|
293
452
|
</Flex>
|
|
294
453
|
</Box>
|
|
295
454
|
|
|
@@ -324,17 +483,11 @@ const SettingsPage = () => {
|
|
|
324
483
|
placeholder={hasExistingToken ? "••••••••••••••••" : "github_pat_xxxxxxxxxxxx"}
|
|
325
484
|
value={settings.githubToken}
|
|
326
485
|
onChange={handleChange('githubToken')}
|
|
327
|
-
onBlur={handleBlur('githubToken', validateToken)}
|
|
328
486
|
/>
|
|
329
487
|
{hasExistingToken && maskedToken ? (
|
|
330
488
|
<Typography variant="pi" textColor="neutral600">
|
|
331
489
|
Existing token:{' '}
|
|
332
|
-
<Typography
|
|
333
|
-
variant="pi"
|
|
334
|
-
fontWeight="bold"
|
|
335
|
-
textColor="success600"
|
|
336
|
-
tag="span"
|
|
337
|
-
>
|
|
490
|
+
<Typography variant="pi" fontWeight="bold" textColor="success600" tag="span">
|
|
338
491
|
{maskedToken}
|
|
339
492
|
</Typography>
|
|
340
493
|
. Leave empty to keep existing, or enter new to replace.
|
|
@@ -362,6 +515,26 @@ const SettingsPage = () => {
|
|
|
362
515
|
</Flex>
|
|
363
516
|
</Box>
|
|
364
517
|
</Flex>
|
|
518
|
+
|
|
519
|
+
{/* Delete Confirmation Dialog */}
|
|
520
|
+
<Dialog.Root open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
521
|
+
<Dialog.Content>
|
|
522
|
+
<Dialog.Header>Delete Target</Dialog.Header>
|
|
523
|
+
<Dialog.Body>
|
|
524
|
+
Are you sure you want to delete this deployment target? This action cannot be undone.
|
|
525
|
+
</Dialog.Body>
|
|
526
|
+
<Dialog.Footer>
|
|
527
|
+
<Dialog.Cancel>
|
|
528
|
+
<Button variant="tertiary">Cancel</Button>
|
|
529
|
+
</Dialog.Cancel>
|
|
530
|
+
<Dialog.Action>
|
|
531
|
+
<Button variant="danger-light" onClick={handleDeleteTarget}>
|
|
532
|
+
Delete
|
|
533
|
+
</Button>
|
|
534
|
+
</Dialog.Action>
|
|
535
|
+
</Dialog.Footer>
|
|
536
|
+
</Dialog.Content>
|
|
537
|
+
</Dialog.Root>
|
|
365
538
|
</Layouts.Content>
|
|
366
539
|
</Layouts.Root>
|
|
367
540
|
);
|