mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
Small updates to widgets; ok-ish version of filter query with relative time expressions; initial version of multi-select query for possible-values
This commit is contained in:
@ -21,7 +21,7 @@ Coded by www.creative-tim.com
|
|||||||
<meta name="theme-color" content="#04aaef" />
|
<meta name="theme-color" content="#04aaef" />
|
||||||
<link id="appleIcon" rel="apple-touch-icon" sizes="76x76" href="%PUBLIC_URL%/" />
|
<link id="appleIcon" rel="apple-touch-icon" sizes="76x76" href="%PUBLIC_URL%/" />
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
<title>QQQ Material Dashboard</title>
|
<title></title>
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp" rel="stylesheet" />
|
||||||
</head>
|
</head>
|
||||||
|
@ -243,7 +243,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
{
|
{
|
||||||
widgetMetaData.type === "html" && (
|
widgetMetaData.type === "html" && (
|
||||||
<Widget widgetMetaData={widgetMetaData}>
|
<Widget widgetMetaData={widgetMetaData}>
|
||||||
<Box px={1} pt={0} pb={2}>
|
<Box px={3} pt={0} pb={2}>
|
||||||
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
||||||
{
|
{
|
||||||
widgetData && widgetData[i] && widgetData[i].html ? (
|
widgetData && widgetData[i] && widgetData[i].html ? (
|
||||||
|
@ -142,7 +142,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
|
|||||||
{
|
{
|
||||||
const link = component as HeaderLink
|
const link = component as HeaderLink
|
||||||
return (
|
return (
|
||||||
<Typography variant="body2" p={2} display="inline">
|
<Typography variant="body2" p={2} display="inline" fontSize=".875rem">
|
||||||
{link.to ? <Link to={link.to}>{link.label}</Link> : null}
|
{link.to ? <Link to={link.to}>{link.label}</Link> : null}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
@ -153,7 +153,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
|
|||||||
const addNewRecordButton = component as AddNewRecordButton
|
const addNewRecordButton = component as AddNewRecordButton
|
||||||
return (
|
return (
|
||||||
<Typography variant="body2" p={2} pr={1} display="inline">
|
<Typography variant="body2" p={2} pr={1} display="inline">
|
||||||
<Button onClick={() => openEditForm(addNewRecordButton.table, null, addNewRecordButton.defaultValues, addNewRecordButton.disabledFields)}>{addNewRecordButton.label}</Button>
|
<Button sx={{mt: 0.75}} onClick={() => openEditForm(addNewRecordButton.table, null, addNewRecordButton.defaultValues, addNewRecordButton.disabledFields)}>{addNewRecordButton.label}</Button>
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
const widgetCount = widgets ? widgets.length : 0;
|
const widgetCount = widgets ? widgets.length : 0;
|
||||||
|
|
||||||
// eslint-disable-next-line no-nested-ternary
|
// eslint-disable-next-line no-nested-ternary
|
||||||
const tileSizeLg = (widgetCount === 0 ? 3 : widgetCount === 1 ? 4 : 6);
|
const tileSizeLg = 3;
|
||||||
|
|
||||||
const handleDropdownOnChange = (value: string, index: number) =>
|
const handleDropdownOnChange = (value: string, index: number) =>
|
||||||
{
|
{
|
||||||
@ -207,7 +207,7 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
{
|
{
|
||||||
app.sections ? (
|
app.sections ? (
|
||||||
<Grid item xs={12} lg={widgetCount === 0 ? 12 : widgetCount === 1 ? 9 : 6}>
|
<Grid item xs={12} lg={12}>
|
||||||
{app.sections.map((section) => (
|
{app.sections.map((section) => (
|
||||||
<Box key={section.name} mb={3}>
|
<Box key={section.name} mb={3}>
|
||||||
<Card sx={{overflow: "visible"}}>
|
<Card sx={{overflow: "visible"}}>
|
||||||
|
@ -731,6 +731,73 @@ function InputPossibleValueSourceSingle(tableName: string, field: QFieldMetaData
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// input element for multiple possible values //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
function InputPossibleValueSourceMultiple(tableName: string, field: QFieldMetaData, props: GridFilterInputValueProps)
|
||||||
|
{
|
||||||
|
const SUBMIT_FILTER_STROKE_TIME = 500;
|
||||||
|
const {item, applyValue, focusElementRef = null} = props;
|
||||||
|
|
||||||
|
console.log("Item.value? " + item.value);
|
||||||
|
|
||||||
|
const filterTimeout = useRef<any>();
|
||||||
|
const [ selectedPossibleValues, setSelectedPossibleValues ] = useState(item.value as QPossibleValue[]);
|
||||||
|
const [ applying, setIsApplying ] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
clearTimeout(filterTimeout.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const itemValue = item.value ?? null;
|
||||||
|
}, [ item.value ]);
|
||||||
|
|
||||||
|
const updateFilterValue = (value: QPossibleValue) =>
|
||||||
|
{
|
||||||
|
clearTimeout(filterTimeout.current);
|
||||||
|
|
||||||
|
setIsApplying(true);
|
||||||
|
filterTimeout.current = setTimeout(() =>
|
||||||
|
{
|
||||||
|
setIsApplying(false);
|
||||||
|
applyValue({...item, value: value});
|
||||||
|
}, SUBMIT_FILTER_STROKE_TIME);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (value: QPossibleValue) =>
|
||||||
|
{
|
||||||
|
updateFilterValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "inline-flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "end",
|
||||||
|
height: 48,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DynamicSelect
|
||||||
|
tableName={tableName}
|
||||||
|
fieldName={field.name}
|
||||||
|
isMultiple={true}
|
||||||
|
fieldLabel="Value"
|
||||||
|
initialValues={selectedPossibleValues}
|
||||||
|
inForm={false}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// possible value set operators //
|
// possible value set operators //
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
@ -749,6 +816,18 @@ export const buildQGridPvsOperators = (tableName: string, field: QFieldMetaData)
|
|||||||
getApplyFilterFn: () => null,
|
getApplyFilterFn: () => null,
|
||||||
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceSingle(tableName, field, props)
|
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceSingle(tableName, field, props)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "is any of",
|
||||||
|
value: "isAnyOf",
|
||||||
|
getApplyFilterFn: () => null,
|
||||||
|
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceMultiple(tableName, field, props)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "is none of",
|
||||||
|
value: "isNone",
|
||||||
|
getApplyFilterFn: () => null,
|
||||||
|
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceMultiple(tableName, field, props)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "is empty",
|
label: "is empty",
|
||||||
value: "isEmpty",
|
value: "isEmpty",
|
||||||
|
@ -344,7 +344,8 @@ class FilterUtils
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const qQueryFilter = (filterString !== null) ? JSON.parse(filterString) : JSON.parse(searchParams.get("filter")) as QQueryFilter;
|
const filterJSON = (filterString !== null) ? JSON.parse(filterString) : JSON.parse(searchParams.get("filter"));
|
||||||
|
const qQueryFilter = filterJSON as QQueryFilter;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
// translate from a qqq-style filter to one that the grid wants //
|
// translate from a qqq-style filter to one that the grid wants //
|
||||||
@ -377,6 +378,53 @@ class FilterUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field && field.type == "DATE_TIME" && !values)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const criteria = filterJSON.criteria[i];
|
||||||
|
if (criteria && criteria.expression)
|
||||||
|
{
|
||||||
|
let value = new Date();
|
||||||
|
let amount = Number(criteria.expression.amount);
|
||||||
|
switch (criteria.expression.timeUnit)
|
||||||
|
{
|
||||||
|
case "MINUTES":
|
||||||
|
{
|
||||||
|
amount = amount * 60;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "HOURS":
|
||||||
|
{
|
||||||
|
amount = amount * 60 * 60;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DAYS":
|
||||||
|
{
|
||||||
|
amount = amount * 60 * 60 * 24;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
console.log("Unrecognized time unit: " + criteria.expression.timeUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (criteria.expression.operator == "MINUS")
|
||||||
|
{
|
||||||
|
amount = -amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.setTime(value.getTime() + 1000 * amount);
|
||||||
|
values = [ValueUtils.formatDateTimeISO8601(value)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defaultFilter.items.push({
|
defaultFilter.items.push({
|
||||||
columnField: criteria.fieldName,
|
columnField: criteria.fieldName,
|
||||||
operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, values),
|
operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, values),
|
||||||
|
@ -131,7 +131,7 @@ class ValueUtils
|
|||||||
|
|
||||||
if (field.hasAdornment(AdornmentType.RENDER_HTML))
|
if (field.hasAdornment(AdornmentType.RENDER_HTML))
|
||||||
{
|
{
|
||||||
return (parse(rawValue));
|
return (rawValue ? parse(rawValue) : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.hasAdornment(AdornmentType.CHIP))
|
if (field.hasAdornment(AdornmentType.CHIP))
|
||||||
@ -253,6 +253,16 @@ class ValueUtils
|
|||||||
return (`${date.toString("yyyy-MM-dd hh:mm:ss")} ${date.getHours() < 12 ? "AM" : "PM"} ${date.getTimezone()}`);
|
return (`${date.toString("yyyy-MM-dd hh:mm:ss")} ${date.getHours() < 12 ? "AM" : "PM"} ${date.getTimezone()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static formatDateTimeISO8601(date: Date)
|
||||||
|
{
|
||||||
|
if(!(date instanceof Date))
|
||||||
|
{
|
||||||
|
date = new Date(date)
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
return (`${date.toString("yyyy-MM-ddThh:mm:ssZ")}`);
|
||||||
|
}
|
||||||
|
|
||||||
public static getFullWeekday(date: Date)
|
public static getFullWeekday(date: Date)
|
||||||
{
|
{
|
||||||
if(!(date instanceof Date))
|
if(!(date instanceof Date))
|
||||||
|
Reference in New Issue
Block a user