@samitouri / QOSami-HFS / commits / 0fa7f43e

dev: DateTimeField

Massimo Melina committed Oct 3, 2023 at 23:52 UTC 0fa7f43e29852bfa5ed024fd70f627863852e380
4 files changed +54 -12
admin/package.json
+2
@@ -16,8 +16,10 @@
16 "@mui/lab": "^5.0.0-alpha.142",
17 "@mui/material": "^5.14.8",
18 "@mui/x-data-grid": "^6.13.0",
19 + "@mui/x-date-pickers": "^6.16.0",
20 "@mui/x-tree-view": "^6.0.0-beta.0",
21 "@gregoranders/csv": "^0.0.12",
22 + "dayjs": "^1.11.10",
23 "react": "^18.2.0",
24 "react-dom": "^18.2.0",
25 "react-router-dom": "^6.2.1",
admin/src/App.ts
+5 -2
@@ -9,12 +9,15 @@ import { useMyTheme } from './theme'
9 import { useBreakpoint} from './misc'
10 import { LoginRequired } from './LoginRequired'
11 import { Menu } from '@mui/icons-material'
12 +import { LocalizationProvider } from '@mui/x-date-pickers'
13 +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
14
15 function App() {
16 return h(ThemeProvider, { theme: useMyTheme() },
17 h(ApplyTheme, {},
16 - h(LoginRequired, {},
17 - h(HashRouter, {}, h(Routed)) ) ) )
18 + h(LocalizationProvider, { dateAdapter: AdapterDayjs },
19 + h(LoginRequired, {},
20 + h(HashRouter, {}, h(Routed)) ) ) ) )
21 }
22
23 function ApplyTheme(props:any) {
admin/src/ArrayField.ts
+27 -10
@@ -1,12 +1,14 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { createElement as h, Fragment, useMemo, useState } from 'react'
4 -import { IconBtn, isOrderedEqual, setHidden, swap } from './misc'
4 +import { Dict, IconBtn, isOrderedEqual, setHidden, swap } from './misc'
5 import { Add, Edit, Delete, ArrowUpward, ArrowDownward, Undo } from '@mui/icons-material'
6 import { formDialog } from './dialog'
7 -import { DataGrid, GridActionsCellItem, GridAlignment } from '@mui/x-data-grid'
7 +import { DataGrid, GridActionsCellItem, GridAlignment, GridColDef } from '@mui/x-data-grid'
8 import { FieldDescriptor, FieldProps, labelFromKey } from '@hfs/mui-grid-form'
9 import { Box, FormHelperText, FormLabel } from '@mui/material'
10 +import { DateTimeField } from './DateTimeField'
11 +import _ from 'lodash'
12
13 type ArrayFieldProps<T> = FieldProps<T[]> & { fields: FieldDescriptor[], height?: number, reorder?: boolean, prepend?: boolean }
14 export function ArrayField<T extends object>({ label, helperText, fields, value, onChange, onError, setApi, reorder, prepend, ...rest }: ArrayFieldProps<T>) {
@@ -14,7 +16,7 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
16 setHidden({ ...x } as any, x.hasOwnProperty('id') ? { $idx } : { id: $idx })),
17 [JSON.stringify(value)]) //eslint-disable-line
18 const form = {
17 - fields: fields.map(({ $width, $column, ...rest }) => rest)
19 + fields: fields.map(({ $width, $column, $type, ...rest }) => _.defaults(rest, byType[$type]?.field))
20 }
21 setApi?.({ isEqual: isOrderedEqual }) // don't rely on stringify, as it wouldn't work with non-json values
22 const [undo, setUndo] = useState<typeof value>()
@@ -34,13 +36,17 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
36 }
37 },
38 columns: [
37 - ...fields.map(f => ({
38 - field: f.k,
39 - headerName: f.headerName ?? (typeof f.label === 'string' ? f.label : labelFromKey(f.k)),
40 - disableColumnMenu: true,
41 - ...f.$width >= 8 ? { width: f.$width } : { flex: f.$width || 1 },
42 - ...f.$column,
43 - })),
39 + ...fields.map(f => {
40 + const def = byType[f.$type]?.column
41 + return ({
42 + field: f.k,
43 + headerName: f.headerName ?? (typeof f.label === 'string' ? f.label : labelFromKey(f.k)),
44 + disableColumnMenu: true,
45 + ...def,
46 + ...f.$width ? { [f.$width >= 8 ? 'width' : 'flex']: f.$width } : (!def?.width && !def?.flex && { flex: 1 }),
47 + ...f.$column,
48 + })
49 + }),
50 {
51 field: '',
52 type: 'actions',
@@ -117,3 +123,14 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
123 }
124
125 }
126 +
127 +const byType: Dict<{ field?: Partial<FieldDescriptor>, column?: Partial<GridColDef> }> = {
128 + dateTime: {
129 + field: { comp: DateTimeField },
130 + column: {
131 + type: 'dateTime', minWidth: 90, flex: 0.5,
132 + valueGetter: ({ value }) => value && new Date(value),
133 + renderCell: ({ value }) => value && h(Box, {}, value.toLocaleDateString(), h('br'), value.toLocaleTimeString())
134 + }
135 + }
136 +}
\ No newline at end of file
admin/src/DateTimeField.ts new
+20
@@ -0,0 +1,20 @@
1 +import { DateTimePicker } from '@mui/x-date-pickers'
2 +import dayjs from 'dayjs'
3 +import { FieldProps } from '@hfs/mui-grid-form'
4 +import { createElement as h } from 'react'
5 +import { Box, FormHelperText } from '@mui/material'
6 +import { objSameKeys } from './misc'
7 +
8 +export function DateTimeField({ onChange, error, helperText, ...rest }: FieldProps<Date>) {
9 + return h(Box, {},
10 + h(DateTimePicker, {
11 + ...objSameKeys(rest, x => x && x instanceof Date ? dayjs(x) : (x ?? null)), // null to not be considered uncontrolled
12 + sx: { width: '100%', color: 'error.main', ...rest.sx },
13 + onChange(v: any) {
14 + onChange(v && new Date(v), { was: rest.value, event: undefined })
15 + }
16 + }),
17 + helperText && h(FormHelperText, { error }, helperText),
18 + )
19 +}
20 +