goobs-frontend 0.7.64 → 0.7.66

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goobs-frontend",
3
- "version": "0.7.64",
3
+ "version": "0.7.66",
4
4
  "description": "A comprehensive React-based UI library built on Material-UI, offering a wide range of customizable components including grids, typography, buttons, cards, forms, navigation, pricing tables, steppers, tooltips, accordions, and more. Designed for building responsive and consistent user interfaces with advanced features like form validation, theming, and code syntax highlighting.",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -22,32 +22,32 @@
22
22
  "react-dom": "^17.0.0 || ^18.0.0"
23
23
  },
24
24
  "dependencies": {
25
- "@emotion/cache": "^11.11.0",
26
- "@emotion/react": "^11.11.4",
27
- "@emotion/styled": "^11.11.5",
28
- "@mui/icons-material": "^5.16.0",
29
- "@mui/material": "^5.16.0",
30
- "@types/lodash": "^4.17.6",
31
- "goobs-cache": "^1.4.0",
25
+ "@emotion/cache": "^11.13.1",
26
+ "@emotion/react": "^11.13.0",
27
+ "@emotion/styled": "^11.13.0",
28
+ "@mui/icons-material": "^5.16.6",
29
+ "@mui/material": "^5.16.6",
30
+ "@types/lodash": "^4.17.7",
31
+ "goobs-cache": "^1.6.4",
32
32
  "highlight.js": "^11.10.0",
33
33
  "lodash": "^4.17.21",
34
34
  "next": "14.2.5"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@next/eslint-plugin-next": "^14.2.5",
38
- "@types/node": "^20.14.10",
38
+ "@types/node": "^22.1.0",
39
39
  "@types/react": "18.3.3",
40
40
  "@types/react-dom": "^18.3.0",
41
- "@typescript-eslint/eslint-plugin": "^7.16.0",
42
- "@typescript-eslint/parser": "^7.16.0",
41
+ "@typescript-eslint/eslint-plugin": "^8.0.1",
42
+ "@typescript-eslint/parser": "^8.0.1",
43
43
  "eslint": "^8.57.0",
44
44
  "eslint-config-next": "^14.2.5",
45
45
  "eslint-config-prettier": "^9.1.0",
46
- "eslint-plugin-prettier": "^5.1.3",
47
- "prettier": "^3.3.2",
46
+ "eslint-plugin-prettier": "^5.2.1",
47
+ "prettier": "^3.3.3",
48
48
  "react": "^18.3.1",
49
49
  "react-dom": "^18.3.1",
50
- "typescript": "^5.5.3"
50
+ "typescript": "^5.5.4"
51
51
  },
