@devtron-labs/devtron-fe-common-lib 0.0.48-beta-5 → 0.0.48-beta-7
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 +75 -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 +4 -0
- package/.lintstagedrc +5 -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 -1
- package/dist/Common/CustomInput/CustomInput.d.ts +1 -1
- package/dist/Common/CustomInput/Types.d.ts +2 -1
- 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 -3
- 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 -55
- package/dist/index.d.ts +1 -1
- package/dist/index.js +391 -2023
- package/package.json +19 -4
- package/src/Common/AddCDButton/AddCDButton.tsx +1 -2
- package/src/Common/Api.ts +21 -26
- 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/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 +59 -64
- package/src/Common/Constants.ts +10 -10
- package/src/Common/CustomInput/CustomInput.tsx +22 -28
- package/src/Common/CustomInput/Types.ts +4 -3
- 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 +25 -15
- 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 +23 -87
- package/src/Common/Hooks/UseClickOutside/UseClickOutside.tsx +1 -1
- package/src/Common/Hooks/UseSuperAdmin/UseSuperAdmin.tsx +1 -1
- 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 +12 -70
- package/src/custom.d.ts +7 -7
- package/src/index.ts +1 -1
package/src/Common/Helper.tsx
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
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'
|
|
6
7
|
import { ERROR_EMPTY_SCREEN, TOKEN_COOKIE_NAME } from './Constants'
|
|
7
8
|
import { ServerErrors } from './ServerError'
|
|
8
9
|
import { toastAccessDenied } from './ToastBody'
|
|
9
10
|
import { AsyncOptions, AsyncState, UseSearchString } from './Types'
|
|
10
|
-
import { JSONPath } from 'jsonpath-plus'
|
|
11
|
-
import * as jsonpatch from 'fast-json-patch'
|
|
12
11
|
|
|
13
12
|
toast.configure({
|
|
14
13
|
autoClose: 3000,
|
|
@@ -79,16 +78,19 @@ export const stopPropagation = (event): void => {
|
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
export function useThrottledEffect(callback, delay, deps = []) {
|
|
82
|
-
//function will be executed only once in a given time interval.
|
|
81
|
+
// function will be executed only once in a given time interval.
|
|
83
82
|
const lastRan = useRef(Date.now())
|
|
84
83
|
|
|
85
84
|
useEffect(() => {
|
|
86
|
-
const handler = setTimeout(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
const handler = setTimeout(
|
|
86
|
+
() => {
|
|
87
|
+
if (Date.now() - lastRan.current >= delay) {
|
|
88
|
+
callback()
|
|
89
|
+
lastRan.current = Date.now()
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
delay - (Date.now() - lastRan.current),
|
|
93
|
+
)
|
|
92
94
|
|
|
93
95
|
return () => {
|
|
94
96
|
clearTimeout(handler)
|
|
@@ -117,7 +119,7 @@ export function getRandomColor(email: string): string {
|
|
|
117
119
|
return colors[sum % colors.length]
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
export const getAlphabetIcon = (str: string, rootClassName: string =
|
|
122
|
+
export const getAlphabetIcon = (str: string, rootClassName: string = '') => {
|
|
121
123
|
if (!str) return null
|
|
122
124
|
return (
|
|
123
125
|
<span
|
|
@@ -129,9 +131,7 @@ export const getAlphabetIcon = (str: string, rootClassName: string = "") => {
|
|
|
129
131
|
)
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
export const getEmptyArrayOfLength = (length: number) => {
|
|
133
|
-
return Array.from({ length })
|
|
134
|
-
}
|
|
134
|
+
export const getEmptyArrayOfLength = (length: number) => Array.from({ length })
|
|
135
135
|
|
|
136
136
|
export function noop(...args): any {}
|
|
137
137
|
|
|
@@ -155,7 +155,7 @@ export function getCookie(sKey) {
|
|
|
155
155
|
}
|
|
156
156
|
return (
|
|
157
157
|
document.cookie.replace(
|
|
158
|
-
new RegExp(
|
|
158
|
+
new RegExp(`(?:(?:^|.*;)\\s*${sKey.replace(/[\-\.\+\*]/g, '\\$&')}\\s*\\=\\s*([^;]*).*$)|^.*$`),
|
|
159
159
|
'$1',
|
|
160
160
|
) || null
|
|
161
161
|
)
|
|
@@ -198,7 +198,7 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
198
198
|
// in every re-render in component
|
|
199
199
|
const validateState = useCallback(
|
|
200
200
|
(state) => {
|
|
201
|
-
//check errors in all fields
|
|
201
|
+
// check errors in all fields
|
|
202
202
|
const hasErrorInState = Object.keys(validationSchema).some((key) => {
|
|
203
203
|
const isInputFieldRequired = validationSchema[key].required
|
|
204
204
|
const stateValue = state[key].value // state value
|
|
@@ -225,7 +225,7 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// single validator
|
|
228
|
-
|
|
228
|
+
const _validator = validationSchema[name].validator
|
|
229
229
|
if (_validator && typeof _validator === 'object') {
|
|
230
230
|
if (!_validateSingleValidator(_validator, value)) {
|
|
231
231
|
return _validator.error
|
|
@@ -233,9 +233,9 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
// multiple validators
|
|
236
|
-
|
|
236
|
+
const _validators = validationSchema[name].validators
|
|
237
237
|
if (_validators && typeof _validators === 'object' && Array.isArray(_validators)) {
|
|
238
|
-
|
|
238
|
+
const errors = []
|
|
239
239
|
_validators.forEach((_validator) => {
|
|
240
240
|
if (!_validateSingleValidator(_validator, value)) {
|
|
241
241
|
errors.push(_validator.error)
|
|
@@ -254,7 +254,7 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
|
254
254
|
setIsDirty(true)
|
|
255
255
|
|
|
256
256
|
const { name, value } = event.target
|
|
257
|
-
|
|
257
|
+
const error = validateField(name, value)
|
|
258
258
|
setState((prevState) => ({
|
|
259
259
|
...prevState,
|
|
260
260
|
[name]: { value, error },
|
|
@@ -282,7 +282,7 @@ export function handleUTCTime(ts: string, isRelativeTime = false) {
|
|
|
282
282
|
let timestamp = ''
|
|
283
283
|
try {
|
|
284
284
|
if (ts && ts.length) {
|
|
285
|
-
|
|
285
|
+
const date = moment(ts)
|
|
286
286
|
if (isRelativeTime) timestamp = date.fromNow()
|
|
287
287
|
else timestamp = date.format('ddd DD MMM YYYY HH:mm:ss')
|
|
288
288
|
}
|
|
@@ -366,9 +366,7 @@ export function useAsync<T>(
|
|
|
366
366
|
dependencies: dependencyArray,
|
|
367
367
|
})
|
|
368
368
|
const mounted = useRef(true)
|
|
369
|
-
const dependencies: any[] = useMemo(() =>
|
|
370
|
-
return [...dependencyArray, shouldRun]
|
|
371
|
-
}, [...dependencyArray, shouldRun])
|
|
369
|
+
const dependencies: any[] = useMemo(() => [...dependencyArray, shouldRun], [...dependencyArray, shouldRun])
|
|
372
370
|
|
|
373
371
|
const reload = () => {
|
|
374
372
|
async function call() {
|
|
@@ -434,9 +432,8 @@ export function useAsync<T>(
|
|
|
434
432
|
export const processDeployedTime = (lastDeployed, isArgoInstalled) => {
|
|
435
433
|
if (lastDeployed) {
|
|
436
434
|
return handleUTCTime(lastDeployed, true)
|
|
437
|
-
} else {
|
|
438
|
-
return isArgoInstalled ? '' : 'Not deployed'
|
|
439
435
|
}
|
|
436
|
+
return isArgoInstalled ? '' : 'Not deployed'
|
|
440
437
|
}
|
|
441
438
|
|
|
442
439
|
/**
|
|
@@ -460,64 +457,3 @@ export const getUrlWithSearchParams = (url: string, params: Record<string | numb
|
|
|
460
457
|
* Custom exception logger function for logging errors to sentry
|
|
461
458
|
*/
|
|
462
459
|
export const logExceptionToSentry = Sentry.captureException.bind(window)
|
|
463
|
-
|
|
464
|
-
function removeEmptyObjectKeysAndNullValues(obj) {
|
|
465
|
-
// It recursively removes empty object keys and array values that are null
|
|
466
|
-
for (let key in obj) {
|
|
467
|
-
if (Array.isArray(obj[key])) {
|
|
468
|
-
if (obj[key].length === 0) continue
|
|
469
|
-
obj[key] = obj[key].filter((item) => item !== null)
|
|
470
|
-
// Check if the array is empty
|
|
471
|
-
if (obj[key].length === 0) {
|
|
472
|
-
delete obj[key] // Delete the key if the array is empty
|
|
473
|
-
}
|
|
474
|
-
} else if (obj[key] && typeof obj[key] === 'object') {
|
|
475
|
-
if (removeEmptyObjectKeysAndNullValues(obj[key])) {
|
|
476
|
-
delete obj[key]
|
|
477
|
-
}
|
|
478
|
-
} else if (obj[key] === undefined) {
|
|
479
|
-
delete obj[key]
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
return Object.keys(obj).length === 0
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
export function getUnlockedJSON(json, jsonPathArray) {
|
|
486
|
-
const jsonCopy = JSON.parse(JSON.stringify(json))
|
|
487
|
-
let patches = jsonPathArray.flatMap((jsonPath) => {
|
|
488
|
-
const pathsToRemove = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
|
|
489
|
-
return pathsToRemove.map((result) =>
|
|
490
|
-
Array.isArray(result.parent)
|
|
491
|
-
? { op: 'replace', path: result.pointer, value: null }
|
|
492
|
-
: { op: 'remove', path: result.pointer },
|
|
493
|
-
)
|
|
494
|
-
})
|
|
495
|
-
let newDocument = jsonpatch.applyPatch(jsonCopy, patches).newDocument
|
|
496
|
-
|
|
497
|
-
removeEmptyObjectKeysAndNullValues(newDocument)
|
|
498
|
-
return newDocument
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
export function getLockedJSON(json, jsonPathArray: string[]) {
|
|
502
|
-
const jsonCopy = JSON.parse(JSON.stringify(json))
|
|
503
|
-
let resultJson = {}
|
|
504
|
-
jsonPathArray.forEach((jsonPath) => {
|
|
505
|
-
const elements = JSONPath({ path: jsonPath, json: jsonCopy, resultType: 'all' })
|
|
506
|
-
elements.forEach((element) => {
|
|
507
|
-
const pathArray: string[] = JSONPath.toPathArray(element.path)
|
|
508
|
-
const lastPath = pathArray.pop()
|
|
509
|
-
let current = resultJson
|
|
510
|
-
for (let i = 0; i < pathArray.length; i++) {
|
|
511
|
-
let key = isNaN(Number(pathArray[i])) ? pathArray[i] : parseInt(pathArray[i])
|
|
512
|
-
if (!current[key]) {
|
|
513
|
-
current[key] = isNaN(Number(pathArray[i + 1])) ? {} : []
|
|
514
|
-
}
|
|
515
|
-
current = current[key]
|
|
516
|
-
}
|
|
517
|
-
let key = isNaN(Number(lastPath)) ? lastPath : parseInt(lastPath)
|
|
518
|
-
current[key] = element.value
|
|
519
|
-
})
|
|
520
|
-
})
|
|
521
|
-
return resultJson['$']
|
|
522
|
-
}
|
|
523
|
-
|
|
@@ -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
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Component } from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom'
|
|
3
3
|
|
|
4
|
-
export class VisibleModal extends
|
|
4
|
+
export class VisibleModal extends Component<{
|
|
5
5
|
className: string
|
|
6
6
|
parentClassName?: string
|
|
7
7
|
noBackground?: boolean
|
|
@@ -46,7 +46,11 @@ export class VisibleModal extends React.Component<{
|
|
|
46
46
|
|
|
47
47
|
render() {
|
|
48
48
|
return ReactDOM.createPortal(
|
|
49
|
-
<div
|
|
49
|
+
<div
|
|
50
|
+
className={`visible-modal__body ${this.props.className}`}
|
|
51
|
+
onClick={this.props?.close}
|
|
52
|
+
data-testid="visible-modal-close"
|
|
53
|
+
>
|
|
50
54
|
{this.props.children}
|
|
51
55
|
</div>,
|
|
52
56
|
document.getElementById('visible-modal'),
|