goobs-frontend 0.7.67 → 0.7.69

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.
Files changed (42) hide show
  1. package/package.json +14 -16
  2. package/src/app/_app.tsx +8 -8
  3. package/src/components/Button/index.tsx +95 -203
  4. package/src/components/ConfirmationCodeInput/index.tsx +4 -6
  5. package/src/components/Content/Structure/button/useButton.tsx +1 -35
  6. package/src/components/Content/Structure/datefield/useDateField.tsx +55 -0
  7. package/src/components/Content/Structure/dropdown/useDropdown.tsx +55 -0
  8. package/src/components/Content/Structure/incremementNumberField/useIncremementNumberField.tsx +65 -0
  9. package/src/components/Content/Structure/numberField/useNumberField.tsx +55 -0
  10. package/src/components/Content/Structure/passwordField/usePasswordField.tsx +57 -0
  11. package/src/components/Content/Structure/phoneNumber/usePhoneNumber.tsx +0 -0
  12. package/src/components/Content/Structure/qrcode/useQRCode.tsx +69 -0
  13. package/src/components/Content/Structure/searchbar/useSearchbar.tsx +55 -0
  14. package/src/components/Content/Structure/textfield/useTextField.tsx +84 -0
  15. package/src/components/Content/index.tsx +44 -7
  16. package/src/components/DateField/index.tsx +112 -0
  17. package/src/components/Dropdown/index.tsx +91 -0
  18. package/src/components/Form/Popup/index.tsx +1 -1
  19. package/src/components/IncrementNumberField/index.tsx +123 -0
  20. package/src/components/Nav/HorizontalVariant/index.tsx +18 -12
  21. package/src/components/Nav/VerticalVariant/index.tsx +14 -10
  22. package/src/components/NumberField/index.tsx +95 -0
  23. package/src/components/PasswordField/index.tsx +105 -0
  24. package/src/components/PhoneNumberField/index.tsx +102 -0
  25. package/src/components/PricingTable/index.tsx +10 -8
  26. package/src/components/QRCode/index.tsx +105 -0
  27. package/src/components/Searchbar/index.tsx +77 -0
  28. package/src/components/TextField/index.tsx +130 -0
  29. package/src/components/Toolbar/index.tsx +11 -10
  30. package/src/index.ts +54 -9
  31. package/src/components/Button/hook/useHelperFooter.tsx +0 -214
  32. package/src/components/Content/Structure/styledcomponent/useStyledComponent.tsx +0 -104
  33. package/src/components/StyledComponent/adornment/index.tsx +0 -125
  34. package/src/components/StyledComponent/hooks/useDropdown.tsx +0 -150
  35. package/src/components/StyledComponent/hooks/useInputHelperFooter.tsx +0 -524
  36. package/src/components/StyledComponent/hooks/usePhoneNumber.tsx +0 -99
  37. package/src/components/StyledComponent/hooks/useRequiredFieldsValidator.tsx +0 -190
  38. package/src/components/StyledComponent/hooks/useSearchbar.tsx +0 -46
  39. package/src/components/StyledComponent/hooks/useSplitButton.tsx +0 -70
  40. package/src/components/StyledComponent/index.tsx +0 -473
  41. package/src/components/StyledComponent/useCallbacks/index.tsx +0 -46
  42. package/src/styles/StyledComponent/Label/index.ts +0 -76
