@devtron-labs/devtron-fe-common-lib 0.0.12 → 0.0.13-beta-2

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.
@@ -0,0 +1,592 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react'
2
+ import { ReactComponent as Add } from '../Assets/Icon/ic-add.svg'
3
+ import { ReactComponent as Close } from '../Assets/Icon/ic-cross.svg'
4
+ import { ReactComponent as QuestionFilled } from '../Assets/Icon/ic-help.svg'
5
+ import { ReactComponent as Question } from '../Assets/Icon/ic-help-outline.svg'
6
+
7
+ import { ReactComponent as EditIcon } from '../Assets/Icon/ic-pencil.svg'
8
+ import { ReactComponent as Redo } from '../Assets/Icon/ic-arrow-counter-clockwise.svg'
9
+ import { ReactComponent as Minus } from '../Assets/Icon/ic-minus.svg'
10
+ import { toast } from 'react-toastify'
11
+ import { ReactComponent as Info } from '../Assets/Icon/ic-info-filled.svg'
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'
15
+ import { TippyTheme } from './Types'
16
+ import { ImageButtonType, ImageTaggingContainerType, ReleaseTag } from './ImageTags.Types'
17
+ import { showError, stopPropagation } from './Helper'
18
+ import { TippyCustomized } from './TippyCustomized'
19
+ import { setImageTags, getUserRole } from './Common.service'
20
+ import Tippy from '@tippyjs/react'
21
+
22
+ export const ImageTagsContainer = ({
23
+ ciPipelineId,
24
+ artifactId,
25
+ imageComment,
26
+ imageReleaseTags,
27
+ updateCurrentAppMaterial,
28
+ appReleaseTagNames,
29
+ setAppReleaseTagNames,
30
+ tagsEditable,
31
+ setTagsEditable,
32
+ toggleCardMode,
33
+ hideHardDelete,
34
+ forceReInit,
35
+ }: ImageTaggingContainerType) => {
36
+ const [initialTags, setInitialTags] = useState<ReleaseTag[]>(imageReleaseTags ? imageReleaseTags : [])
37
+ const [initialDescription, setInitialDescription] = useState(imageComment ? imageComment.comment : '')
38
+ const [existingTags, setExistingTags] = useState(appReleaseTagNames ? appReleaseTagNames : [])
39
+ const [newDescription, setNewDescription] = useState(imageComment ? imageComment.comment : '')
40
+ const [isEditing, setIsEditing] = useState(false)
41
+ const [showTagsWarning, setShowTagsWarning] = useState(false)
42
+ const [displayedTags, setDisplayedTags] = useState<ReleaseTag[]>(imageReleaseTags ? imageReleaseTags : [])
43
+ const [tagErrorMessage, setTagErrorMessage] = useState('')
44
+ const [createTags, setCreateTags] = useState<ReleaseTag[]>([])
45
+ const [softDeleteTags, setSoftDeleteTags] = useState<ReleaseTag[]>([])
46
+ const [hardDeleteTags, setHardDeleteTags] = useState<ReleaseTag[]>([])
47
+ const [isSuperAdmin, setSuperAdmin] = useState<boolean>(false)
48
+ const [descriptionValidationMessage, setDescriptionValidationMessage] = useState<string>('')
49
+ const [textInput, setTextInput] = useState<string>('')
50
+
51
+ useEffect(() => {
52
+ initialise()
53
+ }, [])
54
+
55
+ useEffect(() => {
56
+ reInitState()
57
+ }, [imageReleaseTags, imageComment, tagsEditable])
58
+
59
+ useEffect(() => {
60
+ setExistingTags(appReleaseTagNames ? appReleaseTagNames : [])
61
+ }, [appReleaseTagNames])
62
+
63
+ async function initialise() {
64
+ try {
65
+ const userRole = await getUserRole()
66
+
67
+ const superAdmin = userRole?.result?.roles?.includes('role:super-admin___')
68
+ setSuperAdmin(superAdmin)
69
+ } catch (err) {
70
+ showError(err)
71
+ }
72
+ }
73
+
74
+ const reInitState = () => {
75
+ if (forceReInit || !isEditing) {
76
+ setInitialTags(imageReleaseTags ? imageReleaseTags : [])
77
+ setInitialDescription(imageComment ? imageComment.comment : '')
78
+ setNewDescription(imageComment ? imageComment.comment : '')
79
+ setDisplayedTags(imageReleaseTags ? imageReleaseTags : [])
80
+ setIsEditing(false)
81
+ }
82
+ }
83
+
84
+ const handleEditClick = () => {
85
+ setIsEditing(!isEditing)
86
+ if (typeof toggleCardMode === 'function') {
87
+ toggleCardMode(artifactId)
88
+ }
89
+ }
90
+
91
+ const handleDescriptionChange = (e) => {
92
+ const description = e.target.value
93
+ description?.length > 500
94
+ ? setDescriptionValidationMessage('comment length cannot exceed 500 characters')
95
+ : setDescriptionValidationMessage('')
96
+ setNewDescription(description)
97
+ }
98
+
99
+ const handleCancel = () => {
100
+ setDisplayedTags(initialTags)
101
+ setNewDescription(initialDescription)
102
+ setCreateTags([])
103
+ setSoftDeleteTags([])
104
+ setHardDeleteTags([])
105
+ handleEditClick()
106
+ setShowTagsWarning(false)
107
+ setTagErrorMessage('')
108
+ }
109
+
110
+ const validateTag = (lowercaseValue) : boolean => {
111
+ if (
112
+ lowercaseValue.length == 0 ||
113
+ lowercaseValue.length >= 128 ||
114
+ lowercaseValue[0] == '.' ||
115
+ lowercaseValue[0] == '-'
116
+ ) {
117
+ setTagErrorMessage('tag name cannot be empty or exceed 128 characters or cannot start with . or -')
118
+ return false
119
+ }
120
+ setTagErrorMessage('')
121
+ const isTagExistsInExistingTags = existingTags.includes(lowercaseValue)
122
+ let isTagExistsInDisplayedTags = false
123
+ for (let i = 0; i < displayedTags?.length; i++) {
124
+ if (displayedTags[i].tagName.toLowerCase() === lowercaseValue) isTagExistsInDisplayedTags = true
125
+ }
126
+ if (isTagExistsInExistingTags || isTagExistsInDisplayedTags || lowercaseValue === 'latest') {
127
+ setTagErrorMessage('This tag is already being used in this application')
128
+ return false
129
+ }
130
+ return true
131
+ }
132
+
133
+ const handleTagCreate = (newValue) => {
134
+ const lowercaseValue = newValue.toLowerCase().trim()
135
+ console.log("handleTagCreate,outside validation : ")
136
+ if(validateTag(lowercaseValue)) {
137
+ console.log("handleTagCreate,inside validation : ")
138
+ const newTag: ReleaseTag = {
139
+ id: 0,
140
+ tagName: lowercaseValue,
141
+ appId: 0,
142
+ deleted: false,
143
+ artifactId: 0,
144
+ duplicateTag: false,
145
+ }
146
+ setCreateTags([...createTags, newTag])
147
+ setDisplayedTags([...displayedTags, newTag])
148
+ setShowTagsWarning(true)
149
+ setTextInput('')
150
+ }
151
+ }
152
+
153
+ const handleTagSoftDelete = (index) => {
154
+ const updatedTags = [...displayedTags]
155
+ updatedTags[index] = {
156
+ ...updatedTags[index],
157
+ deleted: !updatedTags[index].deleted,
158
+ }
159
+ const updatedTag = updatedTags[index]
160
+ let updatedSoftDeleteTags = [...softDeleteTags]
161
+ const tagIndex = updatedSoftDeleteTags.findIndex((tag) => tag.tagName === updatedTag.tagName)
162
+ if (tagIndex !== -1) {
163
+ // Tag already exists in softDeleteTags array, remove it
164
+ updatedSoftDeleteTags.splice(tagIndex, 1)
165
+ } else {
166
+ // Tag doesn't exist in softDeleteTags array, add it
167
+ updatedSoftDeleteTags.push(updatedTag)
168
+ }
169
+ setDisplayedTags(updatedTags)
170
+ setSoftDeleteTags(updatedSoftDeleteTags)
171
+ }
172
+
173
+ const handleTagHardDelete = (index) => {
174
+ const deletedTag = displayedTags[index]
175
+ const updatedCreateTags = createTags.filter((tag) => tag.tagName !== deletedTag.tagName)
176
+ setCreateTags(updatedCreateTags)
177
+ const updatedDisplayedTags = [...displayedTags]
178
+ updatedDisplayedTags.splice(index, 1)
179
+ setDisplayedTags(updatedDisplayedTags)
180
+ const updatedExistingTags = existingTags.filter((tag) => tag.toLowerCase() !== deletedTag.tagName.toLowerCase())
181
+ setExistingTags(updatedExistingTags)
182
+ if (deletedTag.id !== 0) {
183
+ const updatedHardDeleteTags = [...hardDeleteTags, deletedTag]
184
+ setHardDeleteTags(updatedHardDeleteTags)
185
+ }
186
+ }
187
+
188
+ const errorStateHandling = (err) => {
189
+ err.forEach(({ userMessage }) => {
190
+ const tagList = createTags.map((tags) => {
191
+ if (userMessage?.appReleaseTags?.includes(tags.tagName)) {
192
+ return {
193
+ ...tags,
194
+ duplicateTag: true,
195
+ }
196
+ }
197
+ return tags
198
+ })
199
+ setCreateTags(tagList)
200
+
201
+ const displayList = displayedTags.map((obj) => {
202
+ const matchingTag = tagList.find((tag) => tag.tagName === obj.tagName)
203
+ if (matchingTag) {
204
+ return { ...obj, duplicateTag: matchingTag.duplicateTag }
205
+ }
206
+ return obj
207
+ })
208
+
209
+ setDisplayedTags(displayList)
210
+ })
211
+ }
212
+
213
+ const handleSave = async () => {
214
+ if (tagErrorMessage) return
215
+
216
+ const payload = {
217
+ createTags: createTags,
218
+ softDeleteTags: softDeleteTags,
219
+ imageComment: {
220
+ id: 0,
221
+ comment: newDescription.trim(),
222
+ artifactId: 0,
223
+ },
224
+ hardDeleteTags: hardDeleteTags,
225
+ }
226
+
227
+ // set loading state true
228
+ setImageTags(payload, ciPipelineId, artifactId)
229
+ .then((res) => {
230
+ const tags = res.result?.imageReleaseTags?.map((tag) => ({
231
+ id: tag.id,
232
+ tagName: tag.tagName,
233
+ deleted: tag.deleted,
234
+ appId: 0,
235
+ artifactId: 0,
236
+ }))
237
+ if (setAppReleaseTagNames) {
238
+ setAppReleaseTagNames(res.result?.appReleaseTags)
239
+ }
240
+ if (setTagsEditable) {
241
+ setTagsEditable(res.result?.tagsEditable)
242
+ }
243
+ setInitialTags(tags)
244
+ setInitialDescription(res.result?.imageComment?.comment)
245
+ setDisplayedTags(tags)
246
+ setNewDescription(res.result?.imageComment?.comment)
247
+ setCreateTags([])
248
+ setSoftDeleteTags([])
249
+ setHardDeleteTags([])
250
+ handleEditClick()
251
+ setShowTagsWarning(false)
252
+ setTagErrorMessage('')
253
+ if(updateCurrentAppMaterial)updateCurrentAppMaterial(artifactId,tags,res.result?.imageComment)
254
+ })
255
+ .catch((err) => {
256
+ // Fix toast message
257
+ if (err.errors?.[0]?.userMessage?.appReleaseTags?.length) {
258
+ toast.error(err.errors?.[0]?.internalMessage)
259
+ errorStateHandling(err.errors)
260
+ } else {
261
+ showError(err)
262
+ }
263
+ })
264
+ }
265
+
266
+ const renderInfoCard = (): JSX.Element => {
267
+ return (
268
+ <TippyCustomized
269
+ theme={TippyTheme.white}
270
+ className="w-300 fcv-5"
271
+ placement="right"
272
+ Icon={QuestionFilled}
273
+ heading="Release tags"
274
+ showCloseButton={true}
275
+ trigger="click"
276
+ interactive={true}
277
+ additionalContent={getBuildContextAdditionalContent()}
278
+ >
279
+ <div className="icon-dim-16 fcn-5 ml-8 cursor">
280
+ <Question />
281
+ </div>
282
+ </TippyCustomized>
283
+ )
284
+ }
285
+
286
+ const getBuildContextAdditionalContent = () => {
287
+ return (
288
+ <div className="h-250 fs-13 dc__overflow-scroll p-12">
289
+ <div>Release tags allow you to tag container images with readable and relatable tags eg. v1.0.</div>
290
+ <ul className="pl-20 mt-8">
291
+ <li>
292
+ A release tag can only be added if a workflow has CD pipelines deploying to Production
293
+ environments.
294
+ </li>
295
+ <li>Multiple tags can be added to an image.</li>
296
+ <li>Multiple images in an application cannot have the same tag.</li>
297
+ <li>
298
+ Tags cannot be deleted once saved. Although, you can soft delete a tag if an unwanted tag has
299
+ been added.
300
+ </li>
301
+ </ul>
302
+ </div>
303
+ )
304
+ }
305
+
306
+ const creatableRef = useRef(null)
307
+
308
+ if (newDescription === '' && displayedTags.length === 0 && !isEditing) {
309
+ return tagsEditable ? (
310
+ <div className="bcn-0 mt-8 add-image-button">
311
+ <AddImageButton handleEditClick={handleEditClick} />
312
+ </div>
313
+ ) : (
314
+ <div></div>
315
+ )
316
+ }
317
+
318
+ const handleKeydown = (event) => {
319
+ if (event.key === 'Enter' && event.target.value.trim()) {
320
+ handleTagCreate(event.target.value.trim())
321
+ }
322
+ }
323
+
324
+ const setInputValue = (e) => {
325
+ console.log("setInputValue: ")
326
+ console.log(e.target.value)
327
+ e.target.value && validateTag(e.target.value)
328
+ setTextInput(e.target.value)
329
+ }
330
+
331
+ const enterTagCreate = (e) => {
332
+ stopPropagation(e)
333
+ if (textInput.trim()) {
334
+ handleTagCreate(textInput.trim())
335
+ }
336
+ }
337
+
338
+ const onClickSave = () => {
339
+ console.log("onClickSave: ")
340
+ console.log(textInput)
341
+ if (textInput.trim()) {
342
+ handleTagCreate(textInput)
343
+ }
344
+ handleSave()
345
+ }
346
+
347
+ return (
348
+ <div className="mt-8">
349
+ {isEditing && tagsEditable ? (
350
+ <div className="bcn-0 dc__border-top-n1 ">
351
+ <div className="cn-7 mt-12 flex left">
352
+ <span>Release tags (eg. v1.0)</span>
353
+ <div className="flex row ml-0">{renderInfoCard()}</div>
354
+ </div>
355
+ <div className="mt-6 dc__position-rel-imp" data-testid="add-tag-text-area">
356
+ <input
357
+ className="form__input"
358
+ value={textInput}
359
+ onChange={setInputValue}
360
+ placeholder="Type a tag and press enter"
361
+ onKeyDown={handleKeydown}
362
+ onBlur={setInputValue}
363
+ ref={creatableRef}
364
+ />
365
+ {textInput.length > 0 && (
366
+ <div
367
+ onClick={enterTagCreate}
368
+ 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"
369
+ style={{
370
+ top: '50%',
371
+ fontFamily: 'monospace',
372
+ right: '8px',
373
+ transform: 'translateY(-50%)',
374
+ }}
375
+ >
376
+ <Enter className="icon-dim-16 mr-4" />
377
+ Enter
378
+ </div>
379
+ )}
380
+ </div>
381
+
382
+ {tagErrorMessage && (
383
+ <div className="flex left">
384
+ <Error className="form__icon form__icon--error" />
385
+ <div className="form__error">{tagErrorMessage}</div>
386
+ </div>
387
+ )}
388
+ <div className="flex-wrap mt-8 flex left">
389
+ {displayedTags?.map((tag, index) => (
390
+ <ImageTagButton
391
+ key={tag.id}
392
+ text={tag?.tagName}
393
+ isSoftDeleted={tag?.deleted}
394
+ isEditing={isEditing}
395
+ onSoftDeleteClick={(e) => {
396
+ stopPropagation(e)
397
+ handleTagSoftDelete(index)
398
+ }}
399
+ onHardDeleteClick={(e) => {
400
+ stopPropagation(e)
401
+ handleTagHardDelete(index)
402
+ }}
403
+ tagId={tag.id}
404
+ softDeleteTags={softDeleteTags}
405
+ isSuperAdmin={isSuperAdmin}
406
+ duplicateTag={tag?.duplicateTag}
407
+ hideHardDelete={hideHardDelete}
408
+ />
409
+ ))}
410
+ </div>
411
+ {showTagsWarning && (
412
+ <div className="cn-7 mb-8 flex left">
413
+ <Info className="form__icon--info icon-dim-16 mr-2" />
414
+ Tags cannot be edited/removed later
415
+ </div>
416
+ )}
417
+ <div className="cn-7 mt-12">Comment</div>
418
+ <div
419
+ className="flex left flex-wrap dc__gap-8 w-100 mt-6 mb-12"
420
+ data-testid="add-image-comment-text-area"
421
+ >
422
+ <textarea
423
+ value={newDescription}
424
+ onChange={handleDescriptionChange}
425
+ className="flex left flex-wrap dc__gap-8 dc__description-textarea"
426
+ style={{ height: '90px !important' }}
427
+ />
428
+ </div>
429
+ {descriptionValidationMessage !== '' && (
430
+ <div className="flex left">
431
+ <Error className="form__icon form__icon--error" />
432
+ <div className="form__error">{descriptionValidationMessage}</div>
433
+ </div>
434
+ )}
435
+ <div className="w-100 flex right mt-12">
436
+ <button
437
+ className="cta flex cancel h-32 lh-32-imp"
438
+ type="button"
439
+ onClick={(e) => {
440
+ stopPropagation(e)
441
+ handleCancel()
442
+ }}
443
+ >
444
+ Cancel
445
+ </button>
446
+ <button
447
+ className="cta flex h-32 lh-32-imp ml-12"
448
+ data-testid="save-tags-button"
449
+ type="button"
450
+ onClick={(e) => {
451
+ stopPropagation(e)
452
+ onClickSave()
453
+ }}
454
+ >
455
+ Save
456
+ </button>
457
+ </div>
458
+ </div>
459
+ ) : (
460
+ <div className="top br-4 bcn-0 image-tags-container flexbox">
461
+ <div
462
+ className="flex left image-tag-left-border w-100 mt-8 mb-8 pr-10 pl-10"
463
+ data-testid="image-tags-container-hover"
464
+ >
465
+ <div className='w-100'>
466
+ {initialDescription && <div className="mb-6 fs-13 dc__word-break-all">{initialDescription}</div>}
467
+ <div className="flex-wrap flex left">
468
+ {initialTags?.map((tag, index) => (
469
+ <ImageTagButton
470
+ key={tag?.id}
471
+ text={tag?.tagName}
472
+ isSoftDeleted={tag?.deleted}
473
+ isEditing={isEditing}
474
+ onSoftDeleteClick={(e) => {
475
+ stopPropagation(e)
476
+ handleTagSoftDelete(index)
477
+ }}
478
+ onHardDeleteClick={(e) => {
479
+ stopPropagation(e)
480
+ handleTagHardDelete(index)
481
+ }}
482
+ tagId={tag.id}
483
+ softDeleteTags={softDeleteTags}
484
+ isSuperAdmin={isSuperAdmin}
485
+ duplicateTag={tag?.duplicateTag}
486
+ hideHardDelete={hideHardDelete}
487
+ />
488
+ ))}
489
+ </div>
490
+ </div>
491
+ </div>
492
+ <div className="mt-8 mr-6 icon-dim-16">
493
+ {tagsEditable && (
494
+ <EditIcon
495
+ className="icon-dim-16 image-tags-container-edit__icon cursor"
496
+ data-testid="edit-tags-icon"
497
+ onClick={(e) => {
498
+ stopPropagation(e)
499
+ handleEditClick()
500
+ }}
501
+ />
502
+ )}
503
+ </div>
504
+ </div>
505
+ )}
506
+ </div>
507
+ )
508
+ }
509
+
510
+ export const ImageTagButton = ({
511
+ text,
512
+ isSoftDeleted,
513
+ isEditing,
514
+ onSoftDeleteClick,
515
+ onHardDeleteClick,
516
+ tagId,
517
+ softDeleteTags,
518
+ isSuperAdmin,
519
+ duplicateTag,
520
+ hideHardDelete,
521
+ }: ImageButtonType) => {
522
+ const IconComponent = isSoftDeleted ? Redo : Minus
523
+ const isInSoftDeleteTags = isSoftDeleted && softDeleteTags.some((tag) => tag.tagName === text)
524
+ const canTagBeHardDelete = tagId === 0 || (isSuperAdmin && !hideHardDelete)
525
+
526
+ const tabColor = () => {
527
+ if (duplicateTag) {
528
+ return 'cr-5 bcr-1 er-2'
529
+ } else if (isSoftDeleted) {
530
+ return 'cy-7 bcy-1 dc__strike-through ey-2'
531
+ } else {
532
+ return 'cn-9'
533
+ }
534
+ }
535
+
536
+ return (
537
+ <div
538
+ className={`br-4 en-2 bw-1 mr-8 bcn-0 mb-4 pt-2 pr-8 pb-2 pl-8 tag-class dc__word-break-all flex ${
539
+ isEditing ? 'icon-hover' : ''
540
+ } ${tabColor()}`}
541
+ >
542
+ {duplicateTag ? (
543
+ <Warning className="icon-dim-12 mr-4" />
544
+ ) : (
545
+ <Tippy
546
+ className="default-tt"
547
+ arrow={true}
548
+ placement="top"
549
+ content={isInSoftDeleteTags ? 'Restore tag' : 'Soft delete tag'}
550
+ >
551
+ <div
552
+ className={`action-icon mr-4 lh-16 pt-3 ${
553
+ isInSoftDeleteTags || (tagId !== 0 && !isSoftDeleted) ? 'show-icon' : ''
554
+ }`}
555
+ >
556
+ <IconComponent
557
+ className={`icon-dim-12 cursor${isSoftDeleted ? 'scn-6' : 'fcn-6'} `}
558
+ data-testid={`${text}-tag-soft-delete`}
559
+ onClick={onSoftDeleteClick}
560
+ />
561
+ </div>
562
+ </Tippy>
563
+ )}
564
+ {text}
565
+ <Tippy className="default-tt" arrow={true} placement="top" content="Remove tag">
566
+ <div className={`action-icon ml-4 lh-16 pt-3 ${canTagBeHardDelete ? 'show-icon' : ''}`}>
567
+ <Close
568
+ className="icon-dim-12 fcn-6 cursor"
569
+ data-testid={`${text}-tag-hard-delete`}
570
+ onClick={onHardDeleteClick}
571
+ />
572
+ </div>
573
+ </Tippy>
574
+ </div>
575
+ )
576
+ }
577
+
578
+ const AddImageButton = ({ handleEditClick }) => {
579
+ const handleClick = (e) => {
580
+ stopPropagation(e)
581
+ handleEditClick()
582
+ }
583
+
584
+ return (
585
+ <div className="add-tag-button flex pt-12 pr-12" data-testid="add-tags-button" onClick={handleClick}>
586
+ <div className="lh-16 flex">
587
+ <Add className="icon-dim-16 cn-6" />
588
+ <span className="cn-7">Add tags/comment</span>
589
+ </div>
590
+ </div>
591
+ )
592
+ }
@@ -1,5 +1,6 @@
1
- import { ReactNode, CSSProperties } from 'react'
1
+ import React, { ReactNode, CSSProperties } from 'react'
2
2
  import { Placement } from 'tippy.js'
