@devtron-labs/devtron-fe-common-lib 0.0.52-beta-2 → 0.0.52-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/.eslintignore +79 -0
- package/.eslintrc.js +84 -16
- package/.github/pull_request_template.md +25 -0
- package/.github/workflows/ci.yml +31 -0
- package/.github/workflows/release-package.yml +2 -3
- package/.husky/pre-commit +5 -0
- package/.lintstagedrc +3 -0
- package/.nvmrc +1 -0
- package/dist/Common/BreadCrumb/BreadCrumb.d.ts +1 -1
- package/dist/Common/BreadCrumb/BreadcrumbStore.d.ts +2 -2
- package/dist/Common/Constants.d.ts +1 -5
- package/dist/Common/CustomInput/CustomInput.d.ts +1 -1
- package/dist/Common/CustomInput/Types.d.ts +2 -2
- package/dist/Common/CustomTagSelector/ResizableTagTextArea.d.ts +1 -1
- package/dist/Common/DeleteComponentModal/DeleteComponent.d.ts +1 -1
- package/dist/Common/DevtronProgressing/DevtronProgressing.d.ts +1 -1
- package/dist/Common/Dialogs/ConfirmationDialog.d.ts +6 -6
- package/dist/Common/Dialogs/DeleteDialog.d.ts +1 -1
- package/dist/Common/Dialogs/ForceDeleteDialog.d.ts +1 -1
- package/dist/Common/Dialogs/Types.d.ts +1 -1
- package/dist/Common/EmptyState/EmptyState.d.ts +20 -17
- package/dist/Common/EmptyState/GenericEmptyState.d.ts +1 -1
- package/dist/Common/Error.d.ts +2 -1
- package/dist/Common/ErrorScreenNotFound.d.ts +1 -1
- package/dist/Common/Helper.d.ts +1 -1
- package/dist/Common/ImageTags.d.ts +1 -1
- package/dist/Common/InfoColorBar/InfoColourbar.d.ts +1 -1
- package/dist/Common/Modals/Modal.d.ts +2 -2
- package/dist/Common/Modals/OpaqueModal.d.ts +3 -3
- package/dist/Common/Modals/VisibleModal.d.ts +3 -3
- package/dist/Common/Modals/VisibleModal2.d.ts +3 -3
- package/dist/Common/MultiSelectCustomization.d.ts +1 -1
- package/dist/Common/Policy.Types.d.ts +2 -2
- package/dist/Common/PopupMenu.d.ts +5 -5
- package/dist/Common/Progressing.d.ts +2 -2
- package/dist/Common/RadioGroup/RadioGroup.d.ts +1 -1
- package/dist/Common/ResizableTextarea.d.ts +1 -1
- package/dist/Common/TippyCustomized.d.ts +1 -1
- package/dist/Common/ToastBody.d.ts +5 -5
- package/dist/Common/Types.d.ts +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +286 -370
- package/package.json +19 -2
- package/src/Common/AddCDButton/AddCDButton.tsx +1 -2
- package/src/Common/Api.ts +20 -25
- package/src/Common/AppStatus/AppStatus.tsx +12 -12
- package/src/Common/AppStatus/utils.ts +6 -5
- package/src/Common/BreadCrumb/BreadCrumb.tsx +32 -28
- package/src/Common/BreadCrumb/BreadcrumbStore.tsx +2 -2
- package/src/Common/BreadCrumb/Types.ts +11 -12
- package/src/Common/ChartVersionAndTypeSelector.tsx +1 -1
- package/src/Common/Checkbox.tsx +17 -19
- package/src/Common/ClipboardButton/ClipboardButton.tsx +1 -1
- package/src/Common/ClipboardButton/__tests__/ClipboardButton.test.tsx +0 -1
- package/src/Common/Common.service.ts +60 -65
- package/src/Common/Constants.ts +9 -13
- package/src/Common/CustomInput/CustomInput.tsx +22 -29
- package/src/Common/CustomInput/Types.ts +4 -4
- package/src/Common/CustomInput/index.ts +1 -1
- package/src/Common/CustomTagSelector/PropagateTagInfo.tsx +15 -18
- package/src/Common/CustomTagSelector/ResizableTagTextArea.tsx +9 -9
- package/src/Common/CustomTagSelector/TagDetails.tsx +9 -3
- package/src/Common/CustomTagSelector/TagLabelSelect.tsx +7 -9
- package/src/Common/CustomTagSelector/TagLabelValueSelector.tsx +31 -35
- package/src/Common/CustomTagSelector/TagSelector.utils.ts +2 -3
- package/src/Common/CustomTagSelector/Types.ts +1 -1
- package/src/Common/CustomTagSelector/ValidationRules.ts +18 -22
- package/src/Common/CustomTagSelector/index.ts +2 -2
- package/src/Common/DebouncedSearch/DebouncedSearch.tsx +2 -1
- package/src/Common/DebouncedSearch/__tests__/DebouncedSearch.test.tsx +0 -1
- package/src/Common/DeleteComponentModal/DeleteComponent.tsx +27 -32
- package/src/Common/DeleteComponentModal/types.ts +1 -1
- package/src/Common/DevtronProgressing/DevtronProgressing.tsx +5 -8
- package/src/Common/Dialogs/ConfirmationDialog.tsx +24 -24
- package/src/Common/Dialogs/DeleteDialog.tsx +3 -5
- package/src/Common/Dialogs/DialogForm.tsx +31 -35
- package/src/Common/Dialogs/ForceDeleteDialog.tsx +13 -7
- package/src/Common/Dialogs/Types.ts +12 -12
- package/src/Common/Dialogs/index.ts +1 -1
- package/src/Common/Drawer/Drawer.tsx +1 -1
- package/src/Common/EmptyState/EmptyState.tsx +13 -17
- package/src/Common/EmptyState/GenericEmptyState.tsx +7 -29
- package/src/Common/Error.tsx +5 -5
- package/src/Common/ErrorScreenNotFound.tsx +9 -12
- package/src/Common/Grid/Grid.tsx +1 -2
- package/src/Common/Helper.tsx +36 -37
- package/src/Common/Hooks/UseClickOutside/UseClickOutside.tsx +1 -1
- package/src/Common/Hooks/UseSuperAdmin/UseSuperAdmin.tsx +1 -1
- package/src/Common/Hooks/UseWindowSize/UseWindowSize.tsx +5 -7
- package/src/Common/ImageTags.Types.ts +2 -3
- package/src/Common/ImageTags.tsx +61 -66
- package/src/Common/InfoColorBar/InfoColourbar.tsx +10 -5
- package/src/Common/Modals/Modal.tsx +3 -3
- package/src/Common/Modals/OpaqueModal.tsx +25 -18
- package/src/Common/Modals/VisibleModal.tsx +7 -3
- package/src/Common/Modals/VisibleModal2.tsx +20 -15
- package/src/Common/MultiSelectCustomization.tsx +9 -13
- package/src/Common/Policy.Types.ts +38 -38
- package/src/Common/PopupMenu.tsx +10 -9
- package/src/Common/Progressing.tsx +15 -18
- package/src/Common/RJSF/Form.tsx +0 -1
- package/src/Common/RJSF/common/FieldRow.tsx +0 -1
- package/src/Common/RJSF/config.ts +0 -1
- package/src/Common/RJSF/templates/ArrayFieldItemTemplate.tsx +1 -1
- package/src/Common/RJSF/templates/ArrayFieldTemplate.tsx +0 -1
- package/src/Common/RJSF/templates/BaseInput.tsx +0 -1
- package/src/Common/RJSF/templates/ButtonTemplates/AddButton.tsx +0 -1
- package/src/Common/RJSF/templates/ButtonTemplates/RemoveButton.tsx +0 -1
- package/src/Common/RJSF/templates/ButtonTemplates/SubmitButton.tsx +1 -6
- package/src/Common/RJSF/templates/Description.tsx +0 -1
- package/src/Common/RJSF/templates/Field.tsx +14 -3
- package/src/Common/RJSF/templates/FieldErrorTemplate.tsx +6 -9
- package/src/Common/RJSF/templates/ObjectFieldTemplate.tsx +5 -7
- package/src/Common/RJSF/templates/TitleField.tsx +0 -1
- package/src/Common/RJSF/templates/WrapIfAdditionalTemplate.tsx +0 -1
- package/src/Common/RJSF/utils.ts +53 -57
- package/src/Common/RJSF/widgets/Checkbox.tsx +2 -2
- package/src/Common/RJSF/widgets/Select.tsx +0 -1
- package/src/Common/RadioGroup/RadioGroup.tsx +18 -23
- package/src/Common/RadioGroup.tsx +24 -23
- package/src/Common/Reload.tsx +0 -1
- package/src/Common/ResizableTextarea.tsx +18 -17
- package/src/Common/Security/ScanVulnerabilitiesTable.tsx +29 -34
- package/src/Common/ServerError.ts +5 -1
- package/src/Common/TippyCustomized.tsx +41 -38
- package/src/Common/ToastBody.tsx +6 -7
- package/src/Common/Toggle/Toggle.tsx +3 -3
- package/src/Common/Types.ts +19 -20
- package/src/custom.d.ts +7 -7
- package/src/index.ts +1 -1
package/src/Common/Helper.tsx
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as Sentry from '@sentry/browser'
|
|
2
2
|
import moment from 'moment'
|
|
3
|
-
import
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
|
+
import * as React from 'react'
|
|
4
5
|
import { useLocation } from 'react-router-dom'
|
|
5
6
|
import { toast } from 'react-toastify'
|
|
7
|
+
import { JSONPath } from 'jsonpath-plus'
|
|
8
|
+
import * as jsonpatch from 'fast-json-patch'
|
|
6
9
|
import { ERROR_EMPTY_SCREEN, TOKEN_COOKIE_NAME } from './Constants'
|
|
7
10
|
import { ServerErrors } from './ServerError'
|
|
8
11
|
import { toastAccessDenied } from './ToastBody'
|
|
9
12
|
import { AsyncOptions, AsyncState, UseSearchString } from './Types'
|
|
10
|
-
import { JSONPath } from 'jsonpath-plus'
|
|
11
|
-
import * as jsonpatch from 'fast-json-patch'
|
|
12
13
|
|
|
13
14
|
toast.configure({
|
|
14
15
|
autoClose: 3000,
|
|
@@ -79,16 +80,19 @@ export const stopPropagation = (event): void => {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
export function useThrottledEffect(callback, delay, deps = []) {
|
|
82
|
-
//function will be executed only once in a given time interval.
|
|
83
|
+
// function will be executed only once in a given time interval.
|
|
83
84
|
const lastRan = useRef(Date.now())
|
|
84
85
|
|
|
85
86
|
useEffect(() => {
|
|
86
|
-
const handler = setTimeout(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
const handler = setTimeout(
|
|
88
|
+
() => {
|
|
89
|
+
if (Date.now() - lastRan.current >= delay) {
|
|
90
|
+
callback()
|
|
91
|
+
lastRan.current = Date.now()
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
delay - (Date.now() - lastRan.current),
|
|
95
|
+
)
|
|
92
96
|
|
|
93
97
|
return () => {
|
|
94
98
|
clearTimeout(handler)
|
|
@@ -117,7 +121,7 @@ export function getRandomColor(email: string): string {
|
|
|
117
121
|
return colors[sum % colors.length]
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
export const getAlphabetIcon = (str: string, rootClassName: string =
|
|
124
|
+
export const getAlphabetIcon = (str: string, rootClassName: string = '') => {
|
|
121
125
|
if (!str) return null
|
|
122
126
|
return (
|
|
123
127
|
<span
|
|
@@ -129,9 +133,7 @@ export const getAlphabetIcon = (str: string, rootClassName: string = "") => {
|
|
|
129
133
|
)
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
export const getEmptyArrayOfLength = (length: number) => {
|
|
133
|
-
return Array.from({ length })
|
|
134
|
-
}
|
|
136
|
+
export const getEmptyArrayOfLength = (length: number) => Array.from({ length })
|
|
135
137
|
|
|
136
138
|
export function noop(...args): any {}
|
|
137
139
|
|
|
@@ -155,7 +157,7 @@ export function getCookie(sKey) {
|
|
|
155
157
|
}
|
|
156
158
|
return (
|
|
157
159
|
document.cookie.replace(
|
|
158
|
-
new RegExp(
|
|
160
|
+
new RegExp(`(?:(?:^|.*;)\\s*${sKey.replace(/[\-\.\+\*]/g, '\\$&')}\\s*\\=\\s*([^;]*).*$)|^.*$`),
|
|
159
161
|
'$1',
|
|
160
162
|
) || null
|
|
161
163
|
)
|
|
@@ -198,7 +200,7 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
198
200
|
// in every re-render in component
|
|
199
201
|
const validateState = useCallback(
|
|
200
202
|
(state) => {
|
|
201
|
-
//check errors in all fields
|
|
203
|
+
// check errors in all fields
|
|
202
204
|
const hasErrorInState = Object.keys(validationSchema).some((key) => {
|
|
203
205
|
const isInputFieldRequired = validationSchema[key].required
|
|
204
206
|
const stateValue = state[key].value // state value
|
|
@@ -225,7 +227,7 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
// single validator
|
|
228
|
-
|
|
230
|
+
const _validator = validationSchema[name].validator
|
|
229
231
|
if (_validator && typeof _validator === 'object') {
|
|
230
232
|
if (!_validateSingleValidator(_validator, value)) {
|
|
231
233
|
return _validator.error
|
|
@@ -233,9 +235,9 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
233
235
|
}
|
|
234
236
|
|
|
235
237
|
// multiple validators
|
|
236
|
-
|
|
238
|
+
const _validators = validationSchema[name].validators
|
|
237
239
|
if (_validators && typeof _validators === 'object' && Array.isArray(_validators)) {
|
|
238
|
-
|
|
240
|
+
const errors = []
|
|
239
241
|
_validators.forEach((_validator) => {
|
|
240
242
|
if (!_validateSingleValidator(_validator, value)) {
|
|
241
243
|
errors.push(_validator.error)
|
|
@@ -254,7 +256,7 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
254
256
|
setIsDirty(true)
|
|
255
257
|
|
|
256
258
|
const { name, value } = event.target
|
|
257
|
-
|
|
259
|
+
const error = validateField(name, value)
|
|
258
260
|
setState((prevState) => ({
|
|
259
261
|
...prevState,
|
|
260
262
|
[name]: { value, error },
|
|
@@ -282,7 +284,7 @@ export function handleUTCTime(ts: string, isRelativeTime = false) {
|
|
|
282
284
|
let timestamp = ''
|
|
283
285
|
try {
|
|
284
286
|
if (ts && ts.length) {
|
|
285
|
-
|
|
287
|
+
const date = moment(ts)
|
|
286
288
|
if (isRelativeTime) timestamp = date.fromNow()
|
|
287
289
|
else timestamp = date.format('ddd DD MMM YYYY HH:mm:ss')
|
|
288
290
|
}
|
|
@@ -366,9 +368,7 @@ export function useAsync<T>(
|
|
|
366
368
|
dependencies: dependencyArray,
|
|
367
369
|
})
|
|
368
370
|
const mounted = useRef(true)
|
|
369
|
-
const dependencies: any[] = useMemo(() =>
|
|
370
|
-
return [...dependencyArray, shouldRun]
|
|
371
|
-
}, [...dependencyArray, shouldRun])
|
|
371
|
+
const dependencies: any[] = useMemo(() => [...dependencyArray, shouldRun], [...dependencyArray, shouldRun])
|
|
372
372
|
|
|
373
373
|
const reload = () => {
|
|
374
374
|
async function call() {
|
|
@@ -434,9 +434,8 @@ export function useAsync<T>(
|
|
|
434
434
|
export const processDeployedTime = (lastDeployed, isArgoInstalled) => {
|
|
435
435
|
if (lastDeployed) {
|
|
436
436
|
return handleUTCTime(lastDeployed, true)
|
|
437
|
-
} else {
|
|
438
|
-
return isArgoInstalled ? '' : 'Not deployed'
|
|
439
437
|
}
|
|
438
|
+
return isArgoInstalled ? '' : 'Not deployed'
|
|
440
439
|
}
|
|
441
440
|
|
|
442
441
|
/**
|
|
@@ -489,19 +488,19 @@ export const customStyles = {
|
|
|
489
488
|
}),
|
|
490
489
|
}
|
|
491
490
|
|
|
492
|
-
export const getFilteredChartVersions = (charts, selectedChartType) =>
|
|
491
|
+
export const getFilteredChartVersions = (charts, selectedChartType) =>
|
|
493
492
|
// Filter chart versions based on selected chart type
|
|
494
|
-
|
|
493
|
+
charts
|
|
495
494
|
.filter((item) => item?.chartType === selectedChartType.value)
|
|
496
495
|
.map((item) => ({
|
|
497
496
|
value: item?.chartVersion,
|
|
498
497
|
label: item?.chartVersion,
|
|
499
498
|
chartRefId: item.chartRefId,
|
|
500
499
|
}))
|
|
501
|
-
|
|
500
|
+
|
|
502
501
|
function removeEmptyObjectKeysAndNullValues(obj) {
|
|
503
502
|
// It recursively removes empty object keys and array values that are null
|
|
504
|
-
for (
|
|
503
|
+
for (const key in obj) {
|
|
505
504
|
if (Array.isArray(obj[key])) {
|
|
506
505
|
if (obj[key].length === 0) continue
|
|
507
506
|
obj[key] = obj[key].filter((item) => item !== null)
|
|
@@ -522,7 +521,7 @@ function removeEmptyObjectKeysAndNullValues(obj) {
|
|
|
522
521
|
|
|
523
522
|
export function getUnlockedJSON(json, jsonPathArray) {
|
|
524
523
|
const jsonCopy = JSON.parse(JSON.stringify(json))
|
|
525
|
-
|
|
524
|
+
const patches = jsonPathArray.flatMap((jsonPath) => {
|
|
526
525
|
const pathsToRemove = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
|
|
527
526
|
return pathsToRemove.map((result) =>
|
|
528
527
|
Array.isArray(result.parent)
|
|
@@ -530,7 +529,7 @@ export function getUnlockedJSON(json, jsonPathArray) {
|
|
|
530
529
|
: { op: 'remove', path: result.pointer },
|
|
531
530
|
)
|
|
532
531
|
})
|
|
533
|
-
|
|
532
|
+
const { newDocument } = jsonpatch.applyPatch(jsonCopy, patches)
|
|
534
533
|
|
|
535
534
|
removeEmptyObjectKeysAndNullValues(newDocument)
|
|
536
535
|
return newDocument
|
|
@@ -538,7 +537,7 @@ export function getUnlockedJSON(json, jsonPathArray) {
|
|
|
538
537
|
|
|
539
538
|
export function getLockedJSON(json, jsonPathArray: string[]) {
|
|
540
539
|
const jsonCopy = JSON.parse(JSON.stringify(json))
|
|
541
|
-
|
|
540
|
+
const resultJson = {}
|
|
542
541
|
jsonPathArray.forEach((jsonPath) => {
|
|
543
542
|
const elements = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
|
|
544
543
|
elements.forEach((element) => {
|
|
@@ -546,16 +545,16 @@ export function getLockedJSON(json, jsonPathArray: string[]) {
|
|
|
546
545
|
const lastPath = pathArray.pop()
|
|
547
546
|
let current = resultJson
|
|
548
547
|
for (let i = 0; i < pathArray.length; i++) {
|
|
549
|
-
|
|
548
|
+
const key = isNaN(Number(pathArray[i])) ? pathArray[i] : parseInt(pathArray[i])
|
|
550
549
|
if (!current[key]) {
|
|
551
|
-
current[key] = isNaN(Number(pathArray[i + 1]??lastPath)) ? {} : []
|
|
550
|
+
current[key] = isNaN(Number(pathArray[i + 1] ?? lastPath)) ? {} : []
|
|
552
551
|
}
|
|
553
552
|
current = current[key]
|
|
554
553
|
}
|
|
555
|
-
|
|
554
|
+
const key = isNaN(Number(lastPath)) ? lastPath : parseInt(lastPath)
|
|
556
555
|
current[key] = element.value
|
|
557
556
|
})
|
|
558
557
|
})
|
|
558
|
+
// eslint-disable-next-line dot-notation
|
|
559
559
|
return resultJson['$']
|
|
560
560
|
}
|
|
561
|
-
|
|
@@ -2,12 +2,10 @@ import { useState, useEffect } from 'react'
|
|
|
2
2
|
|
|
3
3
|
let timeout
|
|
4
4
|
|
|
5
|
-
const getSize = () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
}
|
|
5
|
+
const getSize = () => ({
|
|
6
|
+
width: window.innerWidth,
|
|
7
|
+
height: window.innerHeight,
|
|
8
|
+
})
|
|
11
9
|
|
|
12
10
|
export const useWindowSize = () => {
|
|
13
11
|
const [windowSize, setWindowSize] = useState(getSize)
|
|
@@ -17,7 +15,7 @@ export const useWindowSize = () => {
|
|
|
17
15
|
window.cancelAnimationFrame(timeout)
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
timeout = window.requestAnimationFrame(
|
|
18
|
+
timeout = window.requestAnimationFrame(() => {
|
|
21
19
|
setWindowSize(getSize())
|
|
22
20
|
})
|
|
23
21
|
}
|
|
@@ -13,13 +13,12 @@ export interface ImageComment {
|
|
|
13
13
|
artifactId: number
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
export interface ImageTaggingContainerType {
|
|
18
17
|
ciPipelineId?: number
|
|
19
18
|
artifactId?: number
|
|
20
19
|
imageComment?: ImageComment
|
|
21
20
|
imageReleaseTags?: ReleaseTag[]
|
|
22
|
-
updateCurrentAppMaterial
|
|
21
|
+
updateCurrentAppMaterial?: (matId: number, releaseTags?: ReleaseTag[], imageComment?: ImageComment) => void
|
|
23
22
|
appReleaseTagNames?: string[]
|
|
24
23
|
tagsEditable?: boolean
|
|
25
24
|
forceReInit?: boolean
|
|
@@ -41,4 +40,4 @@ export interface ImageButtonType {
|
|
|
41
40
|
isSuperAdmin: any
|
|
42
41
|
duplicateTag?: boolean
|
|
43
42
|
hideHardDelete?: boolean
|
|
44
|
-
}
|
|
43
|
+
}
|
package/src/Common/ImageTags.tsx
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import { toast } from 'react-toastify'
|
|
3
|
+
import Tippy from '@tippyjs/react'
|
|
2
4
|
import { ReactComponent as Add } from '../Assets/Icon/ic-add.svg'
|
|
3
5
|
import { ReactComponent as Close } from '../Assets/Icon/ic-cross.svg'
|
|
4
6
|
import { ReactComponent as QuestionFilled } from '../Assets/Icon/ic-help.svg'
|
|
@@ -7,7 +9,6 @@ import { ReactComponent as Question } from '../Assets/Icon/ic-help-outline.svg'
|
|
|
7
9
|
import { ReactComponent as EditIcon } from '../Assets/Icon/ic-pencil.svg'
|
|
8
10
|
import { ReactComponent as Redo } from '../Assets/Icon/ic-arrow-counter-clockwise.svg'
|
|
9
11
|
import { ReactComponent as Minus } from '../Assets/Icon/ic-minus.svg'
|
|
10
|
-
import { toast } from 'react-toastify'
|
|
11
12
|
import { ReactComponent as Info } from '../Assets/Icon/ic-info-filled.svg'
|
|
12
13
|
import { ReactComponent as Error } from '../Assets/Icon/ic-warning.svg'
|
|
13
14
|
import { ReactComponent as Warning } from '../Assets/Icon/ic-error-exclamation.svg'
|
|
@@ -17,7 +18,6 @@ import { ImageButtonType, ImageTaggingContainerType, ReleaseTag } from './ImageT
|
|
|
17
18
|
import { showError, stopPropagation } from './Helper'
|
|
18
19
|
import { TippyCustomized } from './TippyCustomized'
|
|
19
20
|
import { setImageTags } from './Common.service'
|
|
20
|
-
import Tippy from '@tippyjs/react'
|
|
21
21
|
import { Progressing } from './Progressing'
|
|
22
22
|
|
|
23
23
|
export const ImageTagsContainer = ({
|
|
@@ -34,15 +34,15 @@ export const ImageTagsContainer = ({
|
|
|
34
34
|
toggleCardMode,
|
|
35
35
|
hideHardDelete,
|
|
36
36
|
forceReInit,
|
|
37
|
-
isSuperAdmin
|
|
37
|
+
isSuperAdmin,
|
|
38
38
|
}: ImageTaggingContainerType) => {
|
|
39
|
-
const [initialTags, setInitialTags] = useState<ReleaseTag[]>(imageReleaseTags
|
|
39
|
+
const [initialTags, setInitialTags] = useState<ReleaseTag[]>(imageReleaseTags || [])
|
|
40
40
|
const [initialDescription, setInitialDescription] = useState(imageComment ? imageComment.comment : '')
|
|
41
|
-
const [existingTags, setExistingTags] = useState(appReleaseTagNames
|
|
41
|
+
const [existingTags, setExistingTags] = useState(appReleaseTagNames || [])
|
|
42
42
|
const [newDescription, setNewDescription] = useState(imageComment ? imageComment.comment : '')
|
|
43
43
|
const [isEditing, setIsEditing] = useState(false)
|
|
44
44
|
const [showTagsWarning, setShowTagsWarning] = useState(false)
|
|
45
|
-
const [displayedTags, setDisplayedTags] = useState<ReleaseTag[]>(imageReleaseTags
|
|
45
|
+
const [displayedTags, setDisplayedTags] = useState<ReleaseTag[]>(imageReleaseTags || [])
|
|
46
46
|
const [tagErrorMessage, setTagErrorMessage] = useState('')
|
|
47
47
|
const [createTags, setCreateTags] = useState<ReleaseTag[]>([])
|
|
48
48
|
const [softDeleteTags, setSoftDeleteTags] = useState<ReleaseTag[]>([])
|
|
@@ -51,22 +51,20 @@ export const ImageTagsContainer = ({
|
|
|
51
51
|
const [textInput, setTextInput] = useState<string>('')
|
|
52
52
|
const [loading, setLoading] = useState<boolean>(false)
|
|
53
53
|
|
|
54
|
-
|
|
55
54
|
useEffect(() => {
|
|
56
55
|
reInitState()
|
|
57
56
|
}, [imageReleaseTags, imageComment, tagsEditable])
|
|
58
57
|
|
|
59
58
|
useEffect(() => {
|
|
60
|
-
setExistingTags(appReleaseTagNames
|
|
59
|
+
setExistingTags(appReleaseTagNames || [])
|
|
61
60
|
}, [appReleaseTagNames])
|
|
62
61
|
|
|
63
|
-
|
|
64
62
|
const reInitState = () => {
|
|
65
63
|
if (forceReInit || !isEditing) {
|
|
66
|
-
setInitialTags(imageReleaseTags
|
|
64
|
+
setInitialTags(imageReleaseTags || [])
|
|
67
65
|
setInitialDescription(imageComment ? imageComment.comment : '')
|
|
68
66
|
setNewDescription(imageComment ? imageComment.comment : '')
|
|
69
|
-
setDisplayedTags(imageReleaseTags
|
|
67
|
+
setDisplayedTags(imageReleaseTags || [])
|
|
70
68
|
setIsEditing(false)
|
|
71
69
|
}
|
|
72
70
|
}
|
|
@@ -97,7 +95,7 @@ export const ImageTagsContainer = ({
|
|
|
97
95
|
setTagErrorMessage('')
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
const validateTag = (lowercaseValue)
|
|
98
|
+
const validateTag = (lowercaseValue): boolean => {
|
|
101
99
|
if (
|
|
102
100
|
lowercaseValue.length == 0 ||
|
|
103
101
|
lowercaseValue.length >= 128 ||
|
|
@@ -120,9 +118,9 @@ export const ImageTagsContainer = ({
|
|
|
120
118
|
return true
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
const handleTagCreate = (newValue)
|
|
121
|
+
const handleTagCreate = (newValue): ReleaseTag[] => {
|
|
124
122
|
const lowercaseValue = newValue.toLowerCase().trim()
|
|
125
|
-
if(validateTag(lowercaseValue)) {
|
|
123
|
+
if (validateTag(lowercaseValue)) {
|
|
126
124
|
const newTag: ReleaseTag = {
|
|
127
125
|
id: 0,
|
|
128
126
|
tagName: lowercaseValue,
|
|
@@ -148,7 +146,7 @@ export const ImageTagsContainer = ({
|
|
|
148
146
|
deleted: !updatedTags[index].deleted,
|
|
149
147
|
}
|
|
150
148
|
const updatedTag = updatedTags[index]
|
|
151
|
-
|
|
149
|
+
const updatedSoftDeleteTags = [...softDeleteTags]
|
|
152
150
|
const tagIndex = updatedSoftDeleteTags.findIndex((tag) => tag.tagName === updatedTag.tagName)
|
|
153
151
|
if (tagIndex !== -1) {
|
|
154
152
|
// Tag already exists in softDeleteTags array, remove it
|
|
@@ -206,13 +204,13 @@ export const ImageTagsContainer = ({
|
|
|
206
204
|
|
|
207
205
|
const payload = {
|
|
208
206
|
createTags: tagsToBeCreated,
|
|
209
|
-
softDeleteTags
|
|
207
|
+
softDeleteTags,
|
|
210
208
|
imageComment: {
|
|
211
209
|
id: 0,
|
|
212
210
|
comment: newDescription.trim(),
|
|
213
211
|
artifactId: 0,
|
|
214
212
|
},
|
|
215
|
-
hardDeleteTags
|
|
213
|
+
hardDeleteTags,
|
|
216
214
|
}
|
|
217
215
|
|
|
218
216
|
// set loading state true
|
|
@@ -242,7 +240,7 @@ export const ImageTagsContainer = ({
|
|
|
242
240
|
handleEditClick()
|
|
243
241
|
setShowTagsWarning(false)
|
|
244
242
|
setTagErrorMessage('')
|
|
245
|
-
if(updateCurrentAppMaterial)updateCurrentAppMaterial(artifactId,tags,res.result?.imageComment)
|
|
243
|
+
if (updateCurrentAppMaterial) updateCurrentAppMaterial(artifactId, tags, res.result?.imageComment)
|
|
246
244
|
})
|
|
247
245
|
.catch((err) => {
|
|
248
246
|
// Fix toast message
|
|
@@ -253,50 +251,45 @@ export const ImageTagsContainer = ({
|
|
|
253
251
|
showError(err)
|
|
254
252
|
}
|
|
255
253
|
})
|
|
256
|
-
.finally(()=>{
|
|
254
|
+
.finally(() => {
|
|
257
255
|
setLoading(false)
|
|
258
256
|
})
|
|
259
257
|
}
|
|
260
258
|
|
|
261
|
-
const renderInfoCard = (): JSX.Element =>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
>
|
|
274
|
-
<
|
|
275
|
-
<Question className="fcn-5 ml-8 cursor icon-dim-16"/>
|
|
276
|
-
</div>
|
|
277
|
-
</TippyCustomized>
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const getBuildContextAdditionalContent = () => {
|
|
282
|
-
return (
|
|
283
|
-
<div className="h-250 fs-13 dc__overflow-scroll p-12">
|
|
284
|
-
<div>Release tags allow you to tag container images with readable and relatable tags eg. v1.0.</div>
|
|
285
|
-
<ul className="pl-20 mt-8">
|
|
286
|
-
<li>
|
|
287
|
-
A release tag can only be added if a workflow has CD pipelines deploying to Production
|
|
288
|
-
environments.
|
|
289
|
-
</li>
|
|
290
|
-
<li>Multiple tags can be added to an image.</li>
|
|
291
|
-
<li>Multiple images in an application cannot have the same tag.</li>
|
|
292
|
-
<li>
|
|
293
|
-
Tags cannot be deleted once saved. Although, you can soft delete a tag if an unwanted tag has
|
|
294
|
-
been added.
|
|
295
|
-
</li>
|
|
296
|
-
</ul>
|
|
259
|
+
const renderInfoCard = (): JSX.Element => (
|
|
260
|
+
<TippyCustomized
|
|
261
|
+
theme={TippyTheme.white}
|
|
262
|
+
className="w-300 fcv-5"
|
|
263
|
+
placement="right"
|
|
264
|
+
Icon={QuestionFilled}
|
|
265
|
+
heading="Release tags"
|
|
266
|
+
showCloseButton
|
|
267
|
+
trigger="click"
|
|
268
|
+
interactive
|
|
269
|
+
additionalContent={getBuildContextAdditionalContent()}
|
|
270
|
+
>
|
|
271
|
+
<div className="flex">
|
|
272
|
+
<Question className="fcn-5 ml-8 cursor icon-dim-16" />
|
|
297
273
|
</div>
|
|
298
|
-
|
|
299
|
-
|
|
274
|
+
</TippyCustomized>
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
const getBuildContextAdditionalContent = () => (
|
|
278
|
+
<div className="h-250 fs-13 dc__overflow-scroll p-12">
|
|
279
|
+
<div>Release tags allow you to tag container images with readable and relatable tags eg. v1.0.</div>
|
|
280
|
+
<ul className="pl-20 mt-8">
|
|
281
|
+
<li>
|
|
282
|
+
A release tag can only be added if a workflow has CD pipelines deploying to Production environments.
|
|
283
|
+
</li>
|
|
284
|
+
<li>Multiple tags can be added to an image.</li>
|
|
285
|
+
<li>Multiple images in an application cannot have the same tag.</li>
|
|
286
|
+
<li>
|
|
287
|
+
Tags cannot be deleted once saved. Although, you can soft delete a tag if an unwanted tag has been
|
|
288
|
+
added.
|
|
289
|
+
</li>
|
|
290
|
+
</ul>
|
|
291
|
+
</div>
|
|
292
|
+
)
|
|
300
293
|
|
|
301
294
|
const creatableRef = useRef(null)
|
|
302
295
|
|
|
@@ -306,7 +299,7 @@ export const ImageTagsContainer = ({
|
|
|
306
299
|
<AddImageButton handleEditClick={handleEditClick} />
|
|
307
300
|
</div>
|
|
308
301
|
) : (
|
|
309
|
-
<div
|
|
302
|
+
<div />
|
|
310
303
|
)
|
|
311
304
|
}
|
|
312
305
|
|
|
@@ -456,8 +449,10 @@ export const ImageTagsContainer = ({
|
|
|
456
449
|
className="flex left image-tag-left-border w-100 mt-8 mb-8 pr-10 pl-10"
|
|
457
450
|
data-testid="image-tags-container-hover"
|
|
458
451
|
>
|
|
459
|
-
<div className=
|
|
460
|
-
{initialDescription &&
|
|
452
|
+
<div className="w-100">
|
|
453
|
+
{initialDescription && (
|
|
454
|
+
<div className="mb-6 fs-13 lh-20 dc__word-break-all">{initialDescription}</div>
|
|
455
|
+
)}
|
|
461
456
|
<div className="flex-wrap flex left">
|
|
462
457
|
{initialTags?.map((tag, index) => (
|
|
463
458
|
<ImageTagButton
|
|
@@ -520,11 +515,11 @@ export const ImageTagButton = ({
|
|
|
520
515
|
const tabColor = () => {
|
|
521
516
|
if (duplicateTag) {
|
|
522
517
|
return 'cr-5 bcr-1 er-2'
|
|
523
|
-
}
|
|
518
|
+
}
|
|
519
|
+
if (isSoftDeleted) {
|
|
524
520
|
return 'cy-7 bcy-1 dc__strike-through ey-2'
|
|
525
|
-
} else {
|
|
526
|
-
return 'cn-9'
|
|
527
521
|
}
|
|
522
|
+
return 'cn-9'
|
|
528
523
|
}
|
|
529
524
|
|
|
530
525
|
return (
|
|
@@ -538,7 +533,7 @@ export const ImageTagButton = ({
|
|
|
538
533
|
) : (
|
|
539
534
|
<Tippy
|
|
540
535
|
className="default-tt"
|
|
541
|
-
arrow
|
|
536
|
+
arrow
|
|
542
537
|
placement="top"
|
|
543
538
|
content={isInSoftDeleteTags ? 'Restore tag' : 'Soft delete tag'}
|
|
544
539
|
>
|
|
@@ -556,7 +551,7 @@ export const ImageTagButton = ({
|
|
|
556
551
|
</Tippy>
|
|
557
552
|
)}
|
|
558
553
|
{text}
|
|
559
|
-
<Tippy className="default-tt" arrow
|
|
554
|
+
<Tippy className="default-tt" arrow placement="top" content="Remove tag">
|
|
560
555
|
<div className={`action-icon ml-4 lh-16 pt-3 ${canTagBeHardDelete ? 'show-icon' : ''}`}>
|
|
561
556
|
<Close
|
|
562
557
|
className="icon-dim-12 fcn-6 cursor"
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import { Link } from 'react-router-dom'
|
|
3
2
|
import { InfoColourBarType } from '../Types'
|
|
4
3
|
import './infoColourBar.scss'
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const InfoColourBar = ({
|
|
7
6
|
message,
|
|
8
7
|
classname,
|
|
9
8
|
Icon,
|
|
@@ -16,11 +15,12 @@ function InfoColourBar({
|
|
|
16
15
|
linkClass,
|
|
17
16
|
internalLink,
|
|
18
17
|
styles,
|
|
19
|
-
}: InfoColourBarType) {
|
|
18
|
+
}: InfoColourBarType) => {
|
|
20
19
|
const renderLink = () => {
|
|
21
20
|
if (!linkText) {
|
|
22
21
|
return null
|
|
23
|
-
}
|
|
22
|
+
}
|
|
23
|
+
if (redirectLink) {
|
|
24
24
|
if (internalLink) {
|
|
25
25
|
return (
|
|
26
26
|
<Link
|
|
@@ -41,6 +41,7 @@ function InfoColourBar({
|
|
|
41
41
|
onClick={linkOnClick}
|
|
42
42
|
data-testid="info-bar-redirectLink"
|
|
43
43
|
className="cursor dc__link dc__underline-onhover mr-5"
|
|
44
|
+
rel="noreferrer"
|
|
44
45
|
>
|
|
45
46
|
{linkText}
|
|
46
47
|
</a>
|
|
@@ -49,7 +50,11 @@ function InfoColourBar({
|
|
|
49
50
|
|
|
50
51
|
return (
|
|
51
52
|
linkOnClick && (
|
|
52
|
-
<div
|
|
53
|
+
<div
|
|
54
|
+
onClick={linkOnClick}
|
|
55
|
+
className="cursor dc__link dc__underline-onhover"
|
|
56
|
+
data-testid="info-bar-linkText"
|
|
57
|
+
>
|
|
53
58
|
{linkText}
|
|
54
59
|
</div>
|
|
55
60
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useRef, useEffect } from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom'
|
|
3
3
|
import { ModalType } from '../Types'
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ export const Modal = ({
|
|
|
12
12
|
preventWheelDisable = false,
|
|
13
13
|
noBackDrop,
|
|
14
14
|
}: ModalType) => {
|
|
15
|
-
const innerRef =
|
|
15
|
+
const innerRef = useRef(null)
|
|
16
16
|
function handleClick(e) {
|
|
17
17
|
if (typeof onClick !== 'function') return
|
|
18
18
|
if (innerRef && innerRef.current?.contains(e.target)) {
|
|
@@ -35,7 +35,7 @@ export const Modal = ({
|
|
|
35
35
|
}
|
|
36
36
|
useEffect(() => {
|
|
37
37
|
document.addEventListener('click', handleClick)
|
|
38
|
-
|
|
38
|
+
const modal = document.getElementById('visible-modal')
|
|
39
39
|
if (modal) modal.classList.add('show')
|
|
40
40
|
if (noBackDrop) modal.classList.add('no-back-drop')
|
|
41
41
|
if (!preventWheelDisable) document.body.addEventListener('wheel', disableWheel, { passive: false })
|
|
@@ -1,36 +1,43 @@
|
|
|
1
|
-
import
|
|
2
|
-
import ReactDOM from 'react-dom'
|
|
1
|
+
import { Component } from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom'
|
|
3
3
|
import closeIcon from '../../Assets/Icon/ic-cross.svg'
|
|
4
4
|
|
|
5
5
|
export interface OpaqueModalProps {
|
|
6
|
-
className?: string
|
|
7
|
-
onHide?: any
|
|
8
|
-
noBackground?: boolean
|
|
6
|
+
className?: string
|
|
7
|
+
onHide?: any
|
|
8
|
+
noBackground?: boolean
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export class OpaqueModal extends
|
|
12
|
-
modalRef = document.getElementById('full-screen-modal')
|
|
11
|
+
export class OpaqueModal extends Component<OpaqueModalProps> {
|
|
12
|
+
modalRef = document.getElementById('full-screen-modal')
|
|
13
13
|
|
|
14
14
|
componentDidMount() {
|
|
15
|
-
if (this.props.noBackground)
|
|
16
|
-
|
|
17
|
-
else this.modalRef.classList.add("show-with-bg");
|
|
15
|
+
if (this.props.noBackground) this.modalRef.classList.add('show')
|
|
16
|
+
else this.modalRef.classList.add('show-with-bg')
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
componentWillUnmount() {
|
|
21
|
-
this.modalRef.classList.remove(
|
|
22
|
-
this.modalRef.classList.remove(
|
|
20
|
+
this.modalRef.classList.remove('show')
|
|
21
|
+
this.modalRef.classList.remove('show-with-bg')
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
render() {
|
|
26
|
-
const { className =
|
|
25
|
+
const { className = '', onHide = null } = { ...this.props }
|
|
27
26
|
|
|
28
27
|
return ReactDOM.createPortal(
|
|
29
28
|
<div className={`full-screen-modal__body-container ${className}`}>
|
|
30
29
|
{this.props.children}
|
|
31
|
-
{typeof onHide === 'function' &&
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
{typeof onHide === 'function' && (
|
|
31
|
+
<div
|
|
32
|
+
className="close-btn icon-dim-24"
|
|
33
|
+
onClick={(e) => onHide(false)}
|
|
34
|
+
data-testid="opaque-modal-close"
|
|
35
|
+
>
|
|
36
|
+
<img className="close-img" src={closeIcon} alt="close" />
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>,
|
|
40
|
+
document.getElementById('full-screen-modal'),
|
|
41
|
+
)
|
|
35
42
|
}
|
|
36
|
-
}
|
|
43
|
+
}
|