goobs-frontend 0.8.17 → 0.8.19
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 +13 -10
- package/src/components/Button/index.tsx +120 -112
- package/src/components/Card/index.tsx +52 -1
- package/src/components/Card/variants/task/index.tsx +89 -0
- package/src/components/Checkbox/index.tsx +1 -1
- package/src/components/Content/Structure/projectboard/useProjectBoard.tsx +84 -0
- package/src/components/Content/index.tsx +5 -0
- package/src/components/DataGrid/Footer/index.tsx +41 -24
- package/src/components/DataGrid/ManageColumn/index.tsx +1 -1
- package/src/components/DataGrid/ManageRow/index.tsx +19 -14
- package/src/components/DataGrid/Table/ColumnHeaderRow/index.tsx +152 -0
- package/src/components/DataGrid/Table/Rows/index.tsx +216 -0
- package/src/components/DataGrid/Table/index.tsx +182 -235
- package/src/components/DataGrid/index.tsx +122 -230
- package/src/components/DataGrid/types/index.ts +163 -0
- package/src/components/DataGrid/utils/useComputeTableResize.tsx +194 -0
- package/src/components/DataGrid/utils/useInitializeGrid.tsx +71 -0
- package/src/components/DataGrid/utils/useManageColumn.tsx +1 -1
- package/src/components/DataGrid/utils/useManageRow.tsx +38 -0
- package/src/components/DataGrid/utils/useSelectColumn.tsx +37 -0
- package/src/components/DataGrid/utils/useSelectRows.tsx +47 -0
- package/src/components/DataGrid/utils/useToolbarSearchbar.tsx +152 -0
- package/src/components/Dropdown/index.tsx +44 -10
- package/src/components/Form/DataGrid/index.tsx +4 -1
- package/src/components/Form/ProjectBoard/index.tsx +142 -0
- package/src/components/Grid/index.tsx +89 -39
- package/src/components/Nav/VerticalVariant/mainNav/{index.tsx → expanding.tsx} +41 -55
- package/src/components/Nav/VerticalVariant/mainNav/list.tsx +56 -0
- package/src/components/Nav/VerticalVariant/subNav/{index.tsx → expanding.tsx} +31 -53
- package/src/components/Nav/VerticalVariant/subNav/list.tsx +59 -0
- package/src/components/Nav/VerticalVariant/viewNav/index.tsx +2 -2
- package/src/components/Nav/index.tsx +81 -47
- package/src/components/ProjectBoard/AddTask/client.tsx +316 -0
- package/src/components/ProjectBoard/Column/index.tsx +139 -0
- package/src/components/ProjectBoard/ManageTask/client.tsx +421 -0
- package/src/components/ProjectBoard/ShowTask/client.tsx +413 -0
- package/src/components/ProjectBoard/index.tsx +523 -0
- package/src/components/ProjectBoard/jotai/task.ts +0 -0
- package/src/components/ProjectBoard/utils/useDragandDropColumns.tsx +200 -0
- package/src/components/SearchableDropdown/index.tsx +156 -239
- package/src/components/Tabs/index.tsx +63 -111
- package/src/components/Toolbar/index.tsx +159 -68
- package/src/components/TransferList/index.tsx +283 -74
- package/src/index.ts +33 -8
- package/src/styles/palette.ts +1 -1
- package/src/components/DataGrid/utils/useSearchbar.tsx +0 -122
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.19",
|
|
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",
|
|
@@ -22,28 +22,31 @@
|
|
|
22
22
|
"@emotion/cache": "^11.14.0",
|
|
23
23
|
"@emotion/react": "^11.14.0",
|
|
24
24
|
"@emotion/styled": "^11.14.0",
|
|
25
|
-
"@mui/icons-material": "^6.3.
|
|
26
|
-
"@mui/material": "^6.3.
|
|
27
|
-
"@types/lodash": "^4.17.
|
|
25
|
+
"@mui/icons-material": "^6.3.1",
|
|
26
|
+
"@mui/material": "^6.3.1",
|
|
27
|
+
"@types/lodash": "^4.17.14",
|
|
28
|
+
"formik": "^2.4.6",
|
|
28
29
|
"highlight.js": "^11.11.1",
|
|
29
30
|
"jotai": "^2.11.0",
|
|
30
31
|
"lodash": "^4.17.21",
|
|
31
32
|
"next": "15.1.3",
|
|
32
33
|
"otplib": "^12.0.1",
|
|
33
|
-
"react-datepicker": "^7.
|
|
34
|
+
"react-datepicker": "^7.6.0",
|
|
34
35
|
"react-qr-code": "^2.0.15",
|
|
35
36
|
"slate": "^0.112.0",
|
|
36
37
|
"slate-dom": "^0.111.0",
|
|
37
38
|
"slate-history": "^0.110.3",
|
|
38
|
-
"slate-react": "^0.112.0"
|
|
39
|
+
"slate-react": "^0.112.0",
|
|
40
|
+
"zod": "^3.24.1",
|
|
41
|
+
"zod-formik-adapter": "^1.3.0"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@next/eslint-plugin-next": "^15.1.3",
|
|
42
|
-
"@types/node": "^22.10.
|
|
43
|
-
"@types/react": "19.0.
|
|
45
|
+
"@types/node": "^22.10.5",
|
|
46
|
+
"@types/react": "19.0.3",
|
|
44
47
|
"@types/react-dom": "^19.0.2",
|
|
45
|
-
"@typescript-eslint/eslint-plugin": "^8.19.
|
|
46
|
-
"@typescript-eslint/parser": "^8.19.
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
49
|
+
"@typescript-eslint/parser": "^8.19.1",
|
|
47
50
|
"eslint": "^9.17.0",
|
|
48
51
|
"eslint-config-next": "^15.1.3",
|
|
49
52
|
"eslint-config-prettier": "^9.1.0",
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import React, { JSX } from 'react'
|
|
3
3
|
import { Button, Box, ButtonProps } from '@mui/material'
|
|
4
|
+
import { SxProps, Theme } from '@mui/system'
|
|
4
5
|
import Typography from '../Typography'
|
|
5
6
|
import { SvgIconProps } from '@mui/material/SvgIcon'
|
|
6
|
-
import { styled } from '@mui/material/styles'
|
|
7
7
|
|
|
8
8
|
export interface CustomButtonProps extends ButtonProps {
|
|
9
9
|
text?: string
|
|
10
|
+
/**
|
|
11
|
+
* The background color for the button when not disabled.
|
|
12
|
+
* If `backgroundcolor` is "none", it behaves like a text button.
|
|
13
|
+
*/
|
|
10
14
|
backgroundcolor?: string
|
|
15
|
+
/** The text color. Defaults to white unless disabled. */
|
|
11
16
|
fontcolor?: string
|
|
12
17
|
fontvariant?: 'merriparagraph' | 'merrihelperfooter'
|
|
13
18
|
width?: string
|
|
14
19
|
height?: string
|
|
20
|
+
/**
|
|
21
|
+
* If you want to disable the button in a custom way (string),
|
|
22
|
+
* we unify this with MUI's `disabled` boolean.
|
|
23
|
+
*/
|
|
15
24
|
disableButton?: 'true' | 'false'
|
|
25
|
+
|
|
26
|
+
/** Optional icon to display. */
|
|
16
27
|
icon?: React.ReactElement<SvgIconProps>
|
|
17
28
|
iconcolor?: string
|
|
18
29
|
iconsize?: string
|
|
@@ -20,87 +31,10 @@ export interface CustomButtonProps extends ButtonProps {
|
|
|
20
31
|
fontlocation?: 'left' | 'center' | 'right'
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
prop !== 'fontlocation',
|
|
28
|
-
})<{
|
|
29
|
-
backgroundcolor?: string
|
|
30
|
-
iconlocation?: 'left' | 'right' | 'above'
|
|
31
|
-
fontlocation?: 'left' | 'center' | 'right'
|
|
32
|
-
}>(({ backgroundcolor, iconlocation, fontlocation }) => {
|
|
33
|
-
// Determine styles based on backgroundcolor
|
|
34
|
-
let backgroundStyles = {}
|
|
35
|
-
if (backgroundcolor && backgroundcolor !== 'none') {
|
|
36
|
-
backgroundStyles = {
|
|
37
|
-
backgroundColor: backgroundcolor,
|
|
38
|
-
'&:hover': {
|
|
39
|
-
backgroundColor: backgroundcolor,
|
|
40
|
-
opacity: 0.9,
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
} else if (backgroundcolor === 'none') {
|
|
44
|
-
// No background, behave like a text button
|
|
45
|
-
backgroundStyles = {
|
|
46
|
-
backgroundColor: 'transparent',
|
|
47
|
-
'&:hover': {
|
|
48
|
-
backgroundColor: 'transparent',
|
|
49
|
-
opacity: 0.9,
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
minWidth: 'auto',
|
|
56
|
-
width: '100%',
|
|
57
|
-
height: '40px',
|
|
58
|
-
padding: '8px 16px',
|
|
59
|
-
display: 'flex',
|
|
60
|
-
flexDirection: iconlocation === 'above' ? 'column' : 'row',
|
|
61
|
-
alignItems: 'center',
|
|
62
|
-
justifyContent:
|
|
63
|
-
fontlocation === 'left'
|
|
64
|
-
? 'flex-start'
|
|
65
|
-
: fontlocation === 'right'
|
|
66
|
-
? 'flex-end'
|
|
67
|
-
: 'center',
|
|
68
|
-
gap: '8px',
|
|
69
|
-
...backgroundStyles,
|
|
70
|
-
'& .MuiButton-startIcon': {
|
|
71
|
-
margin: 0,
|
|
72
|
-
},
|
|
73
|
-
'& .MuiButton-endIcon': {
|
|
74
|
-
margin: 0,
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
const StyledBox = styled(Box)({
|
|
80
|
-
display: 'flex',
|
|
81
|
-
flexDirection: 'column',
|
|
82
|
-
alignItems: 'center',
|
|
83
|
-
width: '100%',
|
|
84
|
-
height: '40px',
|
|
85
|
-
minWidth: 'fit-content',
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
const ContentWrapper = styled(Box)<{
|
|
89
|
-
fontlocation?: 'left' | 'center' | 'right'
|
|
90
|
-
}>(({ fontlocation }) => ({
|
|
91
|
-
display: 'flex',
|
|
92
|
-
alignItems: 'center',
|
|
93
|
-
justifyContent:
|
|
94
|
-
fontlocation === 'left'
|
|
95
|
-
? 'flex-start'
|
|
96
|
-
: fontlocation === 'right'
|
|
97
|
-
? 'flex-end'
|
|
98
|
-
: 'center',
|
|
99
|
-
width: '100%',
|
|
100
|
-
height: '100%',
|
|
101
|
-
gap: '8px',
|
|
102
|
-
}))
|
|
103
|
-
|
|
34
|
+
/**
|
|
35
|
+
* CustomButton uses sx props for styling:
|
|
36
|
+
* - We define dynamic logic based on "disabled", "backgroundcolor", etc.
|
|
37
|
+
*/
|
|
104
38
|
function CustomButton({
|
|
105
39
|
text,
|
|
106
40
|
variant = 'contained',
|
|
@@ -117,17 +51,18 @@ function CustomButton({
|
|
|
117
51
|
iconlocation = 'left',
|
|
118
52
|
fontlocation = 'center',
|
|
119
53
|
sx,
|
|
54
|
+
disabled,
|
|
120
55
|
...restProps
|
|
121
56
|
}: CustomButtonProps): JSX.Element {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
57
|
+
// Merge MUI's "disabled" with our "disableButton"
|
|
58
|
+
const isReallyDisabled = disabled || disableButton === 'true'
|
|
59
|
+
|
|
60
|
+
const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
125
61
|
event.preventDefault()
|
|
126
62
|
onClick?.(event)
|
|
127
63
|
}
|
|
128
64
|
|
|
129
|
-
|
|
130
|
-
|
|
65
|
+
// If user provides an icon, clone it to override color/size if desired
|
|
131
66
|
const IconComponent = icon
|
|
132
67
|
? React.cloneElement(icon, {
|
|
133
68
|
sx: {
|
|
@@ -140,39 +75,112 @@ function CustomButton({
|
|
|
140
75
|
} as Partial<SvgIconProps>)
|
|
141
76
|
: null
|
|
142
77
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
78
|
+
// Construct base sx styles
|
|
79
|
+
const buttonSx: SxProps<Theme> = {
|
|
80
|
+
minWidth: 'fit-content',
|
|
81
|
+
width: 'auto',
|
|
82
|
+
height: '40px',
|
|
83
|
+
padding: '8px 16px',
|
|
84
|
+
display: 'inline-flex',
|
|
85
|
+
flexShrink: 0,
|
|
86
|
+
flexWrap: 'nowrap',
|
|
87
|
+
whiteSpace: 'nowrap',
|
|
88
|
+
|
|
89
|
+
flexDirection: iconlocation === 'above' ? 'column' : 'row',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
justifyContent:
|
|
92
|
+
fontlocation === 'left'
|
|
93
|
+
? 'flex-start'
|
|
94
|
+
: fontlocation === 'right'
|
|
95
|
+
? 'flex-end'
|
|
96
|
+
: 'center',
|
|
97
|
+
|
|
98
|
+
gap: '8px',
|
|
99
|
+
|
|
100
|
+
'& .MuiButton-startIcon': { margin: 0 },
|
|
101
|
+
'& .MuiButton-endIcon': { margin: 0 },
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If disabled, force a grey background with no hover effect
|
|
105
|
+
if (isReallyDisabled) {
|
|
106
|
+
buttonSx.backgroundColor = '#cccccc'
|
|
107
|
+
buttonSx['&:hover'] = {
|
|
108
|
+
backgroundColor: '#cccccc',
|
|
109
|
+
opacity: 1,
|
|
110
|
+
cursor: 'not-allowed',
|
|
111
|
+
}
|
|
112
|
+
} else if (backgroundcolor && backgroundcolor !== 'none') {
|
|
113
|
+
// Normal colored background
|
|
114
|
+
buttonSx.backgroundColor = backgroundcolor
|
|
115
|
+
buttonSx['&:hover'] = {
|
|
116
|
+
backgroundColor: backgroundcolor,
|
|
117
|
+
opacity: 0.9,
|
|
118
|
+
}
|
|
119
|
+
} else if (backgroundcolor === 'none') {
|
|
120
|
+
// No background => text button
|
|
121
|
+
buttonSx.backgroundColor = 'transparent'
|
|
122
|
+
buttonSx['&:hover'] = {
|
|
123
|
+
backgroundColor: 'transparent',
|
|
124
|
+
opacity: 0.9,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Merge any sx passed in from parent
|
|
129
|
+
const mergedSx = Array.isArray(sx)
|
|
130
|
+
? [buttonSx, ...sx]
|
|
131
|
+
: { ...buttonSx, ...sx }
|
|
157
132
|
|
|
158
133
|
return (
|
|
159
|
-
<
|
|
160
|
-
|
|
134
|
+
<Box
|
|
135
|
+
sx={{
|
|
136
|
+
display: 'flex',
|
|
137
|
+
flexDirection: 'column',
|
|
138
|
+
alignItems: 'center',
|
|
139
|
+
width: width || 'auto',
|
|
140
|
+
height: height || '40px',
|
|
141
|
+
minWidth: 'fit-content',
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
<Button
|
|
161
145
|
{...restProps}
|
|
162
146
|
variant={variant}
|
|
163
147
|
onClick={handleButtonClick}
|
|
164
|
-
disabled={
|
|
148
|
+
disabled={isReallyDisabled}
|
|
165
149
|
disableElevation
|
|
166
150
|
disableRipple
|
|
167
|
-
|
|
168
|
-
backgroundcolor={backgroundcolor}
|
|
169
|
-
iconlocation={iconlocation}
|
|
170
|
-
fontlocation={fontlocation}
|
|
171
|
-
sx={sx}
|
|
151
|
+
sx={mergedSx}
|
|
172
152
|
>
|
|
173
|
-
{
|
|
174
|
-
|
|
175
|
-
|
|
153
|
+
{/* If iconlocation="above", show the icon first */}
|
|
154
|
+
{iconlocation === 'above' && IconComponent}
|
|
155
|
+
|
|
156
|
+
{/* The text+icon container */}
|
|
157
|
+
<Box
|
|
158
|
+
sx={{
|
|
159
|
+
display: 'flex',
|
|
160
|
+
alignItems: 'center',
|
|
161
|
+
justifyContent:
|
|
162
|
+
fontlocation === 'left'
|
|
163
|
+
? 'flex-start'
|
|
164
|
+
: fontlocation === 'right'
|
|
165
|
+
? 'flex-end'
|
|
166
|
+
: 'center',
|
|
167
|
+
width: '100%',
|
|
168
|
+
height: '100%',
|
|
169
|
+
gap: '8px',
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{iconlocation === 'left' && IconComponent}
|
|
173
|
+
|
|
174
|
+
<Typography
|
|
175
|
+
fontvariant={fontvariant}
|
|
176
|
+
fontcolor={isReallyDisabled ? 'grey' : fontcolor || 'white'}
|
|
177
|
+
text={text || ''}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
{iconlocation === 'right' && IconComponent}
|
|
181
|
+
</Box>
|
|
182
|
+
</Button>
|
|
183
|
+
</Box>
|
|
176
184
|
)
|
|
177
185
|
}
|
|
178
186
|
|
|
@@ -9,10 +9,19 @@ import DetailedPricingSummary from './variants/detailedpricingsummary'
|
|
|
9
9
|
import ProductCard from './variants/product'
|
|
10
10
|
import ProductSummaryCard from './variants/productsummary'
|
|
11
11
|
import DefaultCard from './variants/defaultconfig'
|
|
12
|
+
import TaskCard from './variants/task' // <-- NEW
|
|
12
13
|
import { columnconfig } from '../Grid'
|
|
13
14
|
import { CustomButtonProps } from '../Button'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* We omit children, draggable, and the drag event props from BoxProps,
|
|
18
|
+
* to avoid conflicts with our custom "draggable" and "onDragX" logic
|
|
19
|
+
* for the task variant.
|
|
20
|
+
*/
|
|
21
|
+
type CardProps = Omit<
|
|
22
|
+
BoxProps,
|
|
23
|
+
'children' | 'draggable' | 'onDragStart' | 'onDragOver' | 'onDrop'
|
|
24
|
+
> & {
|
|
16
25
|
/** Title of the card */
|
|
17
26
|
title?: string
|
|
18
27
|
/** Whether to show an underline for the title */
|
|
@@ -57,6 +66,7 @@ type CardProps = Omit<BoxProps, 'children'> & {
|
|
|
57
66
|
| 'detailedpricingsummary'
|
|
58
67
|
| 'product'
|
|
59
68
|
| 'productsummary'
|
|
69
|
+
| 'task' // <-- NEW
|
|
60
70
|
/** Props for the pricing summary variant */
|
|
61
71
|
pricingSummaryProps?: {
|
|
62
72
|
subtotal?: string
|
|
@@ -118,6 +128,27 @@ type CardProps = Omit<BoxProps, 'children'> & {
|
|
|
118
128
|
button1Props?: CustomButtonProps
|
|
119
129
|
button2Props?: CustomButtonProps
|
|
120
130
|
}
|
|
131
|
+
/** Props for the new "task" variant */
|
|
132
|
+
taskProps?: {
|
|
133
|
+
/** Title of the task */
|
|
134
|
+
title?: string
|
|
135
|
+
/** Description of the task */
|
|
136
|
+
description?: string
|
|
137
|
+
/** Whether the task is completed (checked) */
|
|
138
|
+
checked?: boolean
|
|
139
|
+
/** Callback when the checkbox changes */
|
|
140
|
+
onCheck?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Optional drag & drop props if you want the card itself
|
|
144
|
+
* to handle drag events.
|
|
145
|
+
* Must be booleans or DragEventHandlers, not strings like "true".
|
|
146
|
+
*/
|
|
147
|
+
draggable?: boolean
|
|
148
|
+
onDragStart?: React.DragEventHandler<HTMLDivElement>
|
|
149
|
+
onDragOver?: React.DragEventHandler<HTMLDivElement>
|
|
150
|
+
onDrop?: React.DragEventHandler<HTMLDivElement>
|
|
151
|
+
}
|
|
121
152
|
/** Configuration for grid columns */
|
|
122
153
|
columnconfig?: columnconfig
|
|
123
154
|
}
|
|
@@ -147,6 +178,7 @@ function Card({
|
|
|
147
178
|
inventoryProps,
|
|
148
179
|
productProps,
|
|
149
180
|
productSummaryProps,
|
|
181
|
+
taskProps,
|
|
150
182
|
...rest
|
|
151
183
|
}: CardProps): JSX.Element | null {
|
|
152
184
|
if (variant === 'default') {
|
|
@@ -232,6 +264,25 @@ function Card({
|
|
|
232
264
|
)
|
|
233
265
|
}
|
|
234
266
|
|
|
267
|
+
// NEW: Return our "task" variant
|
|
268
|
+
if (variant === 'task') {
|
|
269
|
+
return (
|
|
270
|
+
<TaskCard
|
|
271
|
+
title={taskProps?.title}
|
|
272
|
+
description={taskProps?.description}
|
|
273
|
+
checked={taskProps?.checked}
|
|
274
|
+
onCheck={taskProps?.onCheck}
|
|
275
|
+
draggable={taskProps?.draggable}
|
|
276
|
+
onDragStart={taskProps?.onDragStart}
|
|
277
|
+
onDragOver={taskProps?.onDragOver}
|
|
278
|
+
onDrop={taskProps?.onDrop}
|
|
279
|
+
width={width}
|
|
280
|
+
height={height}
|
|
281
|
+
{...rest}
|
|
282
|
+
/>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
235
286
|
return null
|
|
236
287
|
}
|
|
237
288
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Paper, Box, Checkbox } from '@mui/material'
|
|
5
|
+
import Typography from '../../../../components/Typography'
|
|
6
|
+
|
|
7
|
+
interface TaskCardProps {
|
|
8
|
+
title?: string
|
|
9
|
+
description?: string
|
|
10
|
+
/** Whether the card is currently checked/selected. */
|
|
11
|
+
checked?: boolean
|
|
12
|
+
/** Called when the user toggles the checkbox. */
|
|
13
|
+
onCheck?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
14
|
+
/** Whether this card is “draggable.” Typically true only if `checked` is true. */
|
|
15
|
+
draggable?: boolean
|
|
16
|
+
/** Drag event handlers from your board logic. */
|
|
17
|
+
onDragStart?: (e: React.DragEvent<HTMLDivElement>) => void
|
|
18
|
+
onDragOver?: (e: React.DragEvent<HTMLDivElement>) => void
|
|
19
|
+
onDrop?: (e: React.DragEvent<HTMLDivElement>) => void
|
|
20
|
+
|
|
21
|
+
width?: string
|
|
22
|
+
height?: string | number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const TaskCard: React.FC<TaskCardProps> = ({
|
|
26
|
+
title = 'Task Title',
|
|
27
|
+
description = 'Description',
|
|
28
|
+
checked = false,
|
|
29
|
+
onCheck,
|
|
30
|
+
draggable = false, // default to not draggable
|
|
31
|
+
onDragStart,
|
|
32
|
+
onDragOver,
|
|
33
|
+
onDrop,
|
|
34
|
+
width = '100%',
|
|
35
|
+
height = 'auto',
|
|
36
|
+
}) => {
|
|
37
|
+
return (
|
|
38
|
+
<Paper
|
|
39
|
+
elevation={1}
|
|
40
|
+
/*
|
|
41
|
+
Make the entire Paper draggable if the parent says so
|
|
42
|
+
(based on whether it’s selected).
|
|
43
|
+
*/
|
|
44
|
+
draggable={draggable}
|
|
45
|
+
onDragStart={onDragStart}
|
|
46
|
+
onDragOver={onDragOver}
|
|
47
|
+
onDrop={onDrop}
|
|
48
|
+
sx={{
|
|
49
|
+
position: 'relative',
|
|
50
|
+
display: 'flex',
|
|
51
|
+
flexDirection: 'column',
|
|
52
|
+
justifyContent: 'flex-start',
|
|
53
|
+
alignItems: 'flex-start',
|
|
54
|
+
width,
|
|
55
|
+
height,
|
|
56
|
+
p: 2,
|
|
57
|
+
border: '1px solid #e8e8e8',
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{/* A checkbox in the upper-right corner */}
|
|
61
|
+
<Checkbox
|
|
62
|
+
checked={checked}
|
|
63
|
+
onChange={onCheck}
|
|
64
|
+
color="primary"
|
|
65
|
+
sx={{
|
|
66
|
+
position: 'absolute',
|
|
67
|
+
top: 8,
|
|
68
|
+
right: 8,
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', marginRight: 4 }}>
|
|
73
|
+
<Typography
|
|
74
|
+
text={title}
|
|
75
|
+
fontcolor="black"
|
|
76
|
+
fontvariant="merrih5"
|
|
77
|
+
sx={{ marginBottom: '4px' }}
|
|
78
|
+
/>
|
|
79
|
+
<Typography
|
|
80
|
+
text={description}
|
|
81
|
+
fontcolor="black"
|
|
82
|
+
fontvariant="merriparagraph"
|
|
83
|
+
/>
|
|
84
|
+
</Box>
|
|
85
|
+
</Paper>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default TaskCard
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import ProjectBoard, { ProjectBoardProps } from '../../../ProjectBoard'
|
|
4
|
+
import { columnconfig, cellconfig } from '../../../Grid'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This mirrors the pattern of your Extended*Props from useTextField, etc.
|
|
8
|
+
* ExtendedProjectBoardProps extends your ProjectBoardProps but also includes:
|
|
9
|
+
* - optional columnconfig for row/col positioning
|
|
10
|
+
* - optional cellconfig for styling
|
|
11
|
+
*
|
|
12
|
+
* If you’d like to pass more custom props, add them here.
|
|
13
|
+
*/
|
|
14
|
+
type ExtendedColumnConfig = Omit<columnconfig, 'component'> & {
|
|
15
|
+
component?: columnconfig['component']
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ExtendedProjectBoardProps extends ProjectBoardProps {
|
|
19
|
+
columnconfig?: ExtendedColumnConfig
|
|
20
|
+
cellconfig?: cellconfig
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const useProjectBoard = (grid: {
|
|
24
|
+
/**
|
|
25
|
+
* We can accept a single ExtendedProjectBoardProps or an array of them.
|
|
26
|
+
* This matches how your existing hooks handle single vs array items.
|
|
27
|
+
*/
|
|
28
|
+
projectboard?: ExtendedProjectBoardProps | ExtendedProjectBoardProps[]
|
|
29
|
+
}): columnconfig | columnconfig[] | null => {
|
|
30
|
+
if (!grid.projectboard) return null
|
|
31
|
+
|
|
32
|
+
// This helper function converts a single ExtendedProjectBoardProps
|
|
33
|
+
// into a columnconfig object for your <CustomGrid> layout system.
|
|
34
|
+
const renderProjectBoard = (
|
|
35
|
+
item: ExtendedProjectBoardProps,
|
|
36
|
+
index: number
|
|
37
|
+
): columnconfig => {
|
|
38
|
+
const {
|
|
39
|
+
columnconfig: itemColumnConfig,
|
|
40
|
+
cellconfig,
|
|
41
|
+
// In your existing `ProjectBoardProps`, you have `columns` plus any other props.
|
|
42
|
+
columns,
|
|
43
|
+
...restProps
|
|
44
|
+
} = item
|
|
45
|
+
|
|
46
|
+
// Ensure columnconfig is valid
|
|
47
|
+
if (
|
|
48
|
+
!itemColumnConfig ||
|
|
49
|
+
typeof itemColumnConfig.row !== 'number' ||
|
|
50
|
+
typeof itemColumnConfig.column !== 'number'
|
|
51
|
+
) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'columnconfig must be an object with numeric row and column'
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Build the final columnconfig
|
|
58
|
+
const mergedConfig: columnconfig = {
|
|
59
|
+
...itemColumnConfig,
|
|
60
|
+
cellconfig: {
|
|
61
|
+
...cellconfig,
|
|
62
|
+
},
|
|
63
|
+
component: (
|
|
64
|
+
<ProjectBoard
|
|
65
|
+
key={`projectboard-${index}`}
|
|
66
|
+
columns={columns}
|
|
67
|
+
{...restProps}
|
|
68
|
+
/>
|
|
69
|
+
),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return mergedConfig
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (Array.isArray(grid.projectboard)) {
|
|
76
|
+
// If it's an array, map each item to a columnconfig
|
|
77
|
+
return grid.projectboard.map(renderProjectBoard)
|
|
78
|
+
} else {
|
|
79
|
+
// If it's a single item, just render one
|
|
80
|
+
return renderProjectBoard(grid.projectboard, 0)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default useProjectBoard
|
|
@@ -72,6 +72,9 @@ import useSearchableDropdown, {
|
|
|
72
72
|
import useAccordion, {
|
|
73
73
|
ExtendedAccordionProps,
|
|
74
74
|
} from './Structure/accordion/useAccordion'
|
|
75
|
+
import useProjectBoard, {
|
|
76
|
+
ExtendedProjectBoardProps,
|
|
77
|
+
} from './Structure/projectboard/useProjectBoard'
|
|
75
78
|
|
|
76
79
|
/**
|
|
77
80
|
* Props for the ContentSection component.
|
|
@@ -88,6 +91,7 @@ export interface ContentSectionProps {
|
|
|
88
91
|
searchableDropdown?:
|
|
89
92
|
| ExtendedSearchableDropdownProps
|
|
90
93
|
| ExtendedSearchableDropdownProps[]
|
|
94
|
+
projectboard?: ExtendedProjectBoardProps | ExtendedProjectBoardProps[]
|
|
91
95
|
complexeditor?: ExtendedComplexEditorProps | ExtendedComplexEditorProps[]
|
|
92
96
|
typography?: TypographyProps | TypographyProps[]
|
|
93
97
|
accordion?: ExtendedAccordionProps | ExtendedAccordionProps[]
|
|
@@ -156,6 +160,7 @@ const RenderContent: React.FC<
|
|
|
156
160
|
addToColumnConfigs(useSearchableDropdown(props))
|
|
157
161
|
addToColumnConfigs(useTextField(props))
|
|
158
162
|
addToColumnConfigs(useDateField(props))
|
|
163
|
+
addToColumnConfigs(useProjectBoard(props))
|
|
159
164
|
addToColumnConfigs(useAccordion(props))
|
|
160
165
|
addToColumnConfigs(useCheckbox(props))
|
|
161
166
|
addToColumnConfigs(usePhoneNumber(props))
|