goobs-frontend 0.8.6 → 0.8.8
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 +18 -18
- package/src/components/ConfirmationCodeInput/index.tsx +120 -46
- package/src/components/Content/Structure/phoneNumber/usePhoneNumber.tsx +3 -4
- package/src/components/Content/Structure/textfield/useTextField.tsx +5 -29
- package/src/components/DataGrid/Checkbox/index.tsx +79 -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 +182 -0
- package/src/components/DataGrid/Table/index.tsx +227 -0
- package/src/components/DataGrid/VerticalDivider/index.tsx +6 -0
- package/src/components/DataGrid/index.tsx +267 -0
- package/src/components/DataGrid/utils/useManageColumn.tsx +138 -0
- package/src/components/DataGrid/utils/useSearchbar.tsx +122 -0
- package/src/components/Form/DataGrid/index.tsx +63 -0
- package/src/components/PhoneNumberField/index.tsx +91 -67
- package/src/components/Searchbar/index.tsx +98 -42
- package/src/components/TextField/index.tsx +21 -33
- package/src/components/Toolbar/index.tsx +16 -18
- package/src/index.ts +16 -3
- package/src/components/ConfirmationCodeInput/utils/useCodeConfirmation.tsx +0 -150
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useState, useEffect } from 'react'
|
|
3
|
+
import { Box, Alert, CircularProgress } from '@mui/material'
|
|
4
|
+
import { useAtom, useSetAtom } from 'jotai'
|
|
5
|
+
import {
|
|
6
|
+
columnVisibilityAtom,
|
|
7
|
+
columnsAtom,
|
|
8
|
+
columnVisibilityActions,
|
|
9
|
+
} from './Jotai/atom'
|
|
10
|
+
import CustomToolbar from '../Toolbar'
|
|
11
|
+
import Table, { ColumnDef, RowData } from './Table'
|
|
12
|
+
import CustomFooter from './Footer'
|
|
13
|
+
import ManageRow from './ManageRow'
|
|
14
|
+
import type { CustomButtonProps } from '../Button'
|
|
15
|
+
import type { DropdownProps } from '../Dropdown'
|
|
16
|
+
import type { SearchbarProps } from '../Searchbar'
|
|
17
|
+
import { woad } from '../../styles/palette'
|
|
18
|
+
import { useSearchbar } from './utils/useSearchbar'
|
|
19
|
+
|
|
20
|
+
export interface DatagridProps {
|
|
21
|
+
columns: ColumnDef[]
|
|
22
|
+
rows: RowData[]
|
|
23
|
+
buttons?: CustomButtonProps[]
|
|
24
|
+
dropdowns?: Omit<DropdownProps, 'onChange'>[]
|
|
25
|
+
searchbarProps?: Omit<SearchbarProps, 'onChange' | 'value'>
|
|
26
|
+
onRefresh?: () => void
|
|
27
|
+
loading?: boolean
|
|
28
|
+
error?: Error | null
|
|
29
|
+
onDuplicate?: () => void
|
|
30
|
+
onDelete?: () => void
|
|
31
|
+
checkboxSelection?: boolean
|
|
32
|
+
selectedRows?: string[]
|
|
33
|
+
onSelectionChange?: (selectedIds: string[]) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function DataGrid({
|
|
37
|
+
columns,
|
|
38
|
+
rows: providedRows,
|
|
39
|
+
buttons,
|
|
40
|
+
dropdowns,
|
|
41
|
+
searchbarProps,
|
|
42
|
+
loading = false,
|
|
43
|
+
error = null,
|
|
44
|
+
onDuplicate,
|
|
45
|
+
onDelete,
|
|
46
|
+
}: DatagridProps) {
|
|
47
|
+
console.log('DataGrid render:', { columns, providedRows })
|
|
48
|
+
|
|
49
|
+
const [rows, setRows] = useState<RowData[]>(providedRows || [])
|
|
50
|
+
const [page, setPage] = useState(0)
|
|
51
|
+
const [pageSize, setPageSize] = useState(10)
|
|
52
|
+
const [selectedRows, setSelectedRows] = useState<string[]>([])
|
|
53
|
+
const [manageRowOpen, setManageRowOpen] = useState(false)
|
|
54
|
+
const [searchValue, setSearchValue] = useState('')
|
|
55
|
+
const tableRef = React.useRef(null)
|
|
56
|
+
const initialized = React.useRef(false)
|
|
57
|
+
|
|
58
|
+
// Jotai state setup
|
|
59
|
+
const setColumns = useSetAtom(columnsAtom)
|
|
60
|
+
const [columnVisibility] = useAtom(columnVisibilityAtom)
|
|
61
|
+
const updateVisibility = useSetAtom(columnVisibilityActions)
|
|
62
|
+
|
|
63
|
+
const {
|
|
64
|
+
handleSearchChange,
|
|
65
|
+
filteredRows,
|
|
66
|
+
visibleColumns: searchVisibleColumns,
|
|
67
|
+
tags,
|
|
68
|
+
} = useSearchbar({
|
|
69
|
+
columns,
|
|
70
|
+
rows,
|
|
71
|
+
searchValue,
|
|
72
|
+
setSearchValue,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Update Jotai visibility state when search changes columns visibility
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (tags.length > 0) {
|
|
78
|
+
const newVisibility: { [key: string]: boolean } = {}
|
|
79
|
+
columns.forEach(column => {
|
|
80
|
+
newVisibility[column.field] = searchVisibleColumns.has(column.field)
|
|
81
|
+
})
|
|
82
|
+
console.log('Updating column visibility from search:', newVisibility)
|
|
83
|
+
updateVisibility({
|
|
84
|
+
type: 'save',
|
|
85
|
+
newState: newVisibility,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}, [searchVisibleColumns, columns, updateVisibility, tags])
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
setRows(providedRows || [])
|
|
92
|
+
}, [providedRows])
|
|
93
|
+
|
|
94
|
+
// Initialize columns and visibility in Jotai
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!initialized.current) {
|
|
97
|
+
console.log('Initializing columns and visibility:', columns)
|
|
98
|
+
setColumns(columns.map(col => col.field))
|
|
99
|
+
|
|
100
|
+
// Initialize visibility for new columns
|
|
101
|
+
const initialVisibility: { [key: string]: boolean } = {}
|
|
102
|
+
columns.forEach(column => {
|
|
103
|
+
if (columnVisibility[column.field] === undefined) {
|
|
104
|
+
initialVisibility[column.field] = true
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
if (Object.keys(initialVisibility).length > 0) {
|
|
109
|
+
console.log(
|
|
110
|
+
'Setting initial visibility for columns:',
|
|
111
|
+
initialVisibility
|
|
112
|
+
)
|
|
113
|
+
updateVisibility({
|
|
114
|
+
type: 'save',
|
|
115
|
+
newState: { ...columnVisibility, ...initialVisibility },
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
initialized.current = true
|
|
120
|
+
}
|
|
121
|
+
}, [columns, setColumns, columnVisibility, updateVisibility])
|
|
122
|
+
|
|
123
|
+
const handleRowClick = (row: RowData) => {
|
|
124
|
+
console.log('Row clicked:', row)
|
|
125
|
+
const newSelection = selectedRows.includes(row.id)
|
|
126
|
+
? selectedRows.filter(id => id !== row.id)
|
|
127
|
+
: [...selectedRows, row.id]
|
|
128
|
+
|
|
129
|
+
setSelectedRows(newSelection)
|
|
130
|
+
setManageRowOpen(newSelection.length > 0)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const handleSelectionChange = (selectedIds: string[]) => {
|
|
134
|
+
console.log('Selection changed:', selectedIds)
|
|
135
|
+
setSelectedRows(selectedIds)
|
|
136
|
+
setManageRowOpen(selectedIds.length > 0)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const updatedSearchbarProps = {
|
|
140
|
+
...searchbarProps,
|
|
141
|
+
value: searchValue,
|
|
142
|
+
onChange: handleSearchChange,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get visible columns based on both Jotai state and search
|
|
146
|
+
const visibleColumns = columns.filter(column => {
|
|
147
|
+
const isVisibleInJotai = columnVisibility[column.field] !== false
|
|
148
|
+
const isVisibleInSearch =
|
|
149
|
+
tags.length === 0 || searchVisibleColumns.has(column.field)
|
|
150
|
+
const isVisible = isVisibleInJotai && isVisibleInSearch
|
|
151
|
+
|
|
152
|
+
console.log(`Column ${column.field} visibility:`, {
|
|
153
|
+
jotai: isVisibleInJotai,
|
|
154
|
+
search: isVisibleInSearch,
|
|
155
|
+
final: isVisible,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
return isVisible
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Calculate visible rows based on current page and pageSize
|
|
162
|
+
const startIndex = page * pageSize
|
|
163
|
+
const visibleRows = filteredRows.slice(startIndex, startIndex + pageSize)
|
|
164
|
+
|
|
165
|
+
console.log('DataGrid rendering with:', {
|
|
166
|
+
visibleColumns,
|
|
167
|
+
visibleRows,
|
|
168
|
+
page,
|
|
169
|
+
pageSize,
|
|
170
|
+
selectedRows,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
if (loading) {
|
|
174
|
+
return (
|
|
175
|
+
<Box
|
|
176
|
+
sx={{
|
|
177
|
+
position: 'relative',
|
|
178
|
+
marginTop: '60px',
|
|
179
|
+
display: 'flex',
|
|
180
|
+
justifyContent: 'center',
|
|
181
|
+
alignItems: 'center',
|
|
182
|
+
height: 'calc(100vh - 60px)',
|
|
183
|
+
width: 'calc(100vh)',
|
|
184
|
+
backgroundColor: woad.main,
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<CircularProgress />
|
|
188
|
+
</Box>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<>
|
|
194
|
+
<Box
|
|
195
|
+
sx={{
|
|
196
|
+
position: 'relative',
|
|
197
|
+
marginLeft: '250px',
|
|
198
|
+
marginTop: '60px',
|
|
199
|
+
display: 'flex',
|
|
200
|
+
flexDirection: 'column',
|
|
201
|
+
height: 'calc(100vh - 60px)',
|
|
202
|
+
width: 'calc(100%)',
|
|
203
|
+
overflow: 'hidden',
|
|
204
|
+
p: 0,
|
|
205
|
+
m: 0,
|
|
206
|
+
backgroundColor: woad.main,
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
{error && (
|
|
210
|
+
<Alert severity="error" sx={{ mb: 2 }}>
|
|
211
|
+
{error.message}
|
|
212
|
+
</Alert>
|
|
213
|
+
)}
|
|
214
|
+
|
|
215
|
+
<CustomToolbar
|
|
216
|
+
buttons={buttons}
|
|
217
|
+
dropdowns={dropdowns}
|
|
218
|
+
searchbarProps={updatedSearchbarProps}
|
|
219
|
+
/>
|
|
220
|
+
|
|
221
|
+
<Box
|
|
222
|
+
sx={{
|
|
223
|
+
flexGrow: 1,
|
|
224
|
+
overflow: 'auto',
|
|
225
|
+
width: '100%',
|
|
226
|
+
display: 'flex',
|
|
227
|
+
flexDirection: 'column',
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
<Table
|
|
231
|
+
ref={tableRef}
|
|
232
|
+
columns={visibleColumns}
|
|
233
|
+
rows={visibleRows}
|
|
234
|
+
loading={loading}
|
|
235
|
+
page={page}
|
|
236
|
+
pageSize={pageSize}
|
|
237
|
+
rowCount={filteredRows.length}
|
|
238
|
+
onPageChange={setPage}
|
|
239
|
+
onPageSizeChange={setPageSize}
|
|
240
|
+
onRowClick={handleRowClick}
|
|
241
|
+
selectedRows={selectedRows}
|
|
242
|
+
onSelectionChange={handleSelectionChange}
|
|
243
|
+
checkboxSelection
|
|
244
|
+
/>
|
|
245
|
+
|
|
246
|
+
<CustomFooter
|
|
247
|
+
page={page}
|
|
248
|
+
pageSize={pageSize}
|
|
249
|
+
rowCount={filteredRows.length}
|
|
250
|
+
onPageChange={setPage}
|
|
251
|
+
onPageSizeChange={setPageSize}
|
|
252
|
+
columns={columns}
|
|
253
|
+
/>
|
|
254
|
+
</Box>
|
|
255
|
+
</Box>
|
|
256
|
+
|
|
257
|
+
<ManageRow
|
|
258
|
+
open={manageRowOpen}
|
|
259
|
+
handleClose={() => setManageRowOpen(false)}
|
|
260
|
+
onDuplicate={onDuplicate}
|
|
261
|
+
onDelete={onDelete}
|
|
262
|
+
/>
|
|
263
|
+
</>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export default DataGrid
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import { useAtom, useSetAtom } from 'jotai'
|
|
3
|
+
import { columnVisibilityAtom, columnVisibilityActions } from '../Jotai/atom'
|
|
4
|
+
import type { ColumnDef } from '../Table'
|
|
5
|
+
|
|
6
|
+
type ColumnVisibilityModel = { [key: string]: boolean }
|
|
7
|
+
|
|
8
|
+
interface UseManageColumnProps {
|
|
9
|
+
columns: ColumnDef[]
|
|
10
|
+
handleClose: () => void
|
|
11
|
+
isPopupOpen: boolean
|
|
12
|
+
initialSearchInput?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const useManageColumn = ({
|
|
16
|
+
columns,
|
|
17
|
+
handleClose,
|
|
18
|
+
isPopupOpen,
|
|
19
|
+
initialSearchInput = '',
|
|
20
|
+
}: UseManageColumnProps) => {
|
|
21
|
+
const [tempVisibleColumns, setTempVisibleColumns] =
|
|
22
|
+
useState<ColumnVisibilityModel>({})
|
|
23
|
+
const [columnVisibility] = useAtom(columnVisibilityAtom)
|
|
24
|
+
const updateVisibility = useSetAtom(columnVisibilityActions)
|
|
25
|
+
const [searchInput, setSearchInput] = useState(initialSearchInput)
|
|
26
|
+
const [isAllChecked, setIsAllChecked] = useState(true)
|
|
27
|
+
const initialized = useRef(false)
|
|
28
|
+
|
|
29
|
+
console.log('useManageColumn hook render:', {
|
|
30
|
+
isPopupOpen,
|
|
31
|
+
columnVisibility,
|
|
32
|
+
tempVisibleColumns,
|
|
33
|
+
searchInput,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (isPopupOpen) {
|
|
38
|
+
const currentVisibility: ColumnVisibilityModel = {}
|
|
39
|
+
columns.forEach(column => {
|
|
40
|
+
currentVisibility[column.field] = columnVisibility[column.field] ?? true
|
|
41
|
+
})
|
|
42
|
+
console.log('Initializing tempVisibleColumns:', currentVisibility)
|
|
43
|
+
setTempVisibleColumns(currentVisibility)
|
|
44
|
+
setIsAllChecked(
|
|
45
|
+
columns.every(column => currentVisibility[column.field] === true)
|
|
46
|
+
)
|
|
47
|
+
initialized.current = true
|
|
48
|
+
}
|
|
49
|
+
}, [isPopupOpen, columns, columnVisibility])
|
|
50
|
+
|
|
51
|
+
const handleAllCols = useCallback(
|
|
52
|
+
(checked: boolean) => {
|
|
53
|
+
console.log('handleAllCols called with checked:', checked)
|
|
54
|
+
setIsAllChecked(checked)
|
|
55
|
+
|
|
56
|
+
setTempVisibleColumns(prev => {
|
|
57
|
+
const newVisibility: ColumnVisibilityModel = {}
|
|
58
|
+
columns.forEach(column => {
|
|
59
|
+
newVisibility[column.field] = checked
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
console.log('handleAllCols:', {
|
|
63
|
+
before: prev,
|
|
64
|
+
after: newVisibility,
|
|
65
|
+
checked,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return newVisibility
|
|
69
|
+
})
|
|
70
|
+
},
|
|
71
|
+
[columns]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const toggleColumnState = useCallback(
|
|
75
|
+
(field: string) => {
|
|
76
|
+
setTempVisibleColumns(prev => {
|
|
77
|
+
const newState = {
|
|
78
|
+
...prev,
|
|
79
|
+
[field]: !prev[field],
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Update isAllChecked based on new state
|
|
83
|
+
const areAllVisible = columns.every(column =>
|
|
84
|
+
field === column.field ? newState[field] : prev[column.field]
|
|
85
|
+
)
|
|
86
|
+
setIsAllChecked(areAllVisible)
|
|
87
|
+
|
|
88
|
+
console.log('toggleColumnState:', {
|
|
89
|
+
field,
|
|
90
|
+
before: prev[field],
|
|
91
|
+
after: newState[field],
|
|
92
|
+
allState: newState,
|
|
93
|
+
areAllVisible,
|
|
94
|
+
})
|
|
95
|
+
return newState
|
|
96
|
+
})
|
|
97
|
+
},
|
|
98
|
+
[columns]
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
const onSaveColumnView = useCallback(() => {
|
|
102
|
+
console.log('Saving column visibility state:', tempVisibleColumns)
|
|
103
|
+
updateVisibility({ type: 'save', newState: tempVisibleColumns })
|
|
104
|
+
handleClose()
|
|
105
|
+
}, [tempVisibleColumns, updateVisibility, handleClose])
|
|
106
|
+
|
|
107
|
+
const formatColumnName = useCallback((fieldName: string): string => {
|
|
108
|
+
const formatted = fieldName
|
|
109
|
+
.replace(/([A-Z])/g, ' $1')
|
|
110
|
+
.replace(/^./, str => str.toUpperCase())
|
|
111
|
+
.trim()
|
|
112
|
+
console.log('Formatting column name:', { fieldName, formatted })
|
|
113
|
+
return formatted
|
|
114
|
+
}, [])
|
|
115
|
+
|
|
116
|
+
const handlePageUnload = useCallback(() => {
|
|
117
|
+
console.log('Page unload - closing manage columns')
|
|
118
|
+
handleClose()
|
|
119
|
+
}, [handleClose])
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
window.addEventListener('beforeunload', handlePageUnload)
|
|
123
|
+
return () => {
|
|
124
|
+
window.removeEventListener('beforeunload', handlePageUnload)
|
|
125
|
+
}
|
|
126
|
+
}, [handlePageUnload])
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
handleAllCols,
|
|
130
|
+
toggleColumnState,
|
|
131
|
+
visibleColumns: tempVisibleColumns,
|
|
132
|
+
onSaveColumnView,
|
|
133
|
+
formatColumnName,
|
|
134
|
+
searchInput,
|
|
135
|
+
setSearchInput,
|
|
136
|
+
isAllChecked,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState, useEffect } from 'react'
|
|
2
|
+
import type { ColumnDef, RowData } from '../Table'
|
|
3
|
+
|
|
4
|
+
interface UseSearchbarProps {
|
|
5
|
+
columns: ColumnDef[]
|
|
6
|
+
rows: RowData[]
|
|
7
|
+
searchValue: string
|
|
8
|
+
setSearchValue: (value: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useSearchbar = ({
|
|
12
|
+
columns,
|
|
13
|
+
rows,
|
|
14
|
+
searchValue,
|
|
15
|
+
setSearchValue,
|
|
16
|
+
}: UseSearchbarProps) => {
|
|
17
|
+
const [tags, setTags] = useState<string[]>([])
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
console.log('Search value changed:', searchValue)
|
|
21
|
+
setTags(searchValue.trim() ? searchValue.toLowerCase().split(' ') : [])
|
|
22
|
+
}, [searchValue])
|
|
23
|
+
|
|
24
|
+
const handleSearchChange = useCallback(
|
|
25
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
26
|
+
const newValue = event.target.value
|
|
27
|
+
console.log('Search input changed:', {
|
|
28
|
+
oldValue: searchValue,
|
|
29
|
+
newValue,
|
|
30
|
+
})
|
|
31
|
+
setSearchValue(newValue)
|
|
32
|
+
},
|
|
33
|
+
[searchValue, setSearchValue]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const filteredRows = useMemo(() => {
|
|
37
|
+
if (!searchValue.trim()) {
|
|
38
|
+
return rows
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const searchTerms = searchValue.toLowerCase().trim().split(' ')
|
|
42
|
+
|
|
43
|
+
return rows.filter(row => {
|
|
44
|
+
return searchTerms.some(term => {
|
|
45
|
+
return columns.some(column => {
|
|
46
|
+
// Check column header name
|
|
47
|
+
const headerMatch = column.headerName?.toLowerCase().includes(term)
|
|
48
|
+
|
|
49
|
+
// Check column field name
|
|
50
|
+
const fieldMatch = column.field.toLowerCase().includes(term)
|
|
51
|
+
|
|
52
|
+
// Check cell value
|
|
53
|
+
const cellValue = row[column.field]
|
|
54
|
+
const valueMatch =
|
|
55
|
+
cellValue != null && String(cellValue).toLowerCase().includes(term)
|
|
56
|
+
|
|
57
|
+
console.log('Searching:', {
|
|
58
|
+
field: column.field,
|
|
59
|
+
header: column.headerName,
|
|
60
|
+
value: cellValue,
|
|
61
|
+
searchTerm: term,
|
|
62
|
+
headerMatch,
|
|
63
|
+
fieldMatch,
|
|
64
|
+
valueMatch,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return headerMatch || fieldMatch || valueMatch
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}, [rows, searchValue, columns])
|
|
72
|
+
|
|
73
|
+
const visibleColumns = useMemo(() => {
|
|
74
|
+
if (!searchValue.trim()) {
|
|
75
|
+
return new Set(columns.map(col => col.field))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const searchTerms = searchValue.toLowerCase().trim().split(' ')
|
|
79
|
+
|
|
80
|
+
return new Set(
|
|
81
|
+
columns
|
|
82
|
+
.filter(col => {
|
|
83
|
+
return searchTerms.some(term => {
|
|
84
|
+
// Check column header name
|
|
85
|
+
const headerMatch = col.headerName?.toLowerCase().includes(term)
|
|
86
|
+
|
|
87
|
+
// Check column field name
|
|
88
|
+
const fieldMatch = col.field.toLowerCase().includes(term)
|
|
89
|
+
|
|
90
|
+
// Check if any row has matching data in this column
|
|
91
|
+
const hasMatchingData = filteredRows.some(row => {
|
|
92
|
+
const cellValue = row[col.field]
|
|
93
|
+
return (
|
|
94
|
+
cellValue != null &&
|
|
95
|
+
String(cellValue).toLowerCase().includes(term)
|
|
96
|
+
)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
console.log('Column visibility check:', {
|
|
100
|
+
field: col.field,
|
|
101
|
+
header: col.headerName,
|
|
102
|
+
searchTerm: term,
|
|
103
|
+
headerMatch,
|
|
104
|
+
fieldMatch,
|
|
105
|
+
hasMatchingData,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
return headerMatch || fieldMatch || hasMatchingData
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
.map(col => col.field)
|
|
112
|
+
)
|
|
113
|
+
}, [columns, searchValue, filteredRows])
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
searchValue,
|
|
117
|
+
handleSearchChange,
|
|
118
|
+
filteredRows,
|
|
119
|
+
visibleColumns,
|
|
120
|
+
tags,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Box } from '@mui/material'
|
|
4
|
+
import type { DatagridProps } from '../../DataGrid'
|
|
5
|
+
import DataGrid from '../../DataGrid'
|
|
6
|
+
|
|
7
|
+
export interface FormDataGridProps {
|
|
8
|
+
title: string
|
|
9
|
+
description: string
|
|
10
|
+
datagrid: DatagridProps
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function FormDataGrid({ title, description, datagrid }: FormDataGridProps) {
|
|
14
|
+
return (
|
|
15
|
+
<Box
|
|
16
|
+
sx={{
|
|
17
|
+
width: '100%',
|
|
18
|
+
height: 'auto',
|
|
19
|
+
overflow: 'hidden',
|
|
20
|
+
'& *': {
|
|
21
|
+
overflow: 'hidden !important',
|
|
22
|
+
},
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
<Box
|
|
26
|
+
sx={{
|
|
27
|
+
marginTop: 1,
|
|
28
|
+
marginBottom: 1,
|
|
29
|
+
width: '100%',
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<Box
|
|
33
|
+
sx={{
|
|
34
|
+
marginBottom: 0.5,
|
|
35
|
+
width: '100%',
|
|
36
|
+
textAlign: 'left',
|
|
37
|
+
fontFamily: 'Merriweather',
|
|
38
|
+
fontSize: '1.5rem',
|
|
39
|
+
fontWeight: 400,
|
|
40
|
+
color: 'black',
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{title}
|
|
44
|
+
</Box>
|
|
45
|
+
<Box
|
|
46
|
+
sx={{
|
|
47
|
+
width: '100%',
|
|
48
|
+
textAlign: 'left',
|
|
49
|
+
fontFamily: 'Merriweather',
|
|
50
|
+
fontSize: '1.25rem',
|
|
51
|
+
fontWeight: 400,
|
|
52
|
+
color: 'black',
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{description}
|
|
56
|
+
</Box>
|
|
57
|
+
</Box>
|
|
58
|
+
<DataGrid {...datagrid} />
|
|
59
|
+
</Box>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default FormDataGrid
|