52
52
  "files": [
53
53
  "src"
@@ -1,251 +1,224 @@
1
- import { useState, useEffect, useCallback, useMemo, useRef } from 'react'
2
- import { get } from 'goobs-cache'
1
+ import { useCallback, useMemo } from 'react'
2
+ import { session } from 'goobs-cache'
3
3
 
4
- /**
5
- * Represents the structure of a helper footer message.
6
- * @interface HelperFooterMessage
7
- */
8
4
  export interface HelperFooterMessage {
9
- /** The status of the message, either 'error' or 'success'. */
10
- status: 'error' | 'success'
11
- /** A message describing the status. */
5
+ status: 'error' | 'success' | 'emptyAndRequired'
12
6
  statusMessage: string
13
- /** A message to be displayed to the user. */
14
7
  spreadMessage: string
15
- /** A number indicating the priority of the message. Lower numbers indicate higher priority. */
16
8
  spreadMessagePriority: number
17
- /** The name of the form associated with this message. */
18
- formname: string
19
- /** Indicates whether this message is required to be addressed. */
20
9
  required: boolean
10
+ hasInput?: boolean
21
11
  }
22
12
 
23
- /**
24
- * A type definition for the interval ID returned by setInterval.
25
- */
26
- type IntervalID = ReturnType<typeof setInterval>
27
-
28
- /**
29
- * A custom hook for managing helper footer messages and form validation.
30
- *
31
- * @param {string} [initialFormname] - The initial name of the form to fetch helper footers for.
32
- * @returns {Object} An object containing the current error message, form validity state, and functions to update and fetch form validation.
33
- */
34
13
  const useHelperFooter = (initialFormname?: string) => {
35
- /**
36
- * State for storing the current error message.
37
- */
38
- const [errorMessage, setErrorMessage] = useState<string | undefined>(
39
- undefined
14
+ console.log(
15
+ 'useHelperFooter: Hook called with initialFormname:',
16
+ initialFormname
40
17
  )
41
18
 
42
- /**
43
- * State for storing the current form validity.
44
- */
45
- const [isFormValid, setIsFormValid] = useState<boolean>(true)
46
-
47
- /**
48
- * State for storing the current helper footers.
49
- */
50
- const [helperFooters, setHelperFooters] = useState<HelperFooterMessage[]>([])
51
-
52
- /**
53
- * Ref for storing the previous helper footers to compare against.
54
- */
55
- const prevHelperFooters = useRef<HelperFooterMessage[]>([])
56
-
57
- /**
58
- * Ref for storing the previous error message to compare against.
59
- */
60
- const prevErrorMessage = useRef<string | undefined>(undefined)
61
-
62
- /**
63
- * Ref for storing the previous form validity to compare against.
64
- */
65
- const prevIsFormValid = useRef<boolean>(true)
66
-
67
- /**
68
- * Ref for storing the interval ID for periodic helper footer fetching.
69
- */
70
- const intervalIdRef = useRef<IntervalID | null>(null)
71
-
72
- /**
73
- * Fetches helper footer messages from the cache.
74
- *
75
- * @param {string} [formname] - The name of the form to fetch helper footers for.
76
- * @returns {Promise<HelperFooterMessage[]>} A promise that resolves to an array of HelperFooterMessage objects.
77
- */
78
- const fetchHelperFooters = useCallback(
79
- async (formname?: string): Promise<HelperFooterMessage[]> => {
80
- const currentFormname = formname || initialFormname
81
- if (!currentFormname) {
82
- return []
83
- }
19
+ const helperFooterAtom = session.atom<Record<
20
+ string,
21
+ HelperFooterMessage
22
+ > | null>(null)
23
+ const currentErrorIndexAtom = session.atom<number>(0)
24
+
25
+ const fetchHelperFooters = useCallback(async (): Promise<Record<
26
+ string,
27
+ HelperFooterMessage
28
+ > | null> => {
29
+ console.log('useHelperFooter: fetchHelperFooters called')
30
+ if (!initialFormname) {
31
+ console.log('useHelperFooter: No formname provided, returning null')
32
+ return null
33
+ }
84
34
 
85
- const helperFooterResult = await get(
86
- 'helperfooter',
87
- currentFormname,
88
- 'client'
35
+ const [helperFooters, setHelperFooters] = session.useAtom(helperFooterAtom)
36
+
37
+ if (helperFooters === null) {
38
+ const [helperFooterResult] = session.useAtom(
39
+ session.atom(`helperfooter:${initialFormname}`)
89
40
  )
41
+ console.log('useHelperFooter: helperFooterResult:', helperFooterResult)
90
42
 
91
43
  if (
92
44
  helperFooterResult &&
93
45
  typeof helperFooterResult === 'object' &&
94
- 'type' in helperFooterResult &&
95
- helperFooterResult.type === 'json' &&
96
- 'value' in helperFooterResult &&
97
- typeof helperFooterResult.value === 'object' &&
98
- helperFooterResult.value !== null
46
+ helperFooterResult !== null
99
47
  ) {
100
- const fetchedHelperFooters = Object.entries(
101
- helperFooterResult.value as Record<string, unknown>
102
- )
103
- .map(([key, value]): HelperFooterMessage | null => {
104
- if (
105
- typeof value === 'object' &&
106
- value !== null &&
107
- 'status' in value &&
108
- 'statusMessage' in value &&
109
- 'spreadMessage' in value &&
110
- 'spreadMessagePriority' in value &&
111
- 'required' in value
112
- ) {
113
- return {
114
- status: value.status as 'error' | 'success',
115
- statusMessage: String(value.statusMessage),
116
- spreadMessage: String(value.spreadMessage),
117
- spreadMessagePriority: Number(value.spreadMessagePriority),
118
- required: Boolean(value.required),
119
- formname: key,
120
- }
48
+ const fetchedHelperFooters: Record<string, HelperFooterMessage> = {}
49
+
50
+ for (const [key, value] of Object.entries(helperFooterResult)) {
51
+ if (
52
+ typeof value === 'object' &&
53
+ value !== null &&
54
+ 'status' in value &&
55
+ 'statusMessage' in value &&
56
+ 'spreadMessage' in value &&
57
+ 'spreadMessagePriority' in value &&
58
+ 'required' in value &&
59
+ 'hasInput' in value
60
+ ) {
61
+ fetchedHelperFooters[key] = {
62
+ status: value.status as 'error' | 'success' | 'emptyAndRequired',
63
+ statusMessage: String(value.statusMessage),
64
+ spreadMessage: String(value.spreadMessage),
65
+ spreadMessagePriority: Number(value.spreadMessagePriority),
66
+ required: Boolean(value.required),
67
+ hasInput: Boolean(value.hasInput),
121
68
  }
122
- return null
123
- })
124
- .filter((value): value is HelperFooterMessage => value !== null)
125
-
126
- if (
127
- JSON.stringify(fetchedHelperFooters) !==
128
- JSON.stringify(prevHelperFooters.current)
129
- ) {
130
- setHelperFooters(fetchedHelperFooters)
131
- prevHelperFooters.current = fetchedHelperFooters
69
+ }
132
70
  }
71
+
72
+ console.log(
73
+ 'useHelperFooter: Fetched helper footers:',
74
+ fetchedHelperFooters
75
+ )
76
+ setHelperFooters(fetchedHelperFooters)
133
77
  return fetchedHelperFooters
134
78
  }
135
79
 
136
- if (helperFooters.length > 0) {
137
- setHelperFooters([])
138
- prevHelperFooters.current = []
139
- }
140
- return []
141
- },
142
- [initialFormname, helperFooters]
143
- )
80
+ console.log(
81
+ 'useHelperFooter: Invalid helper footer result, returning null'
82
+ )
83
+ return null
84
+ }
144
85
 
145
- /**
146
- * Updates the form validation state based on the fetched helper footers.
147
- *
148
- * @param {string} [formname] - The name of the form to update validation for.
149
- * @returns {Promise<boolean>} A promise that resolves to a boolean indicating whether the form is valid.
150
- */
151
- const updateFormValidation = useCallback(
152
- async (formname?: string): Promise<boolean> => {
153
- const fetchedHelperFooters = await fetchHelperFooters(formname)
154
-
155
- if (fetchedHelperFooters.length === 0) {
156
- setErrorMessage(undefined)
157
- setIsFormValid(true)
158
- return true
159
- }
86
+ return helperFooters
87
+ }, [initialFormname, helperFooterAtom])
160
88
 
161
- const errorFooters = fetchedHelperFooters.filter(
162
- footer => footer.status === 'error' && footer.required
163
- )
89
+ const updateFormValidation = useCallback(async (): Promise<boolean> => {
90
+ console.log('useHelperFooter: updateFormValidation called')
91
+ const fetchedHelperFooters = await fetchHelperFooters()
164
92
 
165
- if (errorFooters.length > 0) {
166
- const highestPriorityError = errorFooters.reduce((prev, current) =>
167
- prev.spreadMessagePriority < current.spreadMessagePriority
168
- ? prev
169
- : current
170
- )
171
- setErrorMessage(highestPriorityError.spreadMessage)
172
- setIsFormValid(false)
173
- return false
174
- }
93
+ if (fetchedHelperFooters) {
94
+ const errorFooters = Object.values(fetchedHelperFooters).filter(
95
+ footer =>
96
+ (footer.status === 'error' || footer.status === 'emptyAndRequired') &&
97
+ footer.required
98
+ )
175
99
 
176
- setErrorMessage(undefined)
177
- setIsFormValid(true)
178
- return true
179
- },
180
- [fetchHelperFooters]
181
- )
100
+ console.log('useHelperFooter: Error footers:', errorFooters)
182
101
 
183
- /**
184
- * Effect to run form validation when the initial form name changes.
185
- */
186
- useEffect(() => {
187
- void updateFormValidation()
188
- }, [initialFormname, updateFormValidation])
189
-
190
- /**
191
- * Effect to set up periodic helper footer fetching.
192
- */
193
- useEffect(() => {
194
- if (initialFormname) {
195
- const fetchAndUpdateHelperFooters = async () => {
196
- await updateFormValidation(initialFormname)
102
+ if (errorFooters.length === 0) {
103
+ const [, setCurrentErrorIndex] = session.useAtom(currentErrorIndexAtom)
104
+ setCurrentErrorIndex(0)
105
+ console.log('useHelperFooter: No errors found, returning true')
106
+ return true
197
107
  }
198
108
 
199
- void fetchAndUpdateHelperFooters()
200
- intervalIdRef.current = setInterval(fetchAndUpdateHelperFooters, 1000)
109
+ errorFooters.sort(
110
+ (a, b) => a.spreadMessagePriority - b.spreadMessagePriority
111
+ )
201
112
 
202
- return () => {
203
- if (intervalIdRef.current) {
204
- clearInterval(intervalIdRef.current)
205
- }
113
+ const [currentErrorIndex, setCurrentErrorIndex] = session.useAtom(
114
+ currentErrorIndexAtom
115
+ )
116
+ if (currentErrorIndex >= errorFooters.length) {
117
+ setCurrentErrorIndex(0)
206
118
  }
119
+
120
+ console.log('useHelperFooter: Errors found, returning false')
121
+ return false
207
122
  }
208
- }, [initialFormname, updateFormValidation])
209
-
210
- /**
211
- * Effect to update refs when error message or form validity changes.
212
- */
213
- useEffect(() => {
214
- if (
215
- errorMessage !== prevErrorMessage.current ||
216
- isFormValid !== prevIsFormValid.current
217
- ) {
218
- prevErrorMessage.current = errorMessage
219
- prevIsFormValid.current = isFormValid
220
- }
221
- }, [errorMessage, isFormValid])
222
123
 
223
- /**
224
- * Memoized helper footers to prevent unnecessary re-renders.
225
- */
226
- const memoizedHelperFooters = useMemo(() => helperFooters, [helperFooters])
124
+ console.log('useHelperFooter: No helper footers, returning true')
125
+ return true
126
+ }, [fetchHelperFooters, currentErrorIndexAtom])
127
+
128
+ const checkFormStatus = useCallback(async () => {
129
+ console.log('useHelperFooter: checkFormStatus called')
130
+ const fetchedHelperFooters = await fetchHelperFooters()
131
+ const status = fetchedHelperFooters
132
+ ? Object.values(fetchedHelperFooters).every(
133
+ value => !value.required || (value.required && value.hasInput)
134
+ )
135
+ : true
136
+ console.log('useHelperFooter: Form status:', status)
137
+ return status
138
+ }, [fetchHelperFooters])
139
+
140
+ const getEmptyRequiredFields = useCallback(async () => {
141
+ console.log('useHelperFooter: getEmptyRequiredFields called')
142
+ const fetchedHelperFooters = await fetchHelperFooters()
143
+ if (!fetchedHelperFooters) return []
144
+ const emptyFields = Object.entries(fetchedHelperFooters)
145
+ .filter(([, value]) => value.required && !value.hasInput)
146
+ .map(([key]) => key)
147
+ console.log('useHelperFooter: Empty required fields:', emptyFields)
148
+ return emptyFields
149
+ }, [fetchHelperFooters])
150
+
151
+ const getCurrentErrorMessage = useCallback(async () => {
152
+ console.log('useHelperFooter: getCurrentErrorMessage called')
153
+ const fetchedHelperFooters = await fetchHelperFooters()
154
+ if (!fetchedHelperFooters) return undefined
155
+ const errorFooters = Object.values(fetchedHelperFooters).filter(
156
+ footer =>
157
+ (footer.status === 'error' || footer.status === 'emptyAndRequired') &&
158
+ footer.required
159
+ )
160
+ if (errorFooters.length === 0) return undefined
161
+ const [currentErrorIndex] = session.useAtom(currentErrorIndexAtom)
162
+ const message = errorFooters[currentErrorIndex]?.spreadMessage
163
+ console.log('useHelperFooter: Current error message:', message)
164
+ return message
165
+ }, [fetchHelperFooters, currentErrorIndexAtom])
166
+
167
+ const isFormValid = useCallback(async () => {
168
+ const fetchedHelperFooters = await fetchHelperFooters()
169
+ if (!fetchedHelperFooters) return true
170
+ const valid = Object.values(fetchedHelperFooters).every(
171
+ footer =>
172
+ footer.status !== 'error' && footer.status !== 'emptyAndRequired'
173
+ )
174
+ console.log('useHelperFooter: isFormValid:', valid)
175
+ return valid
176
+ }, [fetchHelperFooters])
177
+
178
+ const nextError = useCallback(async () => {
179
+ console.log('useHelperFooter: nextError called')
180
+ const fetchedHelperFooters = await fetchHelperFooters()
181
+ if (!fetchedHelperFooters) return
182
+ const errorFooters = Object.values(fetchedHelperFooters).filter(
183
+ footer =>
184
+ (footer.status === 'error' || footer.status === 'emptyAndRequired') &&
185
+ footer.required
186
+ )
187
+ if (errorFooters.length > 0) {
188
+ const [currentErrorIndex, setCurrentErrorIndex] = session.useAtom(
189
+ currentErrorIndexAtom
190
+ )
191
+ setCurrentErrorIndex((currentErrorIndex + 1) % errorFooters.length)
192
+ console.log(
193
+ 'useHelperFooter: New error index:',
194
+ (currentErrorIndex + 1) % errorFooters.length
195
+ )
196
+ }
197
+ }, [fetchHelperFooters, currentErrorIndexAtom])
227
198
 
228
- /**
229
- * Memoized return value of the hook to prevent unnecessary re-renders.
230
- */
231
- const returnValue = useMemo(
199
+ const result = useMemo(
232
200
  () => ({
233
- errorMessage,
201
+ errorMessage: getCurrentErrorMessage,
234
202
  isFormValid,
235
203
  updateFormValidation,
236
204
  fetchHelperFooters,
237
- helperFooters: memoizedHelperFooters,
205
+ nextError,
206
+ checkFormStatus,
207
+ getEmptyRequiredFields,
238
208
  }),
239
209
  [
240
- errorMessage,
210
+ getCurrentErrorMessage,
241
211
  isFormValid,
242
212
  updateFormValidation,
243
213
  fetchHelperFooters,
244
- memoizedHelperFooters,
214
+ nextError,
215
+ checkFormStatus,
216
+ getEmptyRequiredFields,
245
217
  ]
246
218
  )
247
219
 
248
- return returnValue
220
+ console.log('useHelperFooter: Returning result:', result)
221
+ return result
249
222
  }
250
223
 
251
224
  export default useHelperFooter