goobs-frontend 0.7.65 → 0.7.67
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/package.json +2 -2
- package/src/app/_app.tsx +8 -8
- package/src/components/Button/hook/useHelperFooter.tsx +65 -75
- package/src/components/Button/index.tsx +4 -9
- package/src/components/Form/Popup/index.tsx +108 -145
- package/src/components/StyledComponent/hooks/useDropdown.tsx +31 -27
- package/src/components/StyledComponent/hooks/useInputHelperFooter.tsx +133 -176
- package/src/components/StyledComponent/hooks/usePhoneNumber.tsx +9 -6
- package/src/components/StyledComponent/hooks/useRequiredFieldsValidator.tsx +130 -77
- package/src/components/StyledComponent/hooks/useSearchbar.tsx +2 -0
- package/src/components/StyledComponent/hooks/useSplitButton.tsx +15 -11
- package/src/components/StyledComponent/index.tsx +212 -165
- package/src/components/StyledComponent/{useEffects → useCallbacks}/index.tsx +17 -20
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState, useCallback } from 'react'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Formats a string of digits into a US phone number format.
|
|
@@ -37,9 +39,10 @@ export const formatPhoneNumber = (value: string): string => {
|
|
|
37
39
|
* @property {string} phoneNumber - The current formatted phone number.
|
|
38
40
|
* @property {function} handlePhoneNumberChange - A function to handle changes to the phone number input.
|
|
39
41
|
* @property {function} updatePhoneNumber - A function to directly update the phone number.
|
|
42
|
+
* @property {function} checkAndUpdatePhoneNumber - A function to check and update the phone number based on component variant and initial value.
|
|
40
43
|
*
|
|
41
44
|
* @example
|
|
42
|
-
* const { phoneNumber, handlePhoneNumberChange, updatePhoneNumber } = usePhoneNumber();
|
|
45
|
+
* const { phoneNumber, handlePhoneNumberChange, updatePhoneNumber, checkAndUpdatePhoneNumber } = usePhoneNumber();
|
|
43
46
|
*/
|
|
44
47
|
export const usePhoneNumber = (
|
|
45
48
|
initialValue: string = '',
|
|
@@ -58,10 +61,8 @@ export const usePhoneNumber = (
|
|
|
58
61
|
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
59
62
|
const input = e.target.value
|
|
60
63
|
let strippedInput = input.replace(/^\+1\s?/, '').replace(/\D/g, '')
|
|
61
|
-
|
|
62
64
|
// Ensure we don't exceed 10 digits
|
|
63
65
|
strippedInput = strippedInput.slice(0, 10)
|
|
64
|
-
|
|
65
66
|
// Only format if there's actual input beyond "+1 "
|
|
66
67
|
const formattedValue =
|
|
67
68
|
strippedInput.length > 0 ? formatPhoneNumber(strippedInput) : '+1 '
|
|
@@ -80,9 +81,10 @@ export const usePhoneNumber = (
|
|
|
80
81
|
}, [])
|
|
81
82
|
|
|
82
83
|
/**
|
|
83
|
-
*
|
|
84
|
+
* Checks and updates the phone number based on component variant and initial value.
|
|
85
|
+
* This function replaces the useEffect from the original implementation.
|
|
84
86
|
*/
|
|
85
|
-
|
|
87
|
+
const checkAndUpdatePhoneNumber = useCallback(() => {
|
|
86
88
|
if (componentvariant === 'phonenumber' && initialValue) {
|
|
87
89
|
updatePhoneNumber(initialValue)
|
|
88
90
|
}
|
|
@@ -92,5 +94,6 @@ export const usePhoneNumber = (
|
|
|
92
94
|
phoneNumber,
|
|
93
95
|
handlePhoneNumberChange,
|
|
94
96
|
updatePhoneNumber,
|
|
97
|
+
checkAndUpdatePhoneNumber,
|
|
95
98
|
}
|
|
96
99
|
}
|
|
@@ -1,82 +1,107 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, MutableRefObject, useRef } from 'react'
|
|
2
4
|
import { session } from 'goobs-cache'
|
|
3
5
|
import { StyledComponentProps } from '../index'
|
|
4
6
|
import { HelperFooterMessage } from './useInputHelperFooter'
|
|
7
|
+
import { ClientLogger } from 'goobs-testing'
|
|
5
8
|
|
|
6
9
|
type HelperFooterState = Record<string, HelperFooterMessage>
|
|
7
10
|
|
|
8
11
|
export const useRequiredFieldsValidator = (
|
|
9
12
|
formname: string,
|
|
10
13
|
components: StyledComponentProps[],
|
|
11
|
-
hasInputRef: MutableRefObject<boolean
|
|
14
|
+
hasInputRef: MutableRefObject<boolean>,
|
|
15
|
+
helperFooterAtom: ReturnType<typeof session.atom<HelperFooterState>>
|
|
12
16
|
) => {
|
|
13
|
-
|
|
17
|
+
ClientLogger.debug('useRequiredFieldsValidator called', {
|
|
14
18
|
formname,
|
|
15
|
-
components,
|
|
16
|
-
hasInputRef,
|
|
19
|
+
componentsCount: components.length,
|
|
20
|
+
hasInputRefCurrent: hasInputRef.current,
|
|
17
21
|
})
|
|
18
22
|
|
|
19
|
-
const
|
|
20
|
-
const
|
|
23
|
+
const [helperFooters, setHelperFooters] = session.useAtom(helperFooterAtom)
|
|
24
|
+
const failedAttempts = useRef<Record<string, number>>({})
|
|
21
25
|
|
|
22
26
|
const initializeComponentStatuses = useCallback(() => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
component.name &&
|
|
30
|
-
component.required &&
|
|
31
|
-
component.label &&
|
|
32
|
-
!initialHelperFooters[component.name]
|
|
33
|
-
) {
|
|
34
|
-
initialHelperFooters[component.name] = {
|
|
35
|
-
status: 'emptyAndRequired',
|
|
36
|
-
statusMessage: `${component.label} is required.`,
|
|
37
|
-
spreadMessage: `${component.label} is required.`,
|
|
38
|
-
spreadMessagePriority: component.spreadMessagePriority || 1,
|
|
39
|
-
required: true,
|
|
40
|
-
hasInput: false,
|
|
41
|
-
}
|
|
42
|
-
console.log(
|
|
43
|
-
`Created new helper footer for ${component.name}:`,
|
|
44
|
-
initialHelperFooters[component.name]
|
|
45
|
-
)
|
|
27
|
+
ClientLogger.debug('initializeComponentStatuses called')
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const initialHelperFooters: HelperFooterState = {
|
|
31
|
+
...(helperFooters || {}),
|
|
46
32
|
}
|
|
47
|
-
})
|
|
48
33
|
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
components.forEach(component => {
|
|
35
|
+
if (
|
|
36
|
+
component.name &&
|
|
37
|
+
component.required &&
|
|
38
|
+
component.label &&
|
|
39
|
+
!initialHelperFooters[component.name]
|
|
40
|
+
) {
|
|
41
|
+
initialHelperFooters[component.name] = {
|
|
42
|
+
status: 'emptyAndRequired',
|
|
43
|
+
statusMessage: `${component.label} is required.`,
|
|
44
|
+
spreadMessage: `${component.label} is required.`,
|
|
45
|
+
spreadMessagePriority: component.spreadMessagePriority || 1,
|
|
46
|
+
required: true,
|
|
47
|
+
hasInput: false,
|
|
48
|
+
}
|
|
49
|
+
ClientLogger.debug(`Created new helper footer`, {
|
|
50
|
+
componentName: component.name,
|
|
51
|
+
helperFooter: initialHelperFooters[component.name],
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
ClientLogger.debug('Setting helper footers in session atom')
|
|
57
|
+
setHelperFooters(initialHelperFooters)
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
ClientLogger.debug(`Initialized helper footers`, { initialHelperFooters })
|
|
60
|
+
} catch (error) {
|
|
61
|
+
ClientLogger.error('Error in initializeComponentStatuses', { error })
|
|
62
|
+
}
|
|
53
63
|
}, [components, helperFooters, setHelperFooters])
|
|
54
64
|
|
|
55
65
|
const checkFormStatus = useCallback(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
ClientLogger.debug('checkFormStatus called')
|
|
67
|
+
try {
|
|
68
|
+
const requiredComponents = components.filter(
|
|
69
|
+
component => component.required
|
|
70
|
+
)
|
|
71
|
+
ClientLogger.debug('Required components', {
|
|
72
|
+
count: requiredComponents.length,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const allRequiredHaveInput = requiredComponents.every(
|
|
76
|
+
component =>
|
|
77
|
+
helperFooters[component.name || '']?.hasInput || hasInputRef.current
|
|
78
|
+
)
|
|
79
|
+
ClientLogger.debug('All required fields have input', {
|
|
80
|
+
allRequiredHaveInput,
|
|
81
|
+
})
|
|
82
|
+
return allRequiredHaveInput
|
|
83
|
+
} catch (error) {
|
|
84
|
+
ClientLogger.error('Error in checkFormStatus', { error })
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
68
87
|
}, [components, hasInputRef, helperFooters])
|
|
69
88
|
|
|
70
89
|
const getEmptyRequiredFields = useCallback(() => {
|
|
71
|
-
|
|
90
|
+
ClientLogger.debug('getEmptyRequiredFields called')
|
|
72
91
|
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
try {
|
|
93
|
+
const emptyFields = Object.entries(helperFooters)
|
|
94
|
+
.filter(
|
|
95
|
+
([, value]) =>
|
|
96
|
+
value.required && !value.hasInput && !hasInputRef.current
|
|
97
|
+
)
|
|
98
|
+
.map(([key]) => key)
|
|
99
|
+
ClientLogger.debug('Empty required fields', { emptyFields })
|
|
100
|
+
return emptyFields
|
|
101
|
+
} catch (error) {
|
|
102
|
+
ClientLogger.error('Error in getEmptyRequiredFields', { error })
|
|
103
|
+
return []
|
|
104
|
+
}
|
|
80
105
|
}, [hasInputRef, helperFooters])
|
|
81
106
|
|
|
82
107
|
const validateRequiredField = useCallback(
|
|
@@ -86,30 +111,58 @@ export const useRequiredFieldsValidator = (
|
|
|
86
111
|
required: boolean,
|
|
87
112
|
spreadMessagePriority: number
|
|
88
113
|
) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
114
|
+
ClientLogger.debug('validateRequiredField called', {
|
|
115
|
+
name,
|
|
116
|
+
label,
|
|
117
|
+
required,
|
|
118
|
+
spreadMessagePriority,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
if (failedAttempts.current[name] && failedAttempts.current[name] >= 3) {
|
|
123
|
+
ClientLogger.warn('Validation attempts exceeded for field', {
|
|
124
|
+
name,
|
|
125
|
+
attempts: failedAttempts.current[name],
|
|
126
|
+
})
|
|
127
|
+
return
|
|
100
128
|
}
|
|
101
129
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
[name]
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
130
|
+
if (
|
|
131
|
+
required &&
|
|
132
|
+
!helperFooters[name]?.hasInput &&
|
|
133
|
+
!hasInputRef.current
|
|
134
|
+
) {
|
|
135
|
+
ClientLogger.debug(
|
|
136
|
+
`${name} is required and empty, updating helper footer`
|
|
137
|
+
)
|
|
138
|
+
const newHelperFooter: HelperFooterMessage = {
|
|
139
|
+
status: 'emptyAndRequired',
|
|
140
|
+
statusMessage: `${label} is required.`,
|
|
141
|
+
spreadMessage: `${label} is required.`,
|
|
142
|
+
spreadMessagePriority,
|
|
143
|
+
required: true,
|
|
144
|
+
hasInput: false,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
setHelperFooters((prev: HelperFooterState) => ({
|
|
148
|
+
...prev,
|
|
149
|
+
[name]: newHelperFooter,
|
|
150
|
+
}))
|
|
151
|
+
|
|
152
|
+
ClientLogger.debug('Updated helperFooters', {
|
|
153
|
+
name,
|
|
154
|
+
newHelperFooter,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
failedAttempts.current[name] = (failedAttempts.current[name] || 0) + 1
|
|
158
|
+
} else {
|
|
159
|
+
ClientLogger.debug(
|
|
160
|
+
`${name} validation not needed or already has input`
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
ClientLogger.error('Error in validateRequiredField', { error, name })
|
|
165
|
+
failedAttempts.current[name] = (failedAttempts.current[name] || 0) + 1
|
|
113
166
|
}
|
|
114
167
|
},
|
|
115
168
|
[hasInputRef, helperFooters, setHelperFooters]
|
|
@@ -130,7 +183,7 @@ export const useRequiredFieldsValidator = (
|
|
|
130
183
|
]
|
|
131
184
|
)
|
|
132
185
|
|
|
133
|
-
|
|
186
|
+
ClientLogger.debug('useRequiredFieldsValidator returning', { result })
|
|
134
187
|
return result
|
|
135
188
|
}
|
|
136
189
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useCallback
|
|
3
|
+
import { useState, useCallback } from 'react'
|
|
4
4
|
import { StyledComponentProps } from '../index'
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -9,16 +9,9 @@ import { StyledComponentProps } from '../index'
|
|
|
9
9
|
* @returns An object containing the value and handlers for the split button.
|
|
10
10
|
*/
|
|
11
11
|
export const useSplitButton = (props: StyledComponentProps) => {
|
|
12
|
-
const [value, setValue] = useState(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* Update the value state when the value prop changes.
|
|
16
|
-
*/
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (props.value !== undefined) {
|
|
19
|
-
setValue(props.value)
|
|
20
|
-
}
|
|
21
|
-
}, [props.value])
|
|
12
|
+
const [value, setValue] = useState(() => {
|
|
13
|
+
return props.value !== undefined ? props.value : '0'
|
|
14
|
+
})
|
|
22
15
|
|
|
23
16
|
/**
|
|
24
17
|
* Increment the value by 1.
|
|
@@ -57,10 +50,21 @@ export const useSplitButton = (props: StyledComponentProps) => {
|
|
|
57
50
|
setValue(numValue === '' ? '0' : numValue)
|
|
58
51
|
}, [])
|
|
59
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Update the value state when the value prop changes.
|
|
55
|
+
* This function replaces the useEffect from the original implementation.
|
|
56
|
+
*/
|
|
57
|
+
const updateValueFromProps = useCallback(() => {
|
|
58
|
+
if (props.value !== undefined) {
|
|
59
|
+
setValue(props.value)
|
|
60
|
+
}
|
|
61
|
+
}, [props.value])
|
|
62
|
+
|
|
60
63
|
return {
|
|
61
64
|
value,
|
|
62
65
|
handleIncrement,
|
|
63
66
|
handleDecrement,
|
|
64
67
|
handleChange,
|
|
68
|
+
updateValueFromProps,
|
|
65
69
|
}
|
|
66
70
|
}
|