goobs-frontend 0.8.20 → 0.8.22
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.8.
|
|
3
|
+
"version": "0.8.22",
|
|
4
4
|
"type": "module",
|
|
5
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.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,20 +41,20 @@
|
|
|
41
41
|
"zod-formik-adapter": "^1.3.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@next/eslint-plugin-next": "^15.1.
|
|
44
|
+
"@next/eslint-plugin-next": "^15.1.4",
|
|
45
45
|
"@types/node": "^22.10.5",
|
|
46
|
-
"@types/react": "19.0.
|
|
47
|
-
"@types/react-dom": "^19.0.
|
|
46
|
+
"@types/react": "19.0.5",
|
|
47
|
+
"@types/react-dom": "^19.0.3",
|
|
48
48
|
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
49
49
|
"@typescript-eslint/parser": "^8.19.1",
|
|
50
|
-
"eslint": "^9.
|
|
50
|
+
"eslint": "^9.18.0",
|
|
51
51
|
"eslint-config-next": "^15.1.4",
|
|
52
52
|
"eslint-config-prettier": "^9.1.0",
|
|
53
53
|
"eslint-plugin-prettier": "^5.2.1",
|
|
54
54
|
"prettier": "^3.4.2",
|
|
55
55
|
"react": "^19.0.0",
|
|
56
56
|
"react-dom": "^19.0.0",
|
|
57
|
-
"typescript": "^5.7.
|
|
57
|
+
"typescript": "^5.7.3"
|
|
58
58
|
},
|
|
59
59
|
"files": [
|
|
60
60
|
"src"
|
|
@@ -15,6 +15,8 @@ interface ManageRowProps {
|
|
|
15
15
|
handleClose?: () => void
|
|
16
16
|
selectedRows?: string[]
|
|
17
17
|
rows?: Array<{ [key: string]: unknown }>
|
|
18
|
+
// Update these to accept an array of strings if you want
|
|
19
|
+
// them to receive the selected row IDs directly:
|
|
18
20
|
onDuplicate?: () => void
|
|
19
21
|
onDelete?: () => void
|
|
20
22
|
onManage?: () => void
|
|
@@ -37,11 +39,11 @@ function ManageRow({
|
|
|
37
39
|
const handleActionSelection = (type: ModalType) => {
|
|
38
40
|
switch (type) {
|
|
39
41
|
case 'duplicate':
|
|
40
|
-
onDuplicate?.()
|
|
42
|
+
onDuplicate?.() // We've already passed selectedRows from DataGrid
|
|
41
43
|
handleClose()
|
|
42
44
|
break
|
|
43
45
|
case 'delete':
|
|
44
|
-
onDelete?.()
|
|
46
|
+
onDelete?.() // same here
|
|
45
47
|
handleClose()
|
|
46
48
|
break
|
|
47
49
|
case 'export':
|
|
@@ -20,20 +20,25 @@ function DataGrid({
|
|
|
20
20
|
dropdowns,
|
|
21
21
|
searchbarProps,
|
|
22
22
|
error = null,
|
|
23
|
-
onDuplicate,
|
|
24
|
-
onDelete,
|
|
23
|
+
onDuplicate, // note type: (selectedIds: string[]) => void
|
|
24
|
+
onDelete, // note type: (selectedIds: string[]) => void
|
|
25
25
|
onManage,
|
|
26
26
|
onShow,
|
|
27
|
+
onSelectionChange, // (selectedIds: string[]) => void
|
|
27
28
|
}: DatagridProps) {
|
|
29
|
+
// Local state
|
|
28
30
|
const [rows, setRows] = useState<RowData[]>(providedRows || [])
|
|
29
31
|
const [selectedRows, setSelectedRows] = useState<string[]>([])
|
|
30
32
|
const [page, setPage] = useState(0)
|
|
31
33
|
const [pageSize, setPageSize] = useState(10)
|
|
32
34
|
|
|
35
|
+
// Initialize columns/rows if needed
|
|
33
36
|
useInitializeGrid({ columns, providedRows, setRows })
|
|
34
37
|
|
|
38
|
+
// 1) When row selection changes
|
|
35
39
|
const handleSelectionChange = (newSelectedIds: string[]) => {
|
|
36
40
|
setSelectedRows(newSelectedIds)
|
|
41
|
+
onSelectionChange?.(newSelectedIds)
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
const handleRowClick = (row: RowData) => {
|
|
@@ -54,22 +59,28 @@ function DataGrid({
|
|
|
54
59
|
selectAllRows(rows, selectedRows, handleSelectionChange)
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
// 2) Search logic
|
|
57
63
|
const { filteredRows, updatedSearchbarProps } = useSearchbar({
|
|
58
64
|
columns,
|
|
59
65
|
rows,
|
|
60
66
|
searchbarProps,
|
|
61
67
|
})
|
|
62
68
|
|
|
69
|
+
// 3) Manage row logic
|
|
63
70
|
const { handleManageRowClose, handleManage } = useManageRow({
|
|
64
71
|
onManage,
|
|
65
72
|
selectedRows,
|
|
66
73
|
handleSelectionChange,
|
|
67
74
|
})
|
|
68
75
|
|
|
76
|
+
// 4) Pagination
|
|
69
77
|
const startIndex = page * pageSize
|
|
70
78
|
const visibleRows = filteredRows.slice(startIndex, startIndex + pageSize)
|
|
71
79
|
|
|
72
|
-
|
|
80
|
+
// Determine if "all rows" are currently selected
|
|
81
|
+
const allRowsSelected =
|
|
82
|
+
rows.length > 0 &&
|
|
83
|
+
rows.every(r => selectedRows.includes(String(r._id ?? r.id)))
|
|
73
84
|
const someRowsSelected =
|
|
74
85
|
rows.length > 0 &&
|
|
75
86
|
selectedRows.length > 0 &&
|
|
@@ -102,8 +113,16 @@ function DataGrid({
|
|
|
102
113
|
<ManageRow
|
|
103
114
|
selectedRows={selectedRows}
|
|
104
115
|
rows={rows}
|
|
105
|
-
onDuplicate={
|
|
106
|
-
|
|
116
|
+
onDuplicate={
|
|
117
|
+
onDuplicate
|
|
118
|
+
? () => onDuplicate(selectedRows) // pass the selected IDs
|
|
119
|
+
: undefined
|
|
120
|
+
}
|
|
121
|
+
onDelete={
|
|
122
|
+
onDelete
|
|
123
|
+
? () => onDelete(selectedRows) // pass the selected IDs
|
|
124
|
+
: undefined
|
|
125
|
+
}
|
|
107
126
|
onManage={handleManage}
|
|
108
127
|
onShow={onShow}
|
|
109
128
|
handleClose={handleManageRowClose}
|
|
@@ -44,12 +44,18 @@ export interface DatagridProps {
|
|
|
44
44
|
columns: ColumnDef[]
|
|
45
45
|
rows: RowData[]
|
|
46
46
|
buttons?: CustomButtonProps[]
|
|
47
|
-
dropdowns?:
|
|
48
|
-
searchbarProps?:
|
|
47
|
+
dropdowns?: DropdownProps[]
|
|
48
|
+
searchbarProps?: SearchbarProps
|
|
49
49
|
error?: Error | null
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
// Single or multi selection callbacks:
|
|
52
52
|
onManage?: () => void
|
|
53
53
|
onShow?: () => void
|
|
54
|
+
|
|
55
|
+
// This is critical: must accept selectedIds as an argument
|
|
56
|
+
onDuplicate?: (selectedIds: string[]) => void
|
|
57
|
+
onDelete?: (selectedIds: string[]) => void
|
|
58
|
+
|
|
59
|
+
// For capturing selection changes
|
|
54
60
|
onSelectionChange?: (selectedIds: string[]) => void
|
|
55
61
|
}
|
|
@@ -5,7 +5,11 @@ import { styled } from '@mui/material/styles'
|
|
|
5
5
|
|
|
6
6
|
export interface NumberFieldProps extends Omit<TextFieldProps, 'onChange'> {
|
|
7
7
|
initialValue?: string
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Now accepts a standard ChangeEvent<HTMLInputElement>
|
|
10
|
+
* so parent can do e.g. (event) => parseInt(event.target.value) ...
|
|
11
|
+
*/
|
|
12
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
9
13
|
backgroundcolor?: string
|
|
10
14
|
outlinecolor?: string
|
|
11
15
|
fontcolor?: string
|
|
@@ -58,20 +62,24 @@ const NumberField: React.FC<NumberFieldProps> = ({
|
|
|
58
62
|
const handleChange = useCallback(
|
|
59
63
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
60
64
|
const newValue = event.target.value.replace(/[^0-9]/g, '')
|
|
65
|
+
|
|
61
66
|
if (newValue === '') {
|
|
62
67
|
setValue('')
|
|
63
|
-
onChange?.()
|
|
68
|
+
onChange?.(event) // Pass empty string up
|
|
64
69
|
return
|
|
65
70
|
}
|
|
71
|
+
|
|
66
72
|
const numValue = parseInt(newValue, 10)
|
|
67
73
|
if (min !== undefined && numValue < min) {
|
|
68
|
-
setValue(min
|
|
74
|
+
setValue(String(min))
|
|
69
75
|
} else if (max !== undefined && numValue > max) {
|
|
70
|
-
setValue(max
|
|
76
|
+
setValue(String(max))
|
|
71
77
|
} else {
|
|
72
78
|
setValue(newValue)
|
|
73
79
|
}
|
|
74
|
-
|
|
80
|
+
|
|
81
|
+
// Call parent's onChange so it knows about the new event/value
|
|
82
|
+
onChange?.(event)
|
|
75
83
|
},
|
|
76
84
|
[onChange, min, max]
|
|
77
85
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src
|
|
1
|
+
// src/components/TransferList/index.tsx
|
|
2
2
|
'use client'
|
|
3
3
|
|
|
4
4
|
import React, { useEffect, useState } from 'react'
|
|
@@ -12,14 +12,12 @@ import Checkbox from '@mui/material/Checkbox'
|
|
|
12
12
|
import Button from '@mui/material/Button'
|
|
13
13
|
import Paper from '@mui/material/Paper'
|
|
14
14
|
import { Box, Typography } from '@mui/material'
|
|
15
|
-
|
|
16
15
|
import Dropdown, { DropdownOption } from '../Dropdown'
|
|
17
16
|
|
|
18
17
|
/** Utility functions for array handling */
|
|
19
18
|
function not(a: readonly string[], b: readonly string[]) {
|
|
20
19
|
return a.filter(value => b.indexOf(value) === -1)
|
|
21
20
|
}
|
|
22
|
-
|
|
23
21
|
function intersection(a: readonly string[], b: readonly string[]) {
|
|
24
22
|
return a.filter(value => b.indexOf(value) !== -1)
|
|
25
23
|
}
|
|
@@ -59,6 +57,14 @@ export interface TransferListProps {
|
|
|
59
57
|
dropdownOptions?: DropdownOption[]
|
|
60
58
|
dropdownDataMap?: TransferListDropdownDataMap
|
|
61
59
|
|
|
60
|
+
/**
|
|
61
|
+
* A map from item-value to label.
|
|
62
|
+
* e.g. { "HIGH": "High Priority", "LOW": "Low Priority" }.
|
|
63
|
+
* If provided, we'll display `itemLabelMap[value]` in the list
|
|
64
|
+
* rather than the raw `value`.
|
|
65
|
+
*/
|
|
66
|
+
itemLabelMap?: Record<string, string>
|
|
67
|
+
|
|
62
68
|
/**
|
|
63
69
|
* Fired whenever left/right arrays change (user clicks the arrows).
|
|
64
70
|
* @param leftItems Updated array for the "left" column
|
|
@@ -92,8 +98,9 @@ const TransferList: React.FC<TransferListProps> = ({
|
|
|
92
98
|
dropdownLabel,
|
|
93
99
|
dropdownOptions = [],
|
|
94
100
|
dropdownDataMap = {},
|
|
95
|
-
|
|
101
|
+
itemLabelMap,
|
|
96
102
|
|
|
103
|
+
onChange,
|
|
97
104
|
leftTitle = 'Unassigned',
|
|
98
105
|
rightTitle = 'Assigned',
|
|
99
106
|
}) => {
|
|
@@ -161,7 +168,6 @@ const TransferList: React.FC<TransferListProps> = ({
|
|
|
161
168
|
const handleToggle = (value: string) => () => {
|
|
162
169
|
const currentIndex = checked.indexOf(value)
|
|
163
170
|
const newChecked = [...checked]
|
|
164
|
-
|
|
165
171
|
if (currentIndex === -1) {
|
|
166
172
|
newChecked.push(value)
|
|
167
173
|
} else {
|
|
@@ -234,6 +240,10 @@ const TransferList: React.FC<TransferListProps> = ({
|
|
|
234
240
|
<List dense component="div" role="list">
|
|
235
241
|
{items.map(value => {
|
|
236
242
|
const labelId = `transfer-list-item-${value}-label`
|
|
243
|
+
const isChecked = checked.indexOf(value) !== -1
|
|
244
|
+
const displayedLabel =
|
|
245
|
+
itemLabelMap && itemLabelMap[value] ? itemLabelMap[value] : value
|
|
246
|
+
|
|
237
247
|
return (
|
|
238
248
|
<ListItemButton
|
|
239
249
|
key={value}
|
|
@@ -242,15 +252,13 @@ const TransferList: React.FC<TransferListProps> = ({
|
|
|
242
252
|
>
|
|
243
253
|
<ListItemIcon>
|
|
244
254
|
<Checkbox
|
|
245
|
-
checked={
|
|
255
|
+
checked={isChecked}
|
|
246
256
|
tabIndex={-1}
|
|
247
257
|
disableRipple
|
|
248
|
-
inputProps={{
|
|
249
|
-
'aria-labelledby': labelId,
|
|
250
|
-
}}
|
|
258
|
+
inputProps={{ 'aria-labelledby': labelId }}
|
|
251
259
|
/>
|
|
252
260
|
</ListItemIcon>
|
|
253
|
-
<ListItemText id={labelId} primary={
|
|
261
|
+
<ListItemText id={labelId} primary={displayedLabel} />
|
|
254
262
|
</ListItemButton>
|
|
255
263
|
)
|
|
256
264
|
})}
|
|
@@ -275,7 +283,7 @@ const TransferList: React.FC<TransferListProps> = ({
|
|
|
275
283
|
)
|
|
276
284
|
}
|
|
277
285
|
|
|
278
|
-
// multipleSelection => show the dropdown above
|
|
286
|
+
// multipleSelection => show the dropdown above the list
|
|
279
287
|
return (
|
|
280
288
|
<Box sx={{ display: 'flex', flexDirection: 'column', width: '100%' }}>
|
|
281
289
|
<Dropdown
|
|
@@ -295,14 +303,10 @@ const TransferList: React.FC<TransferListProps> = ({
|
|
|
295
303
|
|
|
296
304
|
return (
|
|
297
305
|
<CustomGrid
|
|
298
|
-
container
|
|
306
|
+
container
|
|
299
307
|
spacing={2}
|
|
300
308
|
alignItems="flex-start"
|
|
301
309
|
columnconfig={[
|
|
302
|
-
// We'll define a single "row" with 3 "columns": left, middle, right
|
|
303
|
-
// row=1, column=1 => left
|
|
304
|
-
// row=1, column=2 => middle
|
|
305
|
-
// row=1, column=3 => right
|
|
306
310
|
{
|
|
307
311
|
row: 1,
|
|
308
312
|
column: 1,
|