beherbergung/frontend/search/components/ngo/HostOfferLookupTable.tsx

199 lines
6.8 KiB
TypeScript
Raw Normal View History

2022-03-14 13:27:20 +01:00
import React, {ReactNode, useCallback, useEffect, useState} from 'react'
2022-03-09 18:23:27 +01:00
import {CheckBoxOutlineBlank, CheckBox} from '@mui/icons-material'
2022-03-09 18:23:27 +01:00
2022-03-09 17:01:05 +01:00
import '@inovua/reactdatagrid-community/index.css'
import DataGrid from '@inovua/reactdatagrid-community'
import {TypeColumn, TypeFilterValue, TypeSingleFilterValue} from "@inovua/reactdatagrid-community/types"
2022-03-09 17:01:05 +01:00
import DateFilter from '@inovua/reactdatagrid-community/DateFilter'
import StringFilter from '@inovua/reactdatagrid-community/StringFilter'
import BoolFilter from '@inovua/reactdatagrid-community/BoolFilter'
import NumberFilter from "@inovua/reactdatagrid-community/NumberFilter"
import BoolEditor from '@inovua/reactdatagrid-community/BoolEditor'
import { GetOffersQuery, GetRwQuery} from "../../codegen/generates"
import moment from "moment"
2022-03-09 17:19:45 +01:00
import {useTranslation} from "react-i18next"
import {resources} from '../../i18n/config'
2022-03-09 22:51:30 +01:00
import { fetcher } from '../../codegen/fetcher'
import { useAuthStore, AuthState } from '../Login'
2022-03-14 12:00:17 +01:00
import defaultColumnRawDefinition from "../config/defaultColumnRawDefinition";
import defaultColumnGroups from "../config/defaultColumnGroups";
import { ColumnRaw } from '../util/datagrid/columnRaw'
2022-03-14 13:27:20 +01:00
import columnsRaw from "../config/defaultColumnRawDefinition";
import {transformValue} from "../util/tableValueMapper";
import {filterUndefOrNull} from "../util/notEmpty";
import extendedFilter from "../util/datagrid/extendedFilter";
2022-03-09 17:19:45 +01:00
global.moment = moment
2022-03-09 15:39:15 +01:00
export type HostOfferLookupTableDataType = GetOffersQuery["get_offers"] & GetRwQuery["get_rw"];
2022-03-14 14:22:03 +01:00
export type HostOfferLookupTableProps = {
data_ro?: GetOffersQuery["get_offers"],
data_rw?: GetRwQuery["get_rw"], // TODO
refetch_rw: any,
onFilteredDataChange?: (data: HostOfferLookupTableDataType[]) => void
2022-03-09 15:39:15 +01:00
}
2022-03-09 17:01:05 +01:00
const filterMappings = {
string: StringFilter,
boolean: BoolFilter,
number: NumberFilter,
date: DateFilter,
2022-03-09 17:01:05 +01:00
}
const editorMappings = {
string: null,
boolean: BoolEditor,
number: null,
date: null
}
2022-03-09 17:01:05 +01:00
const operatorsForType = {
number: 'gte',
string: 'contains',
date: 'beforeOrOn',
boolean: 'eq',
2022-03-09 18:23:27 +01:00
}
2022-03-09 17:01:05 +01:00
2022-03-09 21:58:51 +01:00
type CustomRendererMatcher = {
match: { [key: string]: any }
render: (...args: any[]) => ReactNode
}
function Email({value}: {value: string}) {
const href = `mailto:${value}`
return <a href={href}>{value}</a>
}
function Phone({value}: {value: string}) {
const href = `tel:${value}`
return <a href={href}>{value}</a>
}
2022-03-09 21:58:51 +01:00
const customRendererForType: CustomRendererMatcher[] = [
2022-03-09 18:23:27 +01:00
{
match: {type: 'boolean'},
2022-03-10 12:41:20 +01:00
render: ({value}) => !!value ? <CheckBox/> : <CheckBoxOutlineBlank/>
2022-03-09 18:23:27 +01:00
},
{
match: {type: 'string', name: 'contact_email'},
render: Email
},
{
match: {type: 'string', name: 'contact_phone'},
render: Phone
2022-03-09 18:23:27 +01:00
}
]
const findMatchingRenderer = (c: Partial<ColumnRaw>) => {
2022-03-09 18:23:27 +01:00
const customRenderer = customRendererForType.find(d => {
// @ts-ignore
2022-03-10 12:41:20 +01:00
return Object.keys(d.match).reduce((prev, cur) => prev && c[cur] === d.match[cur], true)
2022-03-09 18:23:27 +01:00
})
return customRenderer?.render
}
2022-03-09 17:01:05 +01:00
2022-03-14 12:00:17 +01:00
const columns: TypeColumn[] = defaultColumnRawDefinition
2022-03-09 17:01:05 +01:00
.map(c => ({
...c,
2022-03-09 21:58:51 +01:00
render: findMatchingRenderer(c) || undefined,
filterEditor: filterMappings[c.type as 'string' | 'number' | 'boolean' | 'date'],
editor: editorMappings[c.type as 'string' | 'number' | 'boolean' | 'date']
2022-03-09 17:01:05 +01:00
}))
const defaultFilterValue: TypeFilterValue = columns
.filter(({type}) => type && ['string', 'number', 'boolean', 'date'].includes(type))
2022-03-10 12:41:20 +01:00
.map(({name, type}) => {
2022-03-09 17:01:05 +01:00
return {
name,
type,
2022-03-09 18:23:27 +01:00
value: null,
operator: operatorsForType[type as 'string' | 'number' | 'date' | 'boolean']
2022-03-09 17:01:05 +01:00
} as unknown as TypeSingleFilterValue
2022-03-10 12:41:20 +01:00
})
2022-03-09 17:01:05 +01:00
async function mutate(auth: AuthState, onEditComplete: {value: string, columnId: string, rowId: string}) {
const type = typeof(onEditComplete.value)
const onEditCompleteByType = {rowId: onEditComplete.rowId,
columnId: onEditComplete.columnId,
value_boolean: type === 'boolean' && onEditComplete.value || null,
value_string: type === 'string' && onEditComplete.value || null}
const result = await fetcher<any, any>(`mutation WriteRW($auth: Auth!, $onEditCompleteByType: Boolean) {
write_rw(auth: $auth, onEditCompleteByType: $onEditCompleteByType) }`,
{auth, onEditCompleteByType})()
return result?.write_rw
}
const rw_default = {
rw_contacted: false,
rw_contact_replied: false,
rw_offer_occupied: false,
rw_note: ''} // Required for filtering 'Not empty'. TODO: Should be fixed in StringFilter
const HostOfferLookupTable = ({data_ro, data_rw, refetch_rw, onFilteredDataChange}: HostOfferLookupTableProps) => {
const [dataSource, setDataSource] = useState<HostOfferLookupTableDataType[]>([]);
const [filteredData, setFilteredData] = useState<HostOfferLookupTableDataType[]>([]);
const [filterValue, setFilterValue] = useState<TypeFilterValue>(defaultFilterValue);
2022-03-14 23:34:12 +01:00
const filterValueChangeHandler = useCallback((_filterValue?: TypeFilterValue) => {
const data = !_filterValue
? dataSource
: extendedFilter(
dataSource,
_filterValue,
columnsRaw
)
2022-03-14 23:34:12 +01:00
_filterValue && setFilterValue(_filterValue);
setFilteredData(data)
onFilteredDataChange && onFilteredDataChange(data)
}, [dataSource])
2022-03-14 13:27:20 +01:00
useEffect(() => {
const data = filterUndefOrNull( data_ro
?.map( e_ro => ({
2022-03-15 02:27:32 +01:00
...((data_rw?.find((e_rw) => e_ro.id_tmp === e_rw.id || `rw_${e_ro.id}` === e_rw.id
2022-03-15 04:55:46 +01:00
) || rw_default)),
...e_ro
}) ) || []).map(v => transformValue(v, columnsRaw))
// @ts-ignore
data && setDataSource(data)
2022-03-14 23:34:12 +01:00
filterValueChangeHandler()
//setDataSource((/*data_rw?.get_rw || */ data_ro?.get_offers || []).map(v => transformValue(v, columnsRaw)))
2022-03-14 13:27:20 +01:00
}, [data_ro, data_rw]);
const auth = useAuthStore()
const onEditComplete = useCallback(async ({value, columnId, rowId}) => {
/** For now the easiest way to ensure the user can see if data was updated in the db is by calling `refetch_rw()`
TODO: error handling **/
await mutate(auth, {value, columnId, rowId}) && refetch_rw()
2022-03-13 23:53:05 +01:00
}, [auth, refetch_rw])
2022-03-09 22:51:30 +01:00
2022-03-10 12:41:20 +01:00
const {i18n: {language}} = useTranslation()
2022-03-09 22:51:30 +01:00
// @ts-ignore
const reactdatagridi18n = resources[language]?.translation?.reactdatagrid
2022-03-14 13:27:20 +01:00
return <DataGrid
2022-03-10 12:41:20 +01:00
idProperty="id"
filterable
showColumnMenuFilterOptions={true}
showFilteringMenuItems={true}
filterValue={filterValue}
onFilterValueChange={filterValueChangeHandler}
2022-03-10 12:41:20 +01:00
rowIndexColumn
enableSelection
enableColumnAutosize={false}
columns={columns}
dataSource={filteredData}
2022-03-10 12:41:20 +01:00
i18n={reactdatagridi18n || undefined}
2022-03-10 12:41:20 +01:00
style={{height: '100%'}}
2022-03-14 13:27:20 +01:00
onEditComplete={onEditComplete}
groups={defaultColumnGroups}
2022-03-10 12:41:20 +01:00
/>
2022-03-09 15:39:15 +01:00
}
export default HostOfferLookupTable