Skip to content

Commit 589f1db

Browse files
authored
Merge pull request #2706 from devtron-labs/feat/revamp-edit-taints-modal
feat: Revamp Edit Taints Modal
2 parents 377b7d2 + 6cfbd63 commit 589f1db

File tree

11 files changed

+445
-305
lines changed

11 files changed

+445
-305
lines changed

.eslintignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,9 @@ src/components/CIPipelineN/ciPipeline.utils.tsx
5252
src/components/ClusterNodes/ClusterEvents.tsx
5353
src/components/ClusterNodes/ClusterManifest.tsx
5454
src/components/ClusterNodes/ClusterNodeEmptyStates.tsx
55-
src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx
56-
src/components/ClusterNodes/NodeActions/NodeActionsMenu.tsx
57-
src/components/ClusterNodes/NodeActions/validationRules.ts
5855
src/components/ClusterNodes/NodeDetails.tsx
5956
src/components/ClusterNodes/__tests__/ClusterManifest.test.tsx
6057
src/components/ClusterNodes/__tests__/NodeList.test.tsx
61-
src/components/ClusterNodes/constants.ts
6258
src/components/Hotjar/Hotjar.tsx
6359
src/components/Jobs/ExpandedRow/ExpandedRow.tsx
6460
src/components/Jobs/JobDetails/JobDetails.tsx

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.14.1-pre-2",
7+
"@devtron-labs/devtron-fe-common-lib": "1.14.1-pre-3",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",

