datajunction-ui 0.0.30 → 0.0.31
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
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { useContext, useEffect, useState } from 'react';
|
|
2
|
-
import HorizontalHierarchyIcon from '../icons/HorizontalHierarchyIcon';
|
|
1
|
+
import { useContext, useEffect, useState, useRef } from 'react';
|
|
3
2
|
import DJClientContext from '../providers/djclient';
|
|
4
3
|
|
|
5
|
-
export default function NamespaceHeader({ namespace }) {
|
|
4
|
+
export default function NamespaceHeader({ namespace, children }) {
|
|
6
5
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
7
6
|
const [sources, setSources] = useState(null);
|
|
7
|
+
const [recentDeployments, setRecentDeployments] = useState([]);
|
|
8
|
+
const [deploymentsDropdownOpen, setDeploymentsDropdownOpen] = useState(false);
|
|
9
|
+
const dropdownRef = useRef(null);
|
|
8
10
|
|
|
9
11
|
useEffect(() => {
|
|
10
12
|
const fetchSources = async () => {
|
|
@@ -12,6 +14,15 @@ export default function NamespaceHeader({ namespace }) {
|
|
|
12
14
|
try {
|
|
13
15
|
const data = await djClient.namespaceSources(namespace);
|
|
14
16
|
setSources(data);
|
|
17
|
+
|
|
18
|
+
// Fetch recent deployments for this namespace
|
|
19
|
+
try {
|
|
20
|
+
const deployments = await djClient.listDeployments(namespace, 5);
|
|
21
|
+
setRecentDeployments(deployments || []);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error('Failed to fetch deployments:', err);
|
|
24
|
+
setRecentDeployments([]);
|
|
25
|
+
}
|
|
15
26
|
} catch (e) {
|
|
16
27
|
// Silently fail - badge just won't show
|
|
17
28
|
}
|
|
@@ -20,71 +31,418 @@ export default function NamespaceHeader({ namespace }) {
|
|
|
20
31
|
fetchSources();
|
|
21
32
|
}, [djClient, namespace]);
|
|
22
33
|
|
|
34
|
+
// Close dropdown when clicking outside
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const handleClickOutside = event => {
|
|
37
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
38
|
+
setDeploymentsDropdownOpen(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
42
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
23
45
|
const namespaceParts = namespace ? namespace.split('.') : [];
|
|
24
|
-
const namespaceList = namespaceParts.map((piece, index) => {
|
|
25
|
-
return (
|
|
26
|
-
<li className="breadcrumb-item" key={index}>
|
|
27
|
-
<a
|
|
28
|
-
className="link-body-emphasis"
|
|
29
|
-
href={'/namespaces/' + namespaceParts.slice(0, index + 1).join('.')}
|
|
30
|
-
>
|
|
31
|
-
{piece}
|
|
32
|
-
</a>
|
|
33
|
-
</li>
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Render source badge
|
|
38
|
-
const renderSourceBadge = () => {
|
|
39
|
-
if (!sources || sources.total_deployments === 0) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const isGit = sources.primary_source?.type === 'git';
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<li
|
|
47
|
-
className="breadcrumb-item"
|
|
48
|
-
style={{ display: 'flex', alignItems: 'center' }}
|
|
49
|
-
>
|
|
50
|
-
<span
|
|
51
|
-
title={
|
|
52
|
-
isGit
|
|
53
|
-
? `CI-managed: ${sources.primary_source.repository}${
|
|
54
|
-
sources.primary_source.branch
|
|
55
|
-
? ` (${sources.primary_source.branch})`
|
|
56
|
-
: ''
|
|
57
|
-
}`
|
|
58
|
-
: 'Local/adhoc deployment'
|
|
59
|
-
}
|
|
60
|
-
style={{
|
|
61
|
-
display: 'inline-flex',
|
|
62
|
-
alignItems: 'center',
|
|
63
|
-
gap: '4px',
|
|
64
|
-
padding: '2px 8px',
|
|
65
|
-
fontSize: '11px',
|
|
66
|
-
borderRadius: '12px',
|
|
67
|
-
backgroundColor: isGit ? '#d4edda' : '#e2e3e5',
|
|
68
|
-
color: isGit ? '#155724' : '#383d41',
|
|
69
|
-
cursor: 'help',
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
{isGit ? '🔗' : '📁'}
|
|
73
|
-
{isGit ? 'CI' : 'Local'}
|
|
74
|
-
</span>
|
|
75
|
-
</li>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
78
46
|
|
|
79
47
|
return (
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
48
|
+
<div
|
|
49
|
+
style={{
|
|
50
|
+
display: 'flex',
|
|
51
|
+
justifyContent: 'space-between',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
padding: '12px 12px 12px 20px',
|
|
54
|
+
marginBottom: '16px',
|
|
55
|
+
borderTop: '1px solid #e2e8f0',
|
|
56
|
+
borderBottom: '1px solid #e2e8f0',
|
|
57
|
+
background: '#ffffff',
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
61
|
+
<a href="/" style={{ display: 'flex', alignItems: 'center' }}>
|
|
62
|
+
<svg
|
|
63
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
64
|
+
width="16"
|
|
65
|
+
height="16"
|
|
66
|
+
fill="currentColor"
|
|
67
|
+
viewBox="0 0 16 16"
|
|
68
|
+
>
|
|
69
|
+
<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" />
|
|
70
|
+
</svg>
|
|
84
71
|
</a>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
<svg
|
|
73
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
74
|
+
width="12"
|
|
75
|
+
height="12"
|
|
76
|
+
fill="#6c757d"
|
|
77
|
+
viewBox="0 0 16 16"
|
|
78
|
+
>
|
|
79
|
+
<path
|
|
80
|
+
fillRule="evenodd"
|
|
81
|
+
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"
|
|
82
|
+
/>
|
|
83
|
+
</svg>
|
|
84
|
+
{namespace ? (
|
|
85
|
+
namespaceParts.map((part, index, arr) => (
|
|
86
|
+
<span
|
|
87
|
+
key={index}
|
|
88
|
+
style={{
|
|
89
|
+
display: 'flex',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
gap: '8px',
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<a
|
|
95
|
+
href={`/namespaces/${arr.slice(0, index + 1).join('.')}`}
|
|
96
|
+
style={{
|
|
97
|
+
fontWeight: '400',
|
|
98
|
+
color: '#1e293b',
|
|
99
|
+
textDecoration: 'none',
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{part}
|
|
103
|
+
</a>
|
|
104
|
+
{index < arr.length - 1 && (
|
|
105
|
+
<svg
|
|
106
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
107
|
+
width="12"
|
|
108
|
+
height="12"
|
|
109
|
+
fill="#94a3b8"
|
|
110
|
+
viewBox="0 0 16 16"
|
|
111
|
+
>
|
|
112
|
+
<path
|
|
113
|
+
fillRule="evenodd"
|
|
114
|
+
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"
|
|
115
|
+
/>
|
|
116
|
+
</svg>
|
|
117
|
+
)}
|
|
118
|
+
</span>
|
|
119
|
+
))
|
|
120
|
+
) : (
|
|
121
|
+
<span style={{ fontWeight: '600', color: '#1e293b' }}>
|
|
122
|
+
All Namespaces
|
|
123
|
+
</span>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
{/* Deployment badge + dropdown */}
|
|
127
|
+
{sources && sources.total_deployments > 0 && (
|
|
128
|
+
<div
|
|
129
|
+
style={{ position: 'relative', marginLeft: '8px' }}
|
|
130
|
+
ref={dropdownRef}
|
|
131
|
+
>
|
|
132
|
+
<button
|
|
133
|
+
onClick={() =>
|
|
134
|
+
setDeploymentsDropdownOpen(!deploymentsDropdownOpen)
|
|
135
|
+
}
|
|
136
|
+
style={{
|
|
137
|
+
height: '32px',
|
|
138
|
+
padding: '0 12px',
|
|
139
|
+
fontSize: '12px',
|
|
140
|
+
border: 'none',
|
|
141
|
+
borderRadius: '4px',
|
|
142
|
+
backgroundColor: '#ffffff',
|
|
143
|
+
color: '#0b3d91',
|
|
144
|
+
cursor: 'pointer',
|
|
145
|
+
display: 'flex',
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
gap: '4px',
|
|
148
|
+
whiteSpace: 'nowrap',
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{sources.primary_source?.type === 'git' ? (
|
|
152
|
+
<>
|
|
153
|
+
<svg
|
|
154
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
155
|
+
width="12"
|
|
156
|
+
height="12"
|
|
157
|
+
viewBox="0 0 24 24"
|
|
158
|
+
fill="none"
|
|
159
|
+
stroke="currentColor"
|
|
160
|
+
strokeWidth="2"
|
|
161
|
+
strokeLinecap="round"
|
|
162
|
+
strokeLinejoin="round"
|
|
163
|
+
>
|
|
164
|
+
<line x1="6" y1="3" x2="6" y2="15"></line>
|
|
165
|
+
<circle cx="18" cy="6" r="3"></circle>
|
|
166
|
+
<circle cx="6" cy="18" r="3"></circle>
|
|
167
|
+
<path d="M18 9a9 9 0 0 1-9 9"></path>
|
|
168
|
+
</svg>
|
|
169
|
+
Git Managed
|
|
170
|
+
</>
|
|
171
|
+
) : (
|
|
172
|
+
<>
|
|
173
|
+
<svg
|
|
174
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
175
|
+
width="12"
|
|
176
|
+
height="12"
|
|
177
|
+
viewBox="0 0 24 24"
|
|
178
|
+
fill="none"
|
|
179
|
+
stroke="currentColor"
|
|
180
|
+
strokeWidth="2"
|
|
181
|
+
strokeLinecap="round"
|
|
182
|
+
strokeLinejoin="round"
|
|
183
|
+
>
|
|
184
|
+
<circle cx="12" cy="7" r="4" />
|
|
185
|
+
<path d="M5.5 21a6.5 6.5 0 0 1 13 0Z" />
|
|
186
|
+
</svg>
|
|
187
|
+
Local Deploy
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
<span style={{ fontSize: '8px' }}>
|
|
191
|
+
{deploymentsDropdownOpen ? '▲' : '▼'}
|
|
192
|
+
</span>
|
|
193
|
+
</button>
|
|
194
|
+
|
|
195
|
+
{deploymentsDropdownOpen && (
|
|
196
|
+
<div
|
|
197
|
+
style={{
|
|
198
|
+
position: 'absolute',
|
|
199
|
+
top: '100%',
|
|
200
|
+
left: 0,
|
|
201
|
+
marginTop: '4px',
|
|
202
|
+
padding: '12px',
|
|
203
|
+
backgroundColor: 'white',
|
|
204
|
+
border: '1px solid #ddd',
|
|
205
|
+
borderRadius: '8px',
|
|
206
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
|
207
|
+
zIndex: 1000,
|
|
208
|
+
minWidth: 'max-content',
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
{sources.primary_source?.type === 'git' ? (
|
|
212
|
+
<a
|
|
213
|
+
href={
|
|
214
|
+
sources.primary_source.repository?.startsWith('http')
|
|
215
|
+
? sources.primary_source.repository
|
|
216
|
+
: `https://${sources.primary_source.repository}`
|
|
217
|
+
}
|
|
218
|
+
target="_blank"
|
|
219
|
+
rel="noopener noreferrer"
|
|
220
|
+
style={{
|
|
221
|
+
display: 'flex',
|
|
222
|
+
alignItems: 'center',
|
|
223
|
+
gap: '8px',
|
|
224
|
+
fontSize: '13px',
|
|
225
|
+
fontWeight: 400,
|
|
226
|
+
textDecoration: 'none',
|
|
227
|
+
marginBottom: '12px',
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
<svg
|
|
231
|
+
width="16"
|
|
232
|
+
height="16"
|
|
233
|
+
viewBox="0 0 24 24"
|
|
234
|
+
fill="none"
|
|
235
|
+
stroke="currentColor"
|
|
236
|
+
strokeWidth="2"
|
|
237
|
+
strokeLinecap="round"
|
|
238
|
+
strokeLinejoin="round"
|
|
239
|
+
>
|
|
240
|
+
<line x1="6" y1="3" x2="6" y2="15" />
|
|
241
|
+
<circle cx="18" cy="6" r="3" />
|
|
242
|
+
<circle cx="6" cy="18" r="3" />
|
|
243
|
+
<path d="M18 9a9 9 0 0 1-9 9" />
|
|
244
|
+
</svg>
|
|
245
|
+
{sources.primary_source.repository}
|
|
246
|
+
{sources.primary_source.branch &&
|
|
247
|
+
` (${sources.primary_source.branch})`}
|
|
248
|
+
</a>
|
|
249
|
+
) : (
|
|
250
|
+
<div
|
|
251
|
+
style={{
|
|
252
|
+
display: 'flex',
|
|
253
|
+
alignItems: 'center',
|
|
254
|
+
gap: '8px',
|
|
255
|
+
fontSize: '13px',
|
|
256
|
+
fontWeight: 600,
|
|
257
|
+
color: '#0b3d91',
|
|
258
|
+
marginBottom: '12px',
|
|
259
|
+
}}
|
|
260
|
+
>
|
|
261
|
+
<svg
|
|
262
|
+
width="16"
|
|
263
|
+
height="16"
|
|
264
|
+
viewBox="0 0 24 24"
|
|
265
|
+
fill="none"
|
|
266
|
+
stroke="currentColor"
|
|
267
|
+
strokeWidth="2"
|
|
268
|
+
strokeLinecap="round"
|
|
269
|
+
strokeLinejoin="round"
|
|
270
|
+
>
|
|
271
|
+
<circle cx="12" cy="7" r="4" />
|
|
272
|
+
<path d="M5.5 21a6.5 6.5 0 0 1 13 0Z" />
|
|
273
|
+
</svg>
|
|
274
|
+
{recentDeployments?.[0]?.created_by
|
|
275
|
+
? `Local deploys by ${recentDeployments[0].created_by}`
|
|
276
|
+
: 'Local/adhoc deployments'}
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{/* Separator */}
|
|
281
|
+
<div
|
|
282
|
+
style={{
|
|
283
|
+
height: '1px',
|
|
284
|
+
backgroundColor: '#e2e8f0',
|
|
285
|
+
marginBottom: '8px',
|
|
286
|
+
}}
|
|
287
|
+
/>
|
|
288
|
+
|
|
289
|
+
{/* Recent deployments list */}
|
|
290
|
+
{recentDeployments?.length > 0 ? (
|
|
291
|
+
recentDeployments.map((d, idx) => {
|
|
292
|
+
const isGit = d.source?.type === 'git';
|
|
293
|
+
const statusColor =
|
|
294
|
+
d.status === 'success'
|
|
295
|
+
? '#22c55e'
|
|
296
|
+
: d.status === 'failed'
|
|
297
|
+
? '#ef4444'
|
|
298
|
+
: '#94a3b8';
|
|
299
|
+
|
|
300
|
+
const commitUrl =
|
|
301
|
+
isGit && d.source?.repository && d.source?.commit_sha
|
|
302
|
+
? `${
|
|
303
|
+
d.source.repository.startsWith('http')
|
|
304
|
+
? d.source.repository
|
|
305
|
+
: `https://${d.source.repository}`
|
|
306
|
+
}/commit/${d.source.commit_sha}`
|
|
307
|
+
: null;
|
|
308
|
+
|
|
309
|
+
const detail = isGit
|
|
310
|
+
? d.source?.branch || 'main'
|
|
311
|
+
: d.source?.reason || d.source?.hostname || 'adhoc';
|
|
312
|
+
|
|
313
|
+
const shortSha = d.source?.commit_sha?.slice(0, 7);
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<div
|
|
317
|
+
key={`${d.uuid}-${idx}`}
|
|
318
|
+
style={{
|
|
319
|
+
display: 'grid',
|
|
320
|
+
gridTemplateColumns: '18px 1fr auto',
|
|
321
|
+
alignItems: 'center',
|
|
322
|
+
gap: '8px',
|
|
323
|
+
padding: '6px 0',
|
|
324
|
+
borderBottom:
|
|
325
|
+
idx === recentDeployments.length - 1
|
|
326
|
+
? 'none'
|
|
327
|
+
: '1px solid #f1f5f9',
|
|
328
|
+
fontSize: '12px',
|
|
329
|
+
}}
|
|
330
|
+
>
|
|
331
|
+
{/* Status dot */}
|
|
332
|
+
<div
|
|
333
|
+
style={{
|
|
334
|
+
width: '8px',
|
|
335
|
+
height: '8px',
|
|
336
|
+
borderRadius: '50%',
|
|
337
|
+
backgroundColor: statusColor,
|
|
338
|
+
}}
|
|
339
|
+
title={d.status}
|
|
340
|
+
/>
|
|
341
|
+
|
|
342
|
+
{/* User + detail */}
|
|
343
|
+
<div
|
|
344
|
+
style={{
|
|
345
|
+
display: 'flex',
|
|
346
|
+
alignItems: 'center',
|
|
347
|
+
gap: '6px',
|
|
348
|
+
minWidth: 0,
|
|
349
|
+
}}
|
|
350
|
+
>
|
|
351
|
+
<span
|
|
352
|
+
style={{
|
|
353
|
+
fontWeight: 500,
|
|
354
|
+
color: '#0f172a',
|
|
355
|
+
whiteSpace: 'nowrap',
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
{d.created_by || 'unknown'}
|
|
359
|
+
</span>
|
|
360
|
+
<span style={{ color: '#cbd5e1' }}>—</span>
|
|
361
|
+
{isGit ? (
|
|
362
|
+
<>
|
|
363
|
+
<span
|
|
364
|
+
style={{
|
|
365
|
+
color: '#64748b',
|
|
366
|
+
whiteSpace: 'nowrap',
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
369
|
+
{detail}
|
|
370
|
+
</span>
|
|
371
|
+
{shortSha && (
|
|
372
|
+
<>
|
|
373
|
+
<span style={{ color: '#cbd5e1' }}>@</span>
|
|
374
|
+
{commitUrl ? (
|
|
375
|
+
<a
|
|
376
|
+
href={commitUrl}
|
|
377
|
+
target="_blank"
|
|
378
|
+
rel="noopener noreferrer"
|
|
379
|
+
style={{
|
|
380
|
+
fontFamily: 'monospace',
|
|
381
|
+
fontSize: '11px',
|
|
382
|
+
color: '#3b82f6',
|
|
383
|
+
textDecoration: 'none',
|
|
384
|
+
}}
|
|
385
|
+
>
|
|
386
|
+
{shortSha}
|
|
387
|
+
</a>
|
|
388
|
+
) : (
|
|
389
|
+
<span
|
|
390
|
+
style={{
|
|
391
|
+
fontFamily: 'monospace',
|
|
392
|
+
fontSize: '11px',
|
|
393
|
+
color: '#64748b',
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
{shortSha}
|
|
397
|
+
</span>
|
|
398
|
+
)}
|
|
399
|
+
</>
|
|
400
|
+
)}
|
|
401
|
+
</>
|
|
402
|
+
) : (
|
|
403
|
+
<span
|
|
404
|
+
style={{
|
|
405
|
+
color: '#64748b',
|
|
406
|
+
overflow: 'hidden',
|
|
407
|
+
textOverflow: 'ellipsis',
|
|
408
|
+
whiteSpace: 'nowrap',
|
|
409
|
+
}}
|
|
410
|
+
>
|
|
411
|
+
{detail}
|
|
412
|
+
</span>
|
|
413
|
+
)}
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
{/* Timestamp */}
|
|
417
|
+
<span
|
|
418
|
+
style={{
|
|
419
|
+
color: '#94a3b8',
|
|
420
|
+
fontSize: '11px',
|
|
421
|
+
whiteSpace: 'nowrap',
|
|
422
|
+
}}
|
|
423
|
+
>
|
|
424
|
+
{new Date(d.created_at).toLocaleDateString()}
|
|
425
|
+
</span>
|
|
426
|
+
</div>
|
|
427
|
+
);
|
|
428
|
+
})
|
|
429
|
+
) : (
|
|
430
|
+
<div style={{ color: '#94a3b8', fontSize: '12px' }}>
|
|
431
|
+
No recent deployments
|
|
432
|
+
</div>
|
|
433
|
+
)}
|
|
434
|
+
</div>
|
|
435
|
+
)}
|
|
436
|
+
</div>
|
|
437
|
+
)}
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
{/* Right side actions passed as children */}
|
|
441
|
+
{children && (
|
|
442
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
443
|
+
{children}
|
|
444
|
+
</div>
|
|
445
|
+
)}
|
|
446
|
+
</div>
|
|
89
447
|
);
|
|
90
448
|
}
|
|
@@ -25,6 +25,7 @@ describe('<NamespaceHeader />', () => {
|
|
|
25
25
|
branch: 'main',
|
|
26
26
|
},
|
|
27
27
|
}),
|
|
28
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
render(
|
|
@@ -41,8 +42,8 @@ describe('<NamespaceHeader />', () => {
|
|
|
41
42
|
);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
// Should render
|
|
45
|
-
expect(screen.getByText(/
|
|
45
|
+
// Should render Git Managed badge for git source
|
|
46
|
+
expect(screen.getByText(/Git Managed/)).toBeInTheDocument();
|
|
46
47
|
});
|
|
47
48
|
|
|
48
49
|
it('should render git source badge when source type is git without branch', async () => {
|
|
@@ -55,6 +56,7 @@ describe('<NamespaceHeader />', () => {
|
|
|
55
56
|
branch: null,
|
|
56
57
|
},
|
|
57
58
|
}),
|
|
59
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
render(
|
|
@@ -71,8 +73,8 @@ describe('<NamespaceHeader />', () => {
|
|
|
71
73
|
);
|
|
72
74
|
});
|
|
73
75
|
|
|
74
|
-
// Should render
|
|
75
|
-
expect(screen.getByText(/
|
|
76
|
+
// Should render Git Managed badge for git source even without branch
|
|
77
|
+
expect(screen.getByText(/Git Managed/)).toBeInTheDocument();
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
it('should render local source badge when source type is local', async () => {
|
|
@@ -84,6 +86,7 @@ describe('<NamespaceHeader />', () => {
|
|
|
84
86
|
hostname: 'localhost',
|
|
85
87
|
},
|
|
86
88
|
}),
|
|
89
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
87
90
|
};
|
|
88
91
|
|
|
89
92
|
render(
|
|
@@ -100,8 +103,8 @@ describe('<NamespaceHeader />', () => {
|
|
|
100
103
|
);
|
|
101
104
|
});
|
|
102
105
|
|
|
103
|
-
// Should render Local badge for local source
|
|
104
|
-
expect(screen.getByText(/Local/)).toBeInTheDocument();
|
|
106
|
+
// Should render Local Deploy badge for local source
|
|
107
|
+
expect(screen.getByText(/Local Deploy/)).toBeInTheDocument();
|
|
105
108
|
});
|
|
106
109
|
|
|
107
110
|
it('should not render badge when no deployments', async () => {
|
|
@@ -110,6 +113,7 @@ describe('<NamespaceHeader />', () => {
|
|
|
110
113
|
total_deployments: 0,
|
|
111
114
|
primary_source: null,
|
|
112
115
|
}),
|
|
116
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
113
117
|
};
|
|
114
118
|
|
|
115
119
|
render(
|
|
@@ -127,13 +131,14 @@ describe('<NamespaceHeader />', () => {
|
|
|
127
131
|
});
|
|
128
132
|
|
|
129
133
|
// Should not render any source badge
|
|
130
|
-
expect(screen.queryByText(/
|
|
131
|
-
expect(screen.queryByText(/Local/)).not.toBeInTheDocument();
|
|
134
|
+
expect(screen.queryByText(/Git Managed/)).not.toBeInTheDocument();
|
|
135
|
+
expect(screen.queryByText(/Local Deploy/)).not.toBeInTheDocument();
|
|
132
136
|
});
|
|
133
137
|
|
|
134
138
|
it('should handle API error gracefully', async () => {
|
|
135
139
|
const mockDjClient = {
|
|
136
140
|
namespaceSources: jest.fn().mockRejectedValue(new Error('API Error')),
|
|
141
|
+
listDeployments: jest.fn().mockResolvedValue([]),
|
|
137
142
|
};
|
|
138
143
|
|
|
139
144
|
render(
|
|
@@ -153,6 +158,6 @@ describe('<NamespaceHeader />', () => {
|
|
|
153
158
|
// Should still render breadcrumb without badge
|
|
154
159
|
expect(screen.getByText('test')).toBeInTheDocument();
|
|
155
160
|
expect(screen.getByText('namespace')).toBeInTheDocument();
|
|
156
|
-
expect(screen.queryByText(/
|
|
161
|
+
expect(screen.queryByText(/Git Managed/)).not.toBeInTheDocument();
|
|
157
162
|
});
|
|
158
163
|
});
|