goobs-frontend 0.7.63 → 0.7.65

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.
@@ -0,0 +1,137 @@
1
+ import { useCallback, useMemo, MutableRefObject } from 'react'
2
+ import { session } from 'goobs-cache'
3
+ import { StyledComponentProps } from '../index'
4
+ import { HelperFooterMessage } from './useInputHelperFooter'
5
+
6
+ type HelperFooterState = Record<string, HelperFooterMessage>
7
+
8
+ export const useRequiredFieldsValidator = (
9
+ formname: string,
10
+ components: StyledComponentProps[],
11
+ hasInputRef: MutableRefObject<boolean>
12
+ ) => {
13
+ console.log('useRequiredFieldsValidator called with:', {
14
+ formname,
15
+ components,
16
+ hasInputRef,
17
+ })
18
+
19
+ const helperFootersAtom = session.atom<HelperFooterState>({})
20
+ const [helperFooters, setHelperFooters] = session.useAtom(helperFootersAtom)
21
+
22
+ const initializeComponentStatuses = useCallback(() => {
23
+ console.log('initializeComponentStatuses called')
24
+
25
+ const initialHelperFooters: HelperFooterState = { ...helperFooters }
26
+
27
+ components.forEach(component => {
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
+ )
46
+ }
47
+ })
48
+
49
+ console.log('Setting helper footers in session atom')
50
+ setHelperFooters(initialHelperFooters)
51
+
52
+ console.log(`Initialized helper footers:`, initialHelperFooters)
53
+ }, [components, helperFooters, setHelperFooters])
54
+
55
+ const checkFormStatus = useCallback(() => {
56
+ console.log('checkFormStatus called')
57
+ const requiredComponents = components.filter(
58
+ component => component.required
59
+ )
60
+ console.log('Required components:', requiredComponents)
61
+
62
+ const allRequiredHaveInput = requiredComponents.every(
63
+ component =>
64
+ helperFooters[component.name || '']?.hasInput || hasInputRef.current
65
+ )
66
+ console.log('All required fields have input:', allRequiredHaveInput)
67
+ return allRequiredHaveInput
68
+ }, [components, hasInputRef, helperFooters])
69
+
70
+ const getEmptyRequiredFields = useCallback(() => {
71
+ console.log('getEmptyRequiredFields called')
72
+
73
+ const emptyFields = Object.entries(helperFooters)
74
+ .filter(
75
+ ([, value]) => value.required && !value.hasInput && !hasInputRef.current
76
+ )
77
+ .map(([key]) => key)
78
+ console.log('Empty required fields:', emptyFields)
79
+ return emptyFields
80
+ }, [hasInputRef, helperFooters])
81
+
82
+ const validateRequiredField = useCallback(
83
+ (
84
+ name: string,
85
+ label: string,
86
+ required: boolean,
87
+ spreadMessagePriority: number
88
+ ) => {
89
+ console.log(`validateRequiredField called for ${name}`)
90
+
91
+ if (required && !helperFooters[name]?.hasInput && !hasInputRef.current) {
92
+ console.log(`${name} is required and empty, updating helper footer`)
93
+ const newHelperFooter: HelperFooterMessage = {
94
+ status: 'emptyAndRequired',
95
+ statusMessage: `${label} is required.`,
96
+ spreadMessage: `${label} is required.`,
97
+ spreadMessagePriority,
98
+ required: true,
99
+ hasInput: false,
100
+ }
101
+
102
+ setHelperFooters(prev => ({
103
+ ...prev,
104
+ [name]: newHelperFooter,
105
+ }))
106
+
107
+ console.log('Updated helperFooters:', {
108
+ ...helperFooters,
109
+ [name]: newHelperFooter,
110
+ })
111
+ } else {
112
+ console.log(`${name} validation not needed or already has input`)
113
+ }
114
+ },
115
+ [hasInputRef, helperFooters, setHelperFooters]
116
+ )
117
+
118
+ const result = useMemo(
119
+ () => ({
120
+ checkFormStatus,
121
+ getEmptyRequiredFields,
122
+ validateRequiredField,
123
+ initializeComponentStatuses,
124
+ }),
125
+ [
126
+ checkFormStatus,
127
+ getEmptyRequiredFields,
128
+ validateRequiredField,
129
+ initializeComponentStatuses,
130
+ ]
131
+ )
132
+
133
+ console.log('useRequiredFieldsValidator returning:', result)
134
+ return result
135
+ }
136
+
137
+ export default useRequiredFieldsValidator
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { useState, useRef, useEffect } from 'react'
4
4
  import { Box, InputLabel, OutlinedInput, styled } from '@mui/material'
