@orange-soft/strapi-deployment-trigger 1.1.0 → 1.2.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 +34 -13
- package/admin/src/pages/HomePage.jsx +67 -33
- package/admin/src/pages/SettingsPage.jsx +143 -68
- package/dist/_chunks/{App-vakyp6FE.mjs → App-k07qAAvE.mjs} +165 -90
- package/dist/_chunks/{App-CuSCtdH7.js → App-vIrt97zQ.js} +162 -87
- package/dist/_chunks/{index-CZWWYGR3.mjs → index-BwZtnn__.mjs} +1 -1
- package/dist/_chunks/{index-DGatQB-9.js → index-w-vQ80Px.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +95 -27
- package/dist/server/index.mjs +95 -27
- package/package.json +1 -1
- package/server/src/controllers/controller.js +73 -23
- package/server/src/services/service.js +47 -8
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Strapi Plugin: Deployment Trigger
|
|
2
2
|
|
|
3
|
-
A Strapi v5 plugin that allows you to trigger
|
|
3
|
+
A Strapi v5 plugin that allows you to trigger deployments directly from the Strapi admin panel. Supports both **GitHub Actions** and **Vercel** deployments.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- Trigger GitHub Actions `workflow_dispatch` events from the admin UI
|
|
8
|
-
-
|
|
9
|
-
-
|
|
7
|
+
- Trigger **GitHub Actions** `workflow_dispatch` events from the admin UI
|
|
8
|
+
- Trigger **Vercel** deployments via deploy hooks
|
|
9
|
+
- Configure multiple deployment targets (e.g., Production, Staging)
|
|
10
|
+
- Support for different trigger types per target
|
|
11
|
+
- Secure token storage in database
|
|
10
12
|
- Token masking for security
|
|
11
13
|
- Direct link to GitHub Actions to monitor deployment progress
|
|
12
14
|
|
|
@@ -41,12 +43,14 @@ export default () => ({
|
|
|
41
43
|
### 2. Configure via Admin UI
|
|
42
44
|
|
|
43
45
|
1. Go to **Plugins > Deployment Trigger > Settings** in your Strapi admin
|
|
44
|
-
2.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
2. **For GitHub targets:**
|
|
47
|
+
- Enter your GitHub repository URL (e.g., `https://github.com/owner/repo`)
|
|
48
|
+
- Enter your GitHub Personal Access Token
|
|
49
|
+
- Add a target with type "GitHub", workflow filename, and branch
|
|
50
|
+
3. **For Vercel targets:**
|
|
51
|
+
- Add a target with type "Vercel" and paste your deploy hook URL
|
|
48
52
|
|
|
49
|
-
All settings including
|
|
53
|
+
All settings including tokens are stored securely in the Strapi database.
|
|
50
54
|
|
|
51
55
|
## GitHub Token Setup
|
|
52
56
|
|
|
@@ -77,12 +81,29 @@ jobs:
|
|
|
77
81
|
# ... your deployment steps
|
|
78
82
|
```
|
|
79
83
|
|
|
84
|
+
## Vercel Deploy Hook Setup
|
|
85
|
+
|
|
86
|
+
To trigger Vercel deployments, you need to create a Deploy Hook:
|
|
87
|
+
|
|
88
|
+
1. Go to your Vercel project dashboard
|
|
89
|
+
2. Navigate to **Settings** > **Git**
|
|
90
|
+
3. Scroll down to **Deploy Hooks**
|
|
91
|
+
4. Click **Create Hook**
|
|
92
|
+
5. Enter a name (e.g., "Strapi Trigger") and select the branch
|
|
93
|
+
6. Click **Create Hook**
|
|
94
|
+
7. Copy the generated webhook URL (starts with `https://api.vercel.com/v1/integrations/deploy/...`)
|
|
95
|
+
8. Paste this URL when adding a Vercel target in the plugin settings
|
|
96
|
+
|
|
80
97
|
## Usage
|
|
81
98
|
|
|
82
|
-
1. Navigate to **Plugins > Deployment Trigger** in your Strapi admin
|
|
83
|
-
2.
|
|
84
|
-
3.
|
|
85
|
-
|
|
99
|
+
1. Navigate to **Plugins > Deployment Trigger > Settings** in your Strapi admin
|
|
100
|
+
2. For GitHub targets: Configure repository URL and GitHub token
|
|
101
|
+
3. Add deployment targets:
|
|
102
|
+
- **GitHub**: Select type "GitHub", enter name, workflow file, and branch
|
|
103
|
+
- **Vercel**: Select type "Vercel", enter name and webhook URL
|
|
104
|
+
4. Go to the main **Deployment Trigger** page
|
|
105
|
+
5. Click **Trigger** on any configured target
|
|
106
|
+
6. For GitHub targets, click the provided link to monitor progress in GitHub Actions
|
|
86
107
|
|
|
87
108
|
## API
|
|
88
109
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {useState, useEffect} from 'react';
|
|
2
2
|
import {useIntl} from 'react-intl';
|
|
3
3
|
import {Layouts, useFetchClient} from '@strapi/strapi/admin';
|
|
4
|
-
import {Link} from 'react-router-dom';
|
|
4
|
+
import {Link, useLocation} from 'react-router-dom';
|
|
5
5
|
import {
|
|
6
6
|
Box,
|
|
7
7
|
Button,
|
|
@@ -24,6 +24,7 @@ import {getTranslation} from '../utils/getTranslation';
|
|
|
24
24
|
const HomePage = () => {
|
|
25
25
|
const {formatMessage} = useIntl();
|
|
26
26
|
const {get, post} = useFetchClient();
|
|
27
|
+
const location = useLocation();
|
|
27
28
|
const [status, setStatus] = useState(null);
|
|
28
29
|
const [loading, setLoading] = useState(true);
|
|
29
30
|
const [deployingTargetId, setDeployingTargetId] = useState(null);
|
|
@@ -31,6 +32,12 @@ const HomePage = () => {
|
|
|
31
32
|
|
|
32
33
|
useEffect(() => {
|
|
33
34
|
fetchStatus();
|
|
35
|
+
// Check for notification from navigation state (e.g., after saving settings)
|
|
36
|
+
if (location.state?.notification) {
|
|
37
|
+
setNotification(location.state.notification);
|
|
38
|
+
// Clear the state so notification doesn't reappear on refresh
|
|
39
|
+
window.history.replaceState({}, document.title);
|
|
40
|
+
}
|
|
34
41
|
}, []);
|
|
35
42
|
|
|
36
43
|
const fetchStatus = async () => {
|
|
@@ -86,6 +93,20 @@ const HomePage = () => {
|
|
|
86
93
|
const hasToken = status?.hasToken;
|
|
87
94
|
const targets = settings.targets || [];
|
|
88
95
|
|
|
96
|
+
// Check if there are any GitHub targets that need configuration
|
|
97
|
+
const hasGitHubTargets = targets.some(t => (t.type || 'github') === 'github');
|
|
98
|
+
const hasVercelTargets = targets.some(t => t.type === 'vercel');
|
|
99
|
+
|
|
100
|
+
// Determine if trigger buttons should be enabled
|
|
101
|
+
// GitHub targets need repoUrl + token, Vercel targets are self-contained
|
|
102
|
+
const canTrigger = (target) => {
|
|
103
|
+
const targetType = target.type || 'github';
|
|
104
|
+
if (targetType === 'github') {
|
|
105
|
+
return hasToken && parsed.owner && parsed.repo;
|
|
106
|
+
}
|
|
107
|
+
return !!target.webhookUrl;
|
|
108
|
+
};
|
|
109
|
+
|
|
89
110
|
return (
|
|
90
111
|
<Layouts.Root>
|
|
91
112
|
<Layouts.Header
|
|
@@ -127,7 +148,7 @@ const HomePage = () => {
|
|
|
127
148
|
</Box>
|
|
128
149
|
)}
|
|
129
150
|
|
|
130
|
-
{!hasToken && (
|
|
151
|
+
{hasGitHubTargets && !hasToken && (
|
|
131
152
|
<Box paddingBottom={4}>
|
|
132
153
|
<Alert title="Token Missing" variant="danger">
|
|
133
154
|
GitHub Personal Access Token is not configured. Please add it in Settings.
|
|
@@ -135,7 +156,7 @@ const HomePage = () => {
|
|
|
135
156
|
</Box>
|
|
136
157
|
)}
|
|
137
158
|
|
|
138
|
-
{!settings.repoUrl && (
|
|
159
|
+
{hasGitHubTargets && !settings.repoUrl && (
|
|
139
160
|
<Box paddingBottom={4}>
|
|
140
161
|
<Alert title="Configuration Required" variant="warning">
|
|
141
162
|
Please configure your GitHub repository in the Settings page before triggering deployments.
|
|
@@ -204,37 +225,50 @@ const HomePage = () => {
|
|
|
204
225
|
<Table>
|
|
205
226
|
<Thead>
|
|
206
227
|
<Tr>
|
|
228
|
+
<Th><Typography variant="sigma">Type</Typography></Th>
|
|
207
229
|
<Th><Typography variant="sigma">Name</Typography></Th>
|
|
208
|
-
<Th><Typography variant="sigma">
|
|
209
|
-
<Th><Typography variant="sigma">Branch</Typography></Th>
|
|
230
|
+
<Th><Typography variant="sigma">Details</Typography></Th>
|
|
210
231
|
<Th><Typography variant="sigma">Action</Typography></Th>
|
|
211
232
|
</Tr>
|
|
212
233
|
</Thead>
|
|
213
234
|
<Tbody>
|
|
214
|
-
{targets.map((target) =>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
{targets.map((target) => {
|
|
236
|
+
const targetType = target.type || 'github';
|
|
237
|
+
return (
|
|
238
|
+
<Tr key={target.id}>
|
|
239
|
+
<Td>
|
|
240
|
+
<Typography variant="omega" fontWeight="bold" textColor={targetType === 'github' ? 'neutral800' : 'secondary600'}>
|
|
241
|
+
{targetType === 'github' ? 'GitHub' : 'Vercel'}
|
|
242
|
+
</Typography>
|
|
243
|
+
</Td>
|
|
244
|
+
<Td>
|
|
245
|
+
<Typography variant="omega" fontWeight="bold">{target.name}</Typography>
|
|
246
|
+
</Td>
|
|
247
|
+
<Td>
|
|
248
|
+
{targetType === 'github' ? (
|
|
249
|
+
<Typography variant="omega" textColor="neutral600">
|
|
250
|
+
{target.workflow} / {target.branch}
|
|
251
|
+
</Typography>
|
|
252
|
+
) : (
|
|
253
|
+
<Typography variant="omega" textColor="neutral600">
|
|
254
|
+
Webhook
|
|
255
|
+
</Typography>
|
|
256
|
+
)}
|
|
257
|
+
</Td>
|
|
258
|
+
<Td>
|
|
259
|
+
<Button
|
|
260
|
+
onClick={() => handleDeploy(target.id, target.name)}
|
|
261
|
+
loading={deployingTargetId === target.id}
|
|
262
|
+
disabled={!canTrigger(target) || deployingTargetId !== null}
|
|
263
|
+
startIcon={<Rocket />}
|
|
264
|
+
size="S"
|
|
265
|
+
>
|
|
266
|
+
{deployingTargetId === target.id ? 'Triggering...' : 'Trigger'}
|
|
267
|
+
</Button>
|
|
268
|
+
</Td>
|
|
269
|
+
</Tr>
|
|
270
|
+
);
|
|
271
|
+
})}
|
|
238
272
|
</Tbody>
|
|
239
273
|
</Table>
|
|
240
274
|
) : (
|
|
@@ -252,8 +286,8 @@ const HomePage = () => {
|
|
|
252
286
|
</Flex>
|
|
253
287
|
</Box>
|
|
254
288
|
|
|
255
|
-
{/* Instructions Card - Show only when
|
|
256
|
-
{
|
|
289
|
+
{/* Instructions Card - Show only when GitHub targets need configuration */}
|
|
290
|
+
{hasGitHubTargets && (!hasToken || !parsed.owner || !parsed.repo) && (
|
|
257
291
|
<Box
|
|
258
292
|
background="neutral0"
|
|
259
293
|
hasRadius
|
|
@@ -265,10 +299,10 @@ const HomePage = () => {
|
|
|
265
299
|
>
|
|
266
300
|
<Flex direction="column" alignItems="center" justifyContent="center" gap={3}>
|
|
267
301
|
<Typography variant="beta" textColor="neutral600" textAlign="center">
|
|
268
|
-
Setup Incomplete
|
|
302
|
+
GitHub Setup Incomplete
|
|
269
303
|
</Typography>
|
|
270
304
|
<Typography variant="epsilon" textColor="neutral600" textAlign="center">
|
|
271
|
-
Please ensure repository URL and GitHub token are configured in Settings.
|
|
305
|
+
Please ensure repository URL and GitHub token are configured in Settings for GitHub targets.
|
|
272
306
|
</Typography>
|
|
273
307
|
<Box paddingTop={2}>
|
|
274
308
|
<Link to={`/plugins/${PLUGIN_ID}/settings`}>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
2
3
|
import { useFetchClient, Layouts, BackButton } from '@strapi/strapi/admin';
|
|
3
4
|
import {
|
|
4
5
|
Box,
|
|
@@ -14,9 +15,10 @@ import {
|
|
|
14
15
|
Tr,
|
|
15
16
|
Th,
|
|
16
17
|
Td,
|
|
17
|
-
IconButton,
|
|
18
18
|
Dialog,
|
|
19
19
|
Grid,
|
|
20
|
+
SingleSelect,
|
|
21
|
+
SingleSelectOption,
|
|
20
22
|
} from '@strapi/design-system';
|
|
21
23
|
import { Check, Plus, Pencil, Trash } from '@strapi/icons';
|
|
22
24
|
|
|
@@ -26,6 +28,7 @@ import { PLUGIN_ID } from '../pluginId';
|
|
|
26
28
|
const TOKEN_PATTERN = /^github_pat_[a-zA-Z0-9_]+$/;
|
|
27
29
|
const REPO_URL_PATTERN = /^https:\/\/github\.com\/[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+\/?$/;
|
|
28
30
|
const WORKFLOW_PATTERN = /^[a-zA-Z0-9_.-]+\.ya?ml$/;
|
|
31
|
+
const VERCEL_WEBHOOK_PATTERN = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\/.+$/;
|
|
29
32
|
|
|
30
33
|
const validateToken = (value) => {
|
|
31
34
|
if (!value) return null;
|
|
@@ -51,7 +54,16 @@ const validateWorkflow = (value) => {
|
|
|
51
54
|
return null;
|
|
52
55
|
};
|
|
53
56
|
|
|
57
|
+
const validateVercelWebhook = (value) => {
|
|
58
|
+
if (!value) return 'Webhook URL is required';
|
|
59
|
+
if (!VERCEL_WEBHOOK_PATTERN.test(value)) {
|
|
60
|
+
return 'Must be a valid Vercel deploy hook URL (https://api.vercel.com/v1/integrations/deploy/...)';
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
64
|
+
|
|
54
65
|
const SettingsPage = () => {
|
|
66
|
+
const navigate = useNavigate();
|
|
55
67
|
const { get, put, post, del } = useFetchClient();
|
|
56
68
|
const [settings, setSettings] = useState({
|
|
57
69
|
githubToken: '',
|
|
@@ -67,7 +79,7 @@ const SettingsPage = () => {
|
|
|
67
79
|
|
|
68
80
|
// Target form state
|
|
69
81
|
const [editingTarget, setEditingTarget] = useState(null);
|
|
70
|
-
const [targetForm, setTargetForm] = useState({ name: '', workflow: '', branch: '' });
|
|
82
|
+
const [targetForm, setTargetForm] = useState({ type: 'github', name: '', workflow: '', branch: '', webhookUrl: '' });
|
|
71
83
|
const [targetErrors, setTargetErrors] = useState({});
|
|
72
84
|
const [showAddForm, setShowAddForm] = useState(false);
|
|
73
85
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
@@ -122,17 +134,15 @@ const SettingsPage = () => {
|
|
|
122
134
|
|
|
123
135
|
const { data } = await put(`/${PLUGIN_ID}/settings`, { data: dataToSave });
|
|
124
136
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
setSettings(prev => ({ ...prev, githubToken: '' }));
|
|
130
|
-
setNotification({ type: 'success', message: 'Settings saved successfully' });
|
|
137
|
+
// Redirect to main page on success with notification
|
|
138
|
+
navigate(`/plugins/${PLUGIN_ID}`, {
|
|
139
|
+
state: { notification: { type: 'success', message: 'Settings saved successfully' } }
|
|
140
|
+
});
|
|
131
141
|
} catch (error) {
|
|
132
142
|
console.error('Error saving settings:', error);
|
|
133
143
|
setNotification({ type: 'danger', message: 'Failed to save settings' });
|
|
144
|
+
setSaving(false);
|
|
134
145
|
}
|
|
135
|
-
setSaving(false);
|
|
136
146
|
};
|
|
137
147
|
|
|
138
148
|
const handleChange = (field) => (e) => {
|
|
@@ -145,7 +155,7 @@ const SettingsPage = () => {
|
|
|
145
155
|
|
|
146
156
|
// Target management
|
|
147
157
|
const resetTargetForm = () => {
|
|
148
|
-
setTargetForm({ name: '', workflow: 'deploy.yml', branch: 'master' });
|
|
158
|
+
setTargetForm({ type: 'github', name: '', workflow: 'deploy.yml', branch: 'master', webhookUrl: '' });
|
|
149
159
|
setTargetErrors({});
|
|
150
160
|
setEditingTarget(null);
|
|
151
161
|
setShowAddForm(false);
|
|
@@ -154,9 +164,16 @@ const SettingsPage = () => {
|
|
|
154
164
|
const validateTargetForm = () => {
|
|
155
165
|
const newErrors = {};
|
|
156
166
|
if (!targetForm.name.trim()) newErrors.name = 'Name is required';
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
167
|
+
|
|
168
|
+
if (targetForm.type === 'github') {
|
|
169
|
+
const workflowError = validateWorkflow(targetForm.workflow);
|
|
170
|
+
if (workflowError) newErrors.workflow = workflowError;
|
|
171
|
+
if (!targetForm.branch.trim()) newErrors.branch = 'Branch is required';
|
|
172
|
+
} else if (targetForm.type === 'vercel') {
|
|
173
|
+
const webhookError = validateVercelWebhook(targetForm.webhookUrl);
|
|
174
|
+
if (webhookError) newErrors.webhookUrl = webhookError;
|
|
175
|
+
}
|
|
176
|
+
|
|
160
177
|
setTargetErrors(newErrors);
|
|
161
178
|
return Object.keys(newErrors).length === 0;
|
|
162
179
|
};
|
|
@@ -180,7 +197,13 @@ const SettingsPage = () => {
|
|
|
180
197
|
|
|
181
198
|
const handleEditTarget = (target) => {
|
|
182
199
|
setEditingTarget(target.id);
|
|
183
|
-
setTargetForm({
|
|
200
|
+
setTargetForm({
|
|
201
|
+
type: target.type || 'github',
|
|
202
|
+
name: target.name,
|
|
203
|
+
workflow: target.workflow || 'deploy.yml',
|
|
204
|
+
branch: target.branch || 'master',
|
|
205
|
+
webhookUrl: target.webhookUrl || '',
|
|
206
|
+
});
|
|
184
207
|
setShowAddForm(false);
|
|
185
208
|
};
|
|
186
209
|
|
|
@@ -330,7 +353,7 @@ const SettingsPage = () => {
|
|
|
330
353
|
startIcon={<Plus />}
|
|
331
354
|
onClick={() => {
|
|
332
355
|
setShowAddForm(true);
|
|
333
|
-
setTargetForm({ name: '', workflow: 'deploy.yml', branch: 'master' });
|
|
356
|
+
setTargetForm({ type: 'github', name: '', workflow: 'deploy.yml', branch: 'master', webhookUrl: '' });
|
|
334
357
|
}}
|
|
335
358
|
size="S"
|
|
336
359
|
>
|
|
@@ -351,7 +374,19 @@ const SettingsPage = () => {
|
|
|
351
374
|
{editingTarget ? 'Edit Target' : 'Add New Target'}
|
|
352
375
|
</Typography>
|
|
353
376
|
<Grid.Root gap={4}>
|
|
354
|
-
<Grid.Item col={
|
|
377
|
+
<Grid.Item col={3} s={12}>
|
|
378
|
+
<Field.Root name="targetType" required>
|
|
379
|
+
<Field.Label>Type</Field.Label>
|
|
380
|
+
<SingleSelect
|
|
381
|
+
value={targetForm.type}
|
|
382
|
+
onChange={(value) => setTargetForm(prev => ({ ...prev, type: value }))}
|
|
383
|
+
>
|
|
384
|
+
<SingleSelectOption value="github">GitHub</SingleSelectOption>
|
|
385
|
+
<SingleSelectOption value="vercel">Vercel</SingleSelectOption>
|
|
386
|
+
</SingleSelect>
|
|
387
|
+
</Field.Root>
|
|
388
|
+
</Grid.Item>
|
|
389
|
+
<Grid.Item col={3} s={12}>
|
|
355
390
|
<Field.Root name="targetName" required error={targetErrors.name}>
|
|
356
391
|
<Field.Label>Name</Field.Label>
|
|
357
392
|
<Field.Input
|
|
@@ -362,28 +397,49 @@ const SettingsPage = () => {
|
|
|
362
397
|
<Field.Error />
|
|
363
398
|
</Field.Root>
|
|
364
399
|
</Grid.Item>
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
400
|
+
|
|
401
|
+
{/* GitHub-specific fields */}
|
|
402
|
+
{targetForm.type === 'github' && (
|
|
403
|
+
<>
|
|
404
|
+
<Grid.Item col={3} s={12}>
|
|
405
|
+
<Field.Root name="targetWorkflow" required error={targetErrors.workflow}>
|
|
406
|
+
<Field.Label>Workflow File</Field.Label>
|
|
407
|
+
<Field.Input
|
|
408
|
+
placeholder="deploy.yml"
|
|
409
|
+
value={targetForm.workflow}
|
|
410
|
+
onChange={handleTargetFormChange('workflow')}
|
|
411
|
+
/>
|
|
412
|
+
<Field.Error />
|
|
413
|
+
</Field.Root>
|
|
414
|
+
</Grid.Item>
|
|
415
|
+
<Grid.Item col={3} s={12}>
|
|
416
|
+
<Field.Root name="targetBranch" required error={targetErrors.branch}>
|
|
417
|
+
<Field.Label>Branch</Field.Label>
|
|
418
|
+
<Field.Input
|
|
419
|
+
placeholder="main"
|
|
420
|
+
value={targetForm.branch}
|
|
421
|
+
onChange={handleTargetFormChange('branch')}
|
|
422
|
+
/>
|
|
423
|
+
<Field.Error />
|
|
424
|
+
</Field.Root>
|
|
425
|
+
</Grid.Item>
|
|
426
|
+
</>
|
|
427
|
+
)}
|
|
428
|
+
|
|
429
|
+
{/* Vercel-specific fields */}
|
|
430
|
+
{targetForm.type === 'vercel' && (
|
|
431
|
+
<Grid.Item col={6} s={12}>
|
|
432
|
+
<Field.Root name="targetWebhookUrl" required error={targetErrors.webhookUrl}>
|
|
433
|
+
<Field.Label>Webhook URL</Field.Label>
|
|
434
|
+
<Field.Input
|
|
435
|
+
placeholder="https://api.vercel.com/v1/integrations/deploy/..."
|
|
436
|
+
value={targetForm.webhookUrl}
|
|
437
|
+
onChange={handleTargetFormChange('webhookUrl')}
|
|
438
|
+
/>
|
|
439
|
+
<Field.Error />
|
|
440
|
+
</Field.Root>
|
|
441
|
+
</Grid.Item>
|
|
442
|
+
)}
|
|
387
443
|
</Grid.Root>
|
|
388
444
|
<Flex gap={2} justifyContent="flex-end">
|
|
389
445
|
<Button variant="tertiary" onClick={resetTargetForm}>
|
|
@@ -405,41 +461,60 @@ const SettingsPage = () => {
|
|
|
405
461
|
<Table>
|
|
406
462
|
<Thead>
|
|
407
463
|
<Tr>
|
|
464
|
+
<Th><Typography variant="sigma">Type</Typography></Th>
|
|
408
465
|
<Th><Typography variant="sigma">Name</Typography></Th>
|
|
409
|
-
<Th><Typography variant="sigma">
|
|
410
|
-
<Th><Typography variant="sigma">Branch</Typography></Th>
|
|
466
|
+
<Th><Typography variant="sigma">Details</Typography></Th>
|
|
411
467
|
<Th><Typography variant="sigma">Actions</Typography></Th>
|
|
412
468
|
</Tr>
|
|
413
469
|
</Thead>
|
|
414
470
|
<Tbody>
|
|
415
|
-
{settings.targets.map((target) =>
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
<
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
471
|
+
{settings.targets.map((target) => {
|
|
472
|
+
const targetType = target.type || 'github';
|
|
473
|
+
return (
|
|
474
|
+
<Tr key={target.id}>
|
|
475
|
+
<Td>
|
|
476
|
+
<Typography variant="omega" fontWeight="bold" textColor={targetType === 'github' ? 'neutral800' : 'secondary600'}>
|
|
477
|
+
{targetType === 'github' ? 'GitHub' : 'Vercel'}
|
|
478
|
+
</Typography>
|
|
479
|
+
</Td>
|
|
480
|
+
<Td><Typography variant="omega">{target.name}</Typography></Td>
|
|
481
|
+
<Td>
|
|
482
|
+
{targetType === 'github' ? (
|
|
483
|
+
<Typography variant="omega" textColor="neutral600">
|
|
484
|
+
{target.workflow} / {target.branch}
|
|
485
|
+
</Typography>
|
|
486
|
+
) : (
|
|
487
|
+
<Typography variant="omega" textColor="neutral600">
|
|
488
|
+
Webhook configured
|
|
489
|
+
</Typography>
|
|
490
|
+
)}
|
|
491
|
+
</Td>
|
|
492
|
+
<Td>
|
|
493
|
+
<Flex gap={2}>
|
|
494
|
+
<Button
|
|
495
|
+
onClick={() => handleEditTarget(target)}
|
|
496
|
+
variant="tertiary"
|
|
497
|
+
size="S"
|
|
498
|
+
startIcon={<Pencil />}
|
|
499
|
+
>
|
|
500
|
+
Edit
|
|
501
|
+
</Button>
|
|
502
|
+
<Button
|
|
503
|
+
onClick={() => {
|
|
504
|
+
setTargetToDelete(target.id);
|
|
505
|
+
setDeleteDialogOpen(true);
|
|
506
|
+
}}
|
|
507
|
+
variant="danger-light"
|
|
508
|
+
size="S"
|
|
509
|
+
startIcon={<Trash />}
|
|
510
|
+
>
|
|
511
|
+
Delete
|
|
512
|
+
</Button>
|
|
513
|
+
</Flex>
|
|
514
|
+
</Td>
|
|
515
|
+
</Tr>
|
|
516
|
+
);
|
|
517
|
+
})}
|
|
443
518
|
</Tbody>
|
|
444
519
|
</Table>
|
|
445
520
|
) : (
|