@devtron-labs/devtron-fe-common-lib 0.0.10-beta-9 → 0.0.10-beta-13
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/dist/Common/ImageTags.d.ts +1 -1
- package/dist/Common/Types.d.ts +2 -0
- package/dist/index.js +252 -256
- package/package.json +1 -1
- package/src/Assets/Icon/ic-enter.svg +3 -0
- package/src/Assets/Icon/ic-error-exclamation.svg +6 -0
- package/src/Common/ImageTags.tsx +151 -60
- package/src/Common/Types.ts +2 -0
package/package.json
CHANGED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M12.6667 4C12.4899 4 12.3203 4.07024 12.1953 4.19526C12.0703 4.32029 12.0001 4.48986 12.0001 4.66667V7.33333C12.0001 7.51014 11.9298 7.67971 11.8048 7.80474C11.6798 7.92976 11.5102 8 11.3334 8H4.94005L5.80672 7.14C5.93226 7.01446 6.00278 6.8442 6.00278 6.66667C6.00278 6.48913 5.93226 6.31887 5.80672 6.19333C5.68118 6.0678 5.51092 5.99727 5.33339 5.99727C5.15585 5.99727 4.98559 6.0678 4.86005 6.19333L2.86005 8.19333C2.79936 8.25674 2.75178 8.3315 2.72005 8.41333C2.65337 8.57564 2.65337 8.75769 2.72005 8.92C2.75178 9.00183 2.79936 9.0766 2.86005 9.14L4.86005 11.14C4.92203 11.2025 4.99576 11.2521 5.077 11.2859C5.15824 11.3198 5.24538 11.3372 5.33339 11.3372C5.42139 11.3372 5.50853 11.3198 5.58977 11.2859C5.67101 11.2521 5.74474 11.2025 5.80672 11.14C5.86921 11.078 5.9188 11.0043 5.95265 10.9231C5.98649 10.8418 6.00392 10.7547 6.00392 10.6667C6.00392 10.5787 5.98649 10.4915 5.95265 10.4103C5.9188 10.329 5.86921 10.2553 5.80672 10.1933L4.94005 9.33333H11.3334C11.8638 9.33333 12.3725 9.12262 12.7476 8.74755C13.1227 8.37247 13.3334 7.86377 13.3334 7.33333V4.66667C13.3334 4.48986 13.2631 4.32029 13.1381 4.19526C13.0131 4.07024 12.8435 4 12.6667 4Z" fill="#3B444C"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<path fill="#FFF" d="M5 5H15V15H5z"/>
|
|
4
|
+
<path fill="#F33E3E" d="M10 18.333c-4.6 0-8.333-3.733-8.333-8.333S5.4 1.667 10 1.667 18.333 5.4 18.333 10 14.6 18.333 10 18.333zm0-12.5c-.46 0-.833.373-.833.834V10c0 .46.373.833.833.833.46 0 .833-.373.833-.833V6.667c0-.46-.373-.834-.833-.834zm0 6.667c-.46 0-.833.373-.833.833 0 .46.373.834.833.834.46 0 .833-.373.833-.834 0-.46-.373-.833-.833-.833z"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
package/src/Common/ImageTags.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
|
2
2
|
import { ReactComponent as Add } from '../Assets/Icon/ic-add.svg'
|
|
3
|
-
import Creatable from 'react-select/creatable'
|
|
4
3
|
import { ReactComponent as Close } from '../Assets/Icon/ic-cross.svg'
|
|
5
4
|
import { ReactComponent as QuestionFilled } from '../Assets/Icon/ic-help.svg'
|
|
6
5
|
import { ReactComponent as Question } from '../Assets/Icon/ic-help-outline.svg'
|
|
@@ -11,11 +10,14 @@ import { ReactComponent as Minus } from '../Assets/Icon/ic-minus.svg'
|
|
|
11
10
|
|
|
12
11
|
import { ReactComponent as Info } from '../Assets/Icon/ic-info-filled.svg'
|
|
13
12
|
import { ReactComponent as Error } from '../Assets/Icon/ic-warning.svg'
|
|
13
|
+
import { ReactComponent as Warning } from '../Assets/Icon/ic-error-exclamation.svg'
|
|
14
|
+
import { ReactComponent as Enter } from '../Assets/Icon/ic-enter.svg'
|
|
14
15
|
import { ImageButtonType, ImageTaggingContainerType, ReleaseTag, TippyTheme } from './Types'
|
|
15
16
|
import { showError, stopPropagation } from './Helper'
|
|
16
17
|
import { TippyCustomized } from './TippyCustomized'
|
|
17
18
|
import { setImageTags, getUserRole } from './Common.service'
|
|
18
19
|
import Tippy from '@tippyjs/react'
|
|
20
|
+
import { ServerErrors } from './ServerError'
|
|
19
21
|
|
|
20
22
|
export const ImageTagsContainer = ({
|
|
21
23
|
ciPipelineId,
|
|
@@ -41,6 +43,8 @@ export const ImageTagsContainer = ({
|
|
|
41
43
|
const [hardDeleteTags, setHardDeleteTags] = useState<ReleaseTag[]>([])
|
|
42
44
|
const [isSuperAdmin, setSuperAdmin] = useState<boolean>(false)
|
|
43
45
|
const [descriptionValidationMessage, setDescriptionValidationMessage] = useState<string>('')
|
|
46
|
+
const [textInput, setTextInput] = useState<string>('')
|
|
47
|
+
const [saveData, setSaveData] = useState<boolean>(false)
|
|
44
48
|
|
|
45
49
|
useEffect(() => {
|
|
46
50
|
initialise()
|
|
@@ -54,6 +58,13 @@ export const ImageTagsContainer = ({
|
|
|
54
58
|
setExistingTags(appReleaseTagNames ? appReleaseTagNames : [])
|
|
55
59
|
}, [appReleaseTagNames])
|
|
56
60
|
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (saveData) {
|
|
63
|
+
handleSave()
|
|
64
|
+
setSaveData(false)
|
|
65
|
+
}
|
|
66
|
+
}, [saveData])
|
|
67
|
+
|
|
57
68
|
async function initialise() {
|
|
58
69
|
try {
|
|
59
70
|
const userRole = await getUserRole()
|
|
@@ -74,15 +85,6 @@ export const ImageTagsContainer = ({
|
|
|
74
85
|
}
|
|
75
86
|
}
|
|
76
87
|
|
|
77
|
-
const CreatableComponents = useMemo(
|
|
78
|
-
() => ({
|
|
79
|
-
DropdownIndicator: () => null,
|
|
80
|
-
IndicatorSeparator: () => null,
|
|
81
|
-
Menu: () => null,
|
|
82
|
-
}),
|
|
83
|
-
[],
|
|
84
|
-
)
|
|
85
|
-
|
|
86
88
|
const handleEditClick = () => {
|
|
87
89
|
setIsEditing(!isEditing)
|
|
88
90
|
if (typeof toggleCardMode === 'function') {
|
|
@@ -127,9 +129,7 @@ export const ImageTagsContainer = ({
|
|
|
127
129
|
if (displayedTags[i].tagName.toLowerCase() === lowercaseValue) isTagExistsInDisplayedTags = true
|
|
128
130
|
}
|
|
129
131
|
if (isTagExistsInExistingTags || isTagExistsInDisplayedTags || lowercaseValue === 'latest') {
|
|
130
|
-
setTagErrorMessage(
|
|
131
|
-
'This tag is already being used in this application',
|
|
132
|
-
)
|
|
132
|
+
setTagErrorMessage('This tag is already being used in this application')
|
|
133
133
|
return
|
|
134
134
|
}
|
|
135
135
|
const newTag: ReleaseTag = {
|
|
@@ -138,10 +138,12 @@ export const ImageTagsContainer = ({
|
|
|
138
138
|
appId: 0,
|
|
139
139
|
deleted: false,
|
|
140
140
|
artifactId: 0,
|
|
141
|
+
duplicateTag: false,
|
|
141
142
|
}
|
|
142
143
|
setCreateTags([...createTags, newTag])
|
|
143
144
|
setDisplayedTags([...displayedTags, newTag])
|
|
144
145
|
setShowTagsWarning(true)
|
|
146
|
+
setTextInput('')
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
const handleTagSoftDelete = (index) => {
|
|
@@ -179,7 +181,34 @@ export const ImageTagsContainer = ({
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
|
184
|
+
const errorStateHandling = (err) => {
|
|
185
|
+
err.forEach(({ userMessage }) => {
|
|
186
|
+
const tagList = createTags.map((tags) => {
|
|
187
|
+
if (userMessage?.appReleaseTags?.includes(tags.tagName)) {
|
|
188
|
+
return {
|
|
189
|
+
...tags,
|
|
190
|
+
duplicateTag: true,
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return tags
|
|
194
|
+
})
|
|
195
|
+
setCreateTags(tagList)
|
|
196
|
+
|
|
197
|
+
const displayList = displayedTags.map((obj) => {
|
|
198
|
+
const matchingTag = tagList.find((tag) => tag.tagName === obj.tagName)
|
|
199
|
+
if (matchingTag) {
|
|
200
|
+
return { ...obj, duplicateTag: matchingTag.duplicateTag }
|
|
201
|
+
}
|
|
202
|
+
return obj
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
setDisplayedTags(displayList)
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
182
209
|
const handleSave = async () => {
|
|
210
|
+
if (tagErrorMessage) return
|
|
211
|
+
|
|
183
212
|
const payload = {
|
|
184
213
|
createTags: createTags,
|
|
185
214
|
softDeleteTags: softDeleteTags,
|
|
@@ -219,7 +248,22 @@ export const ImageTagsContainer = ({
|
|
|
219
248
|
setTagErrorMessage('')
|
|
220
249
|
})
|
|
221
250
|
.catch((err) => {
|
|
222
|
-
|
|
251
|
+
// Fix toast message
|
|
252
|
+
if (err.errors?.[0]?.userMessage?.appReleaseTags?.length) {
|
|
253
|
+
const customError: ServerErrors = {
|
|
254
|
+
...err,
|
|
255
|
+
errors: [
|
|
256
|
+
{
|
|
257
|
+
...err.errors?.[0],
|
|
258
|
+
userMessage: err.errors?.[0]?.internalMessage,
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
}
|
|
262
|
+
showError(customError)
|
|
263
|
+
errorStateHandling(err.errors)
|
|
264
|
+
} else {
|
|
265
|
+
showError(err)
|
|
266
|
+
}
|
|
223
267
|
})
|
|
224
268
|
}
|
|
225
269
|
|
|
@@ -248,7 +292,7 @@ export const ImageTagsContainer = ({
|
|
|
248
292
|
return (
|
|
249
293
|
<div className="h-250 fs-13 dc__overflow-scroll p-12">
|
|
250
294
|
<div>Release tags allow you to tag container images with readable and relatable tags eg. v1.0.</div>
|
|
251
|
-
<ul className=
|
|
295
|
+
<ul className="pl-20 mt-8">
|
|
252
296
|
<li>
|
|
253
297
|
A release tag can only be added if a workflow has CD pipelines deploying to Production
|
|
254
298
|
environments.
|
|
@@ -268,7 +312,7 @@ export const ImageTagsContainer = ({
|
|
|
268
312
|
|
|
269
313
|
if (newDescription === '' && displayedTags.length === 0 && !isEditing) {
|
|
270
314
|
return tagsEditable ? (
|
|
271
|
-
<div className="bcn-0 mt-8">
|
|
315
|
+
<div className="bcn-0 mt-8 add-image-button">
|
|
272
316
|
<AddImageButton handleEditClick={handleEditClick} />
|
|
273
317
|
</div>
|
|
274
318
|
) : (
|
|
@@ -276,6 +320,29 @@ export const ImageTagsContainer = ({
|
|
|
276
320
|
)
|
|
277
321
|
}
|
|
278
322
|
|
|
323
|
+
const handleKeydown = (event) => {
|
|
324
|
+
if (event.key === 'Enter' && event.target.value.trim()) {
|
|
325
|
+
handleTagCreate(event.target.value.trim())
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const setInputValue = (e) => {
|
|
330
|
+
setTextInput(e.target.value)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const enterTagCreate = () => {
|
|
334
|
+
if (textInput.trim()) {
|
|
335
|
+
handleTagCreate(textInput.trim())
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const onClickSave = () => {
|
|
340
|
+
if (textInput.trim()) {
|
|
341
|
+
handleTagCreate(textInput)
|
|
342
|
+
}
|
|
343
|
+
setSaveData(true)
|
|
344
|
+
}
|
|
345
|
+
|
|
279
346
|
return (
|
|
280
347
|
<div className="mt-8">
|
|
281
348
|
{isEditing && tagsEditable ? (
|
|
@@ -284,13 +351,30 @@ export const ImageTagsContainer = ({
|
|
|
284
351
|
<span>Release tags (eg. v1.0)</span>
|
|
285
352
|
<div className="flex row ml-0">{renderInfoCard()}</div>
|
|
286
353
|
</div>
|
|
287
|
-
<div className="mt-6" data-testid="add-tag-text-area">
|
|
288
|
-
<
|
|
354
|
+
<div className="mt-6 dc__position-rel-imp" data-testid="add-tag-text-area">
|
|
355
|
+
<input
|
|
356
|
+
className="form__input"
|
|
357
|
+
value={textInput}
|
|
358
|
+
onChange={setInputValue}
|
|
289
359
|
placeholder="Type a tag and press enter"
|
|
290
|
-
|
|
360
|
+
onKeyDown={handleKeydown}
|
|
291
361
|
ref={creatableRef}
|
|
292
|
-
components={CreatableComponents}
|
|
293
362
|
/>
|
|
363
|
+
{textInput.length > 0 && (
|
|
364
|
+
<div
|
|
365
|
+
onClick={enterTagCreate}
|
|
366
|
+
className="dc__position-abs bcn-0 flex cursor pt-2 pb-2 pr-4 pl-4 br-2 cn-7 dc__border dc__border-bottom-2"
|
|
367
|
+
style={{
|
|
368
|
+
top: '50%',
|
|
369
|
+
fontFamily: 'monospace',
|
|
370
|
+
right: '8px',
|
|
371
|
+
transform: 'translateY(-50%)',
|
|
372
|
+
}}
|
|
373
|
+
>
|
|
374
|
+
<Enter className="icon-dim-16 mr-4" />
|
|
375
|
+
Enter
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
294
378
|
</div>
|
|
295
379
|
|
|
296
380
|
{tagErrorMessage && (
|
|
@@ -317,6 +401,7 @@ export const ImageTagsContainer = ({
|
|
|
317
401
|
tagId={tag.id}
|
|
318
402
|
softDeleteTags={softDeleteTags}
|
|
319
403
|
isSuperAdmin={isSuperAdmin}
|
|
404
|
+
duplicateTag={tag?.duplicateTag}
|
|
320
405
|
/>
|
|
321
406
|
))}
|
|
322
407
|
</div>
|
|
@@ -327,7 +412,10 @@ export const ImageTagsContainer = ({
|
|
|
327
412
|
</div>
|
|
328
413
|
)}
|
|
329
414
|
<div className="cn-7 mt-12">Comment</div>
|
|
330
|
-
<div
|
|
415
|
+
<div
|
|
416
|
+
className="flex left flex-wrap dc__gap-8 w-100 mt-6 mb-12"
|
|
417
|
+
data-testid="add-image-comment-text-area"
|
|
418
|
+
>
|
|
331
419
|
<textarea
|
|
332
420
|
value={newDescription}
|
|
333
421
|
onChange={handleDescriptionChange}
|
|
@@ -358,7 +446,7 @@ export const ImageTagsContainer = ({
|
|
|
358
446
|
type="button"
|
|
359
447
|
onClick={(e) => {
|
|
360
448
|
stopPropagation(e)
|
|
361
|
-
|
|
449
|
+
onClickSave()
|
|
362
450
|
}}
|
|
363
451
|
>
|
|
364
452
|
Save
|
|
@@ -391,6 +479,7 @@ export const ImageTagsContainer = ({
|
|
|
391
479
|
tagId={tag.id}
|
|
392
480
|
softDeleteTags={softDeleteTags}
|
|
393
481
|
isSuperAdmin={isSuperAdmin}
|
|
482
|
+
duplicateTag={tag?.duplicateTag}
|
|
394
483
|
/>
|
|
395
484
|
))}
|
|
396
485
|
</div>
|
|
@@ -423,58 +512,60 @@ export const ImageTagButton = ({
|
|
|
423
512
|
tagId,
|
|
424
513
|
softDeleteTags,
|
|
425
514
|
isSuperAdmin,
|
|
515
|
+
duplicateTag,
|
|
426
516
|
}: ImageButtonType) => {
|
|
427
517
|
const IconComponent = isSoftDeleted ? Redo : Minus
|
|
518
|
+
const isInSoftDeleteTags = isSoftDeleted && softDeleteTags.some((tag) => tag.tagName === text)
|
|
519
|
+
const canTagBeHardDelete = tagId === 0 || isSuperAdmin
|
|
428
520
|
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (isEditing) {
|
|
437
|
-
setIsHovered(false)
|
|
521
|
+
const tabColor = () => {
|
|
522
|
+
if (duplicateTag) {
|
|
523
|
+
return 'cr-5 bcr-1 er-2'
|
|
524
|
+
} else if (isSoftDeleted) {
|
|
525
|
+
return 'cy-7 bcy-1 dc__strike-through ey-2'
|
|
526
|
+
} else {
|
|
527
|
+
return 'cn-9'
|
|
438
528
|
}
|
|
439
529
|
}
|
|
440
530
|
|
|
441
|
-
const isInSoftDeleteTags = isSoftDeleted && softDeleteTags.some((tag) => tag.tagName === text)
|
|
442
|
-
const canTagBeHardDelete = tagId === 0 || isSuperAdmin
|
|
443
|
-
|
|
444
531
|
return (
|
|
445
532
|
<div
|
|
446
|
-
className={`br-4 en-2 bw-1
|
|
447
|
-
|
|
448
|
-
}`}
|
|
449
|
-
onMouseEnter={handleMouseEnter}
|
|
450
|
-
onMouseLeave={handleMouseLeave}
|
|
533
|
+
className={`br-4 en-2 bw-1 mr-8 bcn-0 mb-4 pt-2 pr-8 pb-2 pl-8 tag-class flex ${
|
|
534
|
+
isEditing ? 'icon-hover' : ''
|
|
535
|
+
} ${tabColor()}`}
|
|
451
536
|
>
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
537
|
+
{duplicateTag ? (
|
|
538
|
+
<Warning className="icon-dim-12 mr-4" />
|
|
539
|
+
) : (
|
|
540
|
+
<Tippy
|
|
541
|
+
className="default-tt"
|
|
542
|
+
arrow={true}
|
|
543
|
+
placement="top"
|
|
544
|
+
content={isInSoftDeleteTags ? 'Restore tag' : 'Soft delete tag'}
|
|
545
|
+
>
|
|
546
|
+
<div
|
|
547
|
+
className={`action-icon mr-4 lh-16 pt-3 ${
|
|
548
|
+
isInSoftDeleteTags || (tagId !== 0 && !isSoftDeleted) ? 'show-icon' : ''
|
|
549
|
+
}`}
|
|
459
550
|
>
|
|
460
551
|
<IconComponent
|
|
461
|
-
className={`icon-dim-12
|
|
552
|
+
className={`icon-dim-12 cursor${isSoftDeleted ? 'scn-6' : 'fcn-6'} `}
|
|
462
553
|
data-testid={`${text}-tag-soft-delete`}
|
|
463
554
|
onClick={onSoftDeleteClick}
|
|
464
555
|
/>
|
|
465
|
-
</
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
</
|
|
556
|
+
</div>
|
|
557
|
+
</Tippy>
|
|
558
|
+
)}
|
|
559
|
+
{text}
|
|
560
|
+
<Tippy className="default-tt" arrow={true} placement="top" content="Remove tag">
|
|
561
|
+
<div className={`action-icon ml-4 lh-16 pt-3 ${canTagBeHardDelete ? 'show-icon' : ''}`}>
|
|
562
|
+
<Close
|
|
563
|
+
className="icon-dim-12 fcn-6 cursor"
|
|
564
|
+
data-testid={`${text}-tag-hard-delete`}
|
|
565
|
+
onClick={onHardDeleteClick}
|
|
566
|
+
/>
|
|
567
|
+
</div>
|
|
568
|
+
</Tippy>
|
|
478
569
|
</div>
|
|
479
570
|
)
|
|
480
571
|
}
|
package/src/Common/Types.ts
CHANGED
|
@@ -341,6 +341,7 @@ export interface ReleaseTag {
|
|
|
341
341
|
appId: number
|
|
342
342
|
deleted: boolean
|
|
343
343
|
artifactId: number
|
|
344
|
+
duplicateTag: boolean
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
export interface ImageComment {
|
|
@@ -371,6 +372,7 @@ export interface ImageButtonType {
|
|
|
371
372
|
tagId: number
|
|
372
373
|
softDeleteTags: any
|
|
373
374
|
isSuperAdmin: any
|
|
375
|
+
duplicateTag?: boolean
|
|
374
376
|
}
|
|
375
377
|
|
|
376
378
|
|