@devtron-labs/devtron-fe-common-lib 0.0.10-beta-8 → 0.0.10-beta-12
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 +265 -251
- 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 +164 -44
- 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,10 +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'
|
|
19
|
+
import Tippy from '@tippyjs/react'
|
|
20
|
+
import { ServerErrors } from './ServerError'
|
|
18
21
|
|
|
19
22
|
export const ImageTagsContainer = ({
|
|
20
23
|
ciPipelineId,
|
|
@@ -40,6 +43,8 @@ export const ImageTagsContainer = ({
|
|
|
40
43
|
const [hardDeleteTags, setHardDeleteTags] = useState<ReleaseTag[]>([])
|
|
41
44
|
const [isSuperAdmin, setSuperAdmin] = useState<boolean>(false)
|
|
42
45
|
const [descriptionValidationMessage, setDescriptionValidationMessage] = useState<string>('')
|
|
46
|
+
const [textInput, setTextInput] = useState<string>('')
|
|
47
|
+
const [saveData, setSaveData] = useState<boolean>(false)
|
|
43
48
|
|
|
44
49
|
useEffect(() => {
|
|
45
50
|
initialise()
|
|
@@ -47,11 +52,18 @@ export const ImageTagsContainer = ({
|
|
|
47
52
|
|
|
48
53
|
useEffect(() => {
|
|
49
54
|
reInitState()
|
|
50
|
-
}, [imageReleaseTags,imageComment,tagsEditable])
|
|
55
|
+
}, [imageReleaseTags, imageComment, tagsEditable])
|
|
51
56
|
|
|
52
57
|
useEffect(() => {
|
|
53
58
|
setExistingTags(appReleaseTagNames ? appReleaseTagNames : [])
|
|
54
|
-
},[appReleaseTagNames])
|
|
59
|
+
}, [appReleaseTagNames])
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if(saveData){
|
|
63
|
+
handleSave()
|
|
64
|
+
setSaveData(false)
|
|
65
|
+
}
|
|
66
|
+
},[saveData])
|
|
55
67
|
|
|
56
68
|
async function initialise() {
|
|
57
69
|
try {
|
|
@@ -73,15 +85,6 @@ export const ImageTagsContainer = ({
|
|
|
73
85
|
}
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
const CreatableComponents = useMemo(
|
|
77
|
-
() => ({
|
|
78
|
-
DropdownIndicator: () => null,
|
|
79
|
-
IndicatorSeparator: () => null,
|
|
80
|
-
Menu: () => null,
|
|
81
|
-
}),
|
|
82
|
-
[],
|
|
83
|
-
)
|
|
84
|
-
|
|
85
88
|
const handleEditClick = () => {
|
|
86
89
|
setIsEditing(!isEditing)
|
|
87
90
|
if (typeof toggleCardMode === 'function') {
|
|
@@ -91,7 +94,9 @@ export const ImageTagsContainer = ({
|
|
|
91
94
|
|
|
92
95
|
const handleDescriptionChange = (e) => {
|
|
93
96
|
const description = e.target.value
|
|
94
|
-
description?.length > 500
|
|
97
|
+
description?.length > 500
|
|
98
|
+
? setDescriptionValidationMessage('comment length cannot exceed 500 characters')
|
|
99
|
+
: setDescriptionValidationMessage('')
|
|
95
100
|
setNewDescription(description)
|
|
96
101
|
}
|
|
97
102
|
|
|
@@ -108,8 +113,13 @@ export const ImageTagsContainer = ({
|
|
|
108
113
|
|
|
109
114
|
const handleTagCreate = (newValue) => {
|
|
110
115
|
const lowercaseValue = newValue.toLowerCase().trim()
|
|
111
|
-
if(
|
|
112
|
-
|
|
116
|
+
if (
|
|
117
|
+
lowercaseValue.length == 0 ||
|
|
118
|
+
lowercaseValue.length >= 128 ||
|
|
119
|
+
lowercaseValue[0] == '.' ||
|
|
120
|
+
lowercaseValue[0] == '-'
|
|
121
|
+
) {
|
|
122
|
+
setTagErrorMessage('tag name cannot be empty or exceed 128 characters or cannot start with . or -')
|
|
113
123
|
return
|
|
114
124
|
}
|
|
115
125
|
setTagErrorMessage('')
|
|
@@ -119,7 +129,7 @@ export const ImageTagsContainer = ({
|
|
|
119
129
|
if (displayedTags[i].tagName.toLowerCase() === lowercaseValue) isTagExistsInDisplayedTags = true
|
|
120
130
|
}
|
|
121
131
|
if (isTagExistsInExistingTags || isTagExistsInDisplayedTags || lowercaseValue === 'latest') {
|
|
122
|
-
setTagErrorMessage(
|
|
132
|
+
setTagErrorMessage('This tag is already being used in this application')
|
|
123
133
|
return
|
|
124
134
|
}
|
|
125
135
|
const newTag: ReleaseTag = {
|
|
@@ -128,10 +138,12 @@ export const ImageTagsContainer = ({
|
|
|
128
138
|
appId: 0,
|
|
129
139
|
deleted: false,
|
|
130
140
|
artifactId: 0,
|
|
141
|
+
duplicateTag: false,
|
|
131
142
|
}
|
|
132
143
|
setCreateTags([...createTags, newTag])
|
|
133
144
|
setDisplayedTags([...displayedTags, newTag])
|
|
134
145
|
setShowTagsWarning(true)
|
|
146
|
+
setTextInput('')
|
|
135
147
|
}
|
|
136
148
|
|
|
137
149
|
const handleTagSoftDelete = (index) => {
|
|
@@ -169,7 +181,34 @@ export const ImageTagsContainer = ({
|
|
|
169
181
|
}
|
|
170
182
|
}
|
|
171
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
|
+
|
|
172
209
|
const handleSave = async () => {
|
|
210
|
+
if(tagErrorMessage) return
|
|
211
|
+
|
|
173
212
|
const payload = {
|
|
174
213
|
createTags: createTags,
|
|
175
214
|
softDeleteTags: softDeleteTags,
|
|
@@ -209,20 +248,33 @@ export const ImageTagsContainer = ({
|
|
|
209
248
|
setTagErrorMessage('')
|
|
210
249
|
})
|
|
211
250
|
.catch((err) => {
|
|
212
|
-
|
|
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
|
+
}
|
|
213
267
|
})
|
|
214
268
|
}
|
|
215
269
|
|
|
216
|
-
|
|
217
270
|
const renderInfoCard = (): JSX.Element => {
|
|
218
271
|
return (
|
|
219
272
|
<TippyCustomized
|
|
220
273
|
theme={TippyTheme.white}
|
|
221
|
-
className="w-300
|
|
274
|
+
className="w-300 fcv-5"
|
|
222
275
|
placement="right"
|
|
223
276
|
Icon={QuestionFilled}
|
|
224
277
|
heading="Release tags"
|
|
225
|
-
infoText="Release tags allow you to tag container images with readable and relatable tags eg. v1.0."
|
|
226
278
|
showCloseButton={true}
|
|
227
279
|
trigger="click"
|
|
228
280
|
interactive={true}
|
|
@@ -238,8 +290,9 @@ export const ImageTagsContainer = ({
|
|
|
238
290
|
|
|
239
291
|
const getBuildContextAdditionalContent = () => {
|
|
240
292
|
return (
|
|
241
|
-
<div className="
|
|
242
|
-
<
|
|
293
|
+
<div className="h-250 fs-13 dc__overflow-scroll p-12">
|
|
294
|
+
<div>Release tags allow you to tag container images with readable and relatable tags eg. v1.0.</div>
|
|
295
|
+
<ul className="pl-20 mt-8">
|
|
243
296
|
<li>
|
|
244
297
|
A release tag can only be added if a workflow has CD pipelines deploying to Production
|
|
245
298
|
environments.
|
|
@@ -259,7 +312,7 @@ export const ImageTagsContainer = ({
|
|
|
259
312
|
|
|
260
313
|
if (newDescription === '' && displayedTags.length === 0 && !isEditing) {
|
|
261
314
|
return tagsEditable ? (
|
|
262
|
-
<div className="bcn-0 mt-8">
|
|
315
|
+
<div className="bcn-0 mt-8 add-image-button">
|
|
263
316
|
<AddImageButton handleEditClick={handleEditClick} />
|
|
264
317
|
</div>
|
|
265
318
|
) : (
|
|
@@ -267,6 +320,29 @@ export const ImageTagsContainer = ({
|
|
|
267
320
|
)
|
|
268
321
|
}
|
|
269
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
|
+
|
|
270
346
|
return (
|
|
271
347
|
<div className="mt-8">
|
|
272
348
|
{isEditing && tagsEditable ? (
|
|
@@ -275,13 +351,30 @@ export const ImageTagsContainer = ({
|
|
|
275
351
|
<span>Release tags (eg. v1.0)</span>
|
|
276
352
|
<div className="flex row ml-0">{renderInfoCard()}</div>
|
|
277
353
|
</div>
|
|
278
|
-
<div className="mt-6" data-testid="add-tag-text-area">
|
|
279
|
-
<
|
|
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}
|
|
280
359
|
placeholder="Type a tag and press enter"
|
|
281
|
-
|
|
360
|
+
onKeyDown={handleKeydown}
|
|
282
361
|
ref={creatableRef}
|
|
283
|
-
components={CreatableComponents}
|
|
284
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
|
+
)}
|
|
285
378
|
</div>
|
|
286
379
|
|
|
287
380
|
{tagErrorMessage && (
|
|
@@ -308,6 +401,7 @@ export const ImageTagsContainer = ({
|
|
|
308
401
|
tagId={tag.id}
|
|
309
402
|
softDeleteTags={softDeleteTags}
|
|
310
403
|
isSuperAdmin={isSuperAdmin}
|
|
404
|
+
duplicateTag={tag?.duplicateTag}
|
|
311
405
|
/>
|
|
312
406
|
))}
|
|
313
407
|
</div>
|
|
@@ -319,7 +413,7 @@ export const ImageTagsContainer = ({
|
|
|
319
413
|
)}
|
|
320
414
|
<div className="cn-7 mt-12">Comment</div>
|
|
321
415
|
<div
|
|
322
|
-
className="flex left flex-wrap dc__gap-8 w-100 mt-6"
|
|
416
|
+
className="flex left flex-wrap dc__gap-8 w-100 mt-6 mb-12"
|
|
323
417
|
data-testid="add-image-comment-text-area"
|
|
324
418
|
>
|
|
325
419
|
<textarea
|
|
@@ -329,7 +423,8 @@ export const ImageTagsContainer = ({
|
|
|
329
423
|
style={{ height: '90px !important' }}
|
|
330
424
|
/>
|
|
331
425
|
</div>
|
|
332
|
-
{
|
|
426
|
+
{descriptionValidationMessage !== '' && (
|
|
427
|
+
<div className="flex left">
|
|
333
428
|
<Error className="form__icon form__icon--error" />
|
|
334
429
|
<div className="form__error">{descriptionValidationMessage}</div>
|
|
335
430
|
</div>
|
|
@@ -351,7 +446,7 @@ export const ImageTagsContainer = ({
|
|
|
351
446
|
type="button"
|
|
352
447
|
onClick={(e) => {
|
|
353
448
|
stopPropagation(e)
|
|
354
|
-
|
|
449
|
+
onClickSave()
|
|
355
450
|
}}
|
|
356
451
|
>
|
|
357
452
|
Save
|
|
@@ -384,6 +479,7 @@ export const ImageTagsContainer = ({
|
|
|
384
479
|
tagId={tag.id}
|
|
385
480
|
softDeleteTags={softDeleteTags}
|
|
386
481
|
isSuperAdmin={isSuperAdmin}
|
|
482
|
+
duplicateTag={tag?.duplicateTag}
|
|
387
483
|
/>
|
|
388
484
|
))}
|
|
389
485
|
</div>
|
|
@@ -416,6 +512,7 @@ export const ImageTagButton = ({
|
|
|
416
512
|
tagId,
|
|
417
513
|
softDeleteTags,
|
|
418
514
|
isSuperAdmin,
|
|
515
|
+
duplicateTag,
|
|
419
516
|
}: ImageButtonType) => {
|
|
420
517
|
const IconComponent = isSoftDeleted ? Redo : Minus
|
|
421
518
|
|
|
@@ -434,29 +531,52 @@ export const ImageTagButton = ({
|
|
|
434
531
|
const isInSoftDeleteTags = isSoftDeleted && softDeleteTags.some((tag) => tag.tagName === text)
|
|
435
532
|
const canTagBeHardDelete = tagId === 0 || isSuperAdmin
|
|
436
533
|
|
|
534
|
+
const tabColor = () => {
|
|
535
|
+
if (duplicateTag) {
|
|
536
|
+
return 'cr-5 bcr-1 er-2'
|
|
537
|
+
} else if (isSoftDeleted) {
|
|
538
|
+
return 'cy-7 bcy-1 dc__strike-through ey-2'
|
|
539
|
+
} else {
|
|
540
|
+
return 'cn-9'
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
437
544
|
return (
|
|
438
545
|
<div
|
|
439
|
-
className={`br-4 en-2 bw-1 dc__w-fit-content dc__word-wrap-anywhere mr-8 bcn-0 flex
|
|
440
|
-
isSoftDeleted ? 'cy-7 bcy-1 dc__strike-through ey-2' : ''
|
|
441
|
-
}`}
|
|
546
|
+
className={`br-4 en-2 bw-1 dc__w-fit-content dc__word-wrap-anywhere mr-8 bcn-0 flex mb-4 ${tabColor()}`}
|
|
442
547
|
onMouseEnter={handleMouseEnter}
|
|
443
548
|
onMouseLeave={handleMouseLeave}
|
|
444
549
|
>
|
|
445
550
|
<div className="flex pt-2 pl-8 pr-8 pb-2">
|
|
446
|
-
{
|
|
447
|
-
<
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
551
|
+
{duplicateTag ? (
|
|
552
|
+
<Warning className="icon-dim-12 mr-4" />
|
|
553
|
+
) : (
|
|
554
|
+
isHovered &&
|
|
555
|
+
isEditing &&
|
|
556
|
+
(isInSoftDeleteTags || (tagId !== 0 && !isSoftDeleted)) && (
|
|
557
|
+
<Tippy
|
|
558
|
+
className="default-tt"
|
|
559
|
+
arrow={true}
|
|
560
|
+
placement="top"
|
|
561
|
+
content={isInSoftDeleteTags ? 'Restore tag' : 'Soft delete tag'}
|
|
562
|
+
>
|
|
563
|
+
<IconComponent
|
|
564
|
+
className={`icon-dim-12 mr-4 cursor ${isSoftDeleted ? 'scn-6' : 'fcn-6'}`}
|
|
565
|
+
data-testid={`${text}-tag-soft-delete`}
|
|
566
|
+
onClick={onSoftDeleteClick}
|
|
567
|
+
/>
|
|
568
|
+
</Tippy>
|
|
569
|
+
)
|
|
452
570
|
)}
|
|
453
571
|
{text}
|
|
454
572
|
{isHovered && isEditing && canTagBeHardDelete && (
|
|
455
|
-
<
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
573
|
+
<Tippy className="default-tt" arrow={true} placement="top" content="Remove tag">
|
|
574
|
+
<Close
|
|
575
|
+
className="icon-dim-12 ml-4 fcn-6 cn-5 cursor"
|
|
576
|
+
data-testid={`${text}-tag-hard-delete`}
|
|
577
|
+
onClick={onHardDeleteClick}
|
|
578
|
+
/>
|
|
579
|
+
</Tippy>
|
|
460
580
|
)}
|
|
461
581
|
</div>
|
|
462
582
|
</div>
|
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
|
|