datajunction-ui 0.0.54 → 0.0.56
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/package.json +1 -1
- package/src/app/components/NamespaceHeader.jsx +431 -11
- package/src/app/components/NodeListActions.jsx +10 -7
- package/src/app/components/__tests__/GitModals.test.jsx +1293 -0
- package/src/app/components/__tests__/NamespaceHeader.test.jsx +764 -0
- package/src/app/components/__tests__/NodeListActions.test.jsx +5 -3
- package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +95 -0
- package/src/app/components/git/CreateBranchModal.jsx +229 -0
- package/src/app/components/git/CreatePRModal.jsx +270 -0
- package/src/app/components/git/DeleteBranchModal.jsx +173 -0
- package/src/app/components/git/GitSettingsModal.jsx +292 -0
- package/src/app/components/git/SyncToGitModal.jsx +219 -0
- package/src/app/components/git/index.js +5 -0
- package/src/app/icons/DeleteIcon.jsx +3 -3
- package/src/app/icons/EditIcon.jsx +3 -3
- package/src/app/icons/EyeIcon.jsx +3 -4
- package/src/app/icons/JupyterExportIcon.jsx +3 -7
- package/src/app/icons/PythonIcon.jsx +3 -3
- package/src/app/pages/AddEditNodePage/index.jsx +8 -5
- package/src/app/pages/NamespacePage/index.jsx +34 -21
- package/src/app/pages/NodePage/ClientCodePopover.jsx +3 -7
- package/src/app/pages/NodePage/NodeInfoTab.jsx +10 -3
- package/src/app/pages/NodePage/NotebookDownload.jsx +4 -10
- package/src/app/pages/NodePage/WatchNodeButton.jsx +7 -12
- package/src/app/pages/NodePage/index.jsx +42 -13
- package/src/app/services/DJService.js +198 -1
- package/src/styles/index.css +3 -0
- package/src/styles/node-creation.scss +22 -0
- package/src/styles/settings.css +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Modal for confirming branch deletion.
|
|
5
|
+
*/
|
|
6
|
+
export function DeleteBranchModal({
|
|
7
|
+
isOpen,
|
|
8
|
+
onClose,
|
|
9
|
+
onDelete,
|
|
10
|
+
namespace,
|
|
11
|
+
gitBranch,
|
|
12
|
+
parentNamespace,
|
|
13
|
+
}) {
|
|
14
|
+
const [deleteGitBranch, setDeleteGitBranch] = useState(true);
|
|
15
|
+
const [deleting, setDeleting] = useState(false);
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
|
|
18
|
+
const handleSubmit = async e => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
setError(null);
|
|
21
|
+
setDeleting(true);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const result = await onDelete(deleteGitBranch);
|
|
25
|
+
if (result?._error) {
|
|
26
|
+
setError(result.message);
|
|
27
|
+
} else {
|
|
28
|
+
onClose();
|
|
29
|
+
// Redirect to parent namespace
|
|
30
|
+
window.location.href = `/namespaces/${parentNamespace}`;
|
|
31
|
+
}
|
|
32
|
+
} catch (err) {
|
|
33
|
+
setError(err.message || 'Failed to delete branch');
|
|
34
|
+
} finally {
|
|
35
|
+
setDeleting(false);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handleClose = () => {
|
|
40
|
+
setDeleteGitBranch(false);
|
|
41
|
+
setError(null);
|
|
42
|
+
onClose();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (!isOpen) return null;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="modal-overlay" onClick={handleClose}>
|
|
49
|
+
<div className="modal-content" onClick={e => e.stopPropagation()}>
|
|
50
|
+
<div className="modal-header">
|
|
51
|
+
<h3>Delete Branch</h3>
|
|
52
|
+
<button
|
|
53
|
+
className="btn-close-modal"
|
|
54
|
+
onClick={handleClose}
|
|
55
|
+
title="Close"
|
|
56
|
+
>
|
|
57
|
+
×
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<form onSubmit={handleSubmit}>
|
|
62
|
+
<div className="modal-body">
|
|
63
|
+
<div
|
|
64
|
+
style={{
|
|
65
|
+
display: 'flex',
|
|
66
|
+
alignItems: 'flex-start',
|
|
67
|
+
gap: '12px',
|
|
68
|
+
padding: '12px 12px 0 12px',
|
|
69
|
+
backgroundColor: '#fef3c7',
|
|
70
|
+
border: '1px solid #fcd34d',
|
|
71
|
+
borderRadius: '6px',
|
|
72
|
+
marginBottom: '16px',
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<span
|
|
76
|
+
style={{
|
|
77
|
+
fontSize: '20px',
|
|
78
|
+
paddingBottom: '12px',
|
|
79
|
+
margin: '-5px -1px 0 0',
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
⚠️
|
|
83
|
+
</span>
|
|
84
|
+
<div style={{ fontSize: '13px' }}>
|
|
85
|
+
Are you sure you want to delete this branch namespace?
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{error && (
|
|
90
|
+
<div
|
|
91
|
+
style={{
|
|
92
|
+
padding: '12px',
|
|
93
|
+
backgroundColor: '#fef2f2',
|
|
94
|
+
border: '1px solid #fecaca',
|
|
95
|
+
borderRadius: '6px',
|
|
96
|
+
color: '#dc2626',
|
|
97
|
+
fontSize: '13px',
|
|
98
|
+
marginBottom: '16px',
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
{error}
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<div
|
|
106
|
+
style={{
|
|
107
|
+
backgroundColor: '#f8fafc',
|
|
108
|
+
borderRadius: '6px',
|
|
109
|
+
padding: '12px',
|
|
110
|
+
fontSize: '13px',
|
|
111
|
+
marginBottom: '16px',
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<div style={{ marginBottom: '8px' }}>
|
|
115
|
+
<strong>Namespace:</strong> {namespace}
|
|
116
|
+
</div>
|
|
117
|
+
<div style={{ marginBottom: '8px' }}>
|
|
118
|
+
<strong>Git Branch:</strong> {gitBranch}
|
|
119
|
+
</div>
|
|
120
|
+
<div>
|
|
121
|
+
<strong>Parent:</strong> {parentNamespace}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<label
|
|
126
|
+
style={{
|
|
127
|
+
display: 'flex',
|
|
128
|
+
alignItems: 'center',
|
|
129
|
+
gap: '8px',
|
|
130
|
+
fontSize: '13px',
|
|
131
|
+
cursor: 'pointer',
|
|
132
|
+
fontWeight: 'none',
|
|
133
|
+
textTransform: 'none',
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
<input
|
|
137
|
+
type="checkbox"
|
|
138
|
+
checked={deleteGitBranch}
|
|
139
|
+
onChange={e => setDeleteGitBranch(e.target.checked)}
|
|
140
|
+
disabled={deleting}
|
|
141
|
+
/>
|
|
142
|
+
Also delete git branch on GitHub
|
|
143
|
+
</label>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="modal-actions">
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
className="btn-secondary"
|
|
150
|
+
onClick={handleClose}
|
|
151
|
+
disabled={deleting}
|
|
152
|
+
>
|
|
153
|
+
Cancel
|
|
154
|
+
</button>
|
|
155
|
+
<button
|
|
156
|
+
type="submit"
|
|
157
|
+
className="btn-primary"
|
|
158
|
+
disabled={deleting}
|
|
159
|
+
style={{
|
|
160
|
+
backgroundColor: '#dc2626',
|
|
161
|
+
borderColor: '#dc2626',
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
{deleting ? 'Deleting...' : 'Delete Branch'}
|
|
165
|
+
</button>
|
|
166
|
+
</div>
|
|
167
|
+
</form>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export default DeleteBranchModal;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Modal for configuring git settings for a namespace.
|
|
5
|
+
*/
|
|
6
|
+
export function GitSettingsModal({
|
|
7
|
+
isOpen,
|
|
8
|
+
onClose,
|
|
9
|
+
onSave,
|
|
10
|
+
currentConfig,
|
|
11
|
+
namespace,
|
|
12
|
+
}) {
|
|
13
|
+
const [repoPath, setRepoPath] = useState('');
|
|
14
|
+
const [branch, setBranch] = useState('');
|
|
15
|
+
const [path, setPath] = useState('');
|
|
16
|
+
const [gitOnly, setGitOnly] = useState(true);
|
|
17
|
+
const [saving, setSaving] = useState(false);
|
|
18
|
+
const [error, setError] = useState(null);
|
|
19
|
+
const [success, setSuccess] = useState(false);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (currentConfig) {
|
|
23
|
+
setRepoPath(currentConfig.github_repo_path || '');
|
|
24
|
+
setBranch(currentConfig.git_branch || '');
|
|
25
|
+
setPath(currentConfig.git_path || 'nodes/');
|
|
26
|
+
// If git is already configured (has repo path), use the existing git_only value
|
|
27
|
+
// Otherwise, default to read-only (true) for new git configurations
|
|
28
|
+
const hasExistingGitConfig = !!currentConfig.github_repo_path;
|
|
29
|
+
setGitOnly(hasExistingGitConfig ? currentConfig.git_only : true);
|
|
30
|
+
} else {
|
|
31
|
+
// New configuration - default to read-only and nodes/ path
|
|
32
|
+
setPath('nodes/');
|
|
33
|
+
setGitOnly(true);
|
|
34
|
+
}
|
|
35
|
+
setSuccess(false);
|
|
36
|
+
setError(null);
|
|
37
|
+
}, [currentConfig]);
|
|
38
|
+
|
|
39
|
+
const handleSubmit = async e => {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
setError(null);
|
|
42
|
+
setSuccess(false);
|
|
43
|
+
setSaving(true);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const config = {
|
|
47
|
+
github_repo_path: repoPath.trim() || null,
|
|
48
|
+
git_branch: branch.trim() || null,
|
|
49
|
+
git_path: path.trim() || null,
|
|
50
|
+
git_only: gitOnly,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const result = await onSave(config);
|
|
54
|
+
if (result?._error) {
|
|
55
|
+
setError(result.message);
|
|
56
|
+
} else {
|
|
57
|
+
setSuccess(true);
|
|
58
|
+
// Keep modal open so user can see success message
|
|
59
|
+
// User can close manually via Close button or X
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
setError(err.message || 'Failed to save git settings');
|
|
63
|
+
} finally {
|
|
64
|
+
setSaving(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleClose = () => {
|
|
69
|
+
setError(null);
|
|
70
|
+
setSuccess(false);
|
|
71
|
+
onClose();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (!isOpen) return null;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div className="modal-overlay" onClick={handleClose}>
|
|
78
|
+
<div className="modal-content" onClick={e => e.stopPropagation()}>
|
|
79
|
+
<div className="modal-header">
|
|
80
|
+
<h3>Git Configuration</h3>
|
|
81
|
+
<button
|
|
82
|
+
className="btn-close-modal"
|
|
83
|
+
onClick={handleClose}
|
|
84
|
+
title="Close"
|
|
85
|
+
>
|
|
86
|
+
×
|
|
87
|
+
</button>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<form onSubmit={handleSubmit}>
|
|
91
|
+
<div className="modal-body">
|
|
92
|
+
<p
|
|
93
|
+
style={{
|
|
94
|
+
color: '#64748b',
|
|
95
|
+
fontSize: '13px',
|
|
96
|
+
marginBottom: '16px',
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
Configure git integration for <strong>{namespace}</strong> to
|
|
100
|
+
enable branch management and sync changes to GitHub.
|
|
101
|
+
</p>
|
|
102
|
+
|
|
103
|
+
{error && (
|
|
104
|
+
<div
|
|
105
|
+
style={{
|
|
106
|
+
padding: '12px',
|
|
107
|
+
backgroundColor: '#fef2f2',
|
|
108
|
+
border: '1px solid #fecaca',
|
|
109
|
+
borderRadius: '6px',
|
|
110
|
+
color: '#dc2626',
|
|
111
|
+
fontSize: '13px',
|
|
112
|
+
marginBottom: '16px',
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{error}
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
<div className="form-group">
|
|
120
|
+
<label htmlFor="git-repo-path">Repository</label>
|
|
121
|
+
<input
|
|
122
|
+
id="git-repo-path"
|
|
123
|
+
type="text"
|
|
124
|
+
placeholder="owner/repo"
|
|
125
|
+
value={repoPath}
|
|
126
|
+
onChange={e => setRepoPath(e.target.value)}
|
|
127
|
+
disabled={saving}
|
|
128
|
+
/>
|
|
129
|
+
<span className="form-hint">
|
|
130
|
+
GitHub repository path (e.g., "myorg/dj-definitions")
|
|
131
|
+
</span>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div className="form-group">
|
|
135
|
+
<label htmlFor="git-branch">Branch</label>
|
|
136
|
+
<input
|
|
137
|
+
id="git-branch"
|
|
138
|
+
type="text"
|
|
139
|
+
placeholder="main"
|
|
140
|
+
value={branch}
|
|
141
|
+
onChange={e => setBranch(e.target.value)}
|
|
142
|
+
disabled={saving}
|
|
143
|
+
/>
|
|
144
|
+
<span className="form-hint">
|
|
145
|
+
Git branch for this namespace (e.g., "main" or "production")
|
|
146
|
+
</span>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div className="form-group">
|
|
150
|
+
<label htmlFor="git-path">Path</label>
|
|
151
|
+
<input
|
|
152
|
+
id="git-path"
|
|
153
|
+
type="text"
|
|
154
|
+
placeholder="nodes/"
|
|
155
|
+
value={path}
|
|
156
|
+
onChange={e => setPath(e.target.value)}
|
|
157
|
+
disabled={saving}
|
|
158
|
+
required
|
|
159
|
+
/>
|
|
160
|
+
<span className="form-hint">
|
|
161
|
+
Subdirectory within the repo for node YAML files
|
|
162
|
+
</span>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div
|
|
166
|
+
style={{
|
|
167
|
+
marginTop: '16px',
|
|
168
|
+
padding: '12px',
|
|
169
|
+
backgroundColor: gitOnly ? '#fef3c7' : '#f0fdf4',
|
|
170
|
+
borderRadius: '6px',
|
|
171
|
+
border: `1px solid ${gitOnly ? '#fcd34d' : '#86efac'}`,
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<label
|
|
175
|
+
style={{
|
|
176
|
+
display: 'flex',
|
|
177
|
+
alignItems: 'flex-start',
|
|
178
|
+
gap: '10px',
|
|
179
|
+
cursor: 'pointer',
|
|
180
|
+
margin: 0,
|
|
181
|
+
textTransform: 'none',
|
|
182
|
+
letterSpacing: 'normal',
|
|
183
|
+
fontSize: '14px',
|
|
184
|
+
fontWeight: 'normal',
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<input
|
|
188
|
+
type="checkbox"
|
|
189
|
+
checked={gitOnly}
|
|
190
|
+
onChange={e => setGitOnly(e.target.checked)}
|
|
191
|
+
disabled={saving}
|
|
192
|
+
style={{ marginTop: '3px' }}
|
|
193
|
+
/>
|
|
194
|
+
<span>
|
|
195
|
+
<span
|
|
196
|
+
style={{
|
|
197
|
+
fontWeight: 600,
|
|
198
|
+
color: gitOnly ? '#92400e' : '#166534',
|
|
199
|
+
textTransform: 'none',
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
{gitOnly
|
|
203
|
+
? 'Read-only (Git is source of truth)'
|
|
204
|
+
: 'Editable (UI edits allowed)'}
|
|
205
|
+
</span>
|
|
206
|
+
<span
|
|
207
|
+
style={{
|
|
208
|
+
display: 'block',
|
|
209
|
+
marginTop: '4px',
|
|
210
|
+
fontSize: '12px',
|
|
211
|
+
color: '#64748b',
|
|
212
|
+
fontWeight: 'normal',
|
|
213
|
+
textTransform: 'none',
|
|
214
|
+
}}
|
|
215
|
+
>
|
|
216
|
+
{gitOnly
|
|
217
|
+
? 'Changes must be made via git and deployed through CI/CD. UI editing is disabled.'
|
|
218
|
+
: 'Users can edit nodes in the UI. Changes can be synced to git.'}
|
|
219
|
+
</span>
|
|
220
|
+
</span>
|
|
221
|
+
</label>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{success && (
|
|
225
|
+
<div
|
|
226
|
+
style={{
|
|
227
|
+
marginTop: '16px',
|
|
228
|
+
padding: '12px',
|
|
229
|
+
backgroundColor: '#f0fdf4',
|
|
230
|
+
border: '1px solid #86efac',
|
|
231
|
+
borderRadius: '6px',
|
|
232
|
+
color: '#166534',
|
|
233
|
+
fontSize: '13px',
|
|
234
|
+
display: 'flex',
|
|
235
|
+
alignItems: 'center',
|
|
236
|
+
gap: '8px',
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
<svg
|
|
240
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
241
|
+
width="16"
|
|
242
|
+
height="16"
|
|
243
|
+
viewBox="0 0 24 24"
|
|
244
|
+
fill="none"
|
|
245
|
+
stroke="currentColor"
|
|
246
|
+
strokeWidth="2"
|
|
247
|
+
strokeLinecap="round"
|
|
248
|
+
strokeLinejoin="round"
|
|
249
|
+
>
|
|
250
|
+
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
251
|
+
<polyline points="22 4 12 14.01 9 11.01" />
|
|
252
|
+
</svg>
|
|
253
|
+
Git configuration saved successfully!
|
|
254
|
+
</div>
|
|
255
|
+
)}
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div className="modal-actions">
|
|
259
|
+
<button
|
|
260
|
+
type="button"
|
|
261
|
+
className="btn-secondary"
|
|
262
|
+
onClick={handleClose}
|
|
263
|
+
disabled={saving}
|
|
264
|
+
>
|
|
265
|
+
{success ? 'Close' : 'Cancel'}
|
|
266
|
+
</button>
|
|
267
|
+
{!success && (
|
|
268
|
+
<button
|
|
269
|
+
type="submit"
|
|
270
|
+
className="btn-primary"
|
|
271
|
+
disabled={saving}
|
|
272
|
+
style={
|
|
273
|
+
saving
|
|
274
|
+
? {
|
|
275
|
+
opacity: 0.7,
|
|
276
|
+
cursor: 'wait',
|
|
277
|
+
backgroundColor: '#9ca3af',
|
|
278
|
+
}
|
|
279
|
+
: {}
|
|
280
|
+
}
|
|
281
|
+
>
|
|
282
|
+
{saving ? 'Saving...' : 'Save Settings'}
|
|
283
|
+
</button>
|
|
284
|
+
)}
|
|
285
|
+
</div>
|
|
286
|
+
</form>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export default GitSettingsModal;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Modal for syncing namespace to git.
|
|
5
|
+
*/
|
|
6
|
+
export function SyncToGitModal({
|
|
7
|
+
isOpen,
|
|
8
|
+
onClose,
|
|
9
|
+
onSync,
|
|
10
|
+
namespace,
|
|
11
|
+
gitBranch,
|
|
12
|
+
repoPath,
|
|
13
|
+
}) {
|
|
14
|
+
const [commitMessage, setCommitMessage] = useState('');
|
|
15
|
+
const [syncing, setSyncing] = useState(false);
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
const [result, setResult] = useState(null);
|
|
18
|
+
|
|
19
|
+
const handleSubmit = async e => {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
setError(null);
|
|
22
|
+
setSyncing(true);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const res = await onSync(commitMessage.trim() || null);
|
|
26
|
+
if (res?._error) {
|
|
27
|
+
setError(res.message);
|
|
28
|
+
} else {
|
|
29
|
+
setResult(res);
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
setError(err.message || 'Failed to sync to git');
|
|
33
|
+
} finally {
|
|
34
|
+
setSyncing(false);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleClose = () => {
|
|
39
|
+
setCommitMessage('');
|
|
40
|
+
setError(null);
|
|
41
|
+
setResult(null);
|
|
42
|
+
onClose();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (!isOpen) return null;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="modal-overlay" onClick={handleClose}>
|
|
49
|
+
<div className="modal-content" onClick={e => e.stopPropagation()}>
|
|
50
|
+
<div className="modal-header">
|
|
51
|
+
<h3>Sync to Git</h3>
|
|
52
|
+
<button
|
|
53
|
+
className="btn-close-modal"
|
|
54
|
+
onClick={handleClose}
|
|
55
|
+
title="Close"
|
|
56
|
+
>
|
|
57
|
+
×
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{result ? (
|
|
62
|
+
<div className="modal-body">
|
|
63
|
+
<div
|
|
64
|
+
style={{
|
|
65
|
+
textAlign: 'center',
|
|
66
|
+
padding: '16px 0',
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<span
|
|
70
|
+
style={{
|
|
71
|
+
display: 'inline-flex',
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
justifyContent: 'center',
|
|
74
|
+
width: '48px',
|
|
75
|
+
height: '48px',
|
|
76
|
+
borderRadius: '50%',
|
|
77
|
+
backgroundColor: '#dcfce7',
|
|
78
|
+
color: '#16a34a',
|
|
79
|
+
fontSize: '24px',
|
|
80
|
+
marginBottom: '12px',
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
✓
|
|
84
|
+
</span>
|
|
85
|
+
<h4 style={{ margin: '0 0 8px 0' }}>
|
|
86
|
+
Synced {result.files_synced} file
|
|
87
|
+
{result.files_synced !== 1 ? 's' : ''}!
|
|
88
|
+
</h4>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div
|
|
92
|
+
style={{
|
|
93
|
+
backgroundColor: '#f8fafc',
|
|
94
|
+
borderRadius: '6px',
|
|
95
|
+
padding: '12px',
|
|
96
|
+
fontSize: '13px',
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
<div style={{ marginBottom: '8px' }}>
|
|
100
|
+
<strong>Commit:</strong>{' '}
|
|
101
|
+
<a
|
|
102
|
+
href={result.commit_url}
|
|
103
|
+
target="_blank"
|
|
104
|
+
rel="noopener noreferrer"
|
|
105
|
+
style={{
|
|
106
|
+
fontFamily: 'monospace',
|
|
107
|
+
fontSize: '12px',
|
|
108
|
+
color: '#3b82f6',
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
{result.commit_sha?.slice(0, 7)}
|
|
112
|
+
</a>
|
|
113
|
+
</div>
|
|
114
|
+
<div>
|
|
115
|
+
<strong>Branch:</strong> {gitBranch}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="modal-actions">
|
|
120
|
+
<button className="btn-secondary" onClick={handleClose}>
|
|
121
|
+
Close
|
|
122
|
+
</button>
|
|
123
|
+
<a
|
|
124
|
+
href={result.commit_url}
|
|
125
|
+
target="_blank"
|
|
126
|
+
rel="noopener noreferrer"
|
|
127
|
+
className="btn-primary"
|
|
128
|
+
style={{ textDecoration: 'none' }}
|
|
129
|
+
>
|
|
130
|
+
View Commit
|
|
131
|
+
</a>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
) : (
|
|
135
|
+
<form onSubmit={handleSubmit}>
|
|
136
|
+
<div className="modal-body">
|
|
137
|
+
<p
|
|
138
|
+
style={{
|
|
139
|
+
color: '#64748b',
|
|
140
|
+
fontSize: '13px',
|
|
141
|
+
marginBottom: '16px',
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
Sync all nodes in <strong>{namespace}</strong> to git as YAML
|
|
145
|
+
files.
|
|
146
|
+
</p>
|
|
147
|
+
|
|
148
|
+
{error && (
|
|
149
|
+
<div
|
|
150
|
+
style={{
|
|
151
|
+
padding: '12px',
|
|
152
|
+
backgroundColor: '#fef2f2',
|
|
153
|
+
border: '1px solid #fecaca',
|
|
154
|
+
borderRadius: '6px',
|
|
155
|
+
color: '#dc2626',
|
|
156
|
+
fontSize: '13px',
|
|
157
|
+
marginBottom: '16px',
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
{error}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
<div
|
|
165
|
+
style={{
|
|
166
|
+
backgroundColor: '#f8fafc',
|
|
167
|
+
borderRadius: '6px',
|
|
168
|
+
padding: '12px',
|
|
169
|
+
fontSize: '13px',
|
|
170
|
+
marginBottom: '16px',
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
<div style={{ marginBottom: '4px' }}>
|
|
174
|
+
<strong>Repository:</strong> {repoPath}
|
|
175
|
+
</div>
|
|
176
|
+
<div>
|
|
177
|
+
<strong>Branch:</strong> {gitBranch}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<div className="form-group">
|
|
182
|
+
<label htmlFor="commit-message">
|
|
183
|
+
Commit Message (optional)
|
|
184
|
+
</label>
|
|
185
|
+
<input
|
|
186
|
+
id="commit-message"
|
|
187
|
+
type="text"
|
|
188
|
+
placeholder={`Sync ${namespace}`}
|
|
189
|
+
value={commitMessage}
|
|
190
|
+
onChange={e => setCommitMessage(e.target.value)}
|
|
191
|
+
disabled={syncing}
|
|
192
|
+
/>
|
|
193
|
+
<span className="form-hint">
|
|
194
|
+
Leave blank to use default message
|
|
195
|
+
</span>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div className="modal-actions">
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
className="btn-secondary"
|
|
203
|
+
onClick={handleClose}
|
|
204
|
+
disabled={syncing}
|
|
205
|
+
>
|
|
206
|
+
Cancel
|
|
207
|
+
</button>
|
|
208
|
+
<button type="submit" className="btn-primary" disabled={syncing}>
|
|
209
|
+
{syncing ? 'Syncing...' : 'Sync Now'}
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
</form>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export default SyncToGitModal;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { GitSettingsModal } from './GitSettingsModal';
|
|
2
|
+
export { CreateBranchModal } from './CreateBranchModal';
|
|
3
|
+
export { SyncToGitModal } from './SyncToGitModal';
|
|
4
|
+
export { CreatePRModal } from './CreatePRModal';
|
|
5
|
+
export { DeleteBranchModal } from './DeleteBranchModal';
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
const DeleteIcon =
|
|
1
|
+
const DeleteIcon = ({ size = 14 }) => (
|
|
2
2
|
<svg
|
|
3
3
|
className="feather feather-trash-2"
|
|
4
4
|
fill="none"
|
|
5
|
-
height=
|
|
5
|
+
height={size}
|
|
6
6
|
stroke="currentColor"
|
|
7
7
|
strokeLinecap="round"
|
|
8
8
|
strokeLinejoin="round"
|
|
9
9
|
strokeWidth="2"
|
|
10
10
|
viewBox="0 0 24 24"
|
|
11
|
-
width=
|
|
11
|
+
width={size}
|
|
12
12
|
xmlns="http://www.w3.org/2000/svg"
|
|
13
13
|
>
|
|
14
14
|
<polyline points="3 6 5 6 21 6" />
|