5
+ import { session } from 'goobs-cache'
5
6
  import { useDropdown } from './hooks/useDropdown'
6
7
  import { usePhoneNumber } from './hooks/usePhoneNumber'
7
8
  import { useSplitButton } from './hooks/useSplitButton'
@@ -9,9 +10,10 @@ import { Typography } from './../Typography'
9
10
  import { red, green } from '../../styles/palette'
10
11
  import { StartAdornment, EndAdornment } from './adornment'
11
12
  import {
12
- useHelperFooter,
13
+ useInputHelperFooter,
13
14
  HelperFooterMessage,
14
- } from './helperfooter/useHelperFooter'
15
+ } from './hooks/useInputHelperFooter'
16
+ import { useRequiredFieldsValidator } from './hooks/useRequiredFieldsValidator'
15
17
  import labelStyles from '../../styles/StyledComponent/Label'
16
18
  import { useHasInputEffect, usePreventAutocompleteEffect } from './useEffects'
17
19
 
@@ -94,6 +96,8 @@ export interface StyledComponentProps {
94
96
  onOptionSelect?: (option: string) => void
95
97
  /** Callback function when the input value changes */
96
98
  onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
99
+ /** Priority of the spread message */
100
+ spreadMessagePriority?: number
97
101
  }
98
102
 
99
103
  /**
@@ -135,7 +139,6 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
135
139
  value,
136
140
  valuestatus,
137
141
  placeholder,
138
- required = false,
139
142
  formname,
140
143
  formSubmitted = false,
141
144
  'aria-label': ariaLabel,
@@ -144,17 +147,22 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
144
147
  'aria-describedby': ariaDescribedBy,
145
148
  onOptionSelect,
146
149
  onChange,
150
+ spreadMessagePriority,
147
151
  } = props
148
152
 
149
- const {
150
- validateField,
151
- validateRequiredField,
152
- helperFooterValue,
153
- initializeRequiredFields,
154
- } = useHelperFooter()
153
+ const { validateField } = useInputHelperFooter()
154
+ const helperFooterAtom = session.atom<Record<string, HelperFooterMessage>>({})
155
+ const [helperFooterValue] = session.useAtom(helperFooterAtom)
156
+
157
+ const showErrorAtom = session.atom<boolean>(false)
158
+ const [showError, setShowError] = session.useAtom(showErrorAtom)
159
+
160
+ const hasInputRef = useRef(false)
161
+
162
+ useRequiredFieldsValidator(formname || '', [props], hasInputRef)
163
+
155
164
  const [isFocused, setIsFocused] = useState(false)
156
165
  const [hasInput, setHasInput] = useState(false)
157
- const [showError, setShowError] = useState(false)
158
166
  const [passwordVisible, setPasswordVisible] = useState(false)
159
167
  const inputRefInternal = useRef<HTMLInputElement>(null)
160
168
  const inputBoxRef = useRef<HTMLDivElement>(null)
@@ -164,8 +172,10 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
164
172
  inputBoxRef,
165
173
  onOptionSelect
166
174
  )
167
- const { phoneNumber, handlePhoneNumberChange, updatePhoneNumber } =
168
- usePhoneNumber(value || '')
175
+ const { phoneNumber, handlePhoneNumberChange } = usePhoneNumber(
176
+ value || '',
177
+ componentvariant
178
+ )
169
179
  const {
170
180
  value: splitButtonValue,
171
181
  handleIncrement,
@@ -175,24 +185,6 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
175
185
  useHasInputEffect(value, valuestatus, setHasInput)
176
186
  usePreventAutocompleteEffect(inputRefInternal)
177
187
 
178
- /**
179
- * Initialize required fields when the form name changes
180
- */
181
- useEffect(() => {
182
- if (formname) {
183
- initializeRequiredFields(formname)
184
- }
185
- }, [formname, initializeRequiredFields])
186
-
187
- /**
188
- * Validate required field when relevant props change
189
- */
190
- useEffect(() => {
191
- if (required && formname && name && label) {
192
- validateRequiredField(required, formname, name, label)
193
- }
194
- }, [required, formname, name, label, validateRequiredField])
195
-
196
188
  /**
197
189
  * Show error after a delay when form is submitted or input has value
198
190
  */
