css changes for audits, pvs filters

This commit is contained in:
2023-02-15 16:29:54 -06:00
parent 57c02fe7c8
commit 3d3f7efa1d
9 changed files with 269 additions and 62 deletions

View File

@ -11,8 +11,8 @@
"@mui/material": "5.11.1", "@mui/material": "5.11.1",
"@mui/styles": "5.11.1", "@mui/styles": "5.11.1",
"@mui/system": "5.11.1", "@mui/system": "5.11.1",
"@mui/x-data-grid": "5.17.6", "@mui/x-data-grid": "5.17.23",
"@mui/x-data-grid-pro": "5.17.6", "@mui/x-data-grid-pro": "5.17.23",
"@mui/x-license-pro": "5.12.3", "@mui/x-license-pro": "5.12.3",
"@react-jvectormap/core": "1.0.1", "@react-jvectormap/core": "1.0.1",
"@react-jvectormap/unitedstates": "1.0.1", "@react-jvectormap/unitedstates": "1.0.1",

View File

@ -34,7 +34,7 @@ import ToggleButton from "@mui/material/ToggleButton";
import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import {useContext, useEffect, useState} from "react"; import React, {JSXElementConstructor, useContext, useEffect, useState} from "react";
import QContext from "QContext"; import QContext from "QContext";
import Client from "qqq/utils/qqq/Client"; import Client from "qqq/utils/qqq/Client";
import ValueUtils from "qqq/utils/qqq/ValueUtils"; import ValueUtils from "qqq/utils/qqq/ValueUtils";
@ -58,10 +58,128 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
const [limit, setLimit] = useState(1000); const [limit, setLimit] = useState(1000);
const [statusString, setStatusString] = useState("Loading audits..."); const [statusString, setStatusString] = useState("Loading audits...");
const [auditsByDate, setAuditsByDate] = useState([] as QRecord[][]); const [auditsByDate, setAuditsByDate] = useState([] as QRecord[][]);
const [auditDetailMap, setAuditDetailMap] = useState(null as Map<number, string[]>) const [auditDetailMap, setAuditDetailMap] = useState(null as Map<number, JSX.Element[]>)
const [fieldChangeMap, setFieldChangeMap] = useState(null as Map<number, JSX.Element>)
const [sortDirection, setSortDirection] = useState(localStorage.getItem("audit.sortDirection") === "true"); const [sortDirection, setSortDirection] = useState(localStorage.getItem("audit.sortDirection") === "true");
const {accentColor} = useContext(QContext); const {accentColor} = useContext(QContext);
function wrapValue(value: any): JSX.Element
{
return <span style={{fontWeight: "500", color: " rgb(123, 128, 154)"}}>{value}</span>
}
function wasValue(value: any): JSX.Element
{
return <span style={{fontWeight: "100", color: " rgb(123, 128, 154)"}}>{value}</span>
}
function getAuditDetailFieldChangeRow(qRecord: QRecord): JSX.Element | null
{
const message = qRecord.values.get("auditDetail.message");
const fieldName = qRecord.values.get("auditDetail.fieldName");
const oldValue = qRecord.values.get("auditDetail.oldValue");
const newValue = qRecord.values.get("auditDetail.newValue");
if(fieldName && (oldValue !== null || newValue !== null))
{
const fieldLabel = tableMetaData?.fields?.get(fieldName)?.label ?? fieldName
return (<tr><td>{fieldLabel}</td><td>{oldValue}</td><td>{newValue}</td></tr>)
}
return (null);
}
function getAuditDetailElement(qRecord: QRecord): JSX.Element | null
{
const message = qRecord.values.get("auditDetail.message");
const fieldName = qRecord.values.get("auditDetail.fieldName");
const oldValue = qRecord.values.get("auditDetail.oldValue");
const newValue = qRecord.values.get("auditDetail.newValue");
if(fieldName && (oldValue !== null || newValue !== null))
{
const fieldLabel = tableMetaData?.fields?.get(fieldName)?.label ?? fieldName;
if(oldValue !== undefined && newValue !== undefined)
{
return (<>{fieldLabel}: Changed from {(oldValue)} to <b>{(newValue)}</b></>);
}
else if(newValue !== undefined)
{
return (<>{fieldLabel}: Set to <b>{(newValue)}</b></>);
}
else if(oldValue !== undefined)
{
return (<>{fieldLabel}: Removed value {(oldValue)}</>);
}
/*
const fieldLabel = <span style={{fontWeight: "700", color: "rgb(52, 71, 103)"}}>{tableMetaData?.fields?.get(fieldName)?.label ?? fieldName}</span>;
if(oldValue !== undefined && newValue !== undefined)
{
return (<>Changed {fieldLabel} from {wrapValue(oldValue)} to {wrapValue(newValue)}</>);
}
else if(newValue !== undefined)
{
return (<>Set {fieldLabel} to {wrapValue(newValue)}</>);
}
else if(oldValue !== undefined)
{
return (<>Removed {fieldLabel} value {wrapValue(oldValue)}</>);
}
*/
/*
const fieldLabel = <span style={{fontWeight: "700", color: "rgb(52, 71, 103)"}}>{tableMetaData?.fields?.get(fieldName)?.label ?? fieldName}:</span>;
if(oldValue !== undefined && newValue !== undefined)
{
return (<>{fieldLabel} {wrapValue(newValue)} (was {oldValue})</>);
}
else if(newValue !== undefined)
{
return (<>{fieldLabel} {wrapValue(newValue)} (was --)</>);
}
else if(oldValue !== undefined)
{
return (<>{fieldLabel} {wrapValue("--")} (was {oldValue})</>);
}
*/
/*
const fieldLabel = <span style={{fontWeight: "700", color: "rgb(52, 71, 103)"}}>{tableMetaData?.fields?.get(fieldName)?.label ?? fieldName}:</span>;
if(oldValue !== undefined && newValue !== undefined)
{
return (<>{fieldLabel} {newValue} {wasValue(`(was ${oldValue})`)}</>);
}
else if(newValue !== undefined)
{
return (<>{fieldLabel} {newValue} {wasValue("(was --)")}</>);
}
else if(oldValue !== undefined)
{
return (<>{fieldLabel} -- {wasValue(`(was ${oldValue})`)}</>);
}
*/
/*
const fieldLabel = <span style={{fontWeight: "700", color: "rgb(52, 71, 103)"}}>{tableMetaData?.fields?.get(fieldName)?.label ?? fieldName}:</span>;
if(oldValue !== undefined && newValue !== undefined)
{
return (<>{fieldLabel} Changed to {wrapValue(newValue)} (was {oldValue})</>);
}
else if(newValue !== undefined)
{
return (<>{fieldLabel} Set to {wrapValue(newValue)}</>);
}
else if(oldValue !== undefined)
{
return (<>{fieldLabel} Removed value (was {oldValue})</>);
}
*/
}
else if(message)
{
return (<>{message}</>);
}
return (null);
}
useEffect(() => useEffect(() =>
{ {
(async () => (async () =>
@ -112,7 +230,8 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
// group the audits by auditId (e.g., this is a list that joined audit & auditDetail, so un-flatten it) // // group the audits by auditId (e.g., this is a list that joined audit & auditDetail, so un-flatten it) //
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
const unflattenedAudits: QRecord[] = [] const unflattenedAudits: QRecord[] = []
const detailMap: Map<number, string[]> = new Map(); const detailMap: Map<number, JSX.Element[]> = new Map();
const fieldChangeRowsMap: Map<number, JSX.Element[]> = new Map();
for (let i = 0; i < audits.length; i++) for (let i = 0; i < audits.length; i++)
{ {
let id = audits[i].values.get("id"); let id = audits[i].values.get("id");
@ -121,7 +240,7 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
unflattenedAudits.push(audits[i]); unflattenedAudits.push(audits[i]);
} }
let auditDetail = audits[i].values.get("auditDetail.message"); let auditDetail = getAuditDetailElement(audits[i]);
if(auditDetail) if(auditDetail)
{ {
if(!detailMap.has(id)) if(!detailMap.has(id))
@ -131,10 +250,45 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
detailMap.get(id).push(auditDetail) detailMap.get(id).push(auditDetail)
} }
// table version, probably not to commit
let fieldChangeRow = getAuditDetailFieldChangeRow(audits[i]);
if(auditDetail)
{
if(!fieldChangeRowsMap.has(id))
{
fieldChangeRowsMap.set(id, []);
}
// fieldChangeRowsMap.get(id).push(fieldChangeRow)
}
} }
audits = unflattenedAudits; audits = unflattenedAudits;
setAuditDetailMap(detailMap); setAuditDetailMap(detailMap);
console.log(detailMap);
const fieldChangeMap: Map<number, JSX.Element> = new Map();
for (let i = 0; i < unflattenedAudits.length; i++)
{
let id = unflattenedAudits[i].values.get("id");
if(fieldChangeRowsMap.has(id) && fieldChangeRowsMap.get(id).length > 0)
{
const fieldChangeTable = (
<table style={{fontSize: "0.875rem"}} className="auditDetailTable" cellSpacing="0">
<thead>
<tr>
<td>Field</td>
<td>Old Value</td>
<td>New Value</td>
</tr>
</thead>
<tbody>
{fieldChangeRowsMap.get(id).map((row, key) => <React.Fragment key={key}>{row}</React.Fragment>)}
</tbody>
</table>
)
fieldChangeMap.set(id, fieldChangeTable);
}
}
setFieldChangeMap(fieldChangeMap)
////////////////////////////// //////////////////////////////
// group the audits by date // // group the audits by date //
@ -265,11 +419,11 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
<Avatar sx={{bgcolor: accentColor, zIndex: 2}}> <Avatar sx={{bgcolor: accentColor, zIndex: 2}}>
<Icon>check</Icon> <Icon>check</Icon>
</Avatar> </Avatar>
<Box p={1}> <Box p={1} width="100%">
<Box fontSize="0.875rem" color="rgb(123, 128, 154)"> <Box fontSize="0.875rem" color="rgb(123, 128, 154)">
{timestampParts[1]} {timestampParts[2]} {timestampParts[3]} &nbsp; {audit.displayValues.get("auditUserId")} {timestampParts[1]} {timestampParts[2]} {timestampParts[3]} &nbsp; {audit.displayValues.get("auditUserId")}
</Box> </Box>
<Box fontSize="1rem"> <Box fontSize="0.875rem">
{audit.values.get("message")} {audit.values.get("message")}
</Box> </Box>
<Box fontSize="0.875rem"> <Box fontSize="0.875rem">
@ -282,6 +436,9 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
} }
</ul> </ul>
</Box> </Box>
{
fieldChangeMap.has(audit.values.get("id")) && fieldChangeMap.get(audit.values.get("id"))
}
</Box> </Box>
</Box> </Box>
); );

