@devtron-labs/devtron-fe-common-lib 0.0.7-beta-49 → 0.0.7-beta-50
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/CIPipeline.Types.d.ts +263 -0
- package/dist/Common/Constants.d.ts +5 -0
- package/dist/Common/CustomTagSelector/ValidationRules.d.ts +1 -1
- package/dist/Common/Helper.d.ts +0 -22
- package/dist/Common/Types.d.ts +28 -2
- package/dist/Common/index.d.ts +1 -1
- package/dist/index.js +123 -242
- package/package.json +1 -1
- package/src/Assets/Icon/ic-help.svg +3 -0
- package/src/Common/CIPipeline.Types.ts +275 -0
- package/src/Common/Constants.ts +6 -0
- package/src/Common/CustomTagSelector/TagDetails.tsx +2 -2
- package/src/Common/CustomTagSelector/TagLabelSelect.tsx +1 -5
- package/src/Common/CustomTagSelector/TagLabelValueSelector.tsx +2 -2
- package/src/Common/CustomTagSelector/ValidationRules.ts +17 -21
- package/src/Common/Helper.tsx +36 -199
- package/src/Common/TippyCustomized.tsx +28 -2
- package/src/Common/Types.ts +30 -2
- package/src/Common/index.ts +1 -1
- package/dist/Common/DeleteComponentModal/DeleteComponent.d.ts +0 -4
- package/dist/Common/DeleteComponentModal/types.d.ts +0 -15
- package/src/Assets/Icon/ic-info-filled.svg +0 -7
- package/src/Assets/Icon/ic-warning.svg +0 -7
- package/src/Common/DeleteComponentModal/DeleteComponent.tsx +0 -95
- package/src/Common/DeleteComponentModal/types.ts +0 -15
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
export interface MaterialType {
|
|
2
|
+
name: string
|
|
3
|
+
type: string
|
|
4
|
+
value: string
|
|
5
|
+
gitMaterialId: number
|
|
6
|
+
id: number
|
|
7
|
+
isSelected: boolean
|
|
8
|
+
gitHostId: number
|
|
9
|
+
gitProviderId: number
|
|
10
|
+
regex?: string
|
|
11
|
+
isRegex: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Githost {
|
|
15
|
+
id: number
|
|
16
|
+
name: string
|
|
17
|
+
active: boolean
|
|
18
|
+
webhookSecret: string
|
|
19
|
+
webhookUrl: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WebhookEvent {
|
|
23
|
+
id: number
|
|
24
|
+
gitHostId: number
|
|
25
|
+
name: string
|
|
26
|
+
isActive: boolean
|
|
27
|
+
selectors: WebhookEventSelectors[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface WebhookEventSelectors {
|
|
31
|
+
id: number
|
|
32
|
+
eventId: number
|
|
33
|
+
name: string
|
|
34
|
+
selector: string
|
|
35
|
+
toShowInCiFilter: boolean
|
|
36
|
+
fixValue: string
|
|
37
|
+
toShow: boolean
|
|
38
|
+
possibleValues: string
|
|
39
|
+
isActive: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface CiPipelineSourceTypeOption {
|
|
43
|
+
label: string
|
|
44
|
+
value: string
|
|
45
|
+
isDisabled: boolean
|
|
46
|
+
isSelected: boolean
|
|
47
|
+
isWebhook: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export enum RefVariableType {
|
|
51
|
+
GLOBAL = 'GLOBAL',
|
|
52
|
+
FROM_PREVIOUS_STEP = 'FROM_PREVIOUS_STEP',
|
|
53
|
+
NEW = 'NEW',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export enum PluginType {
|
|
57
|
+
INLINE = 'INLINE',
|
|
58
|
+
PLUGIN_REF = 'REF_PLUGIN',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export enum ScriptType {
|
|
62
|
+
SHELL = 'SHELL',
|
|
63
|
+
DOCKERFILE = 'DOCKERFILE',
|
|
64
|
+
CONTAINERIMAGE = 'CONTAINER_IMAGE',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export enum MountPath {
|
|
68
|
+
TRUE = 'Yes',
|
|
69
|
+
FALSE = 'No',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export enum ConditionType {
|
|
73
|
+
SKIP = 'SKIP',
|
|
74
|
+
TRIGGER = 'TRIGGER',
|
|
75
|
+
PASS = 'PASS',
|
|
76
|
+
FAIL = 'FAIL',
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export enum RefVariableStageType {
|
|
80
|
+
PRE_CI = 'PRE_CI',
|
|
81
|
+
POST_CI = 'POST_CI',
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface VariableType {
|
|
85
|
+
id: number
|
|
86
|
+
name: string
|
|
87
|
+
value: string
|
|
88
|
+
format: string
|
|
89
|
+
description: string
|
|
90
|
+
defaultValue: string
|
|
91
|
+
variableType: RefVariableType
|
|
92
|
+
refVariableStepIndex: number
|
|
93
|
+
refVariableName: string
|
|
94
|
+
refVariableStage?: RefVariableStageType
|
|
95
|
+
variableStepIndexInPlugin?: number
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface CommandArgsMap {
|
|
99
|
+
command: string
|
|
100
|
+
args: string[]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface PortMapType {
|
|
104
|
+
portOnLocal: number
|
|
105
|
+
portOnContainer: number
|
|
106
|
+
}
|
|
107
|
+
interface ConditionDetails {
|
|
108
|
+
id: number
|
|
109
|
+
conditionOnVariable: string
|
|
110
|
+
conditionOperator: string
|
|
111
|
+
conditionType: ConditionType
|
|
112
|
+
conditionalValue: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface InlineStepDetailType {
|
|
116
|
+
scriptType: ScriptType
|
|
117
|
+
isMountCustomScript?: boolean
|
|
118
|
+
script?: string
|
|
119
|
+
dockerFileExists?: boolean
|
|
120
|
+
mountPath?: string
|
|
121
|
+
mountCodeToContainer?: boolean
|
|
122
|
+
mountDirectoryFromHost?: boolean
|
|
123
|
+
containerImagePath?: string
|
|
124
|
+
imagePullSecret?: string
|
|
125
|
+
commandArgsMap?: CommandArgsMap[]
|
|
126
|
+
portMap?: PortMapType[]
|
|
127
|
+
mountPathMap?: {
|
|
128
|
+
filePathOnDisk: string
|
|
129
|
+
filePathOnContainer: string
|
|
130
|
+
}[]
|
|
131
|
+
inputVariables?: VariableType[]
|
|
132
|
+
outputVariables?: VariableType[]
|
|
133
|
+
conditionDetails: ConditionDetails[]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface PluginRefStepDetailType {
|
|
137
|
+
id: number
|
|
138
|
+
pluginId: number
|
|
139
|
+
inputVariables?: VariableType[]
|
|
140
|
+
outputVariables?: VariableType[]
|
|
141
|
+
conditionDetails?: ConditionDetails[]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface StepType {
|
|
145
|
+
id: number
|
|
146
|
+
index: number
|
|
147
|
+
name: string
|
|
148
|
+
description: string
|
|
149
|
+
stepType: PluginType
|
|
150
|
+
outputDirectoryPath: string[]
|
|
151
|
+
inlineStepDetail?: InlineStepDetailType
|
|
152
|
+
pluginRefStepDetail?: PluginRefStepDetailType
|
|
153
|
+
triggerIfParentStageFail: boolean
|
|
154
|
+
isMandatory?: boolean
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface BuildStageType {
|
|
158
|
+
id: number
|
|
159
|
+
steps: StepType[]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export enum CIBuildType {
|
|
163
|
+
SELF_DOCKERFILE_BUILD_TYPE = 'self-dockerfile-build',
|
|
164
|
+
MANAGED_DOCKERFILE_BUILD_TYPE = 'managed-dockerfile-build',
|
|
165
|
+
BUILDPACK_BUILD_TYPE = 'buildpack-build',
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface BuildPackConfigType {
|
|
169
|
+
builderId: string
|
|
170
|
+
language: string
|
|
171
|
+
languageVersion: string
|
|
172
|
+
projectPath: string
|
|
173
|
+
builderLangEnvParam?: string
|
|
174
|
+
currentBuilderLangEnvParam?: string
|
|
175
|
+
buildPacks?: any
|
|
176
|
+
args?: Record<string, string>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface DockerBuildConfigType {
|
|
180
|
+
dockerfileContent: string
|
|
181
|
+
dockerfileRelativePath: string
|
|
182
|
+
buildContext: string
|
|
183
|
+
dockerfilePath?: string
|
|
184
|
+
dockerfileRepository?: string
|
|
185
|
+
args?: Record<string, string>
|
|
186
|
+
targetPlatform?: any
|
|
187
|
+
language?: string
|
|
188
|
+
languageFramework?: string
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface CIBuildConfigType {
|
|
192
|
+
buildPackConfig: BuildPackConfigType
|
|
193
|
+
ciBuildType: CIBuildType
|
|
194
|
+
dockerBuildConfig: DockerBuildConfigType
|
|
195
|
+
gitMaterialId: number
|
|
196
|
+
buildContextGitMaterialId: number
|
|
197
|
+
id?: number
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface DockerConfigOverrideType {
|
|
201
|
+
dockerRegistry: string
|
|
202
|
+
dockerRepository: string
|
|
203
|
+
ciBuildConfig: CIBuildConfigType
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface FormType {
|
|
207
|
+
name: string
|
|
208
|
+
args: { key: string; value: string }[]
|
|
209
|
+
materials: MaterialType[]
|
|
210
|
+
gitHost: Githost
|
|
211
|
+
webhookEvents: WebhookEvent[]
|
|
212
|
+
ciPipelineSourceTypeOptions: CiPipelineSourceTypeOption[]
|
|
213
|
+
webhookConditionList: { selectorId: number; value: string }[]
|
|
214
|
+
triggerType: string
|
|
215
|
+
scanEnabled?: boolean
|
|
216
|
+
beforeDockerBuildScripts?: {
|
|
217
|
+
id: number
|
|
218
|
+
name: string
|
|
219
|
+
outputLocation: string
|
|
220
|
+
script: string
|
|
221
|
+
isCollapsed: boolean
|
|
222
|
+
index: number
|
|
223
|
+
}[]
|
|
224
|
+
afterDockerBuildScripts?: {
|
|
225
|
+
id: number
|
|
226
|
+
name: string
|
|
227
|
+
outputLocation: string
|
|
228
|
+
script: string
|
|
229
|
+
isCollapsed: boolean
|
|
230
|
+
index: number
|
|
231
|
+
}[]
|
|
232
|
+
ciPipelineEditable: true
|
|
233
|
+
preBuildStage?: BuildStageType
|
|
234
|
+
postBuildStage?: BuildStageType
|
|
235
|
+
isDockerConfigOverridden?: boolean
|
|
236
|
+
dockerConfigOverride?: DockerConfigOverrideType
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface ErrorObj {
|
|
240
|
+
isValid: boolean
|
|
241
|
+
message: string | null
|
|
242
|
+
}
|
|
243
|
+
export interface TaskErrorObj {
|
|
244
|
+
isValid: boolean
|
|
245
|
+
name: ErrorObj
|
|
246
|
+
inlineStepDetail?: { inputVariables?: ErrorObj[]; outputVariables?: ErrorObj[] }
|
|
247
|
+
pluginRefStepDetail?: { inputVariables?: ErrorObj[]; outputVariables?: ErrorObj[] }
|
|
248
|
+
}
|
|
249
|
+
export interface FormErrorObjectType {
|
|
250
|
+
name: ErrorObj
|
|
251
|
+
materials?: MaterialType[]
|
|
252
|
+
preBuildStage?: {
|
|
253
|
+
isValid: boolean
|
|
254
|
+
steps: TaskErrorObj[]
|
|
255
|
+
}
|
|
256
|
+
buildStage?: {
|
|
257
|
+
isValid: boolean
|
|
258
|
+
name: ErrorObj
|
|
259
|
+
}
|
|
260
|
+
postBuildStage?: {
|
|
261
|
+
isValid: boolean
|
|
262
|
+
steps: TaskErrorObj[]
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface PluginDetailType {
|
|
267
|
+
id: number
|
|
268
|
+
name: string
|
|
269
|
+
type: string
|
|
270
|
+
description: string
|
|
271
|
+
icon: string
|
|
272
|
+
tags: string[]
|
|
273
|
+
inputVariables?: VariableType[]
|
|
274
|
+
outputVariables?: VariableType[]
|
|
275
|
+
}
|
package/src/Common/Constants.ts
CHANGED
|
@@ -57,4 +57,10 @@ export const TOKEN_COOKIE_NAME = 'argocd.token'
|
|
|
57
57
|
export const TriggerTypeMap = {
|
|
58
58
|
automatic: 'Auto',
|
|
59
59
|
manual: 'Manual',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const BuildStageVariable = {
|
|
63
|
+
PreBuild: 'preBuildStage',
|
|
64
|
+
Build: 'buildStage',
|
|
65
|
+
PostBuild: 'postBuildStage',
|
|
60
66
|
}
|
|
@@ -33,7 +33,7 @@ export const TagDetails = ({
|
|
|
33
33
|
_tagData.isInvalidKey =
|
|
34
34
|
_tagData.key || _tagData.value ? !validationRules.propagateTagKey(_tagData.key).isValid : false
|
|
35
35
|
_tagData.isInvalidValue = _tagData.value
|
|
36
|
-
? !validationRules.propagateTagValue(_tagData.value
|
|
36
|
+
? !validationRules.propagateTagValue(_tagData.value).isValid
|
|
37
37
|
: false
|
|
38
38
|
}
|
|
39
39
|
setTagData(index, _tagData)
|
|
@@ -41,7 +41,7 @@ export const TagDetails = ({
|
|
|
41
41
|
return (
|
|
42
42
|
<div className="flexbox mb-8">
|
|
43
43
|
<div
|
|
44
|
-
className={`dc__border h-30 pl-4 pr-4 br-4 mr-8 pointer ${tagData.propagate ? 'bcn-7' : ''}
|
|
44
|
+
className={`dc__border h-30 pl-4 pr-4 br-4 mr-8 pointer ${tagData.propagate ? 'bcn-7' : ''}`}
|
|
45
45
|
onClick={propagateTagToResource}
|
|
46
46
|
data-testid={`propagate-tag-${index}`}
|
|
47
47
|
>
|
|
@@ -13,11 +13,7 @@ export const TagLabelSelect = ({
|
|
|
13
13
|
}: TagLabelSelectType) => {
|
|
14
14
|
const setTagData = (index, tagValue): void => {
|
|
15
15
|
const _tags = [...labelTags]
|
|
16
|
-
|
|
17
|
-
if(_tagValue.key.startsWith("devtron.ai/")){
|
|
18
|
-
_tagValue.propagate = false
|
|
19
|
-
}
|
|
20
|
-
_tags[index] = _tagValue
|
|
16
|
+
_tags[index] = tagValue
|
|
21
17
|
setLabelTags(_tags)
|
|
22
18
|
}
|
|
23
19
|
|
|
@@ -50,7 +50,7 @@ export const TagLabelValueSelector = ({
|
|
|
50
50
|
? !validationRules.propagateTagKey(selectedValue).isValid
|
|
51
51
|
: _tagData.value !== ''
|
|
52
52
|
} else if (selectedValue || isRequired || _tagData.propagate) {
|
|
53
|
-
_tagData.isInvalidValue = !validationRules.propagateTagValue(selectedValue
|
|
53
|
+
_tagData.isInvalidValue = !validationRules.propagateTagValue(selectedValue).isValid
|
|
54
54
|
_tagData.isInvalidKey = !_tagData.key || _tagData.isInvalidKey
|
|
55
55
|
} else {
|
|
56
56
|
_tagData.isInvalidValue = false
|
|
@@ -80,7 +80,7 @@ export const TagLabelValueSelector = ({
|
|
|
80
80
|
field = validationRules.propagateTagKey(selectedValue)
|
|
81
81
|
}
|
|
82
82
|
} else if (isRequired || selectedValue || tagData.propagate) {
|
|
83
|
-
field = validationRules.propagateTagValue(selectedValue
|
|
83
|
+
field = validationRules.propagateTagValue(selectedValue)
|
|
84
84
|
}
|
|
85
85
|
if (!field.isValid) {
|
|
86
86
|
return (
|
|
@@ -21,37 +21,33 @@ export class ValidationRules {
|
|
|
21
21
|
if (!key) {
|
|
22
22
|
errorList.push('Key is required')
|
|
23
23
|
} else {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
errorList.push('Prefix: Can be max 253 characters')
|
|
34
|
-
}
|
|
35
|
-
const validPrefix = PATTERNS.KUBERNETES_KEY_PREFIX.test(prefix)
|
|
36
|
-
if (!validPrefix) {
|
|
37
|
-
errorList.push('Prefix: Must be a DNS subdomain (a series of DNS labels separated by dots (.)')
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
errorList.push(...validateTagValue(key).map((error) => `Name: ${error}`))
|
|
24
|
+
const re = new RegExp('/', 'g')
|
|
25
|
+
const noOfSlashInKey = key.match(re)?.length
|
|
26
|
+
if (noOfSlashInKey > 1) {
|
|
27
|
+
errorList.push('Key: Max 1 ( / ) allowed')
|
|
28
|
+
} else if (noOfSlashInKey === 1) {
|
|
29
|
+
const [prefix, name] = key.split('/')
|
|
30
|
+
errorList.push(...validateTagValue(name).map((error) => `Name: ${error}`))
|
|
31
|
+
if (prefix.length > 253) {
|
|
32
|
+
errorList.push('Prefix: Can be max 253 characters')
|
|
41
33
|
}
|
|
34
|
+
const validPrefix = PATTERNS.KUBERNETES_KEY_PREFIX.test(prefix)
|
|
35
|
+
if (!validPrefix) {
|
|
36
|
+
errorList.push('Prefix: Must be a DNS subdomain (a series of DNS labels separated by dots (.)')
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
errorList.push(...validateTagValue(key).map((error) => `Name: ${error}`))
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
return { isValid: errorList.length === 0, messages: errorList }
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
propagateTagValue = (value: string
|
|
45
|
+
propagateTagValue = (value: string): { isValid: boolean; messages: string[] } => {
|
|
48
46
|
const errorList = []
|
|
49
47
|
if (!value) {
|
|
50
48
|
errorList.push('Value is required')
|
|
51
49
|
} else {
|
|
52
|
-
|
|
53
|
-
errorList.push(...validateTagValue(value))
|
|
54
|
-
}
|
|
50
|
+
errorList.push(...validateTagValue(value))
|
|
55
51
|
}
|
|
56
52
|
return { isValid: errorList.length === 0, messages: errorList }
|
|
57
53
|
}
|
package/src/Common/Helper.tsx
CHANGED
|
@@ -1,49 +1,48 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useRef } from 'react'
|
|
2
2
|
import { ServerErrors } from './ServerError'
|
|
3
3
|
import { toast } from 'react-toastify'
|
|
4
4
|
import * as Sentry from '@sentry/browser'
|
|
5
5
|
import { toastAccessDenied } from './ToastBody'
|
|
6
6
|
import { ERROR_EMPTY_SCREEN, TOKEN_COOKIE_NAME } from './Constants'
|
|
7
|
-
import { ReactComponent as FormError } from '../Assets/Icon/ic-warning.svg'
|
|
8
7
|
|
|
9
8
|
toast.configure({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
autoClose: 3000,
|
|
10
|
+
hideProgressBar: true,
|
|
11
|
+
pauseOnHover: true,
|
|
12
|
+
pauseOnFocusLoss: true,
|
|
13
|
+
closeOnClick: false,
|
|
14
|
+
newestOnTop: true,
|
|
15
|
+
toastClassName: 'devtron-toast',
|
|
16
|
+
bodyClassName: 'devtron-toast__body',
|
|
18
17
|
})
|
|
19
18
|
|
|
20
19
|
export function showError(serverError, showToastOnUnknownError = true, hideAccessError = false) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
20
|
+
if (serverError instanceof ServerErrors && Array.isArray(serverError.errors)) {
|
|
21
|
+
serverError.errors.map(({ userMessage, internalMessage }) => {
|
|
22
|
+
if (
|
|
23
|
+
serverError.code === 403 &&
|
|
24
|
+
(userMessage === ERROR_EMPTY_SCREEN.UNAUTHORIZED || userMessage === ERROR_EMPTY_SCREEN.FORBIDDEN)
|
|
25
|
+
) {
|
|
26
|
+
if (!hideAccessError) {
|
|
27
|
+
toastAccessDenied()
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
toast.error(userMessage || internalMessage)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
} else {
|
|
34
|
+
if (serverError.code !== 403 && serverError.code !== 408) {
|
|
35
|
+
Sentry.captureException(serverError)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (showToastOnUnknownError) {
|
|
39
|
+
if (serverError.message) {
|
|
40
|
+
toast.error(serverError.message)
|
|
41
|
+
} else {
|
|
42
|
+
toast.error('Some Error Occurred')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
interface ConditionalWrapper<T> {
|
|
@@ -168,166 +167,4 @@ export function getLoginInfo() {
|
|
|
168
167
|
return null
|
|
169
168
|
}
|
|
170
169
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function useForm(stateSchema, validationSchema = {}, callback) {
|
|
174
|
-
const [state, setState] = useState(stateSchema)
|
|
175
|
-
const [disable, setDisable] = useState(true)
|
|
176
|
-
const [isDirty, setIsDirty] = useState(false)
|
|
177
|
-
|
|
178
|
-
// Disable button in initial render.
|
|
179
|
-
useEffect(() => {
|
|
180
|
-
setDisable(true)
|
|
181
|
-
}, [])
|
|
182
|
-
|
|
183
|
-
// For every changed in our state this will be fired
|
|
184
|
-
// To be able to disable the button
|
|
185
|
-
useEffect(() => {
|
|
186
|
-
if (isDirty) {
|
|
187
|
-
setDisable(validateState(state))
|
|
188
|
-
}
|
|
189
|
-
}, [state, isDirty])
|
|
190
|
-
|
|
191
|
-
// Used to disable submit button if there's an error in state
|
|
192
|
-
// or the required field in state has no value.
|
|
193
|
-
// Wrapped in useCallback to cached the function to avoid intensive memory leaked
|
|
194
|
-
// in every re-render in component
|
|
195
|
-
const validateState = useCallback(
|
|
196
|
-
(state) => {
|
|
197
|
-
//check errors in all fields
|
|
198
|
-
const hasErrorInState = Object.keys(validationSchema).some((key) => {
|
|
199
|
-
const isInputFieldRequired = validationSchema[key].required
|
|
200
|
-
const stateValue = state[key].value // state value
|
|
201
|
-
const stateError = state[key].error // state error
|
|
202
|
-
return (isInputFieldRequired && !stateValue) || stateError
|
|
203
|
-
})
|
|
204
|
-
return hasErrorInState
|
|
205
|
-
},
|
|
206
|
-
[state, validationSchema],
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
function validateField(name, value): string | string[] {
|
|
210
|
-
if (validationSchema[name].required) {
|
|
211
|
-
if (!value) {
|
|
212
|
-
return 'This is a required field.'
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function _validateSingleValidator(validator, value) {
|
|
217
|
-
if (value && !validator.regex.test(value)) {
|
|
218
|
-
return false
|
|
219
|
-
}
|
|
220
|
-
return true
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// single validator
|
|
224
|
-
let _validator = validationSchema[name].validator
|
|
225
|
-
if (_validator && typeof _validator === 'object') {
|
|
226
|
-
if (!_validateSingleValidator(_validator, value)) {
|
|
227
|
-
return _validator.error
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// multiple validators
|
|
232
|
-
let _validators = validationSchema[name].validators
|
|
233
|
-
if (_validators && typeof _validators === 'object' && Array.isArray(_validators)) {
|
|
234
|
-
let errors = []
|
|
235
|
-
_validators.forEach((_validator) => {
|
|
236
|
-
if (!_validateSingleValidator(_validator, value)) {
|
|
237
|
-
errors.push(_validator.error)
|
|
238
|
-
}
|
|
239
|
-
})
|
|
240
|
-
if (errors.length > 0) {
|
|
241
|
-
return errors
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return ''
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const handleOnChange = useCallback(
|
|
249
|
-
(event) => {
|
|
250
|
-
setIsDirty(true)
|
|
251
|
-
|
|
252
|
-
const { name, value } = event.target
|
|
253
|
-
let error = validateField(name, value)
|
|
254
|
-
setState((prevState) => ({
|
|
255
|
-
...prevState,
|
|
256
|
-
[name]: { value, error },
|
|
257
|
-
}))
|
|
258
|
-
},
|
|
259
|
-
[validationSchema],
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
const handleOnSubmit = (event) => {
|
|
263
|
-
event.preventDefault()
|
|
264
|
-
const newState = Object.keys(validationSchema).reduce((agg, curr) => {
|
|
265
|
-
agg[curr] = { ...state[curr], error: validateField(curr, state[curr].value) }
|
|
266
|
-
return agg
|
|
267
|
-
}, state)
|
|
268
|
-
if (!validateState(newState)) {
|
|
269
|
-
callback(state)
|
|
270
|
-
} else {
|
|
271
|
-
setState({ ...newState })
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return { state, disable, handleOnChange, handleOnSubmit }
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function handleError(error: any): any[] {
|
|
278
|
-
if (!error) {
|
|
279
|
-
return []
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (!Array.isArray(error)) {
|
|
283
|
-
return [error]
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return error
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function CustomInput({
|
|
290
|
-
name,
|
|
291
|
-
value,
|
|
292
|
-
error,
|
|
293
|
-
onChange,
|
|
294
|
-
onBlur = (e) => {},
|
|
295
|
-
onFocus = (e) => {},
|
|
296
|
-
label,
|
|
297
|
-
type = 'text',
|
|
298
|
-
disabled = false,
|
|
299
|
-
autoComplete = 'off',
|
|
300
|
-
labelClassName = '',
|
|
301
|
-
placeholder = '',
|
|
302
|
-
tabIndex = 1,
|
|
303
|
-
dataTestid = '',
|
|
304
|
-
}) {
|
|
305
|
-
return (
|
|
306
|
-
<div className="flex column left top">
|
|
307
|
-
<label className={`form__label ${labelClassName}`}>{label}</label>
|
|
308
|
-
<input
|
|
309
|
-
data-testid={dataTestid}
|
|
310
|
-
type={type}
|
|
311
|
-
name={name}
|
|
312
|
-
autoComplete="off"
|
|
313
|
-
className="form__input"
|
|
314
|
-
onChange={(e) => {
|
|
315
|
-
e.persist()
|
|
316
|
-
onChange(e)
|
|
317
|
-
}}
|
|
318
|
-
onBlur={onBlur}
|
|
319
|
-
onFocus={onFocus}
|
|
320
|
-
placeholder={placeholder}
|
|
321
|
-
value={value}
|
|
322
|
-
disabled={disabled}
|
|
323
|
-
tabIndex={tabIndex}
|
|
324
|
-
/>
|
|
325
|
-
{handleError(error).map((err) => (
|
|
326
|
-
<div className="form__error">
|
|
327
|
-
<FormError className="form__icon form__icon--error" />
|
|
328
|
-
{err}
|
|
329
|
-
</div>
|
|
330
|
-
))}
|
|
331
|
-
</div>
|
|
332
|
-
)
|
|
333
|
-
}
|
|
170
|
+
}
|