@@ -202,16 +194,11 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
202
194
  }, 1000)
203
195
 
204
196
  return () => clearTimeout(timer)
205
- }, [formSubmitted, hasInput])
197
+ }, [formSubmitted, hasInput, setShowError])
206
198
 
207
- /**
208
- * Update phone number when componentvariant is 'phonenumber' and value changes
209
- */
210
199
  useEffect(() => {
211
- if (componentvariant === 'phonenumber' && value) {
212
- updatePhoneNumber(value)
213
- }
214
- }, [componentvariant, value, updatePhoneNumber])
200
+ hasInputRef.current = !!value
201
+ }, [value])
215
202
 
216
203
  const currentHelperFooter = name ? helperFooterValue[name] : undefined
217
204
 
@@ -238,11 +225,12 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
238
225
  }
239
226
 
240
227
  setHasInput(!!e.target.value)
228
+ hasInputRef.current = !!e.target.value
241
229
 
242
230
  const formData = new FormData()
243
231
  formData.append(e.target.name, e.target.value)
244
232
  if (name && label && formname) {
245
- validateField(name, formData, label, required, formname)
233
+ validateField(name, formData, label, formname, spreadMessagePriority)
246
234
  }
247
235
  }
248
236
 
@@ -261,7 +249,7 @@ const StyledComponent: React.FC<StyledComponentProps> = props => {
261
249
  if (name && label && !hasInput && formname) {
262
250
  const formData = new FormData()
263
251
  formData.append(name, '')
264
- validateField(name, formData, label, required, formname)
252
+ validateField(name, formData, label, formname, spreadMessagePriority)
265
253
  }
266
254
  }
267
255
 
@@ -80,7 +80,9 @@ const TransferList: React.FC<TransferListProps> = ({
80
80
  const newRight = right.concat(left)
81
81
  setRight(newRight)
82
82
  setLeft([])
83
- onChange && onChange(newRight)
83
+ if (onChange) {
84
+ onChange(newRight)
85
+ }
84
86
  }
85
87
 
86
88
  /**
@@ -92,7 +94,9 @@ const TransferList: React.FC<TransferListProps> = ({
92
94
  setRight(newRight)
93
95
  setLeft(newLeft)
94
96
  setChecked(not(checked, leftChecked))
95
- onChange && onChange(newRight)
97
+ if (onChange) {
98
+ onChange(newRight)
99
+ }
96
100
  }
97
101
 
98
102
  /**
@@ -104,7 +108,9 @@ const TransferList: React.FC<TransferListProps> = ({
104
108
  setLeft(newLeft)
105
109
  setRight(newRight)
106
110
  setChecked(not(checked, rightChecked))
107
- onChange && onChange(newRight)
111
+ if (onChange) {
112
+ onChange(newRight)
113
+ }
108
114
  }
109
115
 
110
116
  /**
@@ -114,7 +120,9 @@ const TransferList: React.FC<TransferListProps> = ({
114
120
  const newLeft = left.concat(right)
115
121
  setLeft(newLeft)
116
122
  setRight([])
117
- onChange && onChange([])
123
+ if (onChange) {
124
+ onChange([])
125
+ }
118
126
  }
119
127
 
120
128
  /**