src/Pages/GlobalConfigurations/DeploymentCharts/List/UploadChartModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ const UploadChartModal = ({ closeUploadPopup }: UploadChartModalType) => {
199199
2. Image descriptor template file - .image_descriptor_template.json.
200200
</div>
201201
<div className="cn-7 fw-4 fs-14 cn-7 mb-20">3. Custom chart packaged in the *.tgz format.</div>
202-
<div className="flexbox-col dc__align-start">
202+
<div className="flexbox-col dc__align-start pt-20">
203203
<div className="fw-6 fs-13 cn-9 mb-8">
204204
📙 Need help?&nbsp;
205205
<DocLink

src/components/CIPipelineN/Sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ export const Sidebar = ({
403403
)}
404404
</div>
405405
) : (
406-
<div className="flexbox-col dc__gap-4">
406+
<div className="sidebar-action-container flexbox-col dc__gap-4">
407407
<div className="dc__uppercase fw-6 fs-12 cn-6">Trigger {isJobCard ? 'JOB' : 'BUILD'} PIPELINE</div>
408408
<div>
409409
<RadioGroup

src/components/CIPipelineN/VariableDataTable/VariableDataTable.component.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ export const VariableDataTable = ({ type, isCustomTask = false }: VariableDataTa
568568
onRowDelete={handleRowDelete}
569569
onRowAdd={handleRowAdd}
570570
addBtnTooltip={VARIABLE_DATA_TABLE_ADD_BUTTON_TIPPY_MAP[type]}
571+
shouldAutoFocusOnMount
571572
{...(isFELibAvailable && isInputPluginVariable
572573
? {
573574
actionButtonConfig: {

src/components/ClusterNodes/NodeActions/EditTaintsModal.tsx

Lines changed: 180 additions & 200 deletions
Large diffs are not rendered by default.
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {
18+
DynamicDataTableCellValidationState,
19+
DynamicDataTableRowDataType,
20+
getUniqueId,
21+
} from '@devtron-labs/devtron-fe-common-lib'
22+
23+
import { PATTERNS } from '@Config/constants'
24+
25+
import { TAINT_OPTIONS, TAINTS_TABLE_HEADERS } from '../constants'
26+
import { EFFECT_TYPE, TaintsTableHeaderKeys, TaintsTableType, TaintType } from '../types'
27+
28+
export const getTaintsTableRow = (taint?: TaintType, id?: number): TaintsTableType['rows'][number] => ({
29+
id: id ?? getUniqueId(),
30+
data: {
31+
[TaintsTableHeaderKeys.KEY]: {
32+
type: DynamicDataTableRowDataType.TEXT,
33+
value: taint?.key || '',
34+
props: {
35+
placeholder: 'Enter key',
36+
},
37+
},
38+
[TaintsTableHeaderKeys.VALUE]: {
39+
type: DynamicDataTableRowDataType.TEXT,
40+
value: taint?.value || '',
41+
props: {
42+
placeholder: 'Enter value',
43+
},
44+
},
45+
[TaintsTableHeaderKeys.EFFECT]: {
46+
type: DynamicDataTableRowDataType.DROPDOWN,
47+
value: taint?.effect || EFFECT_TYPE.PreferNoSchedule,
48+
props: {
49+
options: TAINT_OPTIONS,
50+
},
51+
},
52+
},
53+
})
54+
55+
export const getTaintsTableRows = (taints: TaintType[]): TaintsTableType['rows'] =>
56+
taints?.length ? taints.map(getTaintsTableRow) : []
57+
58+
export const getTaintsRowCellError = () =>
59+
TAINTS_TABLE_HEADERS.reduce(
60+
(headerAcc, { key }) => ({ ...headerAcc, [key]: { isValid: true, errorMessages: [] } }),
61+
{},
62+
)
63+
64+
export const getTaintsTableCellError = (taintList: TaintsTableType['rows']): TaintsTableType['cellError'] =>
65+
taintList.reduce((acc, curr) => {
66+
if (!acc[curr.id]) {
67+
acc[curr.id] = getTaintsRowCellError()
68+
}
69+
70+
return acc
71+
}, {})
72+
73+
export const getTaintsTableCellValidateState = (
74+
headerKey: TaintsTableHeaderKeys,
75+
value: string,
76+
): DynamicDataTableCellValidationState => {
77+
if (headerKey === TaintsTableHeaderKeys.KEY) {
78+
const keyPrefixRegex = new RegExp(PATTERNS.KUBERNETES_KEY_PREFIX)
79+
const keyNameRegex = new RegExp(PATTERNS.KUBERNETES_KEY_NAME)
80+
81+
if (!value) {
82+
return { errorMessages: ['Key is required'], isValid: false }
83+
}
84+
85+
if (value.length > 253) {
86+
return { errorMessages: ['Maximum 253 chars are allowed'], isValid: false }
87+
}
88+
89+
if (value.indexOf('/') !== -1) {
90+
const keyArr = value.split('/')
91+
92+
if (keyArr.length > 2 || !keyPrefixRegex.test(keyArr[0])) {
93+
return {
94+
errorMessages: ["The key can begin with a DNS subdomain prefix and a single '/'"],
95+
isValid: false,
96+
}
97+
}
98+
99+
if (!keyNameRegex.test(keyArr[1])) {
100+
return {
101+
errorMessages: [
102+
'The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores',
103+
],
104+
isValid: false,
105+
}
106+
}
107+
} else if (!keyNameRegex.test(value)) {
108+
return {
109+
errorMessages: [
110+
'The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores',
111+
],
112+
isValid: false,
113+
}
114+
}
115+
}
116+
117+
if (headerKey === TaintsTableHeaderKeys.VALUE && value) {
118+
const valueRegex = new RegExp(PATTERNS.KUBERNETES_VALUE)
119+
120+
if (value.length > 63) {
121+
return { errorMessages: ['Maximum 63 chars are allowed'], isValid: false }
122+
}
123+
if (!valueRegex.test(value)) {
124+
return {
125+
errorMessages: [
126+
'The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores',
127+
],
128+
isValid: false,
129+
}
130+
}
131+
}
132+
133+
return {
134+
errorMessages: [],
135+
isValid: true,
136+
}
137+
}
138+
139+
const getTaintUniqueKey = (data: TaintsTableType['rows'][number]['data']) =>
140+
`${data[TaintsTableHeaderKeys.KEY].value}-${data[TaintsTableHeaderKeys.EFFECT].value}`
141+
142+
export const validateUniqueTaintKey = ({
143+
taintList,
144+
taintCellError,
145+
}: {
146+
taintList: TaintsTableType['rows']
147+
taintCellError: TaintsTableType['cellError']
148+
}) => {
149+
const updatedCellError = taintCellError
150+
const uniqueTaintKeyMap = taintList.reduce((acc, curr) => {
151+
const key = getTaintUniqueKey(curr.data)
152+
acc[key] = (acc[key] || 0) + 1
153+
154+
return acc
155+
}, {})
156+
const uniqueKeyErrorMsg = 'Key and effect must be a unique combination'
157+
158+
taintList.forEach(({ id, data }) => {
159+
const key = getTaintUniqueKey(data)
160+
161+
if (data[TaintsTableHeaderKeys.KEY].value && data[TaintsTableHeaderKeys.EFFECT].value) {
162+
if (
163+
updatedCellError[id][TaintsTableHeaderKeys.KEY].isValid &&
164+
updatedCellError[id][TaintsTableHeaderKeys.EFFECT].isValid &&
165+
uniqueTaintKeyMap[key] > 1
166+
) {
167+
updatedCellError[id][TaintsTableHeaderKeys.KEY] = {
168+
errorMessages: [uniqueKeyErrorMsg],
169+
isValid: false,
170+
}
171+
updatedCellError[id][TaintsTableHeaderKeys.EFFECT] = {
172+
errorMessages: [uniqueKeyErrorMsg],
173+
isValid: false,
174+
}
175+
} else if (uniqueTaintKeyMap[key] < 2) {
176+
if (updatedCellError[id][TaintsTableHeaderKeys.KEY].errorMessages[0] === uniqueKeyErrorMsg) {
177+
updatedCellError[id][TaintsTableHeaderKeys.KEY] = {
178+
errorMessages: [],
179+
isValid: true,
180+
}
181+
}
182+
if (updatedCellError[id][TaintsTableHeaderKeys.EFFECT].errorMessages[0] === uniqueKeyErrorMsg) {
183+
updatedCellError[id][TaintsTableHeaderKeys.EFFECT] = {
184+
errorMessages: [],
185+
isValid: true,
186+
}
187+
}
188+
}
189+
}
190+
})
191+
}
192+
193+
export const getTaintTableValidateState = ({ taintList }: { taintList: TaintsTableType['rows'] }) => {
194+
const taintCellError: TaintsTableType['cellError'] = taintList.reduce((acc, curr) => {
195+
acc[curr.id] = TAINTS_TABLE_HEADERS.reduce(
196+
(headerAcc, { key }) => ({
197+
...headerAcc,
198+
[key]: getTaintsTableCellValidateState(key, curr.data[key].value),
199+
}),
200+
{},
201+
)
202+
return acc
203+
}, {})
204+
205+
validateUniqueTaintKey({ taintCellError, taintList })
206+
207+
const isInvalid = Object.values(taintCellError).some(
208+
({ effect, key, value }) => !(effect.isValid && key.isValid && value.isValid),
209+
)
210+
211+
return { isValid: !isInvalid, taintCellError }
212+
}
213+
214+
export const getTaintsPayload = (taintList: TaintsTableType['rows']) =>
215+
taintList.map(({ data }) => ({
216+
key: data.key.value,
217+
value: data.value.value,
218+
effect: data.effect.value as EFFECT_TYPE,
219+
}))

src/components/ClusterNodes/NodeActions/validationRules.ts

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)