3
+ import { ImageComment, ReleaseTag } from "./ImageTags.Types";
3
4
  export interface ResponseType {
4
5
  code: number
5
6
  status: string
@@ -212,6 +213,34 @@ export interface UserApprovalMetadataType {
212
213
  requestedUserData: ApprovalUserDataType
213
214
  }
214
215
 
216
+
217
+ export interface CDMaterialType {
218
+ index: number
219
+ id: string
220
+ materialInfo: MaterialInfo[]
221
+ tab: CDModalTabType
222
+ scanEnabled: boolean
223
+ scanned: boolean
224
+ vulnerabilitiesLoading: boolean
225
+ lastExecution: string //timestamp
226
+ vulnerabilities: VulnerabilityType[]
227
+ vulnerable: boolean
228
+ deployedTime: string
229
+ deployedBy?: string
230
+ wfrId?: number
231
+ buildTime: string
232
+ image: string
233
+ isSelected: boolean
234
+ showSourceInfo: boolean
235
+ latest: boolean
236
+ runningOnParentCd?: boolean
237
+ userApprovalMetadata?: UserApprovalMetadataType
238
+ triggeredBy?: number
239
+ imageComment?: ImageComment
240
+ imageReleaseTags?: ReleaseTag[]
241
+ artifactStatus?: string
242
+ }
243
+
215
244
  export interface CommonNodeAttr {
216
245
  connectingCiPipelineId?: number
217
246
  parents: string | number[] | string[]
@@ -268,6 +297,8 @@ export interface CommonNodeAttr {
268
297
  action: any,
269
298
  metadataField: string
270
299
  }
300
+ appReleaseTagNames?: string[]
301
+ tagsEditable?: boolean
271
302
  }
272
303
 
273
304
  export interface VulnerabilityType {
@@ -297,33 +328,15 @@ export interface MaterialInfo {
297
328
  type?: string
298
329
  }
299
330
 
300
- export interface CDMaterialType {
301
- index: number
302
- id: string
303
- materialInfo: MaterialInfo[]
304
- tab: CDModalTabType
305
- scanEnabled: boolean
306
- scanned: boolean
307
- vulnerabilitiesLoading: boolean
308
- lastExecution: string //timestamp
309
- vulnerabilities: VulnerabilityType[]
310
- vulnerable: boolean
311
- deployedTime: string
312
- deployedBy?: string
313
- wfrId?: number
314
- buildTime: string
315
- image: string
316
- isSelected: boolean
317
- showSourceInfo: boolean
318
- latest: boolean
319
- runningOnParentCd?: boolean
320
- userApprovalMetadata?: UserApprovalMetadataType
321
- triggeredBy?: number
322
- }
323
-
324
331
  export interface CDMaterialResponseType {
325
332
  approvalUsers: string[]
326
333
  materials: any[]
327
334
  userApprovalConfig: UserApprovalConfigType
328
335
  requestedUserId: number
329
- }
336
+ tagsEditable: boolean
337
+ appReleaseTagNames: string[]
338
+ hideImageTaggingHardDelete: boolean
339
+ }
340
+
341
+
342
+
@@ -32,3 +32,5 @@ export { default as StyledRadioGroup } from './RadioGroup/RadioGroup'
32
32
  export * from './CIPipeline.Types'
33
33
  export * from './Policy.Types'
34
34
  export { default as DeleteComponent } from './DeleteComponentModal/DeleteComponent'
35
+ export * from './ImageTags'
36
+ export * from './ImageTags.Types'