@devtron-labs/devtron-fe-common-lib 0.0.10-beta-1 → 0.0.10-beta-3
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/Common.service.d.ts +10 -1
- package/dist/Common/Constants.d.ts +2 -0
- package/dist/Common/ImageTags.d.ts +3 -0
- package/dist/Common/Types.d.ts +20 -0
- package/dist/index.js +11 -1
- package/package.json +1 -1
- package/src/Assets/Icon/ic-arrow-counter-clockwise.svg +5 -0
- package/src/Assets/Icon/ic-minus.svg +3 -0
- package/src/Assets/Icon/ic-pencil.svg +3 -0
- package/src/Common/Common.service.ts +18 -3
- package/src/Common/Constants.ts +2 -0
- package/src/Common/ImageTags.tsx +452 -0
- package/src/Common/Types.ts +22 -0
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
import { TeamList } from './Types';
|
|
1
|
+
import { TeamList, ResponseType } from './Types';
|
|
2
2
|
export declare const getTeamListMin: () => Promise<TeamList>;
|
|
3
|
+
interface UserRole extends ResponseType {
|
|
4
|
+
result?: {
|
|
5
|
+
roles: string[];
|
|
6
|
+
superAdmin: boolean;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export declare function getUserRole(appName?: string): Promise<UserRole>;
|
|
10
|
+
export declare function setImageTags(request: any, pipelineId: number, artifactId: number): Promise<ResponseType>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ImageButtonType, ImageTaggingContainerType } from './Types';
|
|
2
|
+
export declare const ImageTagsContainer: ({ ciPipelineId, artifactId, imageComment, imageReleaseTags, appReleaseTagNames, tagsEditable, toggleCardMode, }: ImageTaggingContainerType) => JSX.Element;
|
|
3
|
+
export declare const ImageTagButton: ({ text, isSoftDeleted, isEditing, onSoftDeleteClick, onHardDeleteClick, tagId, softDeleteTags, isSuperAdmin, }: ImageButtonType) => JSX.Element;
|
package/dist/Common/Types.d.ts
CHANGED
|
@@ -212,6 +212,7 @@ export interface CDMaterialType {
|
|
|
212
212
|
triggeredBy?: number;
|
|
213
213
|
imageComment?: ImageComment;
|
|
214
214
|
imageReleaseTags?: ReleaseTag[];
|
|
215
|
+
artifactStatus?: string;
|
|
215
216
|
}
|
|
216
217
|
export interface CommonNodeAttr {
|
|
217
218
|
connectingCiPipelineId?: number;
|
|
@@ -316,4 +317,23 @@ export interface ImageComment {
|
|
|
316
317
|
comment: string;
|
|
317
318
|
artifactId: number;
|
|
318
319
|
}
|
|
320
|
+
export interface ImageTaggingContainerType {
|
|
321
|
+
ciPipelineId?: number;
|
|
322
|
+
artifactId?: number;
|
|
323
|
+
imageComment?: ImageComment;
|
|
324
|
+
imageReleaseTags?: ReleaseTag[];
|
|
325
|
+
appReleaseTagNames?: string[];
|
|
326
|
+
tagsEditable?: boolean;
|
|
327
|
+
toggleCardMode?: (id: number) => void;
|
|
328
|
+
}
|
|
329
|
+
export interface ImageButtonType {
|
|
330
|
+
text: string;
|
|
331
|
+
isSoftDeleted: boolean;
|
|
332
|
+
isEditing: boolean;
|
|
333
|
+
onSoftDeleteClick?: any;
|
|
334
|
+
onHardDeleteClick?: any;
|
|
335
|
+
tagId: number;
|
|
336
|
+
softDeleteTags: any;
|
|
337
|
+
isSuperAdmin: any;
|
|
338
|
+
}
|
|
319
339
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,8 @@ var URLS = {
|
|
|
42
42
|
};
|
|
43
43
|
var ROUTES = {
|
|
44
44
|
PROJECT_LIST_MIN: 'team/autocomplete',
|
|
45
|
+
USER_CHECK_ROLE: 'user/check/roles',
|
|
46
|
+
IMAGE_TAGGING: 'app/image-tagging',
|
|
45
47
|
};
|
|
46
48
|
exports.KEY_VALUE = void 0;
|
|
47
49
|
(function (KEY_VALUE) {
|
|
@@ -22350,7 +22352,13 @@ var getTeamListMin = function () {
|
|
|
22350
22352
|
result: list,
|
|
22351
22353
|
};
|
|
22352
22354
|
});
|
|
22353
|
-
};
|
|
22355
|
+
};
|
|
22356
|
+
function getUserRole(appName) {
|
|
22357
|
+
return get("".concat(ROUTES.USER_CHECK_ROLE).concat(appName ? "?appName=".concat(appName) : ''));
|
|
22358
|
+
}
|
|
22359
|
+
function setImageTags(request, pipelineId, artifactId) {
|
|
22360
|
+
return post("".concat(ROUTES.IMAGE_TAGGING, "/").concat(pipelineId, "/").concat(artifactId), request);
|
|
22361
|
+
}
|
|
22354
22362
|
|
|
22355
22363
|
/*
|
|
22356
22364
|
Valid States of Checkbox:
|
|
@@ -22639,12 +22647,14 @@ exports.getEmptyArrayOfLength = getEmptyArrayOfLength;
|
|
|
22639
22647
|
exports.getLoginInfo = getLoginInfo;
|
|
22640
22648
|
exports.getRandomColor = getRandomColor;
|
|
22641
22649
|
exports.getTeamListMin = getTeamListMin;
|
|
22650
|
+
exports.getUserRole = getUserRole;
|
|
22642
22651
|
exports.handleUTCTime = handleUTCTime;
|
|
22643
22652
|
exports.multiSelectStyles = multiSelectStyles;
|
|
22644
22653
|
exports.noop = noop$1;
|
|
22645
22654
|
exports.not = not;
|
|
22646
22655
|
exports.post = post;
|
|
22647
22656
|
exports.put = put;
|
|
22657
|
+
exports.setImageTags = setImageTags;
|
|
22648
22658
|
exports.showError = showError;
|
|
22649
22659
|
exports.sortCallback = sortCallback;
|
|
22650
22660
|
exports.stopPropagation = stopPropagation;
|
package/package.json
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M3.74216 4.67419H1.49216V2.42419" fill="#767D84"/>
|
|
3
|
+
<path d="M3.74216 4.67419H1.49216V2.42419" stroke="#596168" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
<path d="M3.08318 8.91682C3.66007 9.49371 4.39507 9.88658 5.19525 10.0457C5.99542 10.2049 6.82482 10.1232 7.57856 9.811C8.33231 9.49879 8.97654 8.97008 9.42981 8.29173C9.88307 7.61338 10.125 6.81585 10.125 6C10.125 5.18415 9.88307 4.38663 9.42981 3.70827C8.97654 3.02992 8.33231 2.50121 7.57856 2.189C6.82482 1.87679 5.99542 1.7951 5.19525 1.95426C4.39507 2.11343 3.66007 2.50629 3.08318 3.08319L1.49219 4.67418" stroke="#596168" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14">
|
|
2
|
+
<path fill="#959BA1" class="fill-color" fill-rule="evenodd" d="M10.5 7.583h-7c-.322 0-.583-.26-.583-.583 0-.322.26-.583.583-.583h7c.322 0 .583.26.583.583 0 .322-.26.583-.583.583z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M10.625 4.99991L15 9.37491M7.5 16.8749H3.75C3.58424 16.8749 3.42527 16.8091 3.30806 16.6918C3.19085 16.5746 3.125 16.4157 3.125 16.2499V12.7588C3.125 12.6767 3.14117 12.5954 3.17258 12.5196C3.20398 12.4438 3.25002 12.3749 3.30806 12.3168L12.6831 2.94185C12.8003 2.82464 12.9592 2.75879 13.125 2.75879C13.2908 2.75879 13.4497 2.82464 13.5669 2.94185L17.0581 6.43296C17.1753 6.55017 17.2411 6.70915 17.2411 6.87491C17.2411 7.04067 17.1753 7.19964 17.0581 7.31685L7.5 16.8749Z" stroke="#596168" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
</svg>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {get, post} from './Api';
|
|
2
2
|
import { ROUTES } from './Constants'
|
|
3
3
|
import { sortCallback } from './Helper';
|
|
4
|
-
import { TeamList } from './Types';
|
|
4
|
+
import { TeamList ,ResponseType } from './Types';
|
|
5
5
|
|
|
6
6
|
export const getTeamListMin = (): Promise<TeamList> => {
|
|
7
7
|
// ignore active field
|
|
@@ -20,4 +20,19 @@ export const getTeamListMin = (): Promise<TeamList> => {
|
|
|
20
20
|
result: list,
|
|
21
21
|
};
|
|
22
22
|
});
|
|
23
|
-
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
interface UserRole extends ResponseType {
|
|
26
|
+
result?: {
|
|
27
|
+
roles: string[]
|
|
28
|
+
superAdmin: boolean
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getUserRole(appName?: string): Promise<UserRole> {
|
|
33
|
+
return get(`${ROUTES.USER_CHECK_ROLE}${appName ? `?appName=${appName}` : ''}`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setImageTags(request, pipelineId: number, artifactId: number){
|
|
37
|
+
return post(`${ROUTES.IMAGE_TAGGING}/${pipelineId}/${artifactId}`,request )
|
|
38
|
+
}
|
package/src/Common/Constants.ts
CHANGED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import { ReactComponent as Add } from '../Assets/Icon/ic-add.svg'
|
|
3
|
+
import Creatable from 'react-select/creatable'
|
|
4
|
+
import { ReactComponent as Close } from '../Assets/Icon/ic-cross.svg'
|
|
5
|
+
import { ReactComponent as QuestionFilled } from '../Assets/Icon/ic-help.svg'
|
|
6
|
+
import { ReactComponent as Question } from '../Assets/Icon/ic-help-outline.svg'
|
|
7
|
+
|
|
8
|
+
import { ReactComponent as EditIcon } from '../Assets/icon/ic-pencil.svg'
|
|
9
|
+
import { ReactComponent as Redo } from '../Assets/Icon/ic-arrow-counter-clockwise.svg'
|
|
10
|
+
import { ReactComponent as Minus } from '../Assets/Icon/ic-minus.svg'
|
|
11
|
+
|
|
12
|
+
import { ReactComponent as Info } from '../Assets/Icon/ic-info-filled.svg'
|
|
13
|
+
import { ReactComponent as Error } from '../Assets/Icon/ic-warning.svg'
|
|
14
|
+
import { ImageButtonType, ImageTaggingContainerType, ReleaseTag, TippyTheme } from './Types'
|
|
15
|
+
import { showError, stopPropagation } from './Helper'
|
|
16
|
+
import { TippyCustomized } from './TippyCustomized'
|
|
17
|
+
import { setImageTags, getUserRole } from './Common.service'
|
|
18
|
+
|
|
19
|
+
export const ImageTagsContainer = ({
|
|
20
|
+
ciPipelineId,
|
|
21
|
+
artifactId,
|
|
22
|
+
imageComment,
|
|
23
|
+
imageReleaseTags,
|
|
24
|
+
appReleaseTagNames,
|
|
25
|
+
tagsEditable,
|
|
26
|
+
toggleCardMode,
|
|
27
|
+
}: ImageTaggingContainerType) => {
|
|
28
|
+
const [initialTags, setInitialTags] = useState<ReleaseTag[]>(imageReleaseTags ? imageReleaseTags : [])
|
|
29
|
+
const [initialDescription, setInitialDescription] = useState(imageComment ? imageComment.comment : '')
|
|
30
|
+
const [existingTags, setExistingTags] = useState(appReleaseTagNames ? appReleaseTagNames : [])
|
|
31
|
+
const [newDescription, setNewDescription] = useState(imageComment ? imageComment.comment : '')
|
|
32
|
+
const [isEditing, setIsEditing] = useState(false)
|
|
33
|
+
const [showTagsWarning, setShowTagsWarning] = useState(false)
|
|
34
|
+
const [displayedTags, setDisplayedTags] = useState<ReleaseTag[]>(imageReleaseTags ? imageReleaseTags : [])
|
|
35
|
+
const [tagErrorMessage, setTagErrorMessage] = useState('')
|
|
36
|
+
const [createTags, setCreateTags] = useState<ReleaseTag[]>([])
|
|
37
|
+
const [softDeleteTags, setSoftDeleteTags] = useState<ReleaseTag[]>([])
|
|
38
|
+
const [hardDeleteTags, setHardDeleteTags] = useState<ReleaseTag[]>([])
|
|
39
|
+
const [isSuperAdmin, setSuperAdmin] = useState<boolean>(false)
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
initialise()
|
|
43
|
+
}, [])
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
reInitState()
|
|
47
|
+
}, [imageReleaseTags, imageComment, appReleaseTagNames, tagsEditable])
|
|
48
|
+
|
|
49
|
+
async function initialise() {
|
|
50
|
+
try {
|
|
51
|
+
const userRole = await getUserRole()
|
|
52
|
+
|
|
53
|
+
const superAdmin = userRole?.result?.roles?.includes('role:super-admin___')
|
|
54
|
+
setSuperAdmin(superAdmin)
|
|
55
|
+
} catch (err) {
|
|
56
|
+
showError(err)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const reInitState = () => {
|
|
61
|
+
setInitialTags(imageReleaseTags ? imageReleaseTags : [])
|
|
62
|
+
setInitialDescription(imageComment ? imageComment.comment : '')
|
|
63
|
+
setExistingTags(appReleaseTagNames ? appReleaseTagNames : [])
|
|
64
|
+
setNewDescription(imageComment ? imageComment.comment : '')
|
|
65
|
+
setDisplayedTags(imageReleaseTags ? imageReleaseTags : [])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const CreatableComponents = useMemo(
|
|
69
|
+
() => ({
|
|
70
|
+
DropdownIndicator: () => null,
|
|
71
|
+
IndicatorSeparator: () => null,
|
|
72
|
+
Menu: () => null,
|
|
73
|
+
}),
|
|
74
|
+
[],
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const handleEditClick = () => {
|
|
78
|
+
setIsEditing(!isEditing)
|
|
79
|
+
if (typeof toggleCardMode === 'function') {
|
|
80
|
+
toggleCardMode(artifactId)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const handleDescriptionChange = (e) => {
|
|
85
|
+
setNewDescription(e.target.value)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const handleCancel = () => {
|
|
89
|
+
setDisplayedTags(initialTags)
|
|
90
|
+
setNewDescription(initialDescription)
|
|
91
|
+
setCreateTags([])
|
|
92
|
+
setSoftDeleteTags([])
|
|
93
|
+
setHardDeleteTags([])
|
|
94
|
+
handleEditClick()
|
|
95
|
+
setShowTagsWarning(false)
|
|
96
|
+
setTagErrorMessage('')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const handleTagCreate = (newValue) => {
|
|
100
|
+
const lowercaseValue = newValue.toLowerCase()
|
|
101
|
+
setTagErrorMessage('')
|
|
102
|
+
const isTagExistsInExistingTags = existingTags.includes(lowercaseValue)
|
|
103
|
+
let isTagExistsInDisplayedTags = false
|
|
104
|
+
for (let i = 0; i < displayedTags?.length; i++) {
|
|
105
|
+
if (displayedTags[i].tagName.toLowerCase() === lowercaseValue) isTagExistsInDisplayedTags = true
|
|
106
|
+
}
|
|
107
|
+
if (isTagExistsInExistingTags || isTagExistsInDisplayedTags) {
|
|
108
|
+
setTagErrorMessage('This tag is already applied on another image in this application')
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
const newTag: ReleaseTag = {
|
|
112
|
+
id: 0,
|
|
113
|
+
tagName: lowercaseValue,
|
|
114
|
+
appId: 0,
|
|
115
|
+
deleted: false,
|
|
116
|
+
artifactId: 0,
|
|
117
|
+
}
|
|
118
|
+
setCreateTags([...createTags, newTag])
|
|
119
|
+
setDisplayedTags([...displayedTags, newTag])
|
|
120
|
+
setShowTagsWarning(true)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const handleTagSoftDelete = (index) => {
|
|
124
|
+
const updatedTags = [...displayedTags]
|
|
125
|
+
updatedTags[index] = {
|
|
126
|
+
...updatedTags[index],
|
|
127
|
+
deleted: !updatedTags[index].deleted,
|
|
128
|
+
}
|
|
129
|
+
const updatedTag = updatedTags[index]
|
|
130
|
+
let updatedSoftDeleteTags = [...softDeleteTags]
|
|
131
|
+
const tagIndex = updatedSoftDeleteTags.findIndex((tag) => tag.tagName === updatedTag.tagName)
|
|
132
|
+
if (tagIndex !== -1) {
|
|
133
|
+
// Tag already exists in softDeleteTags array, remove it
|
|
134
|
+
updatedSoftDeleteTags.splice(tagIndex, 1)
|
|
135
|
+
} else {
|
|
136
|
+
// Tag doesn't exist in softDeleteTags array, add it
|
|
137
|
+
updatedSoftDeleteTags.push(updatedTag)
|
|
138
|
+
}
|
|
139
|
+
setDisplayedTags(updatedTags)
|
|
140
|
+
setSoftDeleteTags(updatedSoftDeleteTags)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const handleTagHardDelete = (index) => {
|
|
144
|
+
const deletedTag = displayedTags[index]
|
|
145
|
+
const updatedCreateTags = createTags.filter((tag) => tag.tagName !== deletedTag.tagName)
|
|
146
|
+
setCreateTags(updatedCreateTags)
|
|
147
|
+
const updatedDisplayedTags = [...displayedTags]
|
|
148
|
+
updatedDisplayedTags.splice(index, 1)
|
|
149
|
+
setDisplayedTags(updatedDisplayedTags)
|
|
150
|
+
const updatedExistingTags = existingTags.filter((tag) => tag.toLowerCase() !== deletedTag.tagName.toLowerCase())
|
|
151
|
+
setExistingTags(updatedExistingTags)
|
|
152
|
+
if (deletedTag.id !== 0) {
|
|
153
|
+
const updatedHardDeleteTags = [...hardDeleteTags, deletedTag]
|
|
154
|
+
setHardDeleteTags(updatedHardDeleteTags)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const handleSave = async () => {
|
|
159
|
+
const payload = {
|
|
160
|
+
createTags: createTags,
|
|
161
|
+
softDeleteTags: softDeleteTags,
|
|
162
|
+
imageComment: {
|
|
163
|
+
id: 0,
|
|
164
|
+
comment: newDescription.trim(),
|
|
165
|
+
artifactId: 0,
|
|
166
|
+
},
|
|
167
|
+
hardDeleteTags: hardDeleteTags,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// set loading state true
|
|
172
|
+
let response = await setImageTags(payload, ciPipelineId, artifactId)
|
|
173
|
+
const tags = response.result?.imageReleaseTags?.map((tag) => ({
|
|
174
|
+
id: tag.id,
|
|
175
|
+
tagName: tag.tagName,
|
|
176
|
+
deleted: tag.deleted,
|
|
177
|
+
appId: 0,
|
|
178
|
+
artifactId: 0,
|
|
179
|
+
}))
|
|
180
|
+
setInitialTags(tags)
|
|
181
|
+
setInitialDescription(response.result?.imageComment?.comment)
|
|
182
|
+
setDisplayedTags(tags)
|
|
183
|
+
setNewDescription(response.result?.imageComment?.comment)
|
|
184
|
+
setCreateTags([])
|
|
185
|
+
setSoftDeleteTags([])
|
|
186
|
+
setHardDeleteTags([])
|
|
187
|
+
handleEditClick()
|
|
188
|
+
setShowTagsWarning(false)
|
|
189
|
+
setTagErrorMessage('')
|
|
190
|
+
} catch (err) {
|
|
191
|
+
showError(err)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const renderInfoCard = (): JSX.Element => {
|
|
196
|
+
return (
|
|
197
|
+
<TippyCustomized
|
|
198
|
+
theme={TippyTheme.white}
|
|
199
|
+
className="w-300 h-250 fcv-5 dc__overflow-scroll"
|
|
200
|
+
placement="right"
|
|
201
|
+
Icon={QuestionFilled}
|
|
202
|
+
heading="Release tags"
|
|
203
|
+
infoText="Release tags allow you to tag container images with readable and relatable tags eg. v1.0."
|
|
204
|
+
showCloseButton={true}
|
|
205
|
+
trigger="click"
|
|
206
|
+
interactive={true}
|
|
207
|
+
documentationLinkText="View Documentation"
|
|
208
|
+
additionalContent={getBuildContextAdditionalContent()}
|
|
209
|
+
>
|
|
210
|
+
<div className="icon-dim-16 fcn-5 ml-8 cursor">
|
|
211
|
+
<Question />
|
|
212
|
+
</div>
|
|
213
|
+
</TippyCustomized>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const getBuildContextAdditionalContent = () => {
|
|
218
|
+
return (
|
|
219
|
+
<div className="p-12 fs-13">
|
|
220
|
+
<ul>
|
|
221
|
+
<li>
|
|
222
|
+
A release tag can only be added if a workflow has CD pipelines deploying to Production
|
|
223
|
+
environments.
|
|
224
|
+
</li>
|
|
225
|
+
<li>Multiple tags can be added to an image.</li>
|
|
226
|
+
<li>Multiple images in an application cannot have the same tag.</li>
|
|
227
|
+
<li>
|
|
228
|
+
Tags cannot be deleted once saved. Although, you can soft delete a tag if an unwanted tag has
|
|
229
|
+
been added.
|
|
230
|
+
</li>
|
|
231
|
+
</ul>
|
|
232
|
+
</div>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const creatableRef = useRef(null)
|
|
237
|
+
|
|
238
|
+
if (newDescription === '' && displayedTags.length === 0 && !isEditing) {
|
|
239
|
+
return tagsEditable ? (
|
|
240
|
+
<div className="bcn-0 mt-8">
|
|
241
|
+
<AddImageButton handleEditClick={handleEditClick} />
|
|
242
|
+
</div>
|
|
243
|
+
) : (
|
|
244
|
+
<div></div>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<div className="mt-8">
|
|
250
|
+
{isEditing && tagsEditable ? (
|
|
251
|
+
<div className="bcn-0 dc__border-top-n1 ">
|
|
252
|
+
<div className="cn-7 mt-12 flex left">
|
|
253
|
+
<span>Release tags (eg. v1.0)</span>
|
|
254
|
+
<div className="flex row ml-0">{renderInfoCard()}</div>
|
|
255
|
+
</div>
|
|
256
|
+
<div className="mt-6" data-testid="add-tag-text-area">
|
|
257
|
+
<Creatable
|
|
258
|
+
placeholder="Type a tag and press enter"
|
|
259
|
+
onCreateOption={handleTagCreate}
|
|
260
|
+
ref={creatableRef}
|
|
261
|
+
components={CreatableComponents}
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
{tagErrorMessage && (
|
|
266
|
+
<div className="flex left">
|
|
267
|
+
<Error className="form__icon form__icon--error" />
|
|
268
|
+
<div className="form__error">{tagErrorMessage}</div>
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
<div className="flex-wrap mt-8 flex left">
|
|
272
|
+
{displayedTags?.map((tag, index) => (
|
|
273
|
+
<ImageTagButton
|
|
274
|
+
key={tag.id}
|
|
275
|
+
text={tag?.tagName}
|
|
276
|
+
isSoftDeleted={tag?.deleted}
|
|
277
|
+
isEditing={isEditing}
|
|
278
|
+
onSoftDeleteClick={(e) => {
|
|
279
|
+
stopPropagation(e)
|
|
280
|
+
handleTagSoftDelete(index)
|
|
281
|
+
}}
|
|
282
|
+
onHardDeleteClick={(e) => {
|
|
283
|
+
stopPropagation(e)
|
|
284
|
+
handleTagHardDelete(index)
|
|
285
|
+
}}
|
|
286
|
+
tagId={tag.id}
|
|
287
|
+
softDeleteTags={softDeleteTags}
|
|
288
|
+
isSuperAdmin={isSuperAdmin}
|
|
289
|
+
/>
|
|
290
|
+
))}
|
|
291
|
+
</div>
|
|
292
|
+
{showTagsWarning && (
|
|
293
|
+
<div className="cn-7 mb-8 flex left">
|
|
294
|
+
<Info className="form__icon--info icon-dim-16 mr-2" />
|
|
295
|
+
Tags cannot be edited/removed later
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
<div className="cn-7 mt-12">Comment</div>
|
|
299
|
+
<div
|
|
300
|
+
className="flex left flex-wrap dc__gap-8 w-100 mt-6 mb-12"
|
|
301
|
+
data-testid="add-image-comment-text-area"
|
|
302
|
+
>
|
|
303
|
+
<textarea
|
|
304
|
+
value={newDescription}
|
|
305
|
+
onChange={handleDescriptionChange}
|
|
306
|
+
className="flex left flex-wrap dc__gap-8 dc__description-textarea"
|
|
307
|
+
style={{ height: '90px !important' }}
|
|
308
|
+
/>
|
|
309
|
+
</div>
|
|
310
|
+
<div className="w-100 flex right">
|
|
311
|
+
<button
|
|
312
|
+
className="cta flex cancel h-32 lh-32-imp"
|
|
313
|
+
type="button"
|
|
314
|
+
onClick={(e) => {
|
|
315
|
+
stopPropagation(e)
|
|
316
|
+
handleCancel()
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
Cancel
|
|
320
|
+
</button>
|
|
321
|
+
<button
|
|
322
|
+
className="cta flex h-32 lh-32-imp ml-12"
|
|
323
|
+
data-testid="save-tags-button"
|
|
324
|
+
type="button"
|
|
325
|
+
onClick={(e) => {
|
|
326
|
+
stopPropagation(e)
|
|
327
|
+
handleSave()
|
|
328
|
+
}}
|
|
329
|
+
>
|
|
330
|
+
Save
|
|
331
|
+
</button>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
) : (
|
|
335
|
+
<div className="top br-4 bcn-0 image-tags-container flexbox">
|
|
336
|
+
<div
|
|
337
|
+
className="flex left image-tag-left-border w-100 mt-8 mb-8 pr-10 pl-10"
|
|
338
|
+
data-testid="image-tags-container-hover"
|
|
339
|
+
>
|
|
340
|
+
<div>
|
|
341
|
+
{initialDescription && <div className="mb-6 fs-13">{initialDescription}</div>}
|
|
342
|
+
<div className="flex-wrap flex left">
|
|
343
|
+
{initialTags?.map((tag, index) => (
|
|
344
|
+
<ImageTagButton
|
|
345
|
+
key={tag?.id}
|
|
346
|
+
text={tag?.tagName}
|
|
347
|
+
isSoftDeleted={tag?.deleted}
|
|
348
|
+
isEditing={isEditing}
|
|
349
|
+
onSoftDeleteClick={(e) => {
|
|
350
|
+
stopPropagation(e)
|
|
351
|
+
handleTagSoftDelete(index)
|
|
352
|
+
}}
|
|
353
|
+
onHardDeleteClick={(e) => {
|
|
354
|
+
stopPropagation(e)
|
|
355
|
+
handleTagHardDelete(index)
|
|
356
|
+
}}
|
|
357
|
+
tagId={tag.id}
|
|
358
|
+
softDeleteTags={softDeleteTags}
|
|
359
|
+
isSuperAdmin={isSuperAdmin}
|
|
360
|
+
/>
|
|
361
|
+
))}
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
<div className="mt-8 mr-6 icon-dim-16">
|
|
366
|
+
{tagsEditable && (
|
|
367
|
+
<EditIcon
|
|
368
|
+
className="icon-dim-16 image-tags-container-edit__icon cursor"
|
|
369
|
+
data-testid="edit-tags-icon"
|
|
370
|
+
onClick={(e) => {
|
|
371
|
+
stopPropagation(e)
|
|
372
|
+
handleEditClick()
|
|
373
|
+
}}
|
|
374
|
+
/>
|
|
375
|
+
)}
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export const ImageTagButton = ({
|
|
384
|
+
text,
|
|
385
|
+
isSoftDeleted,
|
|
386
|
+
isEditing,
|
|
387
|
+
onSoftDeleteClick,
|
|
388
|
+
onHardDeleteClick,
|
|
389
|
+
tagId,
|
|
390
|
+
softDeleteTags,
|
|
391
|
+
isSuperAdmin,
|
|
392
|
+
}: ImageButtonType) => {
|
|
393
|
+
const IconComponent = isSoftDeleted ? Redo : Minus
|
|
394
|
+
|
|
395
|
+
const [isHovered, setIsHovered] = useState(false)
|
|
396
|
+
const handleMouseEnter = () => {
|
|
397
|
+
if (isEditing) {
|
|
398
|
+
setIsHovered(true)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const handleMouseLeave = () => {
|
|
402
|
+
if (isEditing) {
|
|
403
|
+
setIsHovered(false)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const isInSoftDeleteTags = isSoftDeleted && softDeleteTags.some((tag) => tag.tagName === text)
|
|
408
|
+
const canTagBeHardDelete = tagId === 0 || isSuperAdmin
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<div
|
|
412
|
+
className={`br-4 en-2 bw-1 dc__w-fit-content dc__word-wrap-anywhere mr-8 bcn-0 flex h-24 ${
|
|
413
|
+
isSoftDeleted ? 'cy-7 bcy-1 dc__strike-through ey-2' : ''
|
|
414
|
+
}`}
|
|
415
|
+
onMouseEnter={handleMouseEnter}
|
|
416
|
+
onMouseLeave={handleMouseLeave}
|
|
417
|
+
>
|
|
418
|
+
<div className="flex pt-2 pl-8 pr-8 pb-2">
|
|
419
|
+
{isHovered && isEditing && (isInSoftDeleteTags || (tagId !== 0 && !isSoftDeleted)) && (
|
|
420
|
+
<IconComponent
|
|
421
|
+
className={`icon-dim-12 mr-4 cursor ${isSoftDeleted ? 'scn-6' : 'fcn-6'}`}
|
|
422
|
+
data-testid={`${text}-tag-soft-delete`}
|
|
423
|
+
onClick={onSoftDeleteClick}
|
|
424
|
+
/>
|
|
425
|
+
)}
|
|
426
|
+
{text}
|
|
427
|
+
{isHovered && isEditing && canTagBeHardDelete && (
|
|
428
|
+
<Close
|
|
429
|
+
className="icon-dim-12 ml-4 fcn-6 cn-5 cursor"
|
|
430
|
+
data-testid={`${text}-tag-hard-delete`}
|
|
431
|
+
onClick={onHardDeleteClick}
|
|
432
|
+
/>
|
|
433
|
+
)}
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const AddImageButton = ({ handleEditClick }) => {
|
|
440
|
+
const handleClick = () => {
|
|
441
|
+
handleEditClick()
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return (
|
|
445
|
+
<div className="add-tag-button flex pt-12 pr-12" data-testid="add-tags-button" onClick={handleClick}>
|
|
446
|
+
<div className="lh-16 flex">
|
|
447
|
+
<Add className="icon-dim-16 cn-6" />
|
|
448
|
+
<span className="cn-7">Add tags/comment</span>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
)
|
|
452
|
+
}
|
package/src/Common/Types.ts
CHANGED
|
@@ -236,6 +236,7 @@ export interface CDMaterialType {
|
|
|
236
236
|
triggeredBy?: number
|
|
237
237
|
imageComment?: ImageComment
|
|
238
238
|
imageReleaseTags?: ReleaseTag[]
|
|
239
|
+
artifactStatus?: string
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
export interface CommonNodeAttr {
|
|
@@ -349,4 +350,25 @@ export interface ImageComment {
|
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
|
|
353
|
+
export interface ImageTaggingContainerType {
|
|
354
|
+
ciPipelineId?: number
|
|
355
|
+
artifactId?: number
|
|
356
|
+
imageComment?: ImageComment
|
|
357
|
+
imageReleaseTags?: ReleaseTag[]
|
|
358
|
+
appReleaseTagNames?: string[]
|
|
359
|
+
tagsEditable?: boolean
|
|
360
|
+
toggleCardMode?: (id: number) => void
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export interface ImageButtonType {
|
|
364
|
+
text: string
|
|
365
|
+
isSoftDeleted: boolean
|
|
366
|
+
isEditing: boolean
|
|
367
|
+
onSoftDeleteClick?: any
|
|
368
|
+
onHardDeleteClick?: any
|
|
369
|
+
tagId: number
|
|
370
|
+
softDeleteTags: any
|
|
371
|
+
isSuperAdmin: any
|
|
372
|
+
}
|
|
373
|
+
|
|
352
374
|
|