datajunction-ui 0.0.58 → 0.0.61

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.
@@ -24,6 +24,7 @@ export default function NamespaceHeader({
24
24
  const [gitConfigLoading, setGitConfigLoading] = useState(true);
25
25
  const [parentGitConfig, setParentGitConfig] = useState(null);
26
26
  const [existingPR, setExistingPR] = useState(null);
27
+ const [prLoading, setPrLoading] = useState(false);
27
28
 
28
29
  // Modal states
29
30
  const [showGitSettings, setShowGitSettings] = useState(false);
@@ -35,6 +36,8 @@ export default function NamespaceHeader({
35
36
  useEffect(() => {
36
37
  // Reset loading state when namespace changes
37
38
  setGitConfigLoading(true);
39
+ setPrLoading(false);
40
+ setExistingPR(null);
38
41
 
39
42
  const fetchData = async () => {
40
43
  if (namespace) {
@@ -74,12 +77,15 @@ export default function NamespaceHeader({
74
77
  }
75
78
 
76
79
  // Check for existing PR
80
+ setPrLoading(true);
77
81
  try {
78
82
  const pr = await djClient.getPullRequest(namespace);
79
83
  setExistingPR(pr);
80
84
  } catch (e) {
81
85
  // No PR or error - that's fine
82
86
  setExistingPR(null);
87
+ } finally {
88
+ setPrLoading(false);
83
89
  }
84
90
  }
85
91
  } catch (e) {
@@ -109,8 +115,15 @@ export default function NamespaceHeader({
109
115
 
110
116
  const namespaceParts = namespace ? namespace.split('.') : [];
111
117
 
112
- const hasGitConfig = gitConfig?.github_repo_path && gitConfig?.git_branch;
118
+ // Git config is valid if:
119
+ // - Git root: has github_repo_path
120
+ // - Branch namespace: has parent_namespace and git_branch (inherits repo from parent)
121
+ const hasGitConfig =
122
+ gitConfig?.github_repo_path ||
123
+ (gitConfig?.parent_namespace && gitConfig?.git_branch);
113
124
  const isBranchNamespace = !!gitConfig?.parent_namespace;
125
+ const isGitRoot = gitConfig?.github_repo_path && !gitConfig?.parent_namespace;
126
+ const canCreateBranches = isGitRoot && gitConfig?.default_branch;
114
127
 
115
128
  // Handlers for git operations
116
129
  const handleSaveGitConfig = async config => {
@@ -184,266 +197,199 @@ export default function NamespaceHeader({
184
197
  };
185
198
 
186
199
  return (
187
- <div
188
- style={{
189
- display: 'flex',
190
- justifyContent: 'space-between',
191
- alignItems: 'center',
192
- padding: '12px 12px 12px 20px',
193
- marginBottom: '16px',
194
- borderTop: '1px solid #e2e8f0',
195
- borderBottom: '1px solid #e2e8f0',
196
- background: '#ffffff',
197
- }}
198
- >
199
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
200
- <a href="/" style={{ display: 'flex', alignItems: 'center' }}>
200
+ <>
201
+ <style>
202
+ {`
203
+ @keyframes spin {
204
+ from { transform: rotate(0deg); }
205
+ to { transform: rotate(360deg); }
206
+ }
207
+ `}
208
+ </style>
209
+ <div
210
+ style={{
211
+ display: 'flex',
212
+ justifyContent: 'space-between',
213
+ alignItems: 'center',
214
+ padding: '12px 12px 12px 20px',
215
+ marginBottom: '16px',
216
+ borderTop: '1px solid #e2e8f0',
217
+ borderBottom: '1px solid #e2e8f0',
218
+ background: '#ffffff',
219
+ }}
220
+ >
221
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
222
+ <a href="/" style={{ display: 'flex', alignItems: 'center' }}>
223
+ <svg
224
+ xmlns="http://www.w3.org/2000/svg"
225
+ width="16"
226
+ height="16"
227
+ fill="currentColor"
228
+ viewBox="0 0 16 16"
229
+ >
230
+ <path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5 8 5.961 14.154 3.5 8.186 1.113zM15 4.239l-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923l6.5 2.6zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464L7.443.184z" />
231
+ </svg>
232
+ </a>
201
233
  <svg
202
234
  xmlns="http://www.w3.org/2000/svg"
203
- width="16"
204
- height="16"
205
- fill="currentColor"
235
+ width="12"
236
+ height="12"
237
+ fill="#6c757d"
206
238
  viewBox="0 0 16 16"
207
239
  >
208
- <path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5 8 5.961 14.154 3.5 8.186 1.113zM15 4.239l-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923l6.5 2.6zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464L7.443.184z" />
240
+ <path
241
+ fillRule="evenodd"
242
+ d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"
243
+ />
209
244
  </svg>
210
- </a>
211
- <svg
212
- xmlns="http://www.w3.org/2000/svg"
213
- width="12"
214
- height="12"
215
- fill="#6c757d"
216
- viewBox="0 0 16 16"
217
- >
218
- <path
219
- fillRule="evenodd"
220
- d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"
221
- />
222
- </svg>
223
- {namespace ? (
224
- namespaceParts.map((part, index, arr) => (
245
+ {namespace ? (
246
+ namespaceParts.map((part, index, arr) => (
247
+ <span
248
+ key={index}
249
+ style={{
250
+ display: 'flex',
251
+ alignItems: 'center',
252
+ gap: '8px',
253
+ }}
254
+ >
255
+ <a
256
+ href={`/namespaces/${arr.slice(0, index + 1).join('.')}`}
257
+ style={{
258
+ fontWeight: '400',
259
+ color: '#1e293b',
260
+ textDecoration: 'none',
261
+ }}
262
+ >
263
+ {part}
264
+ </a>
265
+ {index < arr.length - 1 && (
266
+ <svg
267
+ xmlns="http://www.w3.org/2000/svg"
268
+ width="12"
269
+ height="12"
270
+ fill="#94a3b8"
271
+ viewBox="0 0 16 16"
272
+ >
273
+ <path
274
+ fillRule="evenodd"
275
+ d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"
276
+ />
277
+ </svg>
278
+ )}
279
+ </span>
280
+ ))
281
+ ) : (
282
+ <span style={{ fontWeight: '600', color: '#1e293b' }}>
283
+ All Namespaces
284
+ </span>
285
+ )}
286
+
287
+ {/* Branch indicator */}
288
+ {isBranchNamespace && (
225
289
  <span
226
- key={index}
227
290
  style={{
228
291
  display: 'flex',
229
292
  alignItems: 'center',
230
- gap: '8px',
293
+ gap: '4px',
294
+ padding: '2px 8px',
295
+ backgroundColor: '#dbeafe',
296
+ borderRadius: '12px',
297
+ fontSize: '11px',
298
+ color: '#1e40af',
299
+ marginLeft: '4px',
231
300
  }}
232
301
  >
302
+ <svg
303
+ xmlns="http://www.w3.org/2000/svg"
304
+ width="12"
305
+ height="12"
306
+ viewBox="0 0 24 24"
307
+ fill="none"
308
+ stroke="currentColor"
309
+ strokeWidth="2"
310
+ strokeLinecap="round"
311
+ strokeLinejoin="round"
312
+ >
313
+ <line x1="6" y1="3" x2="6" y2="15" />
314
+ <circle cx="18" cy="6" r="3" />
315
+ <circle cx="6" cy="18" r="3" />
316
+ <path d="M18 9a9 9 0 0 1-9 9" />
317
+ </svg>
318
+ Branch of{' '}
233
319
  <a
234
- href={`/namespaces/${arr.slice(0, index + 1).join('.')}`}
235
- style={{
236
- fontWeight: '400',
237
- color: '#1e293b',
238
- textDecoration: 'none',
239
- }}
320
+ href={`/namespaces/${gitConfig.parent_namespace}`}
321
+ style={{ color: '#1e40af', textDecoration: 'underline' }}
240
322
  >
241
- {part}
323
+ {gitConfig.parent_namespace}
242
324
  </a>
243
- {index < arr.length - 1 && (
244
- <svg
245
- xmlns="http://www.w3.org/2000/svg"
246
- width="12"
247
- height="12"
248
- fill="#94a3b8"
249
- viewBox="0 0 16 16"
250
- >
251
- <path
252
- fillRule="evenodd"
253
- d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"
254
- />
255
- </svg>
256
- )}
257
325
  </span>
258
- ))
259
- ) : (
260
- <span style={{ fontWeight: '600', color: '#1e293b' }}>
261
- All Namespaces
262
- </span>
263
- )}
264
-
265
- {/* Branch indicator */}
266
- {isBranchNamespace && (
267
- <span
268
- style={{
269
- display: 'flex',
270
- alignItems: 'center',
271
- gap: '4px',
272
- padding: '2px 8px',
273
- backgroundColor: '#dbeafe',
274
- borderRadius: '12px',
275
- fontSize: '11px',
276
- color: '#1e40af',
277
- marginLeft: '4px',
278
- }}
279
- >
280
- <svg
281
- xmlns="http://www.w3.org/2000/svg"
282
- width="12"
283
- height="12"
284
- viewBox="0 0 24 24"
285
- fill="none"
286
- stroke="currentColor"
287
- strokeWidth="2"
288
- strokeLinecap="round"
289
- strokeLinejoin="round"
290
- >
291
- <line x1="6" y1="3" x2="6" y2="15" />
292
- <circle cx="18" cy="6" r="3" />
293
- <circle cx="6" cy="18" r="3" />
294
- <path d="M18 9a9 9 0 0 1-9 9" />
295
- </svg>
296
- Branch of{' '}
297
- <a
298
- href={`/namespaces/${gitConfig.parent_namespace}`}
299
- style={{ color: '#1e40af', textDecoration: 'underline' }}
300
- >
301
- {gitConfig.parent_namespace}
302
- </a>
303
- </span>
304
- )}
305
-
306
- {/* Git-only (read-only) indicator */}
307
- {gitConfig?.git_only && (
308
- <span
309
- style={{
310
- display: 'flex',
311
- alignItems: 'center',
312
- gap: '4px',
313
- padding: '2px 8px',
314
- backgroundColor: '#fef3c7',
315
- borderRadius: '12px',
316
- fontSize: '11px',
317
- color: '#92400e',
318
- marginLeft: '4px',
319
- }}
320
- title="This namespace is git-only. Changes must be made via git and deployed."
321
- >
322
- <svg
323
- xmlns="http://www.w3.org/2000/svg"
324
- width="12"
325
- height="12"
326
- viewBox="0 0 24 24"
327
- fill="none"
328
- stroke="currentColor"
329
- strokeWidth="2"
330
- strokeLinecap="round"
331
- strokeLinejoin="round"
332
- >
333
- <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
334
- <path d="M7 11V7a5 5 0 0 1 10 0v4" />
335
- </svg>
336
- Read-only
337
- </span>
338
- )}
339
-
340
- {/* Deployment badge + dropdown (existing functionality) */}
341
- {sources && sources.total_deployments > 0 && (
342
- <div
343
- style={{ position: 'relative', marginLeft: '8px' }}
344
- ref={dropdownRef}
345
- >
346
- <button
347
- onClick={() =>
348
- setDeploymentsDropdownOpen(!deploymentsDropdownOpen)
349
- }
326
+ )}
327
+
328
+ {/* Git-only (read-only) indicator */}
329
+ {gitConfig?.git_only && (
330
+ <span
350
331
  style={{
351
- height: '32px',
352
- padding: '0 12px',
353
- fontSize: '12px',
354
- border: 'none',
355
- borderRadius: '4px',
356
- backgroundColor: '#ffffff',
357
- color: '#0b3d91',
358
- cursor: 'pointer',
359
332
  display: 'flex',
360
333
  alignItems: 'center',
361
334
  gap: '4px',
362
- whiteSpace: 'nowrap',
335
+ padding: '2px 8px',
336
+ backgroundColor: '#fef3c7',
337
+ borderRadius: '12px',
338
+ fontSize: '11px',
339
+ color: '#92400e',
340
+ marginLeft: '4px',
363
341
  }}
342
+ title="This namespace is git-only. Changes must be made via git and deployed."
364
343
  >
365
- {sources.primary_source?.type === 'git' ? (
366
- <>
367
- <svg
368
- xmlns="http://www.w3.org/2000/svg"
369
- width="12"
370
- height="12"
371
- viewBox="0 0 24 24"
372
- fill="none"
373
- stroke="currentColor"
374
- strokeWidth="2"
375
- strokeLinecap="round"
376
- strokeLinejoin="round"
377
- >
378
- <line x1="6" y1="3" x2="6" y2="15"></line>
379
- <circle cx="18" cy="6" r="3"></circle>
380
- <circle cx="6" cy="18" r="3"></circle>
381
- <path d="M18 9a9 9 0 0 1-9 9"></path>
382
- </svg>
383
- Deployed from Git
384
- </>
385
- ) : (
386
- <>
387
- <svg
388
- xmlns="http://www.w3.org/2000/svg"
389
- width="12"
390
- height="12"
391
- viewBox="0 0 24 24"
392
- fill="none"
393
- stroke="currentColor"
394
- strokeWidth="2"
395
- strokeLinecap="round"
396
- strokeLinejoin="round"
397
- >
398
- <circle cx="12" cy="7" r="4" />
399
- <path d="M5.5 21a6.5 6.5 0 0 1 13 0Z" />
400
- </svg>
401
- Local Deploy
402
- </>
403
- )}
404
- <span style={{ fontSize: '8px' }}>
405
- {deploymentsDropdownOpen ? '▲' : '▼'}
406
- </span>
407
- </button>
344
+ <svg
345
+ xmlns="http://www.w3.org/2000/svg"
346
+ width="12"
347
+ height="12"
348
+ viewBox="0 0 24 24"
349
+ fill="none"
350
+ stroke="currentColor"
351
+ strokeWidth="2"
352
+ strokeLinecap="round"
353
+ strokeLinejoin="round"
354
+ >
355
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
356
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
357
+ </svg>
358
+ Read-only
359
+ </span>
360
+ )}
408
361
 
409
- {deploymentsDropdownOpen && (
410
- <div
362
+ {/* Deployment badge + dropdown (existing functionality) */}
363
+ {sources && sources.total_deployments > 0 && (
364
+ <div
365
+ style={{ position: 'relative', marginLeft: '8px' }}
366
+ ref={dropdownRef}
367
+ >
368
+ <button
369
+ onClick={() =>
370
+ setDeploymentsDropdownOpen(!deploymentsDropdownOpen)
371
+ }
411
372
  style={{
412
- position: 'absolute',
413
- top: '100%',
414
- left: 0,
415
- marginTop: '4px',
416
- padding: '12px',
417
- backgroundColor: 'white',
418
- border: '1px solid #ddd',
419
- borderRadius: '8px',
420
- boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
421
- zIndex: 1000,
422
- minWidth: 'max-content',
373
+ height: '32px',
374
+ padding: '0 12px',
375
+ fontSize: '12px',
376
+ border: 'none',
377
+ borderRadius: '4px',
378
+ backgroundColor: '#ffffff',
379
+ color: '#0b3d91',
380
+ cursor: 'pointer',
381
+ display: 'flex',
382
+ alignItems: 'center',
383
+ gap: '4px',
384
+ whiteSpace: 'nowrap',
423
385
  }}
424
386
  >
425
387
  {sources.primary_source?.type === 'git' ? (
426
- <a
427
- href={
428
- sources.primary_source.repository?.startsWith('http')
429
- ? sources.primary_source.repository
430
- : `https://${sources.primary_source.repository}`
431
- }
432
- target="_blank"
433
- rel="noopener noreferrer"
434
- style={{
435
- display: 'flex',
436
- alignItems: 'center',
437
- gap: '8px',
438
- fontSize: '13px',
439
- fontWeight: 400,
440
- textDecoration: 'none',
441
- marginBottom: '12px',
442
- }}
443
- >
388
+ <>
444
389
  <svg
445
- width="16"
446
- height="16"
390
+ xmlns="http://www.w3.org/2000/svg"
391
+ width="12"
392
+ height="12"
447
393
  viewBox="0 0 24 24"
448
394
  fill="none"
449
395
  stroke="currentColor"
@@ -451,30 +397,19 @@ export default function NamespaceHeader({
451
397
  strokeLinecap="round"
452
398
  strokeLinejoin="round"
453
399
  >
454
- <line x1="6" y1="3" x2="6" y2="15" />
455
- <circle cx="18" cy="6" r="3" />
456
- <circle cx="6" cy="18" r="3" />
457
- <path d="M18 9a9 9 0 0 1-9 9" />
400
+ <line x1="6" y1="3" x2="6" y2="15"></line>
401
+ <circle cx="18" cy="6" r="3"></circle>
402
+ <circle cx="6" cy="18" r="3"></circle>
403
+ <path d="M18 9a9 9 0 0 1-9 9"></path>
458
404
  </svg>
459
- {sources.primary_source.repository}
460
- {sources.primary_source.branch &&
461
- ` (${sources.primary_source.branch})`}
462
- </a>
405
+ Deployed from Git
406
+ </>
463
407
  ) : (
464
- <div
465
- style={{
466
- display: 'flex',
467
- alignItems: 'center',
468
- gap: '8px',
469
- fontSize: '13px',
470
- fontWeight: 600,
471
- color: '#0b3d91',
472
- marginBottom: '12px',
473
- }}
474
- >
408
+ <>
475
409
  <svg
476
- width="16"
477
- height="16"
410
+ xmlns="http://www.w3.org/2000/svg"
411
+ width="12"
412
+ height="12"
478
413
  viewBox="0 0 24 24"
479
414
  fill="none"
480
415
  stroke="currentColor"
@@ -485,202 +420,268 @@ export default function NamespaceHeader({
485
420
  <circle cx="12" cy="7" r="4" />
486
421
  <path d="M5.5 21a6.5 6.5 0 0 1 13 0Z" />
487
422
  </svg>
488
- {recentDeployments?.[0]?.created_by
489
- ? `Local deploys by ${recentDeployments[0].created_by}`
490
- : 'Local/adhoc deployments'}
491
- </div>
423
+ Local Deploy
424
+ </>
492
425
  )}
426
+ <span style={{ fontSize: '8px' }}>
427
+ {deploymentsDropdownOpen ? '▲' : '▼'}
428
+ </span>
429
+ </button>
493
430
 
494
- {/* Separator */}
431
+ {deploymentsDropdownOpen && (
495
432
  <div
496
433
  style={{
497
- height: '1px',
498
- backgroundColor: '#e2e8f0',
499
- marginBottom: '8px',
434
+ position: 'absolute',
435
+ top: '100%',
436
+ left: 0,
437
+ marginTop: '4px',
438
+ padding: '12px',
439
+ backgroundColor: 'white',
440
+ border: '1px solid #ddd',
441
+ borderRadius: '8px',
442
+ boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
443
+ zIndex: 1000,
444
+ minWidth: 'max-content',
500
445
  }}
501
- />
502
-
503
- {/* Recent deployments list */}
504
- {recentDeployments?.length > 0 ? (
505
- recentDeployments.map((d, idx) => {
506
- const isGit = d.source?.type === 'git';
507
- const statusColor =
508
- d.status === 'success'
509
- ? '#22c55e'
510
- : d.status === 'failed'
511
- ? '#ef4444'
512
- : '#94a3b8';
513
-
514
- const commitUrl =
515
- isGit && d.source?.repository && d.source?.commit_sha
516
- ? `${
517
- d.source.repository.startsWith('http')
518
- ? d.source.repository
519
- : `https://${d.source.repository}`
520
- }/commit/${d.source.commit_sha}`
521
- : null;
522
-
523
- const detail = isGit
524
- ? d.source?.branch || 'main'
525
- : d.source?.reason || d.source?.hostname || 'adhoc';
526
-
527
- const shortSha = d.source?.commit_sha?.slice(0, 7);
528
-
529
- return (
530
- <div
531
- key={`${d.uuid}-${idx}`}
532
- style={{
533
- display: 'grid',
534
- gridTemplateColumns: '18px 1fr auto',
535
- alignItems: 'center',
536
- gap: '8px',
537
- padding: '6px 0',
538
- borderBottom:
539
- idx === recentDeployments.length - 1
540
- ? 'none'
541
- : '1px solid #f1f5f9',
542
- fontSize: '12px',
543
- }}
446
+ >
447
+ {sources.primary_source?.type === 'git' ? (
448
+ <a
449
+ href={
450
+ sources.primary_source.repository?.startsWith('http')
451
+ ? sources.primary_source.repository
452
+ : `https://${sources.primary_source.repository}`
453
+ }
454
+ target="_blank"
455
+ rel="noopener noreferrer"
456
+ style={{
457
+ display: 'flex',
458
+ alignItems: 'center',
459
+ gap: '8px',
460
+ fontSize: '13px',
461
+ fontWeight: 400,
462
+ textDecoration: 'none',
463
+ marginBottom: '12px',
464
+ }}
465
+ >
466
+ <svg
467
+ width="16"
468
+ height="16"
469
+ viewBox="0 0 24 24"
470
+ fill="none"
471
+ stroke="currentColor"
472
+ strokeWidth="2"
473
+ strokeLinecap="round"
474
+ strokeLinejoin="round"
544
475
  >
545
- {/* Status dot */}
546
- <div
547
- style={{
548
- width: '8px',
549
- height: '8px',
550
- borderRadius: '50%',
551
- backgroundColor: statusColor,
552
- }}
553
- title={d.status}
554
- />
476
+ <line x1="6" y1="3" x2="6" y2="15" />
477
+ <circle cx="18" cy="6" r="3" />
478
+ <circle cx="6" cy="18" r="3" />
479
+ <path d="M18 9a9 9 0 0 1-9 9" />
480
+ </svg>
481
+ {sources.primary_source.repository}
482
+ {sources.primary_source.branch &&
483
+ ` (${sources.primary_source.branch})`}
484
+ </a>
485
+ ) : (
486
+ <div
487
+ style={{
488
+ display: 'flex',
489
+ alignItems: 'center',
490
+ gap: '8px',
491
+ fontSize: '13px',
492
+ fontWeight: 600,
493
+ color: '#0b3d91',
494
+ marginBottom: '12px',
495
+ }}
496
+ >
497
+ <svg
498
+ width="16"
499
+ height="16"
500
+ viewBox="0 0 24 24"
501
+ fill="none"
502
+ stroke="currentColor"
503
+ strokeWidth="2"
504
+ strokeLinecap="round"
505
+ strokeLinejoin="round"
506
+ >
507
+ <circle cx="12" cy="7" r="4" />
508
+ <path d="M5.5 21a6.5 6.5 0 0 1 13 0Z" />
509
+ </svg>
510
+ {recentDeployments?.[0]?.created_by
511
+ ? `Local deploys by ${recentDeployments[0].created_by}`
512
+ : 'Local/adhoc deployments'}
513
+ </div>
514
+ )}
515
+
516
+ {/* Separator */}
517
+ <div
518
+ style={{
519
+ height: '1px',
520
+ backgroundColor: '#e2e8f0',
521
+ marginBottom: '8px',
522
+ }}
523
+ />
555
524
 
556
- {/* User + detail */}
525
+ {/* Recent deployments list */}
526
+ {recentDeployments?.length > 0 ? (
527
+ recentDeployments.map((d, idx) => {
528
+ const isGit = d.source?.type === 'git';
529
+ const statusColor =
530
+ d.status === 'success'
531
+ ? '#22c55e'
532
+ : d.status === 'failed'
533
+ ? '#ef4444'
534
+ : '#94a3b8';
535
+
536
+ const commitUrl =
537
+ isGit && d.source?.repository && d.source?.commit_sha
538
+ ? `${
539
+ d.source.repository.startsWith('http')
540
+ ? d.source.repository
541
+ : `https://${d.source.repository}`
542
+ }/commit/${d.source.commit_sha}`
543
+ : null;
544
+
545
+ const detail = isGit
546
+ ? d.source?.branch || 'main'
547
+ : d.source?.reason || d.source?.hostname || 'adhoc';
548
+
549
+ const shortSha = d.source?.commit_sha?.slice(0, 7);
550
+
551
+ return (
557
552
  <div
553
+ key={`${d.uuid}-${idx}`}
558
554
  style={{
559
- display: 'flex',
555
+ display: 'grid',
556
+ gridTemplateColumns: '18px 1fr auto',
560
557
  alignItems: 'center',
561
- gap: '6px',
562
- minWidth: 0,
558
+ gap: '8px',
559
+ padding: '6px 0',
560
+ borderBottom:
561
+ idx === recentDeployments.length - 1
562
+ ? 'none'
563
+ : '1px solid #f1f5f9',
564
+ fontSize: '12px',
563
565
  }}
564
566
  >
565
- <span
567
+ {/* Status dot */}
568
+ <div
566
569
  style={{
567
- fontWeight: 500,
568
- color: '#0f172a',
569
- whiteSpace: 'nowrap',
570
+ width: '8px',
571
+ height: '8px',
572
+ borderRadius: '50%',
573
+ backgroundColor: statusColor,
574
+ }}
575
+ title={d.status}
576
+ />
577
+
578
+ {/* User + detail */}
579
+ <div
580
+ style={{
581
+ display: 'flex',
582
+ alignItems: 'center',
583
+ gap: '6px',
584
+ minWidth: 0,
570
585
  }}
571
586
  >
572
- {d.created_by || 'unknown'}
573
- </span>
574
- <span style={{ color: '#cbd5e1' }}>—</span>
575
- {isGit ? (
576
- <>
587
+ <span
588
+ style={{
589
+ fontWeight: 500,
590
+ color: '#0f172a',
591
+ whiteSpace: 'nowrap',
592
+ }}
593
+ >
594
+ {d.created_by || 'unknown'}
595
+ </span>
596
+ <span style={{ color: '#cbd5e1' }}>—</span>
597
+ {isGit ? (
598
+ <>
599
+ <span
600
+ style={{
601
+ color: '#64748b',
602
+ whiteSpace: 'nowrap',
603
+ }}
604
+ >
605
+ {detail}
606
+ </span>
607
+ {shortSha && (
608
+ <>
609
+ <span style={{ color: '#cbd5e1' }}>@</span>
610
+ {commitUrl ? (
611
+ <a
612
+ href={commitUrl}
613
+ target="_blank"
614
+ rel="noopener noreferrer"
615
+ style={{
616
+ fontFamily: 'monospace',
617
+ fontSize: '11px',
618
+ color: '#3b82f6',
619
+ textDecoration: 'none',
620
+ }}
621
+ >
622
+ {shortSha}
623
+ </a>
624
+ ) : (
625
+ <span
626
+ style={{
627
+ fontFamily: 'monospace',
628
+ fontSize: '11px',
629
+ color: '#64748b',
630
+ }}
631
+ >
632
+ {shortSha}
633
+ </span>
634
+ )}
635
+ </>
636
+ )}
637
+ </>
638
+ ) : (
577
639
  <span
578
640
  style={{
579
641
  color: '#64748b',
642
+ overflow: 'hidden',
643
+ textOverflow: 'ellipsis',
580
644
  whiteSpace: 'nowrap',
581
645
  }}
582
646
  >
583
647
  {detail}
584
648
  </span>
585
- {shortSha && (
586
- <>
587
- <span style={{ color: '#cbd5e1' }}>@</span>
588
- {commitUrl ? (
589
- <a
590
- href={commitUrl}
591
- target="_blank"
592
- rel="noopener noreferrer"
593
- style={{
594
- fontFamily: 'monospace',
595
- fontSize: '11px',
596
- color: '#3b82f6',
597
- textDecoration: 'none',
598
- }}
599
- >
600
- {shortSha}
601
- </a>
602
- ) : (
603
- <span
604
- style={{
605
- fontFamily: 'monospace',
606
- fontSize: '11px',
607
- color: '#64748b',
608
- }}
609
- >
610
- {shortSha}
611
- </span>
612
- )}
613
- </>
614
- )}
615
- </>
616
- ) : (
617
- <span
618
- style={{
619
- color: '#64748b',
620
- overflow: 'hidden',
621
- textOverflow: 'ellipsis',
622
- whiteSpace: 'nowrap',
623
- }}
624
- >
625
- {detail}
626
- </span>
627
- )}
628
- </div>
649
+ )}
650
+ </div>
629
651
 
630
- {/* Timestamp */}
631
- <span
632
- style={{
633
- color: '#94a3b8',
634
- fontSize: '11px',
635
- whiteSpace: 'nowrap',
636
- }}
637
- >
638
- {new Date(d.created_at).toLocaleDateString()}
639
- </span>
640
- </div>
641
- );
642
- })
643
- ) : (
644
- <div style={{ color: '#94a3b8', fontSize: '12px' }}>
645
- No recent deployments
646
- </div>
647
- )}
648
- </div>
649
- )}
650
- </div>
651
- )}
652
- </div>
653
-
654
- {/* Right side: git actions + children */}
655
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
656
- {/* Git controls for non-branch namespaces */}
657
- {namespace && !isBranchNamespace && !gitConfigLoading && (
658
- <>
659
- <button
660
- style={buttonStyle}
661
- onClick={() => setShowGitSettings(true)}
662
- title="Git Settings"
663
- >
664
- <svg
665
- xmlns="http://www.w3.org/2000/svg"
666
- width="14"
667
- height="14"
668
- viewBox="0 0 24 24"
669
- fill="none"
670
- stroke="currentColor"
671
- strokeWidth="2"
672
- strokeLinecap="round"
673
- strokeLinejoin="round"
674
- >
675
- <circle cx="12" cy="12" r="3" />
676
- <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
677
- </svg>
678
- Git Settings
679
- </button>
680
- {hasGitConfig ? (
652
+ {/* Timestamp */}
653
+ <span
654
+ style={{
655
+ color: '#94a3b8',
656
+ fontSize: '11px',
657
+ whiteSpace: 'nowrap',
658
+ }}
659
+ >
660
+ {new Date(d.created_at).toLocaleDateString()}
661
+ </span>
662
+ </div>
663
+ );
664
+ })
665
+ ) : (
666
+ <div style={{ color: '#94a3b8', fontSize: '12px' }}>
667
+ No recent deployments
668
+ </div>
669
+ )}
670
+ </div>
671
+ )}
672
+ </div>
673
+ )}
674
+ </div>
675
+
676
+ {/* Right side: git actions + children */}
677
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
678
+ {/* Git controls for non-branch namespaces */}
679
+ {namespace && !isBranchNamespace && !gitConfigLoading && (
680
+ <>
681
681
  <button
682
- style={primaryButtonStyle}
683
- onClick={() => setShowCreateBranch(true)}
682
+ style={buttonStyle}
683
+ onClick={() => setShowGitSettings(true)}
684
+ title="Git Settings"
684
685
  >
685
686
  <svg
686
687
  xmlns="http://www.w3.org/2000/svg"
@@ -693,70 +694,45 @@ export default function NamespaceHeader({
693
694
  strokeLinecap="round"
694
695
  strokeLinejoin="round"
695
696
  >
696
- <line x1="12" y1="5" x2="12" y2="19" />
697
- <line x1="5" y1="12" x2="19" y2="12" />
697
+ <circle cx="12" cy="12" r="3" />
698
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
698
699
  </svg>
699
- New Branch
700
+ Git Settings
700
701
  </button>
701
- ) : (
702
- <></>
703
- )}
704
- </>
705
- )}
706
-
707
- {/* Git controls for branch namespaces */}
708
- {isBranchNamespace && hasGitConfig && (
709
- <>
710
- <button style={buttonStyle} onClick={() => setShowSyncToGit(true)}>
711
- <svg
712
- xmlns="http://www.w3.org/2000/svg"
713
- width="14"
714
- height="14"
715
- viewBox="0 0 24 24"
716
- fill="none"
717
- stroke="currentColor"
718
- strokeWidth="2"
719
- strokeLinecap="round"
720
- strokeLinejoin="round"
721
- >
722
- <path d="M21 12a9 9 0 0 1-9 9m9-9a9 9 0 0 0-9-9m9 9H3m9 9a9 9 0 0 1-9-9m9 9c1.66 0 3-4.03 3-9s-1.34-9-3-9m0 18c-1.66 0-3-4.03-3-9s1.34-9 3-9m-9 9a9 9 0 0 1 9-9" />
723
- </svg>
724
- Sync to Git
725
- </button>
726
- {existingPR ? (
727
- <a
728
- href={existingPR.pr_url}
729
- target="_blank"
730
- rel="noopener noreferrer"
731
- style={{
732
- ...primaryButtonStyle,
733
- textDecoration: 'none',
734
- backgroundColor: '#16a34a',
735
- borderColor: '#16a34a',
736
- }}
737
- >
738
- <svg
739
- xmlns="http://www.w3.org/2000/svg"
740
- width="14"
741
- height="14"
742
- viewBox="0 0 24 24"
743
- fill="none"
744
- stroke="currentColor"
745
- strokeWidth="2"
746
- strokeLinecap="round"
747
- strokeLinejoin="round"
702
+ {canCreateBranches ? (
703
+ <button
704
+ style={primaryButtonStyle}
705
+ onClick={() => setShowCreateBranch(true)}
748
706
  >
749
- <circle cx="18" cy="18" r="3" />
750
- <circle cx="6" cy="6" r="3" />
751
- <path d="M13 6h3a2 2 0 0 1 2 2v7" />
752
- <line x1="6" y1="9" x2="6" y2="21" />
753
- </svg>
754
- View PR #{existingPR.pr_number}
755
- </a>
756
- ) : (
707
+ <svg
708
+ xmlns="http://www.w3.org/2000/svg"
709
+ width="14"
710
+ height="14"
711
+ viewBox="0 0 24 24"
712
+ fill="none"
713
+ stroke="currentColor"
714
+ strokeWidth="2"
715
+ strokeLinecap="round"
716
+ strokeLinejoin="round"
717
+ >
718
+ <line x1="12" y1="5" x2="12" y2="19" />
719
+ <line x1="5" y1="12" x2="19" y2="12" />
720
+ </svg>
721
+ New Branch
722
+ </button>
723
+ ) : (
724
+ <></>
725
+ )}
726
+ </>
727
+ )}
728
+
729
+ {/* Git controls for branch namespaces */}
730
+ {isBranchNamespace && hasGitConfig && (
731
+ <>
757
732
  <button
758
- style={primaryButtonStyle}
759
- onClick={() => setShowCreatePR(true)}
733
+ style={buttonStyle}
734
+ onClick={() => setShowGitSettings(true)}
735
+ title="Git Settings"
760
736
  >
761
737
  <svg
762
738
  xmlns="http://www.w3.org/2000/svg"
@@ -769,91 +745,198 @@ export default function NamespaceHeader({
769
745
  strokeLinecap="round"
770
746
  strokeLinejoin="round"
771
747
  >
772
- <circle cx="18" cy="18" r="3" />
773
- <circle cx="6" cy="6" r="3" />
774
- <path d="M13 6h3a2 2 0 0 1 2 2v7" />
775
- <line x1="6" y1="9" x2="6" y2="21" />
748
+ <circle cx="12" cy="12" r="3" />
749
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
776
750
  </svg>
777
- Create PR
751
+ Git Settings
778
752
  </button>
779
- )}
780
- <button
781
- style={{
782
- ...buttonStyle,
783
- color: '#dc2626',
784
- borderColor: '#fecaca',
785
- }}
786
- onClick={() => setShowDeleteBranch(true)}
787
- title="Delete Branch"
788
- >
789
- <svg
790
- xmlns="http://www.w3.org/2000/svg"
791
- width="14"
792
- height="14"
793
- viewBox="0 0 24 24"
794
- fill="none"
795
- stroke="currentColor"
796
- strokeWidth="2"
797
- strokeLinecap="round"
798
- strokeLinejoin="round"
799
- >
800
- <path d="M3 6h18" />
801
- <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
802
- <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
803
- </svg>
804
- </button>
805
- </>
806
- )}
807
753
 
808
- {/* Additional actions passed as children */}
809
- {children}
754
+ {/* Sync to Git and Create PR only for editable branches (git_only = false) */}
755
+ {!gitConfig?.git_only && (
756
+ <>
757
+ <button
758
+ style={buttonStyle}
759
+ onClick={() => setShowSyncToGit(true)}
760
+ >
761
+ <svg
762
+ xmlns="http://www.w3.org/2000/svg"
763
+ width="14"
764
+ height="14"
765
+ viewBox="0 0 24 24"
766
+ fill="none"
767
+ stroke="currentColor"
768
+ strokeWidth="2"
769
+ strokeLinecap="round"
770
+ strokeLinejoin="round"
771
+ >
772
+ <path d="M21 12a9 9 0 0 1-9 9m9-9a9 9 0 0 0-9-9m9 9H3m9 9a9 9 0 0 1-9-9m9 9c1.66 0 3-4.03 3-9s-1.34-9-3-9m0 18c-1.66 0-3-4.03-3-9s1.34-9 3-9m-9 9a9 9 0 0 1 9-9" />
773
+ </svg>
774
+ Sync to Git
775
+ </button>
776
+ {prLoading ? (
777
+ <button
778
+ style={{
779
+ ...buttonStyle,
780
+ cursor: 'default',
781
+ opacity: 0.6,
782
+ }}
783
+ disabled
784
+ >
785
+ <svg
786
+ xmlns="http://www.w3.org/2000/svg"
787
+ width="14"
788
+ height="14"
789
+ viewBox="0 0 24 24"
790
+ fill="none"
791
+ stroke="currentColor"
792
+ strokeWidth="2"
793
+ strokeLinecap="round"
794
+ strokeLinejoin="round"
795
+ style={{ animation: 'spin 1s linear infinite' }}
796
+ >
797
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
798
+ </svg>
799
+ Checking PR...
800
+ </button>
801
+ ) : existingPR ? (
802
+ <a
803
+ href={existingPR.pr_url}
804
+ target="_blank"
805
+ rel="noopener noreferrer"
806
+ style={{
807
+ ...primaryButtonStyle,
808
+ textDecoration: 'none',
809
+ backgroundColor: '#16a34a',
810
+ borderColor: '#16a34a',
811
+ }}
812
+ >
813
+ <svg
814
+ xmlns="http://www.w3.org/2000/svg"
815
+ width="14"
816
+ height="14"
817
+ viewBox="0 0 24 24"
818
+ fill="none"
819
+ stroke="currentColor"
820
+ strokeWidth="2"
821
+ strokeLinecap="round"
822
+ strokeLinejoin="round"
823
+ >
824
+ <circle cx="18" cy="18" r="3" />
825
+ <circle cx="6" cy="6" r="3" />
826
+ <path d="M13 6h3a2 2 0 0 1 2 2v7" />
827
+ <line x1="6" y1="9" x2="6" y2="21" />
828
+ </svg>
829
+ View PR #{existingPR.pr_number}
830
+ </a>
831
+ ) : (
832
+ <button
833
+ style={primaryButtonStyle}
834
+ onClick={() => setShowCreatePR(true)}
835
+ >
836
+ <svg
837
+ xmlns="http://www.w3.org/2000/svg"
838
+ width="14"
839
+ height="14"
840
+ viewBox="0 0 24 24"
841
+ fill="none"
842
+ stroke="currentColor"
843
+ strokeWidth="2"
844
+ strokeLinecap="round"
845
+ strokeLinejoin="round"
846
+ >
847
+ <circle cx="18" cy="18" r="3" />
848
+ <circle cx="6" cy="6" r="3" />
849
+ <path d="M13 6h3a2 2 0 0 1 2 2v7" />
850
+ <line x1="6" y1="9" x2="6" y2="21" />
851
+ </svg>
852
+ Create PR
853
+ </button>
854
+ )}
855
+
856
+ {/* Delete Branch - only for editable branches */}
857
+ <button
858
+ style={{
859
+ ...buttonStyle,
860
+ color: '#dc2626',
861
+ borderColor: '#fecaca',
862
+ }}
863
+ onClick={() => setShowDeleteBranch(true)}
864
+ title="Delete Branch"
865
+ >
866
+ <svg
867
+ xmlns="http://www.w3.org/2000/svg"
868
+ width="14"
869
+ height="14"
870
+ viewBox="0 0 24 24"
871
+ fill="none"
872
+ stroke="currentColor"
873
+ strokeWidth="2"
874
+ strokeLinecap="round"
875
+ strokeLinejoin="round"
876
+ >
877
+ <path d="M3 6h18" />
878
+ <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
879
+ <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
880
+ </svg>
881
+ </button>
882
+ </>
883
+ )}
884
+ </>
885
+ )}
886
+
887
+ {/* Additional actions passed as children */}
888
+ {children}
889
+ </div>
890
+
891
+ {/* Modals */}
892
+ <GitSettingsModal
893
+ isOpen={showGitSettings}
894
+ onClose={() => setShowGitSettings(false)}
895
+ onSave={handleSaveGitConfig}
896
+ onRemove={handleRemoveGitConfig}
897
+ currentConfig={gitConfig}
898
+ namespace={namespace}
899
+ />
900
+
901
+ <CreateBranchModal
902
+ isOpen={showCreateBranch}
903
+ onClose={() => setShowCreateBranch(false)}
904
+ onCreate={handleCreateBranch}
905
+ namespace={namespace}
906
+ gitBranch={gitConfig?.git_branch || gitConfig?.default_branch}
907
+ isGitRoot={isGitRoot}
908
+ />
909
+
910
+ <SyncToGitModal
911
+ isOpen={showSyncToGit}
912
+ onClose={() => setShowSyncToGit(false)}
913
+ onSync={handleSyncToGit}
914
+ namespace={namespace}
915
+ gitBranch={gitConfig?.git_branch}
916
+ repoPath={gitConfig?.github_repo_path}
917
+ />
918
+
919
+ <CreatePRModal
920
+ isOpen={showCreatePR}
921
+ onClose={() => setShowCreatePR(false)}
922
+ onCreate={handleCreatePR}
923
+ namespace={namespace}
924
+ gitBranch={gitConfig?.git_branch}
925
+ parentBranch={
926
+ parentGitConfig?.git_branch || parentGitConfig?.default_branch
927
+ }
928
+ repoPath={gitConfig?.github_repo_path}
929
+ />
930
+
931
+ <DeleteBranchModal
932
+ isOpen={showDeleteBranch}
933
+ onClose={() => setShowDeleteBranch(false)}
934
+ onDelete={handleDeleteBranch}
935
+ namespace={namespace}
936
+ gitBranch={gitConfig?.git_branch}
937
+ parentNamespace={gitConfig?.parent_namespace}
938
+ />
810
939
  </div>
811
-
812
- {/* Modals */}
813
- <GitSettingsModal
814
- isOpen={showGitSettings}
815
- onClose={() => setShowGitSettings(false)}
816
- onSave={handleSaveGitConfig}
817
- onRemove={handleRemoveGitConfig}
818
- currentConfig={gitConfig}
819
- namespace={namespace}
820
- />
821
-
822
- <CreateBranchModal
823
- isOpen={showCreateBranch}
824
- onClose={() => setShowCreateBranch(false)}
825
- onCreate={handleCreateBranch}
826
- namespace={namespace}
827
- gitBranch={gitConfig?.git_branch}
828
- />
829
-
830
- <SyncToGitModal
831
- isOpen={showSyncToGit}
832
- onClose={() => setShowSyncToGit(false)}
833
- onSync={handleSyncToGit}
834
- namespace={namespace}
835
- gitBranch={gitConfig?.git_branch}
836
- repoPath={gitConfig?.github_repo_path}
837
- />
838
-
839
- <CreatePRModal
840
- isOpen={showCreatePR}
841
- onClose={() => setShowCreatePR(false)}
842
- onCreate={handleCreatePR}
843
- namespace={namespace}
844
- gitBranch={gitConfig?.git_branch}
845
- parentBranch={parentGitConfig?.git_branch}
846
- repoPath={gitConfig?.github_repo_path}
847
- />
848
-
849
- <DeleteBranchModal
850
- isOpen={showDeleteBranch}
851
- onClose={() => setShowDeleteBranch(false)}
852
- onDelete={handleDeleteBranch}
853
- namespace={namespace}
854
- gitBranch={gitConfig?.git_branch}
855
- parentNamespace={gitConfig?.parent_namespace}
856
- />
857
- </div>
940
+ </>
858
941
  );
859
942
  }