View File

@ -413,7 +413,7 @@ function SavedFilters({qController, metaData, tableMetaData, currentSavedFilter,
{currentSavedFilter.values.get("label")} {currentSavedFilter.values.get("label")}
{ {
filterIsModified && ( filterIsModified && (
<Tooltip sx={{cursor: "pointer"}} title={"The current has been modified, click \"Save...\" to save the changes."}> <Tooltip sx={{cursor: "pointer"}} title={"The current filter has been modified, click \"Save...\" to save the changes."}>
<FiberManualRecord sx={{color: "orange", paddingLeft: "2px", paddingTop: "4px"}} /> <FiberManualRecord sx={{color: "orange", paddingLeft: "2px", paddingTop: "4px"}} />
</Tooltip> </Tooltip>
) )

View File

@ -396,9 +396,9 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
{ {
widgetMetaDataList.map((widgetMetaData, i) => ( widgetMetaDataList.map((widgetMetaData, i) => (
omitWrappingGridContainer omitWrappingGridContainer
? renderWidget(widgetMetaData, i) ? widgetMetaData && renderWidget(widgetMetaData, i)
: :
<Grid id={widgetMetaData.name} key={`${widgetMetaData.name}-${i}`} item lg={widgetMetaData.gridColumns ? widgetMetaData.gridColumns : 12} xs={12} sx={{display: "flex", alignItems: "stretch", scrollMarginTop: "100px"}}> widgetMetaData && <Grid id={widgetMetaData.name} key={`${widgetMetaData.name}-${i}`} item lg={widgetMetaData.gridColumns ? widgetMetaData.gridColumns : 12} xs={12} sx={{display: "flex", alignItems: "stretch", scrollMarginTop: "100px"}}>
{renderWidget(widgetMetaData, i)} {renderWidget(widgetMetaData, i)}
</Grid> </Grid>
)) ))

View File

@ -97,6 +97,11 @@ function ParentWidget({urlParams, widgetMetaData, widgetIndex, data, reloadWidge
reloadWidgetCallback(widgetIndex, data); reloadWidgetCallback(widgetIndex, data);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// if this parent widget is in card form, and its children are too, then we need some px //
///////////////////////////////////////////////////////////////////////////////////////////
const px = (widgetMetaData && widgetMetaData.isCard && widgets && widgets[0] && widgets[0].isCard) ? 3 : 0;
// @ts-ignore // @ts-ignore
return ( return (
qInstance && data ? ( qInstance && data ? (
@ -106,7 +111,7 @@ function ParentWidget({urlParams, widgetMetaData, widgetIndex, data, reloadWidge
storeDropdownSelections={storeDropdownSelections} storeDropdownSelections={storeDropdownSelections}
reloadWidgetCallback={parentReloadWidgetCallback} reloadWidgetCallback={parentReloadWidgetCallback}
> >
<Box sx={{height: "100%", width: "100%"}}> <Box sx={{height: "100%", width: "100%"}} px={px}>
<DashboardWidgets widgetMetaDataList={widgets} entityPrimaryKey={entityPrimaryKey} tableName={tableName} childUrlParams={childUrlParams} areChildren={true} parentWidgetMetaData={widgetMetaData}/> <DashboardWidgets widgetMetaDataList={widgets} entityPrimaryKey={entityPrimaryKey} tableName={tableName} childUrlParams={childUrlParams} areChildren={true} parentWidgetMetaData={widgetMetaData}/>
</Box> </Box>
</Widget> </Widget>

View File

@ -34,7 +34,7 @@ import {QJobRunning} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJob
import {QJobStarted} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobStarted"; import {QJobStarted} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobStarted";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter"; import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
import {Button, CircularProgress, Icon, TablePagination} from "@mui/material"; import {Alert, Button, CircularProgress, Icon, TablePagination} from "@mui/material";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Card from "@mui/material/Card"; import Card from "@mui/material/Card";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
@ -405,8 +405,16 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, isReport,
component.type === QComponentType.BULK_EDIT_FORM && ( component.type === QComponentType.BULK_EDIT_FORM && (
tableMetaData && localTableSections ? tableMetaData && localTableSections ?
<Grid container spacing={3} mt={2}> <Grid container spacing={3} mt={2}>
{
localTableSections.length == 0 &&
<Grid item xs={12}>
<Alert color="error">There are no editable fields on this table.</Alert>
</Grid>
}
<Grid item xs={12} lg={3}> <Grid item xs={12} lg={3}>
<QRecordSidebar tableSections={localTableSections} stickyTop="20px" /> {
localTableSections.length > 0 && <QRecordSidebar tableSections={localTableSections} stickyTop="20px" />
}
</Grid> </Grid>
<Grid item xs={12} lg={9}> <Grid item xs={12} lg={9}>

View File

@ -56,7 +56,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
CUSTOM = "Custom", CUSTOM = "Custom",
} }
const delimiterToCharacterMap: {[key: string]: string} = {}; const delimiterToCharacterMap: { [key: string]: string } = {};
delimiterToCharacterMap[Delimiter.COMMA] = "[,\n\r]"; delimiterToCharacterMap[Delimiter.COMMA] = "[,\n\r]";
delimiterToCharacterMap[Delimiter.TAB] = "[\t,\n,\r]"; delimiterToCharacterMap[Delimiter.TAB] = "[\t,\n,\r]";
@ -87,14 +87,14 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
{ {
event.target.blur(); event.target.blur();
setPasteModalIsOpen(true); setPasteModalIsOpen(true);
} };
const applyValue = (item: GridFilterItem) => const applyValue = (item: GridFilterItem) =>
{ {
console.log(`updating grid values: ${JSON.stringify(item.value)}`); console.log(`updating grid values: ${JSON.stringify(item.value)}`);
setGridFilterItem(item); setGridFilterItem(item);
props.applyValue(item); props.applyValue(item);
} };
const clearData = () => const clearData = () =>
{ {
@ -104,7 +104,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
setInputText(""); setInputText("");
setDetectedText(""); setDetectedText("");
setCustomDelimiterValue(""); setCustomDelimiterValue("");
setPasteModalIsOpen(false) setPasteModalIsOpen(false);
}; };
const handleCancelClicked = () => const handleCancelClicked = () =>
@ -115,21 +115,21 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
const handleSaveClicked = () => const handleSaveClicked = () =>
{ {
if(gridFilterItem) if (gridFilterItem)
{ {
//////////////////////////////////////// ////////////////////////////////////////
// if numeric remove any non-numerics // // if numeric remove any non-numerics //
//////////////////////////////////////// ////////////////////////////////////////
let saveData = []; let saveData = [];
for(let i=0; i<chipData.length; i++) for (let i = 0; i < chipData.length; i++)
{ {
if(type !== "number" || ! Number.isNaN(Number(chipData[i]))) if (type !== "number" || !Number.isNaN(Number(chipData[i])))
{ {
saveData.push(chipData[i]); saveData.push(chipData[i]);
} }
} }
if(gridFilterItem.value) if (gridFilterItem.value)
{ {
gridFilterItem.value = [...gridFilterItem.value, ...saveData]; gridFilterItem.value = [...gridFilterItem.value, ...saveData];
} }
@ -155,7 +155,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
console.log(`Delimiter Changed to ${JSON.stringify(newDelimiter)}`); console.log(`Delimiter Changed to ${JSON.stringify(newDelimiter)}`);
setDelimiter(newDelimiter); setDelimiter(newDelimiter);
if(newDelimiter === Delimiter.CUSTOM) if (newDelimiter === Delimiter.CUSTOM)
{ {
setDelimiterCharacter(customDelimiterValue); setDelimiterCharacter(customDelimiterValue);
} }
@ -184,11 +184,11 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
const calculateAutomaticDelimiter = (text: string): string => const calculateAutomaticDelimiter = (text: string): string =>
{ {
const buckets = new Map(); const buckets = new Map();
for(let i=0; i<text.length; i++) for (let i = 0; i < text.length; i++)
{ {
let bucketName = ""; let bucketName = "";
switch(text.charAt(i)) switch (text.charAt(i))
{ {
case "\t": case "\t":
bucketName = Delimiter.TAB; bucketName = Delimiter.TAB;
@ -208,7 +208,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
break; break;
} }
if(bucketName !== "") if (bucketName !== "")
{ {
let currentCount = (buckets.has(bucketName)) ? buckets.get(bucketName) : 0; let currentCount = (buckets.has(bucketName)) ? buckets.get(bucketName) : 0;
buckets.set(bucketName, currentCount + 1); buckets.set(bucketName, currentCount + 1);
@ -220,10 +220,10 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
/////////////////////// ///////////////////////
let highestCount = 0; let highestCount = 0;
let delimiter = Delimiter.COMMA; let delimiter = Delimiter.COMMA;
for(let j = 0; j < delimiterDropdownOptions.length; j++) for (let j = 0; j < delimiterDropdownOptions.length; j++)
{ {
let bucketName = delimiterDropdownOptions[j]; let bucketName = delimiterDropdownOptions[j];
if(buckets.has(bucketName) && buckets.get(bucketName) > highestCount) if (buckets.has(bucketName) && buckets.get(bucketName) > highestCount)
{ {
delimiter = bucketName; delimiter = bucketName;
highestCount = buckets.get(bucketName); highestCount = buckets.get(bucketName);
@ -231,7 +231,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
} }
setDetectedText(`${delimiter} Detected`); setDetectedText(`${delimiter} Detected`);
return(delimiterToCharacterMap[delimiter]); return (delimiterToCharacterMap[delimiter]);
}; };
useEffect(() => useEffect(() =>
@ -242,10 +242,10 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// if no delimiter already set in the state, call function to determine it // // if no delimiter already set in the state, call function to determine it //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
if (! currentDelimiter || currentDelimiter === Delimiter.DETECT_AUTOMATICALLY) if (!currentDelimiter || currentDelimiter === Delimiter.DETECT_AUTOMATICALLY)
{ {
currentDelimiterCharacter = calculateAutomaticDelimiter(inputText); currentDelimiterCharacter = calculateAutomaticDelimiter(inputText);
if(! currentDelimiterCharacter) if (!currentDelimiterCharacter)
{ {
return; return;
} }
@ -254,7 +254,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
setDelimiter(Delimiter.DETECT_AUTOMATICALLY); setDelimiter(Delimiter.DETECT_AUTOMATICALLY);
setDelimiterCharacter(currentDelimiterCharacter); setDelimiterCharacter(currentDelimiterCharacter);
} }
else if(currentDelimiter === Delimiter.CUSTOM) else if (currentDelimiter === Delimiter.CUSTOM)
{ {
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// if custom, make sure to split on new lines too // // if custom, make sure to split on new lines too //
@ -264,7 +264,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
console.log(`current delimiter is: ${currentDelimiter}, delimiting on: ${currentDelimiterCharacter}`); console.log(`current delimiter is: ${currentDelimiter}, delimiting on: ${currentDelimiterCharacter}`);
let regex = new RegExp(currentDelimiterCharacter) let regex = new RegExp(currentDelimiterCharacter);
let parts = inputText.split(regex); let parts = inputText.split(regex);
let chipData = [] as string[]; let chipData = [] as string[];
@ -272,7 +272,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
// if delimiter is empty string, dont split anything // // if delimiter is empty string, dont split anything //
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
setErrorText(""); setErrorText("");
if(currentDelimiterCharacter !== "") if (currentDelimiterCharacter !== "")
{ {
for (let i = 0; i < parts.length; i++) for (let i = 0; i < parts.length; i++)
{ {
@ -284,7 +284,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// if numeric, check that first before pushing as a chip // // if numeric, check that first before pushing as a chip //
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
if(type === "number" && Number.isNaN(Number(part))) if (type === "number" && Number.isNaN(Number(part)))
{ {
setErrorText("Some values are not numbers"); setErrorText("Some values are not numbers");
} }
@ -301,7 +301,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
{ {
props && props &&
( (
<Box id="testId" sx={{width: "100%", display: "inline-flex", flexDirection: "row", alignItems: "end", height: 48}} > <Box id="testId" sx={{width: "100%", display: "inline-flex", flexDirection: "row", alignItems: "end", height: 48}}>
<GridFilterInputMultipleValue <GridFilterInputMultipleValue
sx={{width: "100%"}} sx={{width: "100%"}}
variant="standard" variant="standard"
@ -351,7 +351,8 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
<FormControl sx={{m: 1, width: "100%"}}> <FormControl sx={{m: 1, width: "100%"}}>
<ChipTextField <ChipTextField
handleChipChange={() => handleChipChange={() =>
{}} {
}}
chipData={chipData} chipData={chipData}
chipType={type} chipType={type}
multiline multiline
@ -368,7 +369,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
<Grid container pl={3} pr={3} justifyContent="center" alignItems="stretch" sx={{display: "flex", height: "100%"}}> <Grid container pl={3} pr={3} justifyContent="center" alignItems="stretch" sx={{display: "flex", height: "100%"}}>
<Grid item pl={1} pr={3} xs={6} lg={6} sx={{width: "100%", display: "flex", flexDirection: "column", flexGrow: 1}}> <Grid item pl={1} pr={3} xs={6} lg={6} sx={{width: "100%", display: "flex", flexDirection: "column", flexGrow: 1}}>
<Box sx={{display: "inline-flex", alignItems: "baseline"}}> <Box sx={{display: "inline-flex", alignItems: "baseline"}}>
<FormControl sx={{mt: 2, width: "50%"}}> <FormControl sx={{mt: 2, width: "50%"}}>
<InputLabel htmlFor="select-native"> <InputLabel htmlFor="select-native">
SEPARATOR SEPARATOR
</InputLabel> </InputLabel>
@ -390,7 +391,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
))} ))}
</Select> </Select>
</FormControl> </FormControl>
{ delimiter === Delimiter.CUSTOM.valueOf() && ( {delimiter === Delimiter.CUSTOM.valueOf() && (
<FormControl sx={{pl: 2, top: 5, width: "50%"}}> <FormControl sx={{pl: 2, top: 5, width: "50%"}}>
<TextField <TextField
@ -404,7 +405,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
/> />
</FormControl> </FormControl>
)} )}
{ inputText && delimiter === Delimiter.DETECT_AUTOMATICALLY.valueOf() && ( {inputText && delimiter === Delimiter.DETECT_AUTOMATICALLY.valueOf() && (
<Typography pl={2} variant="button" sx={{top: "1px", textTransform: "revert"}}> <Typography pl={2} variant="button" sx={{top: "1px", textTransform: "revert"}}>
<i>{detectedText}</i> <i>{detectedText}</i>
@ -436,7 +437,7 @@ function CustomIsAnyInput(type: "number" | "text", props: GridFilterInputValuePr
onClickHandler={handleCancelClicked} onClickHandler={handleCancelClicked}
iconName="cancel" iconName="cancel"
disabled={false} /> disabled={false} />
<QSaveButton onClickHandler={handleSaveClicked} label="Add Filters" disabled={false}/> <QSaveButton onClickHandler={handleSaveClicked} label="Add Filters" disabled={false} />
</Grid> </Grid>
</Box> </Box>
</Card> </Card>
@ -503,7 +504,7 @@ let endsWith = gridStringOperators.splice(0, 1)[0];
// remove default isany operator // // remove default isany operator //
/////////////////////////////////// ///////////////////////////////////
gridStringOperators.splice(2, 1)[0]; gridStringOperators.splice(2, 1)[0];
gridStringOperators = [ equals, stringNotEqualsOperator, contains, stringNotContainsOperator, startsWith, stringNotStartsWithOperator, endsWith, stringNotEndWithOperator, ...gridStringOperators, stringIsAnyOfOperator ]; gridStringOperators = [equals, stringNotEqualsOperator, contains, stringNotContainsOperator, startsWith, stringNotStartsWithOperator, endsWith, stringNotEndWithOperator, ...gridStringOperators, stringIsAnyOfOperator];
export const QGridStringOperators = gridStringOperators; export const QGridStringOperators = gridStringOperators;
@ -517,10 +518,10 @@ function InputNumberInterval(props: GridFilterInputValueProps)
const {item, applyValue, focusElementRef = null} = props; const {item, applyValue, focusElementRef = null} = props;
const filterTimeout = useRef<any>(); const filterTimeout = useRef<any>();
const [ filterValueState, setFilterValueState ] = useState<[ string, string ]>( const [filterValueState, setFilterValueState] = useState<[string, string]>(
item.value ?? "", item.value ?? "",
); );
const [ applying, setIsApplying ] = useState(false); const [applying, setIsApplying] = useState(false);
useEffect(() => useEffect(() =>
{ {
@ -532,20 +533,20 @@ function InputNumberInterval(props: GridFilterInputValueProps)
useEffect(() => useEffect(() =>
{ {
const itemValue = item.value ?? [ undefined, undefined ]; const itemValue = item.value ?? [undefined, undefined];
setFilterValueState(itemValue); setFilterValueState(itemValue);
}, [ item.value ]); }, [item.value]);
const updateFilterValue = (lowerBound: string, upperBound: string) => const updateFilterValue = (lowerBound: string, upperBound: string) =>
{ {
clearTimeout(filterTimeout.current); clearTimeout(filterTimeout.current);
setFilterValueState([ lowerBound, upperBound ]); setFilterValueState([lowerBound, upperBound]);
setIsApplying(true); setIsApplying(true);
filterTimeout.current = setTimeout(() => filterTimeout.current = setTimeout(() =>
{ {
setIsApplying(false); setIsApplying(false);
applyValue({...item, value: [ lowerBound, upperBound ]}); applyValue({...item, value: [lowerBound, upperBound]});
}, SUBMIT_FILTER_STROKE_TIME); }, SUBMIT_FILTER_STROKE_TIME);
}; };
@ -628,7 +629,7 @@ const numericIsAnyOfOperator: GridFilterOperator = {
////////////////////////////// //////////////////////////////
let gridNumericOperators = getGridNumericOperators(); let gridNumericOperators = getGridNumericOperators();
gridNumericOperators.splice(8, 1)[0]; gridNumericOperators.splice(8, 1)[0];
export const QGridNumericOperators = [ ...gridNumericOperators, betweenOperator, notBetweenOperator, numericIsAnyOfOperator ]; export const QGridNumericOperators = [...gridNumericOperators, betweenOperator, notBetweenOperator, numericIsAnyOfOperator];
/////////////////////// ///////////////////////
// boolean operators // // boolean operators //
@ -657,7 +658,7 @@ const booleanNotEmptyOperator: GridFilterOperator = {
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
}; };
export const QGridBooleanOperators = [ booleanTrueOperator, booleanFalseOperator, booleanEmptyOperator, booleanNotEmptyOperator ]; export const QGridBooleanOperators = [booleanTrueOperator, booleanFalseOperator, booleanEmptyOperator, booleanNotEmptyOperator];
/////////////////////////////////////// ///////////////////////////////////////
@ -671,9 +672,9 @@ function InputPossibleValueSourceSingle(tableName: string, field: QFieldMetaData
console.log("Item.value? " + item.value); console.log("Item.value? " + item.value);
const filterTimeout = useRef<any>(); const filterTimeout = useRef<any>();
const [ filterValueState, setFilterValueState ] = useState<any>(item.value ?? null); const [filterValueState, setFilterValueState] = useState<any>(item.value ?? null);
const [ selectedPossibleValue, setSelectedPossibleValue ] = useState((item.value ?? null) as QPossibleValue); const [selectedPossibleValue, setSelectedPossibleValue] = useState((item.value ?? null) as QPossibleValue);
const [ applying, setIsApplying ] = useState(false); const [applying, setIsApplying] = useState(false);
useEffect(() => useEffect(() =>
{ {
@ -687,7 +688,7 @@ function InputPossibleValueSourceSingle(tableName: string, field: QFieldMetaData
{ {
const itemValue = item.value ?? null; const itemValue = item.value ?? null;
setFilterValueState(itemValue); setFilterValueState(itemValue);
}, [ item.value ]); }, [item.value]);
const updateFilterValue = (value: QPossibleValue) => const updateFilterValue = (value: QPossibleValue) =>
{ {
@ -742,8 +743,8 @@ function InputPossibleValueSourceMultiple(tableName: string, field: QFieldMetaDa
console.log("Item.value? " + item.value); console.log("Item.value? " + item.value);
const filterTimeout = useRef<any>(); const filterTimeout = useRef<any>();
const [ selectedPossibleValues, setSelectedPossibleValues ] = useState(item.value as QPossibleValue[]); const [selectedPossibleValues, setSelectedPossibleValues] = useState(item.value as QPossibleValue[]);
const [ applying, setIsApplying ] = useState(false); const [applying, setIsApplying] = useState(false);
useEffect(() => useEffect(() =>
{ {
@ -756,7 +757,7 @@ function InputPossibleValueSourceMultiple(tableName: string, field: QFieldMetaDa
useEffect(() => useEffect(() =>
{ {
const itemValue = item.value ?? null; const itemValue = item.value ?? null;
}, [ item.value ]); }, [item.value]);
const updateFilterValue = (value: QPossibleValue) => const updateFilterValue = (value: QPossibleValue) =>
{ {
@ -797,6 +798,31 @@ function InputPossibleValueSourceMultiple(tableName: string, field: QFieldMetaDa
); );
} }
const getPvsValueString = (value: GridFilterItem["value"]): string =>
{
console.log("get pvs value", value);
if (value && value.length)
{
let labels = [] as string[];
for (let i = 0; i < value.length; i++)
{
if(value[i] && value[i].label)
{
labels.push(value[i].label);
}
else
{
labels.push(value);
}
}
return (labels.join(", "));
}
else if (value && value.label)
{
return (value.label);
}
return (value);
};
////////////////////////////////// //////////////////////////////////
// possible value set operators // // possible value set operators //
@ -808,34 +834,40 @@ export const buildQGridPvsOperators = (tableName: string, field: QFieldMetaData)
label: "is", label: "is",
value: "is", value: "is",
getApplyFilterFn: () => null, getApplyFilterFn: () => null,
getValueAsString: getPvsValueString,
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceSingle(tableName, field, props) InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceSingle(tableName, field, props)
}, },
{ {
label: "is not", label: "is not",
value: "isNot", value: "isNot",
getApplyFilterFn: () => null, getApplyFilterFn: () => null,
getValueAsString: getPvsValueString,
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceSingle(tableName, field, props) InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceSingle(tableName, field, props)
}, },
{ {
label: "is any of", label: "is any of",
value: "isAnyOf", value: "isAnyOf",
getValueAsString: getPvsValueString,
getApplyFilterFn: () => null, getApplyFilterFn: () => null,
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceMultiple(tableName, field, props) InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceMultiple(tableName, field, props)
}, },
{ {
label: "is none of", label: "is none of",
value: "isNone", value: "isNone",
getValueAsString: getPvsValueString,
getApplyFilterFn: () => null, getApplyFilterFn: () => null,
InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceMultiple(tableName, field, props) InputComponent: (props: GridFilterInputValueProps<GridApiCommunity>) => InputPossibleValueSourceMultiple(tableName, field, props)
}, },
{ {
label: "is empty", label: "is empty",
value: "isEmpty", value: "isEmpty",
getValueAsString: getPvsValueString,
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
}, },
{ {
label: "is not empty", label: "is not empty",
value: "isNotEmpty", value: "isNotEmpty",
getValueAsString: getPvsValueString,
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
} }
]); ]);

View File

@ -963,9 +963,9 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
Refresh Refresh
</Button> </Button>
</div> </div>
<GridToolbarColumnsButton /> <GridToolbarColumnsButton nonce={undefined} onResize={undefined} onResizeCapture={undefined} />
<div style={{position: "relative"}}> <div style={{position: "relative"}}>
<GridToolbarFilterButton /> <GridToolbarFilterButton nonce={undefined} onResize={undefined} onResizeCapture={undefined} />
{ {
hasValidFilters && ( hasValidFilters && (
@ -991,8 +991,8 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
</div> </div>
) )
} }
<GridToolbarDensitySelector /> <GridToolbarDensitySelector nonce={undefined} onResize={undefined} onResizeCapture={undefined} />
<GridToolbarExportContainer> <GridToolbarExportContainer nonce={undefined} onResize={undefined} onResizeCapture={undefined}>
<ExportMenuItem format="csv" /> <ExportMenuItem format="csv" />
<ExportMenuItem format="xlsx" /> <ExportMenuItem format="xlsx" />
<ExportMenuItem format="json" /> <ExportMenuItem format="json" />

View File

@ -154,6 +154,11 @@
align-items: flex-end; align-items: flex-end;
} }
.MuiDataGrid-filterFormValueInput > div
{
height: auto;
}
/* make filter dropdowns a bit wider, less likely to need to wrap. */ /* make filter dropdowns a bit wider, less likely to need to wrap. */
.MuiDataGrid-filterForm .MuiDataGrid-filterFormColumnInput .MuiDataGrid-filterForm .MuiDataGrid-filterFormColumnInput
{ {