datajunction-ui 0.0.59 → 0.0.62
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 +651 -568
- package/src/app/components/__tests__/GitModals.test.jsx +68 -39
- package/src/app/components/__tests__/NamespaceHeader.test.jsx +12 -16
- package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +150 -140
- package/src/app/components/git/CreateBranchModal.jsx +16 -3
- package/src/app/components/git/GitSettingsModal.jsx +366 -102
- package/src/app/components/git/__tests__/GitSettingsModal.test.jsx +216 -25
|
@@ -9,6 +9,7 @@ export function CreateBranchModal({
|
|
|
9
9
|
onCreate,
|
|
10
10
|
namespace,
|
|
11
11
|
gitBranch,
|
|
12
|
+
isGitRoot,
|
|
12
13
|
}) {
|
|
13
14
|
const [branchName, setBranchName] = useState('');
|
|
14
15
|
const [creating, setCreating] = useState(false);
|
|
@@ -16,10 +17,22 @@ export function CreateBranchModal({
|
|
|
16
17
|
const [result, setResult] = useState(null);
|
|
17
18
|
|
|
18
19
|
// Convert branch name to expected namespace suffix for preview
|
|
20
|
+
// If creating from git root (e.g., "demo.metrics"), use full namespace as prefix: "demo.metrics.rr"
|
|
21
|
+
// If creating from branch namespace (e.g., "demo.main"), use parent prefix: "demo.rr"
|
|
19
22
|
const previewNamespace = branchName
|
|
20
|
-
?
|
|
21
|
-
.replace(/-/g, '_')
|
|
22
|
-
|
|
23
|
+
? (() => {
|
|
24
|
+
const branchSuffix = branchName.replace(/-/g, '_').replace(/\//g, '_');
|
|
25
|
+
if (isGitRoot) {
|
|
26
|
+
// Git root: use full namespace as prefix
|
|
27
|
+
return `${namespace}.${branchSuffix}`;
|
|
28
|
+
} else {
|
|
29
|
+
// Branch namespace: remove last segment to get parent prefix
|
|
30
|
+
const parts = namespace.split('.');
|
|
31
|
+
const prefix =
|
|
32
|
+
parts.length > 1 ? parts.slice(0, -1).join('.') : namespace;
|
|
33
|
+
return `${prefix}.${branchSuffix}`;
|
|
34
|
+
}
|
|
35
|
+
})()
|
|
23
36
|
: '';
|
|
24
37
|
|
|
25
38
|
const handleSubmit = async e => {
|
|
@@ -2,6 +2,11 @@ import React, { useState, useEffect } from 'react';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Modal for configuring git settings for a namespace.
|
|
5
|
+
* Supports two modes:
|
|
6
|
+
* - Git Root: Configure repository and path only (no branch, no git_only flag)
|
|
7
|
+
* - Branch Namespace: Auto-calculates parent from namespace name, user sets branch and git_only
|
|
8
|
+
* (e.g., "demo.main" automatically has parent "demo")
|
|
9
|
+
* git_only determines if the branch is read-only (deployed from git) or editable (UI changes allowed)
|
|
5
10
|
*/
|
|
6
11
|
export function GitSettingsModal({
|
|
7
12
|
isOpen,
|
|
@@ -11,34 +16,55 @@ export function GitSettingsModal({
|
|
|
11
16
|
currentConfig,
|
|
12
17
|
namespace,
|
|
13
18
|
}) {
|
|
19
|
+
const [mode, setMode] = useState('root'); // 'root' or 'branch'
|
|
14
20
|
const [repoPath, setRepoPath] = useState('');
|
|
15
21
|
const [branch, setBranch] = useState('');
|
|
16
22
|
const [path, setPath] = useState('');
|
|
23
|
+
const [defaultBranch, setDefaultBranch] = useState('main');
|
|
17
24
|
const [gitOnly, setGitOnly] = useState(true);
|
|
18
25
|
const [saving, setSaving] = useState(false);
|
|
19
26
|
const [removing, setRemoving] = useState(false);
|
|
20
27
|
const [error, setError] = useState(null);
|
|
21
28
|
const [success, setSuccess] = useState(false);
|
|
22
29
|
const [wasRemoved, setWasRemoved] = useState(false);
|
|
30
|
+
const [parentConfig, setParentConfig] = useState(null);
|
|
31
|
+
|
|
32
|
+
// Auto-calculate parent namespace from current namespace
|
|
33
|
+
// e.g., "demo.main" -> "demo", "demo.metrics.feature1" -> "demo.metrics"
|
|
34
|
+
const parentNamespace = namespace?.includes('.')
|
|
35
|
+
? namespace.substring(0, namespace.lastIndexOf('.'))
|
|
36
|
+
: '';
|
|
23
37
|
|
|
24
38
|
useEffect(() => {
|
|
25
39
|
if (currentConfig) {
|
|
40
|
+
// Determine mode based on whether parent_namespace is set
|
|
41
|
+
const isBranchMode = !!currentConfig.parent_namespace;
|
|
42
|
+
setMode(isBranchMode ? 'branch' : 'root');
|
|
43
|
+
|
|
26
44
|
setRepoPath(currentConfig.github_repo_path || '');
|
|
27
45
|
setBranch(currentConfig.git_branch || '');
|
|
28
46
|
setPath(currentConfig.git_path || 'nodes/');
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
setDefaultBranch(currentConfig.default_branch || 'main');
|
|
48
|
+
|
|
49
|
+
// git_only is only relevant for branch namespaces
|
|
50
|
+
// Default to true (read-only) for new branch configs, use existing value if set
|
|
51
|
+
if (isBranchMode) {
|
|
52
|
+
setGitOnly(
|
|
53
|
+
currentConfig.git_only !== undefined ? currentConfig.git_only : true,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
33
56
|
} else {
|
|
34
|
-
// New configuration - default to
|
|
57
|
+
// New configuration - default to git root mode and nodes/ path
|
|
58
|
+
setMode('root');
|
|
35
59
|
setPath('nodes/');
|
|
60
|
+
// Default git_only to true for when user switches to branch mode
|
|
36
61
|
setGitOnly(true);
|
|
37
62
|
}
|
|
38
63
|
// Don't reset success here - it gets reset when modal closes
|
|
39
64
|
// Otherwise the success banner disappears when currentConfig updates after save
|
|
40
65
|
setError(null);
|
|
41
66
|
setWasRemoved(false);
|
|
67
|
+
setParentConfig(null);
|
|
42
68
|
}, [currentConfig]);
|
|
43
69
|
|
|
44
70
|
const handleSubmit = async e => {
|
|
@@ -46,15 +72,44 @@ export function GitSettingsModal({
|
|
|
46
72
|
setError(null);
|
|
47
73
|
setSuccess(false);
|
|
48
74
|
setWasRemoved(false);
|
|
75
|
+
|
|
76
|
+
// Client-side validation
|
|
77
|
+
if (mode === 'branch') {
|
|
78
|
+
if (!parentNamespace) {
|
|
79
|
+
setError(
|
|
80
|
+
'Cannot configure as branch namespace: namespace has no parent (no dots in name)',
|
|
81
|
+
);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!branch.trim()) {
|
|
85
|
+
setError('Git branch is required for branch mode');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// Git root mode
|
|
90
|
+
if (!repoPath.trim()) {
|
|
91
|
+
setError('Repository is required');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
49
96
|
setSaving(true);
|
|
50
97
|
|
|
51
98
|
try {
|
|
52
|
-
const config =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
const config =
|
|
100
|
+
mode === 'branch'
|
|
101
|
+
? {
|
|
102
|
+
// Branch mode: only send branch, parent, and git_only
|
|
103
|
+
git_branch: branch.trim() || null,
|
|
104
|
+
parent_namespace: parentNamespace || null,
|
|
105
|
+
git_only: gitOnly,
|
|
106
|
+
}
|
|
107
|
+
: {
|
|
108
|
+
// Git root mode: only send repo, path, and default_branch (no git_branch, no git_only)
|
|
109
|
+
github_repo_path: repoPath.trim() || null,
|
|
110
|
+
git_path: path.trim() || null,
|
|
111
|
+
default_branch: defaultBranch.trim() || null,
|
|
112
|
+
};
|
|
58
113
|
|
|
59
114
|
const result = await onSave(config);
|
|
60
115
|
if (result?._error) {
|
|
@@ -71,6 +126,19 @@ export function GitSettingsModal({
|
|
|
71
126
|
}
|
|
72
127
|
};
|
|
73
128
|
|
|
129
|
+
// Fetch parent config when parent namespace changes (only if modal is open)
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (isOpen && mode === 'branch' && parentNamespace) {
|
|
132
|
+
// Fetch parent's git config to show inherited values
|
|
133
|
+
fetch(`/api/namespaces/${parentNamespace}/git`)
|
|
134
|
+
.then(res => (res.ok ? res.json() : null))
|
|
135
|
+
.then(data => setParentConfig(data))
|
|
136
|
+
.catch(() => setParentConfig(null));
|
|
137
|
+
} else {
|
|
138
|
+
setParentConfig(null);
|
|
139
|
+
}
|
|
140
|
+
}, [isOpen, mode, parentNamespace]);
|
|
141
|
+
|
|
74
142
|
const handleRemove = async () => {
|
|
75
143
|
if (
|
|
76
144
|
!window.confirm(
|
|
@@ -163,110 +231,306 @@ export function GitSettingsModal({
|
|
|
163
231
|
</div>
|
|
164
232
|
)}
|
|
165
233
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
<
|
|
169
|
-
id="git-repo-path"
|
|
170
|
-
type="text"
|
|
171
|
-
placeholder="owner/repo"
|
|
172
|
-
value={repoPath}
|
|
173
|
-
onChange={e => setRepoPath(e.target.value)}
|
|
174
|
-
disabled={saving}
|
|
175
|
-
/>
|
|
176
|
-
<span className="form-hint">
|
|
177
|
-
GitHub repository path (e.g., "myorg/dj-definitions")
|
|
178
|
-
</span>
|
|
179
|
-
</div>
|
|
180
|
-
|
|
181
|
-
<div className="form-group">
|
|
182
|
-
<label htmlFor="git-branch">Branch</label>
|
|
183
|
-
<input
|
|
184
|
-
id="git-branch"
|
|
185
|
-
type="text"
|
|
186
|
-
placeholder="main"
|
|
187
|
-
value={branch}
|
|
188
|
-
onChange={e => setBranch(e.target.value)}
|
|
189
|
-
disabled={saving}
|
|
190
|
-
/>
|
|
191
|
-
<span className="form-hint">
|
|
192
|
-
Git branch for this namespace (e.g., "main" or "production")
|
|
193
|
-
</span>
|
|
194
|
-
</div>
|
|
195
|
-
|
|
196
|
-
<div className="form-group">
|
|
197
|
-
<label htmlFor="git-path">Path</label>
|
|
198
|
-
<input
|
|
199
|
-
id="git-path"
|
|
200
|
-
type="text"
|
|
201
|
-
placeholder="nodes/"
|
|
202
|
-
value={path}
|
|
203
|
-
onChange={e => setPath(e.target.value)}
|
|
204
|
-
disabled={saving}
|
|
205
|
-
required
|
|
206
|
-
/>
|
|
207
|
-
<span className="form-hint">
|
|
208
|
-
Subdirectory within the repo for node YAML files
|
|
209
|
-
</span>
|
|
210
|
-
</div>
|
|
211
|
-
|
|
212
|
-
<div
|
|
213
|
-
style={{
|
|
214
|
-
marginTop: '16px',
|
|
215
|
-
padding: '12px',
|
|
216
|
-
backgroundColor: gitOnly ? '#fef3c7' : '#f0fdf4',
|
|
217
|
-
borderRadius: '6px',
|
|
218
|
-
border: `1px solid ${gitOnly ? '#fcd34d' : '#86efac'}`,
|
|
219
|
-
}}
|
|
220
|
-
>
|
|
221
|
-
<label
|
|
234
|
+
{/* Mode Toggle */}
|
|
235
|
+
<div style={{ marginBottom: '20px' }}>
|
|
236
|
+
<div
|
|
222
237
|
style={{
|
|
223
238
|
display: 'flex',
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
textTransform: 'none',
|
|
229
|
-
letterSpacing: 'normal',
|
|
230
|
-
fontSize: '14px',
|
|
231
|
-
fontWeight: 'normal',
|
|
239
|
+
gap: '12px',
|
|
240
|
+
padding: '4px',
|
|
241
|
+
backgroundColor: '#f1f5f9',
|
|
242
|
+
borderRadius: '8px',
|
|
232
243
|
}}
|
|
233
244
|
>
|
|
234
|
-
<
|
|
235
|
-
type="
|
|
236
|
-
|
|
237
|
-
|
|
245
|
+
<button
|
|
246
|
+
type="button"
|
|
247
|
+
onClick={() => setMode('root')}
|
|
248
|
+
disabled={saving}
|
|
249
|
+
style={{
|
|
250
|
+
flex: 1,
|
|
251
|
+
padding: '10px 16px',
|
|
252
|
+
fontSize: '13px',
|
|
253
|
+
fontWeight: 500,
|
|
254
|
+
border: 'none',
|
|
255
|
+
borderRadius: '6px',
|
|
256
|
+
backgroundColor:
|
|
257
|
+
mode === 'root' ? '#ffffff' : 'transparent',
|
|
258
|
+
color: mode === 'root' ? '#0f172a' : '#64748b',
|
|
259
|
+
cursor: saving ? 'not-allowed' : 'pointer',
|
|
260
|
+
boxShadow:
|
|
261
|
+
mode === 'root' ? '0 1px 3px rgba(0,0,0,0.1)' : 'none',
|
|
262
|
+
transition: 'all 0.15s',
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
Git Root
|
|
266
|
+
</button>
|
|
267
|
+
<button
|
|
268
|
+
type="button"
|
|
269
|
+
onClick={() => setMode('branch')}
|
|
238
270
|
disabled={saving}
|
|
239
|
-
style={{
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
271
|
+
style={{
|
|
272
|
+
flex: 1,
|
|
273
|
+
padding: '10px 16px',
|
|
274
|
+
fontSize: '13px',
|
|
275
|
+
fontWeight: 500,
|
|
276
|
+
border: 'none',
|
|
277
|
+
borderRadius: '6px',
|
|
278
|
+
backgroundColor:
|
|
279
|
+
mode === 'branch' ? '#ffffff' : 'transparent',
|
|
280
|
+
color: mode === 'branch' ? '#0f172a' : '#64748b',
|
|
281
|
+
cursor: saving ? 'not-allowed' : 'pointer',
|
|
282
|
+
boxShadow:
|
|
283
|
+
mode === 'branch' ? '0 1px 3px rgba(0,0,0,0.1)' : 'none',
|
|
284
|
+
transition: 'all 0.15s',
|
|
285
|
+
}}
|
|
286
|
+
>
|
|
287
|
+
Branch Namespace
|
|
288
|
+
</button>
|
|
289
|
+
</div>
|
|
290
|
+
<span
|
|
291
|
+
style={{
|
|
292
|
+
display: 'block',
|
|
293
|
+
marginTop: '8px',
|
|
294
|
+
fontSize: '12px',
|
|
295
|
+
color: '#64748b',
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
{mode === 'root'
|
|
299
|
+
? 'Configure repository for this namespace (recommended for most users)'
|
|
300
|
+
: `Link to parent "${
|
|
301
|
+
parentNamespace || '(none)'
|
|
302
|
+
}" and inherit repository configuration`}
|
|
303
|
+
</span>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
{/* Conditional Fields based on Mode */}
|
|
307
|
+
{mode === 'root' ? (
|
|
308
|
+
<>
|
|
309
|
+
{/* Git Root Mode - Repository and Path only */}
|
|
310
|
+
<div className="form-group">
|
|
311
|
+
<label htmlFor="git-repo-path">Repository *</label>
|
|
312
|
+
<input
|
|
313
|
+
id="git-repo-path"
|
|
314
|
+
type="text"
|
|
315
|
+
placeholder="owner/repo"
|
|
316
|
+
value={repoPath}
|
|
317
|
+
onChange={e => setRepoPath(e.target.value)}
|
|
318
|
+
disabled={saving}
|
|
319
|
+
required
|
|
320
|
+
/>
|
|
321
|
+
<span className="form-hint">
|
|
322
|
+
GitHub repository path (e.g., "myorg/dj-definitions")
|
|
323
|
+
</span>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<div className="form-group">
|
|
327
|
+
<label htmlFor="git-path">Path</label>
|
|
328
|
+
<input
|
|
329
|
+
id="git-path"
|
|
330
|
+
type="text"
|
|
331
|
+
placeholder="nodes/"
|
|
332
|
+
value={path}
|
|
333
|
+
onChange={e => setPath(e.target.value)}
|
|
334
|
+
disabled={saving}
|
|
335
|
+
/>
|
|
336
|
+
<span className="form-hint">
|
|
337
|
+
Subdirectory within the repo for node YAML files
|
|
338
|
+
</span>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div className="form-group">
|
|
342
|
+
<label htmlFor="default-branch">Default Branch</label>
|
|
343
|
+
<input
|
|
344
|
+
id="default-branch"
|
|
345
|
+
type="text"
|
|
346
|
+
placeholder="main"
|
|
347
|
+
value={defaultBranch}
|
|
348
|
+
onChange={e => setDefaultBranch(e.target.value)}
|
|
349
|
+
disabled={saving}
|
|
350
|
+
/>
|
|
351
|
+
<span className="form-hint">
|
|
352
|
+
Default branch to use when creating new branches (e.g.,
|
|
353
|
+
"main")
|
|
354
|
+
</span>
|
|
355
|
+
</div>
|
|
356
|
+
</>
|
|
357
|
+
) : (
|
|
358
|
+
<>
|
|
359
|
+
{/* Branch Mode - Show parent and branch field */}
|
|
360
|
+
{!parentNamespace ? (
|
|
361
|
+
<div
|
|
243
362
|
style={{
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
363
|
+
marginBottom: '16px',
|
|
364
|
+
padding: '12px',
|
|
365
|
+
backgroundColor: '#fef2f2',
|
|
366
|
+
borderRadius: '6px',
|
|
367
|
+
border: '1px solid #fecaca',
|
|
368
|
+
color: '#dc2626',
|
|
369
|
+
fontSize: '13px',
|
|
247
370
|
}}
|
|
248
371
|
>
|
|
249
|
-
{
|
|
250
|
-
|
|
251
|
-
|
|
372
|
+
Cannot configure as branch namespace: namespace "{namespace}
|
|
373
|
+
" has no parent. Branch namespaces must have a dot in their
|
|
374
|
+
name (e.g., "demo.main").
|
|
375
|
+
</div>
|
|
376
|
+
) : (
|
|
377
|
+
<>
|
|
378
|
+
{/* Display parent namespace as read-only info */}
|
|
379
|
+
<div
|
|
380
|
+
style={{
|
|
381
|
+
marginBottom: '16px',
|
|
382
|
+
padding: '12px',
|
|
383
|
+
backgroundColor: '#f8fafc',
|
|
384
|
+
borderRadius: '6px',
|
|
385
|
+
border: '1px solid #e2e8f0',
|
|
386
|
+
}}
|
|
387
|
+
>
|
|
388
|
+
<div
|
|
389
|
+
style={{
|
|
390
|
+
fontSize: '12px',
|
|
391
|
+
fontWeight: 600,
|
|
392
|
+
color: '#475569',
|
|
393
|
+
marginBottom: '8px',
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
Parent Namespace
|
|
397
|
+
</div>
|
|
398
|
+
<div
|
|
399
|
+
style={{
|
|
400
|
+
fontSize: '14px',
|
|
401
|
+
fontWeight: 500,
|
|
402
|
+
color: '#0f172a',
|
|
403
|
+
fontFamily: 'monospace',
|
|
404
|
+
}}
|
|
405
|
+
>
|
|
406
|
+
{parentNamespace}
|
|
407
|
+
</div>
|
|
408
|
+
<div
|
|
409
|
+
style={{
|
|
410
|
+
fontSize: '12px',
|
|
411
|
+
color: '#64748b',
|
|
412
|
+
marginTop: '6px',
|
|
413
|
+
}}
|
|
414
|
+
>
|
|
415
|
+
Repository configuration will be inherited from this
|
|
416
|
+
parent
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
{/* Show inherited config from parent */}
|
|
421
|
+
{parentConfig && (
|
|
422
|
+
<div
|
|
423
|
+
style={{
|
|
424
|
+
marginBottom: '16px',
|
|
425
|
+
padding: '12px',
|
|
426
|
+
backgroundColor: '#f0f9ff',
|
|
427
|
+
borderRadius: '6px',
|
|
428
|
+
border: '1px solid #bae6fd',
|
|
429
|
+
}}
|
|
430
|
+
>
|
|
431
|
+
<div
|
|
432
|
+
style={{
|
|
433
|
+
fontSize: '12px',
|
|
434
|
+
fontWeight: 600,
|
|
435
|
+
color: '#0369a1',
|
|
436
|
+
marginBottom: '8px',
|
|
437
|
+
}}
|
|
438
|
+
>
|
|
439
|
+
Inherited Configuration
|
|
440
|
+
</div>
|
|
441
|
+
<div style={{ fontSize: '12px', color: '#64748b' }}>
|
|
442
|
+
<div style={{ marginBottom: '4px' }}>
|
|
443
|
+
<strong>Repository:</strong>{' '}
|
|
444
|
+
{parentConfig.github_repo_path ||
|
|
445
|
+
'(not configured)'}
|
|
446
|
+
</div>
|
|
447
|
+
<div>
|
|
448
|
+
<strong>Path:</strong>{' '}
|
|
449
|
+
{parentConfig.git_path || '(root)'}
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
)}
|
|
454
|
+
</>
|
|
455
|
+
)}
|
|
456
|
+
|
|
457
|
+
<div className="form-group">
|
|
458
|
+
<label htmlFor="git-branch">Branch *</label>
|
|
459
|
+
<input
|
|
460
|
+
id="git-branch"
|
|
461
|
+
type="text"
|
|
462
|
+
placeholder="feature-x, dev, etc."
|
|
463
|
+
value={branch}
|
|
464
|
+
onChange={e => setBranch(e.target.value)}
|
|
465
|
+
disabled={saving}
|
|
466
|
+
required
|
|
467
|
+
/>
|
|
468
|
+
<span className="form-hint">
|
|
469
|
+
Git branch name for this namespace
|
|
252
470
|
</span>
|
|
253
|
-
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
{/* Git-only checkbox - only for branch namespaces */}
|
|
474
|
+
<div
|
|
475
|
+
style={{
|
|
476
|
+
marginTop: '16px',
|
|
477
|
+
padding: '12px',
|
|
478
|
+
backgroundColor: gitOnly ? '#fef3c7' : '#f0fdf4',
|
|
479
|
+
borderRadius: '6px',
|
|
480
|
+
border: `1px solid ${gitOnly ? '#fcd34d' : '#86efac'}`,
|
|
481
|
+
}}
|
|
482
|
+
>
|
|
483
|
+
<label
|
|
254
484
|
style={{
|
|
255
|
-
display: '
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
485
|
+
display: 'flex',
|
|
486
|
+
alignItems: 'flex-start',
|
|
487
|
+
gap: '10px',
|
|
488
|
+
cursor: 'pointer',
|
|
489
|
+
margin: 0,
|
|
260
490
|
textTransform: 'none',
|
|
491
|
+
letterSpacing: 'normal',
|
|
492
|
+
fontSize: '14px',
|
|
493
|
+
fontWeight: 'normal',
|
|
261
494
|
}}
|
|
262
495
|
>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
496
|
+
<input
|
|
497
|
+
type="checkbox"
|
|
498
|
+
checked={gitOnly}
|
|
499
|
+
onChange={e => setGitOnly(e.target.checked)}
|
|
500
|
+
disabled={saving}
|
|
501
|
+
style={{ marginTop: '3px' }}
|
|
502
|
+
/>
|
|
503
|
+
<span>
|
|
504
|
+
<span
|
|
505
|
+
style={{
|
|
506
|
+
fontWeight: 600,
|
|
507
|
+
color: gitOnly ? '#92400e' : '#166534',
|
|
508
|
+
textTransform: 'none',
|
|
509
|
+
}}
|
|
510
|
+
>
|
|
511
|
+
{gitOnly
|
|
512
|
+
? 'Read-only (Git is source of truth)'
|
|
513
|
+
: 'Editable (UI edits allowed)'}
|
|
514
|
+
</span>
|
|
515
|
+
<span
|
|
516
|
+
style={{
|
|
517
|
+
display: 'block',
|
|
518
|
+
marginTop: '4px',
|
|
519
|
+
fontSize: '12px',
|
|
520
|
+
color: '#64748b',
|
|
521
|
+
fontWeight: 'normal',
|
|
522
|
+
textTransform: 'none',
|
|
523
|
+
}}
|
|
524
|
+
>
|
|
525
|
+
{gitOnly
|
|
526
|
+
? 'Changes must be made via git and deployed through CI/CD. UI editing is disabled.'
|
|
527
|
+
: 'Users can edit nodes in the UI. Changes can be synced to git.'}
|
|
528
|
+
</span>
|
|
529
|
+
</span>
|
|
530
|
+
</label>
|
|
531
|
+
</div>
|
|
532
|
+
</>
|
|
533
|
+
)}
|
|
270
534
|
|
|
271
535
|
{success && (
|
|
272
536
|
<div
|