goobs-frontend 0.8.7 → 0.8.9
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 +15 -15
- package/src/components/Button/index.tsx +91 -109
- package/src/components/Checkbox/index.tsx +79 -0
- package/src/components/ConfirmationCodeInput/index.tsx +120 -46
- package/src/components/Content/Structure/checkbox/useCheckbox.tsx +65 -0
- package/src/components/Content/Structure/typography/useGridTypography.tsx +2 -0
- package/src/components/Content/index.tsx +5 -0
- package/src/components/DataGrid/Footer/index.tsx +169 -0
- package/src/components/DataGrid/Jotai/atom.ts +91 -0
- package/src/components/DataGrid/ManageColumn/index.tsx +211 -0
- package/src/components/DataGrid/ManageRow/index.tsx +348 -0
- package/src/components/DataGrid/Table/index.tsx +275 -0
- package/src/components/DataGrid/VerticalDivider/index.tsx +6 -0
- package/src/components/DataGrid/index.tsx +296 -0
- package/src/components/DataGrid/utils/useManageColumn.tsx +138 -0
- package/src/components/DataGrid/utils/useSearchbar.tsx +122 -0
- package/src/components/Dropdown/index.tsx +63 -14
- package/src/components/Form/DataGrid/index.tsx +72 -0
- package/src/components/Form/Popup/index.tsx +30 -63
- package/src/components/Grid/index.tsx +83 -101
- package/src/components/Nav/VerticalVariant/index.tsx +14 -10
- package/src/components/Searchbar/index.tsx +98 -42
- package/src/components/TextField/index.tsx +0 -1
- package/src/components/Toolbar/index.tsx +37 -19
- package/src/components/Typography/index.tsx +8 -4
- package/src/index.ts +16 -1
- package/src/components/ConfirmationCodeInput/utils/useCodeConfirmation.tsx +0 -150
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import { Box } from '@mui/material'
|
|
5
|
+
import { useAtomValue } from 'jotai'
|
|
6
|
+
import { columnVisibilityAtom } from '../Jotai/atom'
|
|
7
|
+
import { VerticalDivider } from '../VerticalDivider'
|
|
8
|
+
import TablePagination from '@mui/material/TablePagination'
|
|
9
|
+
import ManageColumn from '../ManageColumn'
|
|
10
|
+
import { ColumnDef } from '../Table'
|
|
11
|
+
import CustomButton from '../../Button'
|
|
12
|
+
import ShowHideEyeIcon from '@/components/Icons/ShowHideEye'
|
|
13
|
+
|
|
14
|
+
export interface CustomFooterProps {
|
|
15
|
+
page: number
|
|
16
|
+
pageSize: number
|
|
17
|
+
rowCount: number
|
|
18
|
+
onPageChange: (newPage: number) => void
|
|
19
|
+
onPageSizeChange: (newPageSize: number) => void
|
|
20
|
+
columns: ColumnDef[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function CustomFooter({
|
|
24
|
+
page,
|
|
25
|
+
pageSize,
|
|
26
|
+
rowCount,
|
|
27
|
+
onPageChange,
|
|
28
|
+
onPageSizeChange,
|
|
29
|
+
columns,
|
|
30
|
+
}: CustomFooterProps) {
|
|
31
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
32
|
+
const [checkboxWidth] = useState(45)
|
|
33
|
+
const columnVisibility = useAtomValue(columnVisibilityAtom)
|
|
34
|
+
|
|
35
|
+
const handleOpen = () => {
|
|
36
|
+
console.log('Footer handleOpen')
|
|
37
|
+
setIsOpen(true)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const handleClose = () => {
|
|
41
|
+
console.log('Footer handleClose')
|
|
42
|
+
setIsOpen(false)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Calculate total width based on visible columns only
|
|
46
|
+
const totalWidth = columns.reduce((sum, col) => {
|
|
47
|
+
if (columnVisibility[col.field] !== false) {
|
|
48
|
+
return sum + (col.width || 150)
|
|
49
|
+
}
|
|
50
|
+
return sum
|
|
51
|
+
}, 0)
|
|
52
|
+
|
|
53
|
+
const totalPages = Math.ceil(rowCount / pageSize)
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Box
|
|
57
|
+
className="custom-footer-container"
|
|
58
|
+
sx={{
|
|
59
|
+
width: `${totalWidth}px`,
|
|
60
|
+
minWidth: '100%',
|
|
61
|
+
height: '56px',
|
|
62
|
+
position: 'sticky',
|
|
63
|
+
left: 0,
|
|
64
|
+
marginLeft: `${checkboxWidth}px`,
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<Box
|
|
68
|
+
sx={{
|
|
69
|
+
display: 'flex',
|
|
70
|
+
justifyContent: 'space-between',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
flexWrap: 'nowrap',
|
|
73
|
+
width: '100%',
|
|
74
|
+
height: '100%',
|
|
75
|
+
px: 2,
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<Box
|
|
79
|
+
sx={{
|
|
80
|
+
display: 'flex',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
}}
|
|
83
|
+
className="left-box"
|
|
84
|
+
>
|
|
85
|
+
<Box sx={{ display: 'flex', alignItems: 'center', mr: '10px' }}>
|
|
86
|
+
<VerticalDivider />
|
|
87
|
+
</Box>
|
|
88
|
+
<Box
|
|
89
|
+
sx={{
|
|
90
|
+
display: 'flex',
|
|
91
|
+
alignItems: 'center',
|
|
92
|
+
gap: '8px',
|
|
93
|
+
pr: '8px',
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<CustomButton
|
|
97
|
+
onClick={handleOpen}
|
|
98
|
+
text="Manage Columns"
|
|
99
|
+
fontvariant="merriparagraph"
|
|
100
|
+
fontcolor="black"
|
|
101
|
+
icon={<ShowHideEyeIcon visible={true} />}
|
|
102
|
+
iconcolor="black"
|
|
103
|
+
iconlocation="left"
|
|
104
|
+
disableButton="false"
|
|
105
|
+
sx={{
|
|
106
|
+
minWidth: 'unset',
|
|
107
|
+
padding: '8px',
|
|
108
|
+
'& .MuiTypography-root': {
|
|
109
|
+
marginLeft: '16px',
|
|
110
|
+
},
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
</Box>
|
|
114
|
+
<Box
|
|
115
|
+
sx={{
|
|
116
|
+
display: 'flex',
|
|
117
|
+
alignItems: 'center',
|
|
118
|
+
ml: '10px',
|
|
119
|
+
mr: '10px',
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
<VerticalDivider />
|
|
123
|
+
</Box>
|
|
124
|
+
</Box>
|
|
125
|
+
<Box
|
|
126
|
+
sx={{
|
|
127
|
+
display: 'flex',
|
|
128
|
+
alignItems: 'center',
|
|
129
|
+
ml: 'auto',
|
|
130
|
+
}}
|
|
131
|
+
className="right-box"
|
|
132
|
+
>
|
|
133
|
+
<TablePagination
|
|
134
|
+
component="div"
|
|
135
|
+
count={rowCount}
|
|
136
|
+
page={page}
|
|
137
|
+
onPageChange={(_, newPage) => {
|
|
138
|
+
onPageChange(newPage)
|
|
139
|
+
}}
|
|
140
|
+
rowsPerPage={pageSize}
|
|
141
|
+
onRowsPerPageChange={event => {
|
|
142
|
+
const newPageSize = parseInt(event.target.value, 10)
|
|
143
|
+
onPageSizeChange(newPageSize)
|
|
144
|
+
}}
|
|
145
|
+
rowsPerPageOptions={[10, 25, 50, 100]}
|
|
146
|
+
slotProps={{
|
|
147
|
+
actions: {
|
|
148
|
+
previousButton: {
|
|
149
|
+
disabled: page === 0,
|
|
150
|
+
},
|
|
151
|
+
nextButton: {
|
|
152
|
+
disabled: page >= totalPages - 1,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}}
|
|
156
|
+
showFirstButton
|
|
157
|
+
showLastButton
|
|
158
|
+
labelDisplayedRows={({ from, to, count }) =>
|
|
159
|
+
`${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
|
|
160
|
+
}
|
|
161
|
+
/>
|
|
162
|
+
</Box>
|
|
163
|
+
</Box>
|
|
164
|
+
<ManageColumn open={isOpen} handleClose={handleClose} columns={columns} />
|
|
165
|
+
</Box>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export default CustomFooter
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { atom } from 'jotai'
|
|
2
|
+
import { atomWithStorage } from 'jotai/utils'
|
|
3
|
+
|
|
4
|
+
interface ColumnVisibility {
|
|
5
|
+
[key: string]: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Create a persistent atom that saves to localStorage
|
|
9
|
+
export const columnVisibilityAtom = atomWithStorage<ColumnVisibility>(
|
|
10
|
+
'columnVisibility',
|
|
11
|
+
{}
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// Atom for managing initial column setup
|
|
15
|
+
export const columnsAtom = atom<string[]>([])
|
|
16
|
+
|
|
17
|
+
// Actions atom for updating visibility
|
|
18
|
+
export const columnVisibilityActions = atom(
|
|
19
|
+
null,
|
|
20
|
+
(
|
|
21
|
+
get,
|
|
22
|
+
set,
|
|
23
|
+
update: {
|
|
24
|
+
type: 'toggle' | 'setAll' | 'reset' | 'save'
|
|
25
|
+
field?: string
|
|
26
|
+
value?: boolean
|
|
27
|
+
newState?: ColumnVisibility
|
|
28
|
+
}
|
|
29
|
+
) => {
|
|
30
|
+
const currentVisibility = get(columnVisibilityAtom)
|
|
31
|
+
const columns = get(columnsAtom)
|
|
32
|
+
let newVisibility: ColumnVisibility = {}
|
|
33
|
+
|
|
34
|
+
console.log('columnVisibilityActions - before:', {
|
|
35
|
+
type: update.type,
|
|
36
|
+
currentVisibility,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
switch (update.type) {
|
|
40
|
+
case 'toggle': {
|
|
41
|
+
if (update.field) {
|
|
42
|
+
newVisibility = {
|
|
43
|
+
...currentVisibility,
|
|
44
|
+
[update.field]: !currentVisibility[update.field],
|
|
45
|
+
}
|
|
46
|
+
console.log('columnVisibilityActions - toggle:', {
|
|
47
|
+
field: update.field,
|
|
48
|
+
before: currentVisibility[update.field],
|
|
49
|
+
after: newVisibility[update.field],
|
|
50
|
+
})
|
|
51
|
+
set(columnVisibilityAtom, newVisibility)
|
|
52
|
+
}
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case 'setAll': {
|
|
57
|
+
columns.forEach(column => {
|
|
58
|
+
newVisibility[column] = !!update.value
|
|
59
|
+
})
|
|
60
|
+
console.log('columnVisibilityActions - setAll:', {
|
|
61
|
+
value: update.value,
|
|
62
|
+
newState: newVisibility,
|
|
63
|
+
})
|
|
64
|
+
set(columnVisibilityAtom, newVisibility)
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
case 'save': {
|
|
69
|
+
if (update.newState) {
|
|
70
|
+
console.log('columnVisibilityActions - save:', {
|
|
71
|
+
before: currentVisibility,
|
|
72
|
+
after: update.newState,
|
|
73
|
+
})
|
|
74
|
+
set(columnVisibilityAtom, update.newState)
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
case 'reset': {
|
|
80
|
+
columns.forEach(column => {
|
|
81
|
+
newVisibility[column] = true
|
|
82
|
+
})
|
|
83
|
+
console.log('columnVisibilityActions - reset:', newVisibility)
|
|
84
|
+
set(columnVisibilityAtom, newVisibility)
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('columnVisibilityActions - after:', get(columnVisibilityAtom))
|
|
90
|
+
}
|
|
91
|
+
)
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Box, Popover, IconButton } from '@mui/material'
|
|
5
|
+
import { useManageColumn } from '../utils/useManageColumn'
|
|
6
|
+
import CustomButton from '../../Button'
|
|
7
|
+
import Searchbar from '../../Searchbar'
|
|
8
|
+
import Checkbox from '../../Checkbox'
|
|
9
|
+
import ShowHideEyeIcon from '../../Icons/ShowHideEye'
|
|
10
|
+
import { ColumnDef } from '../Table'
|
|
11
|
+
import Typography from '../../Typography'
|
|
12
|
+
import * as palette from '../../../styles/palette'
|
|
13
|
+
|
|
14
|
+
interface ManageColumnProps {
|
|
15
|
+
open?: boolean
|
|
16
|
+
handleClose?: () => void
|
|
17
|
+
columns: ColumnDef[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ManageColumns({
|
|
21
|
+
open = false,
|
|
22
|
+
handleClose = () => {},
|
|
23
|
+
columns,
|
|
24
|
+
}: ManageColumnProps) {
|
|
25
|
+
console.log('ManageColumns render:', { open, columns })
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
handleAllCols,
|
|
29
|
+
toggleColumnState,
|
|
30
|
+
visibleColumns,
|
|
31
|
+
onSaveColumnView,
|
|
32
|
+
formatColumnName,
|
|
33
|
+
searchInput,
|
|
34
|
+
setSearchInput,
|
|
35
|
+
isAllChecked,
|
|
36
|
+
} = useManageColumn({
|
|
37
|
+
columns,
|
|
38
|
+
handleClose,
|
|
39
|
+
isPopupOpen: open,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const someColumnsVisible = React.useMemo(() => {
|
|
43
|
+
return (
|
|
44
|
+
columns.some(column => visibleColumns[column.field] === true) &&
|
|
45
|
+
!columns.every(column => visibleColumns[column.field] === true)
|
|
46
|
+
)
|
|
47
|
+
}, [columns, visibleColumns])
|
|
48
|
+
|
|
49
|
+
const handleEyeClick = (columnField: string) => {
|
|
50
|
+
console.log('Eye icon clicked:', {
|
|
51
|
+
field: columnField,
|
|
52
|
+
currentVisibility: visibleColumns[columnField],
|
|
53
|
+
allVisibility: visibleColumns,
|
|
54
|
+
})
|
|
55
|
+
toggleColumnState(columnField)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const handleCloseAndUpdate = () => {
|
|
59
|
+
console.log('handleCloseAndUpdate called')
|
|
60
|
+
handleClose?.()
|
|
61
|
+
setSearchInput('')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
65
|
+
console.log('Search value changed:', e.target.value)
|
|
66
|
+
setSearchInput(e.target.value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const filteredColumns = columns.filter(column => {
|
|
70
|
+
const matches = formatColumnName(column.field)
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.includes(searchInput.toLowerCase())
|
|
73
|
+
console.log('Filtering column:', {
|
|
74
|
+
field: column.field,
|
|
75
|
+
searchInput,
|
|
76
|
+
matches,
|
|
77
|
+
})
|
|
78
|
+
return matches
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
console.log('Rendering ManageColumns with:', {
|
|
82
|
+
filteredColumns,
|
|
83
|
+
searchInput,
|
|
84
|
+
visibleColumns,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
88
|
+
console.log('Checkbox clicked in ManageColumns:', {
|
|
89
|
+
checked: event.target.checked,
|
|
90
|
+
indeterminate: event.target.indeterminate,
|
|
91
|
+
eventTarget: event.target,
|
|
92
|
+
})
|
|
93
|
+
event.stopPropagation()
|
|
94
|
+
handleAllCols(event.target.checked)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Popover
|
|
99
|
+
id="manage-columns-popover"
|
|
100
|
+
open={Boolean(open)}
|
|
101
|
+
onClose={handleCloseAndUpdate}
|
|
102
|
+
anchorOrigin={{
|
|
103
|
+
vertical: 'center',
|
|
104
|
+
horizontal: 'center',
|
|
105
|
+
}}
|
|
106
|
+
transformOrigin={{
|
|
107
|
+
vertical: 'center',
|
|
108
|
+
horizontal: 'center',
|
|
109
|
+
}}
|
|
110
|
+
sx={{
|
|
111
|
+
'& .MuiPaper-root': {
|
|
112
|
+
border: `1px solid ${palette.black.main}`,
|
|
113
|
+
borderRadius: 2,
|
|
114
|
+
minWidth: '250px',
|
|
115
|
+
boxShadow: 24,
|
|
116
|
+
},
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
<Box
|
|
120
|
+
sx={{
|
|
121
|
+
p: 2,
|
|
122
|
+
bgcolor: palette.white.main,
|
|
123
|
+
display: 'flex',
|
|
124
|
+
flexDirection: 'column',
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<Typography
|
|
128
|
+
text="Manage Columns"
|
|
129
|
+
fontvariant="merriparagraph"
|
|
130
|
+
fontcolor={palette.black.main}
|
|
131
|
+
align="center"
|
|
132
|
+
sx={{ mb: 0 }}
|
|
133
|
+
/>
|
|
134
|
+
<Box sx={{ mt: 1, mb: 0 }}>
|
|
135
|
+
<Searchbar
|
|
136
|
+
value={searchInput}
|
|
137
|
+
onChange={handleSearchChange}
|
|
138
|
+
placeholder="Search Columns"
|
|
139
|
+
iconcolor={palette.black.main}
|
|
140
|
+
outlinecolor={palette.black.main}
|
|
141
|
+
/>
|
|
142
|
+
</Box>
|
|
143
|
+
<Box
|
|
144
|
+
sx={{
|
|
145
|
+
display: 'flex',
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
mt: 0,
|
|
148
|
+
mb: 0,
|
|
149
|
+
justifyContent: 'space-between',
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
<Typography
|
|
153
|
+
text="All Columns"
|
|
154
|
+
fontvariant="merriparagraph"
|
|
155
|
+
fontcolor={palette.black.main}
|
|
156
|
+
sx={{ fontWeight: 'bold' }}
|
|
157
|
+
/>
|
|
158
|
+
<Box sx={{ marginRight: '-4px' }}>
|
|
159
|
+
<Checkbox
|
|
160
|
+
checked={isAllChecked}
|
|
161
|
+
indeterminate={someColumnsVisible && !isAllChecked}
|
|
162
|
+
onChange={handleCheckboxChange}
|
|
163
|
+
/>
|
|
164
|
+
</Box>
|
|
165
|
+
</Box>
|
|
166
|
+
<Box
|
|
167
|
+
sx={{ maxHeight: '160px', overflowY: 'auto', marginBottom: '10px' }}
|
|
168
|
+
>
|
|
169
|
+
{filteredColumns.map((column, index) => {
|
|
170
|
+
const isVisible = visibleColumns[column.field] === true
|
|
171
|
+
console.log('Rendering column row:', {
|
|
172
|
+
field: column.field,
|
|
173
|
+
visible: isVisible,
|
|
174
|
+
})
|
|
175
|
+
return (
|
|
176
|
+
<Box
|
|
177
|
+
key={index}
|
|
178
|
+
sx={{ display: 'flex', alignItems: 'center', mb: 1 }}
|
|
179
|
+
>
|
|
180
|
+
<Typography
|
|
181
|
+
text={formatColumnName(column.field)}
|
|
182
|
+
fontvariant="merriparagraph"
|
|
183
|
+
fontcolor={palette.black.main}
|
|
184
|
+
sx={{ flexGrow: 1, mr: 1 }}
|
|
185
|
+
/>
|
|
186
|
+
<IconButton
|
|
187
|
+
onClick={() => handleEyeClick(column.field)}
|
|
188
|
+
size="small"
|
|
189
|
+
>
|
|
190
|
+
<ShowHideEyeIcon visible={isVisible} />
|
|
191
|
+
</IconButton>
|
|
192
|
+
</Box>
|
|
193
|
+
)
|
|
194
|
+
})}
|
|
195
|
+
</Box>
|
|
196
|
+
<CustomButton
|
|
197
|
+
text="Save"
|
|
198
|
+
backgroundcolor={palette.black.main}
|
|
199
|
+
variant="contained"
|
|
200
|
+
fontcolor={palette.white.main}
|
|
201
|
+
fontvariant="merriparagraph"
|
|
202
|
+
sx={{ mt: 0 }}
|
|
203
|
+
fullWidth
|
|
204
|
+
onClick={onSaveColumnView}
|
|
205
|
+
/>
|
|
206
|
+
</Box>
|
|
207
|
+
</Popover>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export default ManageColumns
|