goobs-frontend 0.8.5 → 0.8.6
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,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"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
6
|
"license": "MIT",
|
|
6
7
|
"main": "./src/index.ts",
|
|
@@ -27,20 +28,20 @@
|
|
|
27
28
|
"highlight.js": "^11.10.0",
|
|
28
29
|
"jotai": "^2.10.1",
|
|
29
30
|
"lodash": "^4.17.21",
|
|
30
|
-
"next": "
|
|
31
|
+
"next": "15.0.0",
|
|
31
32
|
"otplib": "^12.0.1",
|
|
32
33
|
"react-datepicker": "^7.5.0",
|
|
33
34
|
"react-qr-code": "^2.0.15"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@next/eslint-plugin-next": "^
|
|
37
|
-
"@types/node": "^22.7.
|
|
37
|
+
"@next/eslint-plugin-next": "^15.0.0",
|
|
38
|
+
"@types/node": "^22.7.8",
|
|
38
39
|
"@types/react": "18.3.11",
|
|
39
40
|
"@types/react-dom": "^18.3.1",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
41
|
-
"@typescript-eslint/parser": "^8.
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.11.0",
|
|
42
|
+
"@typescript-eslint/parser": "^8.11.0",
|
|
42
43
|
"eslint": "^9.13.0",
|
|
43
|
-
"eslint-config-next": "^
|
|
44
|
+
"eslint-config-next": "^15.0.0",
|
|
44
45
|
"eslint-config-prettier": "^9.1.0",
|
|
45
46
|
"eslint-plugin-prettier": "^5.2.1",
|
|
46
47
|
"prettier": "^3.3.3",
|
|
@@ -19,7 +19,6 @@ export interface CustomButtonProps extends ButtonProps {
|
|
|
19
19
|
|
|
20
20
|
const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
21
21
|
props => {
|
|
22
|
-
console.log('[trace-button] CustomButton: Rendering component', { props })
|
|
23
22
|
const {
|
|
24
23
|
text,
|
|
25
24
|
variant,
|
|
@@ -40,7 +39,6 @@ const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
|
40
39
|
const handleButtonClick = (
|
|
41
40
|
event: React.MouseEvent<HTMLButtonElement>
|
|
42
41
|
): void => {
|
|
43
|
-
console.log('[trace-button] CustomButton: Button clicked')
|
|
44
42
|
event.preventDefault()
|
|
45
43
|
onClick?.(event)
|
|
46
44
|
}
|
|
@@ -63,13 +61,6 @@ const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
|
63
61
|
})
|
|
64
62
|
: null
|
|
65
63
|
|
|
66
|
-
console.log('[trace-button] CustomButton: Rendering button', {
|
|
67
|
-
variant,
|
|
68
|
-
style: buttonStyle,
|
|
69
|
-
disableButton,
|
|
70
|
-
isDisabled,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
64
|
const buttonContent = (
|
|
74
65
|
<>
|
|
75
66
|
{iconlocation === 'above' && IconComponent}
|
|
@@ -140,15 +131,9 @@ const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
|
140
131
|
prevProps.iconlocation === nextProps.iconlocation &&
|
|
141
132
|
prevProps.fontlocation === nextProps.fontlocation
|
|
142
133
|
|
|
143
|
-
console.log('[trace-button] CustomButton: Props comparison', {
|
|
144
|
-
propsAreEqual,
|
|
145
|
-
prevProps: Object.keys(prevProps),
|
|
146
|
-
nextProps: Object.keys(nextProps),
|
|
147
|
-
})
|
|
148
134
|
return propsAreEqual
|
|
149
135
|
}
|
|
150
136
|
)
|
|
151
137
|
|
|
152
138
|
CustomButton.displayName = 'CustomButton'
|
|
153
|
-
console.log('[trace-button] CustomButton: Component defined')
|
|
154
139
|
export default CustomButton
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react'
|
|
3
|
+
import React, { useState, useEffect } from 'react'
|
|
4
4
|
import {
|
|
5
5
|
Select,
|
|
6
6
|
MenuItem,
|
|
@@ -8,9 +8,12 @@ import {
|
|
|
8
8
|
InputLabel,
|
|
9
9
|
SelectProps,
|
|
10
10
|
FormHelperText,
|
|
11
|
-
|
|
11
|
+
Box,
|
|
12
|
+
CircularProgress,
|
|
12
13
|
} from '@mui/material'
|
|
13
14
|
import { styled } from '@mui/material/styles'
|
|
15
|
+
import { black, white } from '../../styles/palette'
|
|
16
|
+
import Typography from '../Typography'
|
|
14
17
|
|
|
15
18
|
export interface SimpleDropdownOption {
|
|
16
19
|
value: string
|
|
@@ -40,37 +43,62 @@ export interface DropdownProps extends Omit<SelectProps, 'onChange'> {
|
|
|
40
43
|
onFocus?: SelectProps['onFocus']
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
const StyledBox = styled(Box)(() => ({
|
|
47
|
+
position: 'relative',
|
|
48
|
+
width: '100%',
|
|
49
|
+
height: '50px',
|
|
50
|
+
marginTop: '5px',
|
|
51
|
+
}))
|
|
52
|
+
|
|
43
53
|
const StyledFormControl = styled(FormControl)<{
|
|
44
54
|
backgroundcolor?: string
|
|
45
55
|
outlinecolor?: string
|
|
46
|
-
}>(({
|
|
47
|
-
|
|
56
|
+
}>(({ outlinecolor }) => ({
|
|
57
|
+
position: 'absolute',
|
|
58
|
+
top: 0,
|
|
59
|
+
left: 0,
|
|
60
|
+
right: 0,
|
|
61
|
+
bottom: 0,
|
|
48
62
|
'& .MuiOutlinedInput-root': {
|
|
49
|
-
backgroundColor:
|
|
63
|
+
backgroundColor: white.main,
|
|
64
|
+
height: '45px',
|
|
50
65
|
'& fieldset': {
|
|
51
|
-
borderColor: outlinecolor ||
|
|
66
|
+
borderColor: outlinecolor || black.main,
|
|
52
67
|
},
|
|
53
68
|
'&:hover fieldset': {
|
|
54
|
-
borderColor: outlinecolor ||
|
|
69
|
+
borderColor: outlinecolor || black.main,
|
|
55
70
|
},
|
|
56
71
|
'&.Mui-focused fieldset': {
|
|
57
|
-
borderColor: outlinecolor ||
|
|
72
|
+
borderColor: outlinecolor || black.main,
|
|
58
73
|
},
|
|
59
74
|
},
|
|
60
75
|
}))
|
|
61
76
|
|
|
62
77
|
const StyledInputLabel = styled(InputLabel)<{ shrunkfontcolor?: string }>(
|
|
63
|
-
(
|
|
78
|
+
() => ({
|
|
79
|
+
color: black.main,
|
|
64
80
|
'&.Mui-focused': {
|
|
65
|
-
color:
|
|
81
|
+
color: black.main,
|
|
82
|
+
},
|
|
83
|
+
'&.MuiInputLabel-shrink': {
|
|
84
|
+
transform: 'translate(13px, -7px) scale(0.75)',
|
|
85
|
+
color: black.main,
|
|
66
86
|
},
|
|
67
87
|
})
|
|
68
88
|
)
|
|
69
89
|
|
|
70
|
-
const StyledMenuItem = styled(MenuItem)((
|
|
90
|
+
const StyledMenuItem = styled(MenuItem)(() => ({
|
|
71
91
|
display: 'flex',
|
|
72
92
|
flexDirection: 'column',
|
|
73
93
|
alignItems: 'flex-start',
|
|
94
|
+
backgroundColor: white.main,
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
const LoadingContainer = styled(Box)(() => ({
|
|
98
|
+
display: 'flex',
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
height: '50px',
|
|
74
102
|
}))
|
|
75
103
|
|
|
76
104
|
const capitalizeFirstLetter = (string: string) => {
|
|
@@ -94,23 +122,33 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|
|
94
122
|
onFocus,
|
|
95
123
|
...rest
|
|
96
124
|
}) => {
|
|
97
|
-
const [selectedValue, setSelectedValue] = useState(
|
|
125
|
+
const [selectedValue, setSelectedValue] = useState<string>('')
|
|
126
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
98
129
|
const defaultOption = options.find(option => option.value === defaultValue)
|
|
99
|
-
|
|
100
|
-
|
|
130
|
+
setSelectedValue(defaultOption ? defaultOption.value : '')
|
|
131
|
+
setIsLoading(false)
|
|
132
|
+
}, [defaultValue, options])
|
|
101
133
|
|
|
102
134
|
const handleChange: SelectProps['onChange'] = (event, child) => {
|
|
103
135
|
const newValue = event.target.value as string
|
|
104
136
|
setSelectedValue(newValue)
|
|
105
|
-
onChange
|
|
137
|
+
if (onChange) {
|
|
138
|
+
onChange(event, child)
|
|
139
|
+
}
|
|
106
140
|
}
|
|
107
141
|
|
|
108
142
|
const handleBlur: SelectProps['onBlur'] = event => {
|
|
109
|
-
onBlur
|
|
143
|
+
if (onBlur) {
|
|
144
|
+
onBlur(event)
|
|
145
|
+
}
|
|
110
146
|
}
|
|
111
147
|
|
|
112
148
|
const handleFocus: SelectProps['onFocus'] = event => {
|
|
113
|
-
onFocus
|
|
149
|
+
if (onFocus) {
|
|
150
|
+
onFocus(event)
|
|
151
|
+
}
|
|
114
152
|
}
|
|
115
153
|
|
|
116
154
|
const renderMenuItem = (option: DropdownOption) => {
|
|
@@ -118,48 +156,107 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|
|
118
156
|
if (!('attribute1' in option)) {
|
|
119
157
|
return (
|
|
120
158
|
<MenuItem key={option.value} value={option.value}>
|
|
121
|
-
{label}
|
|
159
|
+
<Typography fontvariant="merriparagraph" text={label} />
|
|
122
160
|
</MenuItem>
|
|
123
161
|
)
|
|
124
162
|
} else {
|
|
125
163
|
return (
|
|
126
164
|
<StyledMenuItem key={option.value} value={option.value}>
|
|
127
|
-
<Typography
|
|
128
|
-
<Typography
|
|
129
|
-
|
|
130
|
-
{option.attribute2
|
|
131
|
-
|
|
165
|
+
<Typography fontvariant="merriparagraph" text={label} />
|
|
166
|
+
<Typography
|
|
167
|
+
fontvariant="merriparagraph"
|
|
168
|
+
text={`${option.attribute1}${option.attribute2 ? ` | ${option.attribute2}` : ''}`}
|
|
169
|
+
fontcolor="textSecondary"
|
|
170
|
+
/>
|
|
132
171
|
</StyledMenuItem>
|
|
133
172
|
)
|
|
134
173
|
}
|
|
135
174
|
}
|
|
136
175
|
|
|
176
|
+
if (isLoading) {
|
|
177
|
+
return (
|
|
178
|
+
<LoadingContainer>
|
|
179
|
+
<CircularProgress size={24} />
|
|
180
|
+
</LoadingContainer>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
137
184
|
return (
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{label}
|
|
146
|
-
</StyledInputLabel>
|
|
147
|
-
<Select
|
|
148
|
-
labelId={`${name}-label`}
|
|
149
|
-
value={selectedValue}
|
|
150
|
-
onChange={handleChange}
|
|
151
|
-
onBlur={handleBlur}
|
|
152
|
-
onFocus={handleFocus}
|
|
153
|
-
label={label}
|
|
154
|
-
sx={{ color: fontcolor }}
|
|
155
|
-
name={name}
|
|
156
|
-
aria-labelledby={`${name}-label`}
|
|
157
|
-
{...rest}
|
|
185
|
+
<StyledBox>
|
|
186
|
+
<StyledFormControl
|
|
187
|
+
backgroundcolor={backgroundcolor}
|
|
188
|
+
outlinecolor={outlinecolor}
|
|
189
|
+
error={error}
|
|
190
|
+
required={required}
|
|
191
|
+
fullWidth
|
|
158
192
|
>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
193
|
+
<StyledInputLabel
|
|
194
|
+
id={`${name}-label`}
|
|
195
|
+
shrunkfontcolor={shrunkfontcolor}
|
|
196
|
+
>
|
|
197
|
+
{label}
|
|
198
|
+
</StyledInputLabel>
|
|
199
|
+
<Select
|
|
200
|
+
labelId={`${name}-label`}
|
|
201
|
+
value={selectedValue}
|
|
202
|
+
onChange={handleChange}
|
|
203
|
+
onBlur={handleBlur}
|
|
204
|
+
onFocus={handleFocus}
|
|
205
|
+
label={label}
|
|
206
|
+
sx={{
|
|
207
|
+
color: fontcolor || black.main,
|
|
208
|
+
height: '45px',
|
|
209
|
+
backgroundColor: white.main,
|
|
210
|
+
'& .MuiSelect-select': {
|
|
211
|
+
paddingTop: '10px',
|
|
212
|
+
paddingBottom: '10px',
|
|
213
|
+
},
|
|
214
|
+
'& .MuiSvgIcon-root': {
|
|
215
|
+
color: black.main,
|
|
216
|
+
},
|
|
217
|
+
'& .MuiOutlinedInput-notchedOutline': {
|
|
218
|
+
borderColor: black.main,
|
|
219
|
+
},
|
|
220
|
+
'&:hover .MuiOutlinedInput-notchedOutline': {
|
|
221
|
+
borderColor: black.main,
|
|
222
|
+
},
|
|
223
|
+
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
|
224
|
+
borderColor: black.main,
|
|
225
|
+
},
|
|
226
|
+
'& .MuiInputBase-input': {
|
|
227
|
+
color: black.main,
|
|
228
|
+
},
|
|
229
|
+
'& .MuiInputBase-input::placeholder': {
|
|
230
|
+
color: black.main,
|
|
231
|
+
opacity: 1,
|
|
232
|
+
},
|
|
233
|
+
'& .MuiPaper-root': {
|
|
234
|
+
backgroundColor: white.main,
|
|
235
|
+
},
|
|
236
|
+
'& .MuiMenu-list': {
|
|
237
|
+
backgroundColor: white.main,
|
|
238
|
+
},
|
|
239
|
+
}}
|
|
240
|
+
MenuProps={{
|
|
241
|
+
PaperProps: {
|
|
242
|
+
sx: {
|
|
243
|
+
backgroundColor: white.main,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
}}
|
|
247
|
+
name={name}
|
|
248
|
+
aria-labelledby={`${name}-label`}
|
|
249
|
+
{...rest}
|
|
250
|
+
>
|
|
251
|
+
{options.map(renderMenuItem)}
|
|
252
|
+
</Select>
|
|
253
|
+
{helperText && (
|
|
254
|
+
<FormHelperText>
|
|
255
|
+
<Typography fontvariant="merriparagraph" text={helperText} />
|
|
256
|
+
</FormHelperText>
|
|
257
|
+
)}
|
|
258
|
+
</StyledFormControl>
|
|
259
|
+
</StyledBox>
|
|
163
260
|
)
|
|
164
261
|
}
|
|
165
262
|
|
|
@@ -2,78 +2,43 @@
|
|
|
2
2
|
import React, { useState, useEffect } from 'react'
|
|
3
3
|
import { Box, Tabs, Tab } from '@mui/material'
|
|
4
4
|
import { NavProps, SubNav, View } from '../index'
|
|
5
|
+
import { usePathname } from 'next/navigation'
|
|
5
6
|
|
|
6
|
-
/**
|
|
7
|
-
* Represents the possible alignment options for the horizontal navigation.
|
|
8
|
-
*/
|
|
9
7
|
type Alignment = 'left' | 'center' | 'right' | 'inherit' | 'justify'
|
|
10
8
|
|
|
11
|
-
/**
|
|
12
|
-
* Represents the structure of an active tab value.
|
|
13
|
-
*/
|
|
14
9
|
export interface ActiveTabValue {
|
|
15
|
-
|
|
16
|
-
tabId: string
|
|
10
|
+
tabId: string | false
|
|
17
11
|
}
|
|
18
12
|
|
|
19
|
-
/**
|
|
20
|
-
* Props for the HorizontalVariant component.
|
|
21
|
-
*/
|
|
22
13
|
export interface HorizontalVariantProps {
|
|
23
|
-
/** An array of navigation items, sub-navigation items, or views. */
|
|
24
14
|
items: (NavProps | SubNav | View)[]
|
|
25
|
-
/** The height of the navigation bar. Defaults to '80px'. */
|
|
26
15
|
height?: string
|
|
27
|
-
/** The alignment of the navigation items. Defaults to 'left'. */
|
|
28
16
|
alignment?: Alignment
|
|
29
|
-
/** A unique name for this navigation component. Used for state management. */
|
|
30
17
|
navname?: string
|
|
31
18
|
}
|
|
32
19
|
|
|
33
|
-
/**
|
|
34
|
-
* HorizontalVariant component that renders a horizontal navigation bar.
|
|
35
|
-
* It supports dynamic tab management, routing, and custom click handlers.
|
|
36
|
-
*
|
|
37
|
-
* @param {HorizontalVariantProps} props - The props for the HorizontalVariant component.
|
|
38
|
-
* @returns {JSX.Element} The rendered HorizontalVariant component.
|
|
39
|
-
*/
|
|
40
20
|
function HorizontalVariant({
|
|
41
21
|
items,
|
|
42
22
|
height = '80px',
|
|
43
23
|
alignment = 'left',
|
|
44
24
|
navname = '',
|
|
45
25
|
}: HorizontalVariantProps) {
|
|
46
|
-
/**
|
|
47
|
-
* State to keep track of active tab values for different navigation components.
|
|
48
|
-
*/
|
|
49
26
|
const [activeTabValues, setActiveTabValues] = useState<
|
|
50
27
|
Record<string, ActiveTabValue>
|
|
51
28
|
>({})
|
|
29
|
+
const pathname = usePathname()
|
|
52
30
|
|
|
53
|
-
/**
|
|
54
|
-
* Effect hook to initialize the active tab values when the component mounts.
|
|
55
|
-
*/
|
|
56
31
|
useEffect(() => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}, [items, navname, activeTabValues])
|
|
32
|
+
const currentTab = items.find(
|
|
33
|
+
item => 'orientation' in item && item.route === pathname
|
|
34
|
+
) as NavProps | undefined
|
|
35
|
+
|
|
36
|
+
setActiveTabValues(prev => ({
|
|
37
|
+
...prev,
|
|
38
|
+
[navname]: { tabId: currentTab?.title || false },
|
|
39
|
+
}))
|
|
40
|
+
}, [items, navname, pathname])
|
|
69
41
|
|
|
70
|
-
/**
|
|
71
|
-
* Handles tab change events.
|
|
72
|
-
* Updates the active tab values in the state.
|
|
73
|
-
*
|
|
74
|
-
* @param {React.SyntheticEvent} event - The event object.
|
|
75
|
-
* @param {string} newValue - The new value of the selected tab.
|
|
76
|
-
*/
|
|
77
42
|
const handleTabChange = (event: React.SyntheticEvent, newValue: string) => {
|
|
78
43
|
setActiveTabValues(prev => ({
|
|
79
44
|
...prev,
|
|
@@ -81,12 +46,6 @@ function HorizontalVariant({
|
|
|
81
46
|
}))
|
|
82
47
|
}
|
|
83
48
|
|
|
84
|
-
/**
|
|
85
|
-
* Handles click events on individual tabs.
|
|
86
|
-
* Supports different trigger types: route, onClick, and routeonhorizontal.
|
|
87
|
-
*
|
|
88
|
-
* @param {NavProps} tab - The tab object that was clicked.
|
|
89
|
-
*/
|
|
90
49
|
const handleTabClick = (tab: NavProps) => {
|
|
91
50
|
if (tab.trigger === 'route') {
|
|
92
51
|
if (tab.route) {
|
|
@@ -116,7 +75,7 @@ function HorizontalVariant({
|
|
|
116
75
|
}}
|
|
117
76
|
>
|
|
118
77
|
<Tabs
|
|
119
|
-
value={activeTabValues[navname]?.tabId ||
|
|
78
|
+
value={activeTabValues[navname]?.tabId || false}
|
|
120
79
|
onChange={handleTabChange}
|
|
121
80
|
aria-label="nav tabs"
|
|
122
81
|
sx={{
|
|
@@ -136,8 +95,8 @@ function HorizontalVariant({
|
|
|
136
95
|
return (
|
|
137
96
|
<Tab
|
|
138
97
|
key={tab.title}
|
|
139
|
-
value={tab.title}
|
|
140
|
-
label={tab.title}
|
|
98
|
+
value={tab.title || ''}
|
|
99
|
+
label={tab.title || ''}
|
|
141
100
|
onClick={() => handleTabClick(tab)}
|
|
142
101
|
sx={{
|
|
143
102
|
minHeight: 0,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React, { useState, useCallback, useMemo } from 'react'
|
|
3
|
-
import { Box, Paper, SelectChangeEvent } from '@mui/material'
|
|
2
|
+
import React, { useState, useCallback, useMemo, useEffect } from 'react'
|
|
3
|
+
import { Box, Paper, SelectChangeEvent, CircularProgress } from '@mui/material'
|
|
4
4
|
import InfoIcon from '@mui/icons-material/Info'
|
|
5
5
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle'
|
|
6
6
|
import { Typography } from '../Typography'
|
|
@@ -72,17 +72,37 @@ export interface PricingProps {
|
|
|
72
72
|
const PricingTable: React.FC<PricingProps> = props => {
|
|
73
73
|
const router = useRouter()
|
|
74
74
|
const [selectedPackageIndex, setSelectedPackageIndex] = useState(0)
|
|
75
|
+
const [selectedPackage, setSelectedPackage] = useState('')
|
|
76
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
75
77
|
|
|
76
78
|
const config = useMemo(() => {
|
|
77
79
|
return { ...defaultConfig, ...props }
|
|
78
80
|
}, [props])
|
|
79
81
|
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
setIsLoading(false)
|
|
85
|
+
}, 100) // Simulating a 100ms loading time
|
|
86
|
+
|
|
87
|
+
return () => clearTimeout(timer)
|
|
88
|
+
}, [])
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (
|
|
92
|
+
config.packagecolumns?.packagenames &&
|
|
93
|
+
config.packagecolumns.packagenames.length > 0
|
|
94
|
+
) {
|
|
95
|
+
setSelectedPackage(config.packagecolumns.packagenames[0])
|
|
96
|
+
}
|
|
97
|
+
}, [config.packagecolumns?.packagenames])
|
|
98
|
+
|
|
80
99
|
const handlePackageChange = useCallback(
|
|
81
100
|
(event: SelectChangeEvent<unknown>) => {
|
|
82
101
|
const newValue = event.target.value as string
|
|
83
102
|
const newIndex =
|
|
84
103
|
config.packagecolumns?.packagenames?.indexOf(newValue) ?? 0
|
|
85
104
|
setSelectedPackageIndex(newIndex)
|
|
105
|
+
setSelectedPackage(newValue)
|
|
86
106
|
console.log('Package selection changed to:', newValue)
|
|
87
107
|
},
|
|
88
108
|
[config.packagecolumns?.packagenames]
|
|
@@ -116,7 +136,7 @@ const PricingTable: React.FC<PricingProps> = props => {
|
|
|
116
136
|
value: name,
|
|
117
137
|
label: name,
|
|
118
138
|
}))}
|
|
119
|
-
defaultValue={
|
|
139
|
+
defaultValue={selectedPackage}
|
|
120
140
|
backgroundcolor={semiTransparentBlack.main}
|
|
121
141
|
outlinecolor={black.main}
|
|
122
142
|
fontcolor={black.main}
|
|
@@ -276,10 +296,46 @@ const PricingTable: React.FC<PricingProps> = props => {
|
|
|
276
296
|
}
|
|
277
297
|
|
|
278
298
|
return { headerColumnConfigs, featureColumnConfigs }
|
|
279
|
-
}, [
|
|
299
|
+
}, [
|
|
300
|
+
config,
|
|
301
|
+
selectedPackageIndex,
|
|
302
|
+
selectedPackage,
|
|
303
|
+
router,
|
|
304
|
+
handlePackageChange,
|
|
305
|
+
])
|
|
280
306
|
|
|
281
307
|
const { headerColumnConfigs, featureColumnConfigs } = renderColumnConfigs()
|
|
282
308
|
|
|
309
|
+
if (isLoading) {
|
|
310
|
+
return (
|
|
311
|
+
<CustomGrid
|
|
312
|
+
gridconfig={{
|
|
313
|
+
gridwidth: '100%',
|
|
314
|
+
alignment: 'center',
|
|
315
|
+
}}
|
|
316
|
+
columnconfig={[
|
|
317
|
+
{
|
|
318
|
+
row: 1,
|
|
319
|
+
column: 1,
|
|
320
|
+
alignment: 'center',
|
|
321
|
+
component: (
|
|
322
|
+
<Box
|
|
323
|
+
display="flex"
|
|
324
|
+
justifyContent="center"
|
|
325
|
+
alignItems="center"
|
|
326
|
+
height="350px"
|
|
327
|
+
width="100%"
|
|
328
|
+
overflow="auto"
|
|
329
|
+
>
|
|
330
|
+
<CircularProgress size={240} thickness={2} />
|
|
331
|
+
</Box>
|
|
332
|
+
),
|
|
333
|
+
},
|
|
334
|
+
]}
|
|
335
|
+
/>
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
|
|
283
339
|
return (
|
|
284
340
|
<Paper
|
|
285
341
|
elevation={1}
|