@@ -1,524 +0,0 @@
1
- 'use client'
2
-
3
- import { useCallback, useMemo } from 'react'
4
- import { debounce } from 'lodash'
5
- import { session } from 'goobs-cache'
6
- import { ClientLogger } from 'goobs-testing'
7
-
8
- const isValidEmailFormat = (email: string): boolean => {
9
- ClientLogger.debug('Validating email format:', { email })
10
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
11
- const isValid = emailRegex.test(email)
12
- ClientLogger.debug('Email format is valid:', { isValid })
13
- return isValid
14
- }
15
-
16
- export interface HelperFooterMessage {
17
- status: 'error' | 'success' | 'emptyAndRequired'
18
- statusMessage: string
19
- spreadMessage: string
20
- spreadMessagePriority: number
21
- required: boolean
22
- hasInput?: boolean
23
- }
24
-
25
- export const useInputHelperFooter = (
26
- helperFooterAtom: ReturnType<
27
- typeof session.atom<Record<string, HelperFooterMessage>>
28
- >
29
- ) => {
30
- ClientLogger.debug('useInputHelperFooter hook called')
31
-
32
- const validateAtom = useMemo(() => session.atom(''), [])
33
-
34
- const [helperFooterValue, setHelperFooters] =
35
- session.useAtom(helperFooterAtom)
36
- const [validateValue, setValidateValue] = session.useAtom(validateAtom)
37
-
38
- const handleGenericErrorCreation = useCallback(
39
- async (
40
- formData: FormData,
41
- name: string,
42
- label: string,
43
- formname: string,
44
- priority?: number
45
- ): Promise<HelperFooterMessage | undefined> => {
46
- ClientLogger.debug('handleGenericErrorCreation called:', {
47
- name,
48
- label,
49
- formname,
50
- priority,
51
- })
52
- const value = formData.get(name) as string
53
- ClientLogger.debug('Form data value:', { value })
54
- if (!value || !value.trim()) {
55
- ClientLogger.debug('Value is empty or whitespace')
56
- const message: HelperFooterMessage = {
57
- status: 'error',
58
- statusMessage: `${label} is required. Please enter a ${label.toLowerCase()}.`,
59
- spreadMessage: `${label} is required.`,
60
- spreadMessagePriority: priority ?? 1,
61
- required: true,
62
- }
63
- ClientLogger.debug('Created error message:', { message })
64
- setHelperFooters(prev => ({ ...prev, [name]: message }))
65
- return message
66
- } else {
67
- ClientLogger.debug(
68
- 'Value is not empty, removing helper footer from cache'
69
- )
70
- setHelperFooters(prev => {
71
- const newState = { ...prev }
72
- delete newState[name]
73
- return newState
74
- })
75
- }
76
- ClientLogger.debug('Returning undefined (no error)')
77
- return undefined
78
- },
79
- [setHelperFooters]
80
- )
81
-
82
- const handleEmailErrorCreation = useCallback(
83
- async (
84
- formData: FormData,
85
- formname: string
86
- ): Promise<HelperFooterMessage | undefined> => {
87
- ClientLogger.debug('handleEmailErrorCreation called:', { formname })
88
- const email = formData.get('email') as string
89
- ClientLogger.debug('Email value:', { email })
90
- let message: HelperFooterMessage | undefined
91
-
92
- if (!email || !email.trim()) {
93
- ClientLogger.debug('Email is empty or whitespace')
94
- message = {
95
- status: 'error',
96
- statusMessage: 'Please enter an email address.',
97
- spreadMessage: 'Email is required.',
98
- spreadMessagePriority: 1,
99
- required: true,
100
- }
101
- } else if (!isValidEmailFormat(email)) {
102
- ClientLogger.debug('Email format is invalid')
103
- message = {
104
- status: 'error',
105
- statusMessage: 'Please enter a valid email address.',
106
- spreadMessage: 'Invalid email format.',
107
- spreadMessagePriority: 1,
108
- required: true,
109
- }
110
- } else {
111
- ClientLogger.debug('Email is valid')
112
- message = {
113
- status: 'success',
114
- statusMessage: 'Email is valid.',
115
- spreadMessage: 'Email is valid.',
116
- spreadMessagePriority: 1,
117
- required: true,
118
- }
119
- }
120
-
121
- if (message) {
122
- ClientLogger.debug('Message created:', { message })
123
- if (message.status === 'success') {
124
- ClientLogger.debug('Removing helper footer from cache')
125
- setHelperFooters(prev => {
126
- const newState = { ...prev }
127
- delete newState['email']
128
- return newState
129
- })
130
- } else {
131
- setHelperFooters(prev => ({ ...prev, email: message }))
132
- }
133
- }
134
-
135
- ClientLogger.debug('Returning message:', { message })
136
- return message
137
- },
138
- [setHelperFooters]
139
- )
140
-
141
- const handlePasswordErrorCreation = useCallback(
142
- async (
143
- formData: FormData,
144
- formname: string
145
- ): Promise<HelperFooterMessage | undefined> => {
146
- ClientLogger.debug('handlePasswordErrorCreation called:', { formname })
147
- const password = formData.get('verifyPassword') as string
148
- ClientLogger.debug('Password value:', { passwordProvided: !!password })
149
-
150
- const debouncedPasswordStorage = debounce(() => {
151
- if (password) {
152
- ClientLogger.debug('Storing password in cache (debounced)')
153
- setValidateValue(password)
154
- }
155
- }, 2000)
156
-
157
- debouncedPasswordStorage()
158
-
159
- let message: HelperFooterMessage | undefined
160
-
161
- if (!password || !password.trim()) {
162
- ClientLogger.debug('Password is empty or whitespace')
163
- message = {
164
- status: 'error',
165
- statusMessage: 'Password is required.',
166
- spreadMessage: 'Password is required.',
167
- spreadMessagePriority: 2,
168
- required: true,
169
- }
170
- } else {
171
- const passwordRegex =
172
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
173
- const passwordComplexityStatus: 'error' | 'success' =
174
- passwordRegex.test(password) ? 'success' : 'error'
175
- ClientLogger.debug('Password complexity status:', {
176
- passwordComplexityStatus,
177
- })
178
-
179
- if (passwordComplexityStatus === 'error') {
180
- message = {
181
- status: 'error',
182
- statusMessage:
183
- 'Password must include at least 8 characters, one uppercase letter, one lowercase letter, one number, and one special character.',
184
- spreadMessage: 'Invalid password.',
185
- spreadMessagePriority: 1,
186
- required: true,
187
- }
188
- } else {
189
- message = {
190
- status: 'success',
191
- statusMessage: 'Password meets all requirements.',
192
- spreadMessage: 'Password is valid.',
193
- spreadMessagePriority: 1,
194
- required: true,
195
- }
196
- }
197
- }
198
-
199
- if (message) {
200
- ClientLogger.debug('Message created:', { message })
201
- if (message.status === 'success') {
202
- ClientLogger.debug('Removing helper footer from cache')
203
- setHelperFooters(prev => {
204
- const newState = { ...prev }
205
- delete newState['verifyPassword']
206
- return newState
207
- })
208
- } else {
209
- setHelperFooters(prev => ({ ...prev, verifyPassword: message }))
210
- }
211
- }
212
-
213
- ClientLogger.debug('Returning message:', { message })
214
- return message
215
- },
216
- [setHelperFooters, setValidateValue]
217
- )
218
-
219
- const handleConfirmPasswordErrorCreation = useCallback(
220
- async (
221
- formData: FormData,
222
- formname: string
223
- ): Promise<HelperFooterMessage | undefined> => {
224
- ClientLogger.debug('handleConfirmPasswordErrorCreation called:', {
225
- formname,
226
- })
227
- const confirmPassword = formData.get('confirmPassword') as string
228
- ClientLogger.debug('Confirm password value:', {
229
- passwordProvided: !!confirmPassword,
230
- })
231
-
232
- let message: HelperFooterMessage | undefined
233
-
234
- if (!confirmPassword || !confirmPassword.trim()) {
235
- ClientLogger.debug('Confirm password is empty or whitespace')
236
- message = {
237
- status: 'error',
238
- statusMessage: 'Please confirm your password.',
239
- spreadMessage: 'Password confirmation is required.',
240
- spreadMessagePriority: 3,
241
- required: true,
242
- }
243
- } else {
244
- ClientLogger.debug('Fetched verify password from session atom')
245
-
246
- if (!validateValue) {
247
- ClientLogger.debug('Verify password not found')
248
- message = {
249
- status: 'error',
250
- statusMessage: 'Please enter your password first.',
251
- spreadMessage: 'Password not set.',
252
- spreadMessagePriority: 3,
253
- required: true,
254
- }
255
- } else if (confirmPassword !== validateValue) {
256
- ClientLogger.debug('Passwords do not match')
257
- message = {
258
- status: 'error',
259
- statusMessage: 'Passwords do not match.',
260
- spreadMessage: 'Passwords do not match.',
261
- spreadMessagePriority: 3,
262
- required: true,
263
- }
264
- } else {
265
- ClientLogger.debug('Passwords match')
266
- message = {
267
- status: 'success',
268
- statusMessage: 'Passwords match.',
269
- spreadMessage: 'Passwords match.',
270
- spreadMessagePriority: 3,
271
- required: true,
272
- }
273
- }
274
- }
275
-
276
- if (message) {
277
- ClientLogger.debug('Message created:', { message })
278
- if (message.status === 'success') {
279
- ClientLogger.debug('Removing helper footer from cache')
280
- setHelperFooters(prev => {
281
- const newState = { ...prev }
282
- delete newState['confirmPassword']
283
- return newState
284
- })
285
- } else {
286
- setHelperFooters(prev => ({ ...prev, confirmPassword: message }))
287
- }
288
- }
289
-
290
- ClientLogger.debug('Returning message:', { message })
291
- return message
292
- },
293
- [setHelperFooters, validateValue]
294
- )
295
-
296
- const handlePhoneNumberErrorCreation = useCallback(
297
- async (
298
- formData: FormData,
299
- formname: string
300
- ): Promise<HelperFooterMessage | undefined> => {
301
- ClientLogger.debug('handlePhoneNumberErrorCreation called:', { formname })
302
- const phoneNumber = formData.get('phoneNumber') as string
303
- ClientLogger.debug('Phone number value:', { phoneNumber })
304
- let message: HelperFooterMessage | undefined
305
-
306
- if (!phoneNumber || !phoneNumber.trim()) {
307
- ClientLogger.debug('Phone number is empty or whitespace')
308
- message = {
309
- status: 'error',
310
- statusMessage:
311
- 'Phone number is required. Please enter a phone number.',
312
- spreadMessage: 'Phone number is required.',
313
- spreadMessagePriority: 3,
314
- required: true,
315
- }
316
- } else {
317
- const digitsOnly = phoneNumber.replace(/[^\d]/g, '')
318
- const length = digitsOnly.length
319
- ClientLogger.debug('Phone number length (digits only):', { length })
320
- if (
321
- (length === 10 && !digitsOnly.startsWith('1')) ||
322
- (length === 11 && digitsOnly.startsWith('1'))
323
- ) {
324
- ClientLogger.debug('Phone number is valid')
325
- message = {
326
- status: 'success',
327
- statusMessage: 'Phone number is valid.',
328
- spreadMessage: 'Phone number is valid.',
329
- spreadMessagePriority: 1,
330
- required: true,
331
- }
332
- } else {
333
- ClientLogger.debug('Phone number is invalid')
334
- message = {
335
- status: 'error',
336
- statusMessage:
337
- 'Please enter a valid 10-digit phone number or a 10-digit number starting with 1.',
338
- spreadMessage: 'Invalid phone number format.',
339
- spreadMessagePriority: 1,
340
- required: true,
341
- }
342
- }
343
- }
344
-
345
- if (message) {
346
- ClientLogger.debug('Message created:', { message })
347
- if (message.status === 'success') {
348
- ClientLogger.debug('Removing helper footer from cache')
349
- setHelperFooters(prev => {
350
- const newState = { ...prev }
351
- delete newState['phoneNumber']
352
- return newState
353
- })
354
- } else {
355
- setHelperFooters(prev => ({ ...prev, phoneNumber: message }))
356
- }
357
- }
358
-
359
- ClientLogger.debug('Returning message:', { message })
360
- return message
361
- },
362
- [setHelperFooters]
363
- )
364
-
365
- const updateHelperFooter = useCallback(
366
- (name: string, validationResult: HelperFooterMessage | undefined): void => {
367
- ClientLogger.debug('updateHelperFooter called:', {
368
- name,
369
- validationResult,
370
- })
371
- if (validationResult) {
372
- ClientLogger.debug('Updating helper footer with new validation result')
373
- setHelperFooters(prev => ({ ...prev, [name]: validationResult }))
374
- } else {
375
- ClientLogger.debug('Removing field from helper footer')
376
- setHelperFooters(prev => {
377
- const newState = { ...prev }
378
- delete newState[name]
379
- return newState
380
- })
381
- }
382
-
383
- ClientLogger.debug('Helper footer updated')
384
- },
385
- [setHelperFooters]
386
- )
387
-
388
- const validateField = useCallback(
389
- async (
390
- name: string,
391
- formData: FormData,
392
- label: string,
393
- formname: string,
394
- priority?: number
395
- ) => {
396
- ClientLogger.debug('validateField called:', {
397
- name,
398
- label,
399
- formname,
400
- priority,
401
- })
402
- let validationResult: HelperFooterMessage | undefined
403
-
404
- switch (name) {
405
- case 'email':
406
- ClientLogger.debug('Validating email field')
407
- validationResult = await handleEmailErrorCreation(formData, formname)
408
- break
409
- case 'verifyPassword':
410
- ClientLogger.debug('Validating password field')
411
- validationResult = await handlePasswordErrorCreation(
412
- formData,
413
- formname
414
- )
415
- break
416
- case 'confirmPassword':
417
- ClientLogger.debug('Validating confirm password field')
418
- validationResult = await handleConfirmPasswordErrorCreation(
419
- formData,
420
- formname
421
- )
422
- break
423
- case 'phoneNumber':
424
- ClientLogger.debug('Validating phone number field')
425
- validationResult = await handlePhoneNumberErrorCreation(
426
- formData,
427
- formname
428
- )
429
- break
430
- default:
431
- ClientLogger.debug('Validating generic field')
432
- validationResult = await handleGenericErrorCreation(
433
- formData,
434
- name,
435
- label,
436
- formname,
437
- priority
438
- )
439
- }
440
-
441
- ClientLogger.debug('Validation result:', { validationResult })
442
- updateHelperFooter(name, validationResult)
443
- },
444
- [
445
- handleEmailErrorCreation,
446
- handlePasswordErrorCreation,
447
- handleConfirmPasswordErrorCreation,
448
- handlePhoneNumberErrorCreation,
449
- handleGenericErrorCreation,
450
- updateHelperFooter,
451
- ]
452
- )
453
-
454
- const useShowErrorEffect = useCallback(
455
- (
456
- formSubmitted: boolean,
457
- hasInput: boolean,
458
- isFocused: boolean
459
- ): boolean => {
460
- ClientLogger.debug('useShowErrorEffect called:', {
461
- formSubmitted,
462
- hasInput,
463
- isFocused,
464
- })
465
-
466
- const shouldShowError = formSubmitted || (hasInput && !isFocused)
467
- ClientLogger.debug('Calculating shouldShowError:', {
468
- shouldShowError,
469
- formSubmitted,
470
- hasInput,
471
- isFocused,
472
- })
473
-
474
- ClientLogger.debug('Returning showError:', { shouldShowError })
475
- return shouldShowError
476
- },
477
- []
478
- )
479
-
480
- const fetchHelperFooters = useCallback(
481
- async (formname: string): Promise<HelperFooterMessage[]> => {
482
- ClientLogger.debug('fetchHelperFooters called:', { formname })
483
- ClientLogger.debug('Fetched helper footers from session atom:', {
484
- helperFooterValue,
485
- })
486
-
487
- const filteredHelperFooters = Object.values(helperFooterValue).filter(
488
- (item): item is HelperFooterMessage => {
489
- const isValidHelperFooter =
490
- typeof item === 'object' &&
491
- item !== null &&
492
- 'status' in item &&
493
- 'statusMessage' in item &&
494
- 'spreadMessage' in item &&
495
- 'spreadMessagePriority' in item &&
496
- 'required' in item
497
- ClientLogger.debug('Checking if item is valid HelperFooterMessage:', {
498
- item,
499
- isValid: isValidHelperFooter,
500
- })
501
- return isValidHelperFooter
502
- }
503
- )
504
- ClientLogger.debug('Filtered helper footers:', { filteredHelperFooters })
505
- return filteredHelperFooters
506
- },
507
- [helperFooterValue]
508
- )
509
-
510
- const result = useMemo(
511
- () => ({
512
- validateField,
513
- useShowErrorEffect,
514
- fetchHelperFooters,
515
- helperFooterValue,
516
- }),
517
- [validateField, useShowErrorEffect, fetchHelperFooters, helperFooterValue]
518
- )
519
-
520
- ClientLogger.debug('useInputHelperFooter returning result:', { result })
521
- return result
522
- }
523
-
524
- export default useInputHelperFooter
@@ -1,99 +0,0 @@
1
- 'use client'
2
-
3
- import React, { useState, useCallback } from 'react'
4
-
5
- /**
6
- * Formats a string of digits into a US phone number format.
7
- * The "+1" country code is always added at the beginning.
8
- *
9
- * @param {string} value - The input string to be formatted.
10
- * @returns {string} A formatted phone number string.
11
- *
12
- * @example
13
- * formatPhoneNumber("1234567890") // returns "+1 123-456-7890"
14
- * formatPhoneNumber("12345") // returns "+1 123-45"
15
- */
16
- export const formatPhoneNumber = (value: string): string => {
17
- const digits = value.replace(/\D/g, '')
18
- const limitedDigits = digits.slice(0, 10)
19
- let formattedNumber = '+1 '
20
-
21
- if (limitedDigits.length > 0) {
22
- formattedNumber += limitedDigits.slice(0, 3)
23
- if (limitedDigits.length > 3) {
24
- formattedNumber += '-' + limitedDigits.slice(3, 6)
25
- if (limitedDigits.length > 6) {
26
- formattedNumber += '-' + limitedDigits.slice(6)
27
- }
28
- }
29
- }
30
- return formattedNumber.trim()
31
- }
32
-
33
- /**
34
- * A custom React hook for managing and formatting a phone number input.
35
- *
36
- * @param {string} [initialValue=''] - The initial value of the phone number.
37
- * @param {string} [componentvariant=''] - The variant of the component.
38
- * @returns {Object} An object containing the current phone number state and functions to update it.
39
- * @property {string} phoneNumber - The current formatted phone number.
40
- * @property {function} handlePhoneNumberChange - A function to handle changes to the phone number input.
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.
43
- *
44
- * @example
45
- * const { phoneNumber, handlePhoneNumberChange, updatePhoneNumber, checkAndUpdatePhoneNumber } = usePhoneNumber();
46
- */
47
- export const usePhoneNumber = (
48
- initialValue: string = '',
49
- componentvariant: string = ''
50
- ) => {
51
- const [phoneNumber, setPhoneNumber] = useState(
52
- formatPhoneNumber(initialValue)
53
- )
54
-
55
- /**
56
- * Handles changes to the phone number input.
57
- *
58
- * @param {React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>} e - The change event.
59
- */
60
- const handlePhoneNumberChange = useCallback(
61
- (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
62
- const input = e.target.value
63
- let strippedInput = input.replace(/^\+1\s?/, '').replace(/\D/g, '')
64
- // Ensure we don't exceed 10 digits
65
- strippedInput = strippedInput.slice(0, 10)
66
- // Only format if there's actual input beyond "+1 "
67
- const formattedValue =
68
- strippedInput.length > 0 ? formatPhoneNumber(strippedInput) : '+1 '
69
- setPhoneNumber(formattedValue)
70
- },
71
- []
72
- )
73
-
74
- /**
75
- * Updates the phone number state with a new value.
76
- *
77
- * @param {string} newValue - The new phone number value to set.
78
- */
79
- const updatePhoneNumber = useCallback((newValue: string) => {
80
- setPhoneNumber(formatPhoneNumber(newValue))
81
- }, [])
82
-
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.
86
- */
87
- const checkAndUpdatePhoneNumber = useCallback(() => {
88
- if (componentvariant === 'phonenumber' && initialValue) {
89
- updatePhoneNumber(initialValue)
90
- }
91
- }, [componentvariant, initialValue, updatePhoneNumber])
92
-
93
- return {
94
- phoneNumber,
95
- handlePhoneNumberChange,
96
- updatePhoneNumber,
97
- checkAndUpdatePhoneNumber,
98
- }
99
- }