goobs-frontend 0.7.64 → 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.
@@ -1,638 +0,0 @@
1
- 'use client'
2
-
3
- import { useCallback, useState, useMemo, useRef, useEffect } from 'react'
4
- import { debounce } from 'lodash'
5
- import { get, set, remove, StringValue, JSONValue } from 'goobs-cache'
6
-
7
- /**
8
- * Validates if the given email string is in a valid email format.
9
- * @param {string} email - The email string to validate.
10
- * @returns {boolean} True if the email is valid, false otherwise.
11
- */
12
- const isValidEmailFormat = (email: string): boolean => {
13
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
14
- return emailRegex.test(email)
15
- }
16
-
17
- /**
18
- * Represents the structure of a helper footer message.
19
- */
20
- export interface HelperFooterMessage {
21
- /** The status of the message: 'error', 'success', or 'emptyAndRequired'. */
22
- status: 'error' | 'success' | 'emptyAndRequired'
23
- /** A detailed message describing the status. */
24
- statusMessage: string
25
- /** A concise message for display purposes. */
26
- spreadMessage: string
27
- /** A number indicating the priority of the message. Lower numbers indicate higher priority. */
28
- spreadMessagePriority: number
29
- /** Indicates whether this field is required. */
30
- required: boolean
31
- }
32
-
33
- /**
34
- * A custom hook for managing helper footer messages and form validation.
35
- * @returns {Object} An object containing functions and state for managing helper footers.
36
- */
37
- export const useHelperFooter = () => {
38
- const [helperFooterValue, setHelperFooterValue] = useState<
39
- Record<string, HelperFooterMessage>
40
- >({})
41
- const helperFooterRef = useRef<Record<string, HelperFooterMessage>>({})
42
-
43
- /**
44
- * Handles the creation of error messages for generic fields.
45
- * @param {FormData} formData - The form data containing the field value.
46
- * @param {string} name - The name of the field.
47
- * @param {string} label - The label of the field.
48
- * @param {boolean} required - Whether the field is required.
49
- * @param {string} formname - The name of the form.
50
- * @returns {Promise<HelperFooterMessage | undefined>} A promise that resolves to a HelperFooterMessage or undefined.
51
- */
52
- const handleGenericErrorCreation = useCallback(
53
- async (
54
- formData: FormData,
55
- name: string,
56
- label: string,
57
- required: boolean,
58
- formname: string
59
- ): Promise<HelperFooterMessage | undefined> => {
60
- const value = formData.get(name) as string
61
- if (required && (!value || !value.trim())) {
62
- const message: HelperFooterMessage = {
63
- status: 'error',
64
- statusMessage: `${label} is required. Please enter a ${label.toLowerCase()}.`,
65
- spreadMessage: `${label} is required.`,
66
- spreadMessagePriority: 1,
67
- required,
68
- }
69
- const jsonValue: JSONValue = {
70
- type: 'json',
71
- value: {
72
- [name]: {
73
- status: message.status,
74
- statusMessage: message.statusMessage,
75
- spreadMessage: message.spreadMessage,
76
- spreadMessagePriority: message.spreadMessagePriority,
77
- required: message.required,
78
- },
79
- },
80
- }
81
- await set('helperfooter', formname, jsonValue, 'client')
82
- return message
83
- } else {
84
- await remove('helperfooter', formname, 'client')
85
- }
86
- return undefined
87
- },
88
- []
89
- )
90
-
91
- /**
92
- * Handles the creation of error messages for email fields.
93
- * @param {FormData} formData - The form data containing the email value.
94
- * @param {boolean} required - Whether the email field is required.
95
- * @param {string} formname - The name of the form.
96
- * @returns {Promise<HelperFooterMessage | undefined>} A promise that resolves to a HelperFooterMessage or undefined.
97
- */
98
- const handleEmailErrorCreation = useCallback(
99
- async (
100
- formData: FormData,
101
- required: boolean,
102
- formname: string
103
- ): Promise<HelperFooterMessage | undefined> => {
104
- const email = formData.get('email') as string
105
- let message: HelperFooterMessage | undefined
106
-
107
- if (required && (!email || !email.trim())) {
108
- message = {
109
- status: 'error',
110
- statusMessage: 'Please enter an email address.',
111
- spreadMessage: 'Email is required.',
112
- spreadMessagePriority: 1,
113
- required,
114
- }
115
- } else if (email && !isValidEmailFormat(email)) {
116
- message = {
117
- status: 'error',
118
- statusMessage: 'Please enter a valid email address.',
119
- spreadMessage: 'Invalid email format.',
120
- spreadMessagePriority: 1,
121
- required,
122
- }
123
- } else if (email) {
124
- message = {
125
- status: 'success',
126
- statusMessage: 'Email is valid.',
127
- spreadMessage: 'Email is valid.',
128
- spreadMessagePriority: 1,
129
- required,
130
- }
131
- }
132
-
133
- if (message) {
134
- if (message.status === 'success') {
135
- await remove('helperfooter', formname, 'client')
136
- } else {
137
- const jsonValue: JSONValue = {
138
- type: 'json',
139
- value: {
140
- email: {
141
- status: message.status,
142
- statusMessage: message.statusMessage,
143
- spreadMessage: message.spreadMessage,
144
- spreadMessagePriority: message.spreadMessagePriority,
145
- required: message.required,
146
- },
147
- },
148
- }
149
- await set('helperfooter', formname, jsonValue, 'client')
150
- }
151
- }
152
-
153
- return message
154
- },
155
- []
156
- )
157
-
158
- /**
159
- * Handles the creation of error messages for password fields.
160
- * @param {FormData} formData - The form data containing the password value.
161
- * @param {boolean} required - Whether the password field is required.
162
- * @param {string} formname - The name of the form.
163
- * @returns {Promise<HelperFooterMessage | undefined>} A promise that resolves to a HelperFooterMessage or undefined.
164
- */
165
- const handlePasswordErrorCreation = useCallback(
166
- async (
167
- formData: FormData,
168
- required: boolean,
169
- formname: string
170
- ): Promise<HelperFooterMessage | undefined> => {
171
- const password = formData.get('verifyPassword') as string
172
-
173
- const debouncedPasswordStorage = debounce(async () => {
174
- if (password) {
175
- await set(
176
- 'validate',
177
- formname,
178
- { type: 'string', value: password } as StringValue,
179
- 'client'
180
- )
181
- }
182
- }, 2000)
183
-
184
- debouncedPasswordStorage()
185
-
186
- let message: HelperFooterMessage | undefined
187
-
188
- if (required && (!password || !password.trim())) {
189
- message = {
190
- status: 'error',
191
- statusMessage: 'Password is required.',
192
- spreadMessage: 'Password is required.',
193
- spreadMessagePriority: 4,
194
- required,
195
- }
196
- } else if (password) {
197
- const passwordRegex =
198
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
199
- const passwordComplexityStatus: 'error' | 'success' =
200
- passwordRegex.test(password) ? 'success' : 'error'
201
-
202
- if (passwordComplexityStatus === 'error') {
203
- message = {
204
- status: 'error',
205
- statusMessage:
206
- 'Password must include at least 8 characters, one uppercase letter, one lowercase letter, one number, and one special character.',
207
- spreadMessage: 'Invalid password.',
208
- spreadMessagePriority: 1,
209
- required,
210
- }
211
- } else {
212
- message = {
213
- status: 'success',
214
- statusMessage: 'Password meets all requirements.',
215
- spreadMessage: 'Password is valid.',
216
- spreadMessagePriority: 1,
217
- required,
218
- }
219
- }
220
- }
221
-
222
- if (message) {
223
- if (message.status === 'success') {
224
- await remove('helperfooter', formname, 'client')
225
- } else {
226
- const jsonValue: JSONValue = {
227
- type: 'json',
228
- value: {
229
- verifyPassword: {
230
- status: message.status,
231
- statusMessage: message.statusMessage,
232
- spreadMessage: message.spreadMessage,
233
- spreadMessagePriority: message.spreadMessagePriority,
234
- required: message.required,
235
- },
236
- },
237
- }
238
- await set('helperfooter', formname, jsonValue, 'client')
239
- }
240
- }
241
-
242
- return message
243
- },
244
- []
245
- )
246
-
247
- /**
248
- * Handles the creation of error messages for password confirmation fields.
249
- * @param {FormData} formData - The form data containing the confirmation password value.
250
- * @param {boolean} required - Whether the confirmation password field is required.
251
- * @param {string} formname - The name of the form.
252
- * @returns {Promise<HelperFooterMessage | undefined>} A promise that resolves to a HelperFooterMessage or undefined.
253
- */
254
- const handleConfirmPasswordErrorCreation = useCallback(
255
- async (
256
- formData: FormData,
257
- required: boolean,
258
- formname: string
259
- ): Promise<HelperFooterMessage | undefined> => {
260
- const confirmPassword = formData.get('confirmPassword') as string
261
-
262
- let message: HelperFooterMessage | undefined
263
-
264
- if (required && (!confirmPassword || !confirmPassword.trim())) {
265
- message = {
266
- status: 'error',
267
- statusMessage: 'Please confirm your password.',
268
- spreadMessage: 'Password confirmation is required.',
269
- spreadMessagePriority: 4,
270
- required,
271
- }
272
- } else if (confirmPassword) {
273
- let verifyPasswordResult
274
- try {
275
- verifyPasswordResult = await get('validate', formname, 'client')
276
- } catch (error) {
277
- // Error handling
278
- }
279
-
280
- let verifyPassword: string | undefined
281
- if (
282
- verifyPasswordResult &&
283
- typeof verifyPasswordResult === 'object' &&
284
- 'type' in verifyPasswordResult &&
285
- verifyPasswordResult.type === 'string' &&
286
- 'value' in verifyPasswordResult &&
287
- typeof verifyPasswordResult.value === 'string'
288
- ) {
289
- verifyPassword = verifyPasswordResult.value
290
- }
291
-
292
- if (!verifyPassword) {
293
- message = {
294
- status: 'error',
295
- statusMessage: 'Please enter your password first.',
296
- spreadMessage: 'Password not set.',
297
- spreadMessagePriority: 4,
298
- required,
299
- }
300
- } else if (confirmPassword !== verifyPassword) {
301
- message = {
302
- status: 'error',
303
- statusMessage: 'Passwords do not match.',
304
- spreadMessage: 'Passwords do not match.',
305
- spreadMessagePriority: 4,
306
- required,
307
- }
308
- } else {
309
- message = {
310
- status: 'success',
311
- statusMessage: 'Passwords match.',
312
- spreadMessage: 'Passwords match.',
313
- spreadMessagePriority: 4,
314
- required,
315
- }
316
- }
317
- }
318
-
319
- if (message) {
320
- if (message.status === 'success') {
321
- await remove('helperfooter', formname, 'client')
322
- } else {
323
- const jsonValue: JSONValue = {
324
- type: 'json',
325
- value: {
326
- confirmPassword: {
327
- status: message.status,
328
- statusMessage: message.statusMessage,
329
- spreadMessage: message.spreadMessage,
330
- spreadMessagePriority: message.spreadMessagePriority,
331
- required: message.required,
332
- },
333
- },
334
- }
335
- await set('helperfooter', formname, jsonValue, 'client')
336
- }
337
- }
338
-
339
- return message
340
- },
341
- []
342
- )
343
-
344
- /**
345
- * Handles the creation of error messages for phone number fields.
346
- * @param {FormData} formData - The form data containing the phone number value.
347
- * @param {boolean} required - Whether the phone number field is required.
348
- * @param {string} formname - The name of the form.
349
- * @returns {Promise<HelperFooterMessage | undefined>} A promise that resolves to a HelperFooterMessage or undefined.
350
- */
351
- const handlePhoneNumberErrorCreation = useCallback(
352
- async (
353
- formData: FormData,
354
- required: boolean,
355
- formname: string
356
- ): Promise<HelperFooterMessage | undefined> => {
357
- const phoneNumber = formData.get('phoneNumber') as string
358
- let message: HelperFooterMessage | undefined
359
-
360
- if (required && (!phoneNumber || !phoneNumber.trim())) {
361
- message = {
362
- status: 'error',
363
- statusMessage:
364
- 'Phone number is required. Please enter a phone number.',
365
- spreadMessage: 'Phone number is required.',
366
- spreadMessagePriority: 1,
367
- required,
368
- }
369
- } else if (phoneNumber) {
370
- const digitsOnly = phoneNumber.replace(/[^\d]/g, '')
371
- const length = digitsOnly.length
372
- if (
373
- (length === 10 && !digitsOnly.startsWith('1')) ||
374
- (length === 11 && digitsOnly.startsWith('1'))
375
- ) {
376
- message = {
377
- status: 'success',
378
- statusMessage: 'Phone number is valid.',
379
- spreadMessage: 'Phone number is valid.',
380
- spreadMessagePriority: 1,
381
- required,
382
- }
383
- } else {
384
- message = {
385
- status: 'error',
386
- statusMessage:
387
- 'Please enter a valid 10-digit phone number or a 10-digit number starting with 1.',
388
- spreadMessage: 'Invalid phone number format.',
389
- spreadMessagePriority: 1,
390
- required,
391
- }
392
- }
393
- }
394
-
395
- if (message) {
396
- if (message.status === 'success') {
397
- await remove('helperfooter', formname, 'client')
398
- } else {
399
- const jsonValue: JSONValue = {
400
- type: 'json',
401
- value: {
402
- phoneNumber: {
403
- status: message.status,
404
- statusMessage: message.statusMessage,
405
- spreadMessage: message.spreadMessage,
406
- spreadMessagePriority: message.spreadMessagePriority,
407
- required: message.required,
408
- },
409
- },
410
- }
411
- await set('helperfooter', formname, jsonValue, 'client')
412
- }
413
- }
414
-
415
- return message
416
- },
417
- []
418
- )
419
-
420
- /**
421
- * Updates the helper footer state with new validation results.
422
- * @param {string} name - The name of the field.
423
- * @param {HelperFooterMessage | undefined} validationResult - The validation result for the field.
424
- */
425
- const updateHelperFooter = useCallback(
426
- (name: string, validationResult: HelperFooterMessage | undefined): void => {
427
- if (validationResult) {
428
- helperFooterRef.current = {
429
- ...helperFooterRef.current,
430
- [name]: validationResult,
431
- }
432
- } else {
433
- const newHelperFooter: Record<string, HelperFooterMessage> = {}
434
- Object.keys(helperFooterRef.current).forEach(key => {
435
- if (key !== name) {
436
- newHelperFooter[key] = helperFooterRef.current[key]
437
- }
438
- })
439
- helperFooterRef.current = newHelperFooter
440
- }
441
-
442
- setHelperFooterValue({ ...helperFooterRef.current })
443
- },
444
- []
445
- )
446
-
447
- /**
448
- * Initializes required fields for a given form.
449
- * @param {string} formname - The name of the form.
450
- */
451
- const initializeRequiredFields = useCallback(
452
- async (formname: string) => {
453
- const fields = await get('helperfooter', formname, 'client')
454
- if (
455
- fields &&
456
- typeof fields === 'object' &&
457
- 'type' in fields &&
458
- fields.type === 'json' &&
459
- 'value' in fields
460
- ) {
461
- Object.entries(fields.value as Record<string, unknown>).forEach(
462
- ([field, message]) => {
463
- if (message && typeof message === 'object' && 'status' in message) {
464
- updateHelperFooter(field, message as HelperFooterMessage)
465
- }
466
- }
467
- )
468
- }
469
- },
470
- [updateHelperFooter]
471
- )
472
-
473
- /**
474
- * Validates a specific field in the form.
475
- * @param {string} name - The name of the field.
476
- * @param {FormData} formData - The form data containing the field value.
477
- * @param {string} label - The label of the field.
478
- * @param {boolean} required - Whether the field is required.
479
- * @param {string} formname - The name of the form.
480
- */
481
- const validateField = useCallback(
482
- async (
483
- name: string,
484
- formData: FormData,
485
- label: string,
486
- required: boolean,
487
- formname: string
488
- ) => {
489
- let validationResult: HelperFooterMessage | undefined
490
-
491
- switch (name) {
492
- case 'email':
493
- validationResult = await handleEmailErrorCreation(
494
- formData,
495
- required,
496
- formname
497
- )
498
- break
499
- case 'verifyPassword':
500
- validationResult = await handlePasswordErrorCreation(
501
- formData,
502
- required,
503
- formname
504
- )
505
- break
506
- case 'confirmPassword':
507
- validationResult = await handleConfirmPasswordErrorCreation(
508
- formData,
509
- required,
510
- formname
511
- )
512
- break
513
- case 'phoneNumber':
514
- validationResult = await handlePhoneNumberErrorCreation(
515
- formData,
516
- required,
517
- formname
518
- )
519
- break
520
- default:
521
- validationResult = await handleGenericErrorCreation(
522
- formData,
523
- name,
524
- label,
525
- required,
526
- formname
527
- )
528
- }
529
-
530
- updateHelperFooter(name, validationResult)
531
- },
532
- [
533
- handleEmailErrorCreation,
534
- handlePasswordErrorCreation,
535
- handleConfirmPasswordErrorCreation,
536
- handlePhoneNumberErrorCreation,
537
- handleGenericErrorCreation,
538
- updateHelperFooter,
539
- ]
540
- )
541
-
542
- /**
543
- * Validates a required field in the form.
544
- * @param {boolean} required - Whether the field is required.
545
- * @param {string} formname - The name of the form.
546
- * @param {string} name - The name of the field.
547
- * @param {string} label - The label of the field.
548
- */
549
- const validateRequiredField = useCallback(
550
- (required: boolean, formname: string, name: string, label: string) => {
551
- if (required && formname && name && label) {
552
- const emptyFormData = new FormData()
553
- emptyFormData.append(name, '')
554
- validateField(name, emptyFormData, label, required, formname)
555
- }
556
- },
557
- [validateField]
558
- )
559
-
560
- /**
561
- * A custom hook to determine whether to show an error message.
562
- * @param {boolean} formSubmitted - Whether the form has been submitted.
563
- * @param {boolean} hasInput - Whether the field has input.
564
- * @param {boolean} isFocused - Whether the field is currently focused.
565
- * @returns {boolean} Whether to show the error message.
566
- */
567
- const useShowErrorEffect = (
568
- formSubmitted: boolean,
569
- hasInput: boolean,
570
- isFocused: boolean
571
- ): boolean => {
572
- const [showError, setShowError] = useState<boolean>(false)
573
-
574
- useEffect(() => {
575
- const shouldShowError = formSubmitted || (hasInput && !isFocused)
576
- setShowError(shouldShowError)
577
- }, [formSubmitted, hasInput, isFocused])
578
-
579
- return showError
580
- }
581
-
582
- /**
583
- * Fetches helper footer messages for a given form.
584
- * @param {string} formname - The name of the form.
585
- * @returns {Promise<HelperFooterMessage[]>} A promise that resolves to an array of HelperFooterMessages.
586
- */
587
- const fetchHelperFooters = useCallback(
588
- async (formname: string): Promise<HelperFooterMessage[]> => {
589
- const helperFooters = await get('helperfooter', formname, 'client')
590
- if (
591
- helperFooters &&
592
- typeof helperFooters === 'object' &&
593
- 'type' in helperFooters &&
594
- helperFooters.type === 'json' &&
595
- 'value' in helperFooters &&
596
- typeof helperFooters.value === 'object' &&
597
- helperFooters.value !== null
598
- ) {
599
- const value = helperFooters.value as Record<string, unknown>
600
- return Object.values(value).filter(
601
- (item): item is HelperFooterMessage => {
602
- return (
603
- typeof item === 'object' &&
604
- item !== null &&
605
- 'status' in item &&
606
- 'statusMessage' in item &&
607
- 'spreadMessage' in item &&
608
- 'spreadMessagePriority' in item &&
609
- 'required' in item
610
- )
611
- }
612
- )
613
- }
614
- return []
615
- },
616
- []
617
- )
618
-
619
- return useMemo(
620
- () => ({
621
- validateField,
622
- validateRequiredField,
623
- helperFooterValue,
624
- useShowErrorEffect,
625
- initializeRequiredFields,
626
- fetchHelperFooters,
627
- }),
628
- [
629
- validateField,
630
- validateRequiredField,
631
- helperFooterValue,
632
- initializeRequiredFields,
633
- fetchHelperFooters,
634
- ]
635
- )
636
- }
637
-
638
- export default useHelperFooter