mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 13:20:43 +00:00
Checkpoint; nearing completion of custom filter panel
This commit is contained in:
@ -38,6 +38,7 @@ interface Props
|
||||
tableName?: string;
|
||||
processName?: string;
|
||||
fieldName: string;
|
||||
overrideId?: string;
|
||||
fieldLabel: string;
|
||||
inForm: boolean;
|
||||
initialValue?: any;
|
||||
@ -70,29 +71,34 @@ DynamicSelect.defaultProps = {
|
||||
|
||||
const qController = Client.getInstance();
|
||||
|
||||
function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
|
||||
function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
|
||||
{
|
||||
const [ open, setOpen ] = useState(false);
|
||||
const [ options, setOptions ] = useState<readonly QPossibleValue[]>([]);
|
||||
const [ searchTerm, setSearchTerm ] = useState(null);
|
||||
const [ firstRender, setFirstRender ] = useState(true);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [options, setOptions] = useState<readonly QPossibleValue[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState(null);
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// default value - needs to be an array (from initialValues (array) prop) for multiple mode - //
|
||||
// else non-multiple, assume we took in an initialValue (id) and initialDisplayValue (label), //
|
||||
// and build a little object that looks like a possibleValue out of those //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const [defaultValue, _] = isMultiple ? useState(initialValues ?? undefined)
|
||||
let [defaultValue, _] = isMultiple ? useState(initialValues ?? undefined)
|
||||
: useState(initialValue && initialDisplayValue ? [{id: initialValue, label: initialDisplayValue}] : null);
|
||||
|
||||
if (isMultiple && defaultValue === null)
|
||||
{
|
||||
defaultValue = [];
|
||||
}
|
||||
|
||||
// const loading = open && options.length === 0;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [ switchChecked, setSwitchChecked ] = useState(false);
|
||||
const [ isDisabled, setIsDisabled ] = useState(!isEditable || bulkEditMode);
|
||||
const [switchChecked, setSwitchChecked] = useState(false);
|
||||
const [isDisabled, setIsDisabled] = useState(!isEditable || bulkEditMode);
|
||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
||||
|
||||
let setFieldValueRef: (field: string, value: any, shouldValidate?: boolean) => void = null;
|
||||
if(inForm)
|
||||
if (inForm)
|
||||
{
|
||||
const {setFieldValue} = useFormikContext();
|
||||
setFieldValueRef = setFieldValue;
|
||||
@ -239,9 +245,11 @@ function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, i
|
||||
bulkEditSwitchChangeHandler(fieldName, newSwitchValue);
|
||||
};
|
||||
|
||||
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
|
||||
|
||||
const autocomplete = (
|
||||
<Autocomplete
|
||||
id={fieldName}
|
||||
id={overrideId ?? fieldName}
|
||||
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
|
||||
open={open}
|
||||
fullWidth
|
||||
@ -291,6 +299,8 @@ function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, i
|
||||
disabled={isDisabled}
|
||||
multiple={isMultiple}
|
||||
disableCloseOnSelect={isMultiple}
|
||||
limitTags={5}
|
||||
slotProps={{popper: {className: "DynamicSelectPopper"}}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
|
@ -19,6 +19,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
|
||||
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
|
||||
@ -28,7 +29,7 @@ import Button from "@mui/material/Button/Button";
|
||||
import Icon from "@mui/material/Icon/Icon";
|
||||
import {GridFilterPanelProps, GridSlotsComponentsProps} from "@mui/x-data-grid-pro";
|
||||
import React, {forwardRef, useReducer} from "react";
|
||||
import {FilterCriteriaRow} from "qqq/components/query/FilterCriteriaRow";
|
||||
import {FilterCriteriaRow, getDefaultCriteriaValue} from "qqq/components/query/FilterCriteriaRow";
|
||||
|
||||
|
||||
declare module "@mui/x-data-grid"
|
||||
@ -39,6 +40,7 @@ declare module "@mui/x-data-grid"
|
||||
interface FilterPanelPropsOverrides
|
||||
{
|
||||
tableMetaData: QTableMetaData;
|
||||
metaData: QInstance;
|
||||
queryFilter: QQueryFilter;
|
||||
updateFilter: (newFilter: QQueryFilter) => void;
|
||||
}
|
||||
@ -66,9 +68,9 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
|
||||
{
|
||||
setTimeout(() =>
|
||||
{
|
||||
console.log(`Try to focus ${criteriaId - 1}`);
|
||||
try
|
||||
{
|
||||
// console.log(`Try to focus ${criteriaId - 1}`);
|
||||
document.getElementById(`field-${criteriaId - 1}`).focus();
|
||||
}
|
||||
catch (e)
|
||||
@ -80,7 +82,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
|
||||
|
||||
const addCriteria = () =>
|
||||
{
|
||||
const qFilterCriteriaWithId = new QFilterCriteriaWithId(null, QCriteriaOperator.EQUALS, [""]);
|
||||
const qFilterCriteriaWithId = new QFilterCriteriaWithId(null, QCriteriaOperator.EQUALS, getDefaultCriteriaValue());
|
||||
qFilterCriteriaWithId.id = criteriaId++;
|
||||
console.log(`adding criteria id ${qFilterCriteriaWithId.id}`);
|
||||
queryFilter.criteria.push(qFilterCriteriaWithId);
|
||||
@ -98,8 +100,29 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
|
||||
|
||||
if (queryFilter.criteria.length == 0)
|
||||
{
|
||||
/////////////////////////////////////////////
|
||||
// make sure there's at least one criteria //
|
||||
/////////////////////////////////////////////
|
||||
addCriteria();
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure all criteria have an id on them (to be used as react component keys) //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
let updatedAny = false;
|
||||
for (let i = 0; i < queryFilter.criteria.length; i++)
|
||||
{
|
||||
if (!queryFilter.criteria[i].id)
|
||||
{
|
||||
queryFilter.criteria[i].id = criteriaId++;
|
||||
}
|
||||
}
|
||||
if (updatedAny)
|
||||
{
|
||||
props.updateFilter(queryFilter);
|
||||
}
|
||||
}
|
||||
|
||||
if(queryFilter.criteria.length == 1 && !queryFilter.criteria[0].fieldName)
|
||||
{
|
||||
@ -149,6 +172,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
|
||||
id={criteria.id}
|
||||
index={index}
|
||||
tableMetaData={props.tableMetaData}
|
||||
metaData={props.metaData}
|
||||
criteria={criteria}
|
||||
booleanOperator={booleanOperator}
|
||||
updateCriteria={(newCriteria, needDebounce) => updateCriteria(newCriteria, index, needDebounce)}
|
||||
|
@ -36,11 +36,12 @@ import ChipTextField from "qqq/components/forms/ChipTextField";
|
||||
interface Props
|
||||
{
|
||||
type: string;
|
||||
onSave: (newValues: any[]) => void;
|
||||
}
|
||||
|
||||
FilterCriteriaPaster.defaultProps = {};
|
||||
|
||||
function FilterCriteriaPaster({type}: Props): JSX.Element
|
||||
function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
{
|
||||
enum Delimiter
|
||||
{
|
||||
@ -86,14 +87,6 @@ function FilterCriteriaPaster({type}: Props): JSX.Element
|
||||
setPasteModalIsOpen(true);
|
||||
};
|
||||
|
||||
const applyValue = (item: GridFilterItem) =>
|
||||
{
|
||||
console.log(`updating grid values: ${JSON.stringify(item.value)}`);
|
||||
// todo!
|
||||
// setGridFilterItem(item);
|
||||
// props.applyValue(item);
|
||||
};
|
||||
|
||||
const clearData = () =>
|
||||
{
|
||||
setDelimiter("");
|
||||
@ -113,34 +106,19 @@ function FilterCriteriaPaster({type}: Props): JSX.Element
|
||||
|
||||
const handleSaveClicked = () =>
|
||||
{
|
||||
//x if (gridFilterItem)
|
||||
/* todo
|
||||
////////////////////////////////////////
|
||||
// if numeric remove any non-numerics //
|
||||
////////////////////////////////////////
|
||||
let saveData = [];
|
||||
for (let i = 0; i < chipData.length; i++)
|
||||
{
|
||||
////////////////////////////////////////
|
||||
// if numeric remove any non-numerics //
|
||||
////////////////////////////////////////
|
||||
let saveData = [];
|
||||
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)
|
||||
{
|
||||
gridFilterItem.value = [...gridFilterItem.value, ...saveData];
|
||||
}
|
||||
else
|
||||
{
|
||||
gridFilterItem.value = saveData;
|
||||
}
|
||||
|
||||
setGridFilterItem(gridFilterItem);
|
||||
props.applyValue(gridFilterItem);
|
||||
}
|
||||
*/
|
||||
|
||||
onSave(saveData);
|
||||
|
||||
clearData();
|
||||
setPasteModalIsOpen(false);
|
||||
@ -299,7 +277,7 @@ function FilterCriteriaPaster({type}: Props): JSX.Element
|
||||
return (
|
||||
<Box>
|
||||
<Tooltip title="Quickly add many values to your filter by pasting them from a spreadsheet or any other data source.">
|
||||
<Icon onClick={handlePasteClick} fontSize="small" color="info" sx={{marginLeft: "10px", cursor: "pointer"}}>paste_content</Icon>
|
||||
<Icon onClick={handlePasteClick} fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}}>paste_content</Icon>
|
||||
</Tooltip>
|
||||
{
|
||||
pasteModalIsOpen &&
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
|
||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
|
||||
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
|
||||
@ -57,12 +58,14 @@ export interface OperatorOption
|
||||
valueMode: ValueMode;
|
||||
}
|
||||
|
||||
export const getDefaultCriteriaValue = () => [""];
|
||||
|
||||
interface FilterCriteriaRowProps
|
||||
{
|
||||
id: number;
|
||||
index: number;
|
||||
tableMetaData: QTableMetaData;
|
||||
metaData: QInstance;
|
||||
criteria: QFilterCriteria;
|
||||
booleanOperator: "AND" | "OR" | null;
|
||||
updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void;
|
||||
@ -82,11 +85,11 @@ function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: a
|
||||
}
|
||||
}
|
||||
|
||||
export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element
|
||||
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element
|
||||
{
|
||||
// console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`);
|
||||
const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption);
|
||||
const [operatorInputValue, setOperatorInputValue] = useState("")
|
||||
const [operatorInputValue, setOperatorInputValue] = useState("");
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// set up the array of options for the fields Autocomplete //
|
||||
@ -98,12 +101,14 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
|
||||
if (tableMetaData.exposedJoins && tableMetaData.exposedJoins.length > 0)
|
||||
{
|
||||
fieldsGroupBy = (option: any) => `${option.table.label} Fields`;
|
||||
|
||||
for (let i = 0; i < tableMetaData.exposedJoins.length; i++)
|
||||
{
|
||||
const exposedJoin = tableMetaData.exposedJoins[i];
|
||||
makeFieldOptionsForTable(exposedJoin.joinTable, fieldOptions, true);
|
||||
if (metaData.tables.has(exposedJoin.joinTable.name))
|
||||
{
|
||||
fieldsGroupBy = (option: any) => `${option.table.label} fields`;
|
||||
makeFieldOptionsForTable(exposedJoin.joinTable, fieldOptions, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,8 +129,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
//////////////////////////////////////////////////////
|
||||
if (field.possibleValueSourceName)
|
||||
{
|
||||
operatorOptions.push({label: "is", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.PVS_SINGLE});
|
||||
operatorOptions.push({label: "is not", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.PVS_SINGLE});
|
||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.PVS_SINGLE});
|
||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.PVS_SINGLE});
|
||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.PVS_MULTI});
|
||||
@ -138,7 +143,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
case QFieldType.DECIMAL:
|
||||
case QFieldType.INTEGER:
|
||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE});
|
||||
operatorOptions.push({label: "not equals", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.SINGLE});
|
||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE});
|
||||
operatorOptions.push({label: "greater than", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE});
|
||||
operatorOptions.push({label: "greater than or equals", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE});
|
||||
operatorOptions.push({label: "less than", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE});
|
||||
@ -151,8 +156,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.MULTI});
|
||||
break;
|
||||
case QFieldType.DATE:
|
||||
operatorOptions.push({label: "is", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
||||
operatorOptions.push({label: "is not", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE});
|
||||
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE});
|
||||
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE});
|
||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||
@ -163,8 +168,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
//? operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN});
|
||||
break;
|
||||
case QFieldType.DATE_TIME:
|
||||
operatorOptions.push({label: "is", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
operatorOptions.push({label: "is not", value: QCriteriaOperator.NOT_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
operatorOptions.push({label: "is on or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||
@ -175,8 +180,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
//? operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN});
|
||||
break;
|
||||
case QFieldType.BOOLEAN:
|
||||
operatorOptions.push({label: "is yes", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [true]});
|
||||
operatorOptions.push({label: "is no", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [false]});
|
||||
operatorOptions.push({label: "equals yes", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [true]});
|
||||
operatorOptions.push({label: "equals no", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [false]});
|
||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||
/*
|
||||
@ -266,20 +271,39 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
//////////////////////////////////////////
|
||||
const handleFieldChange = (event: any, newValue: any, reason: string) =>
|
||||
{
|
||||
criteria.fieldName = newValue ? newValue.fieldName : null;
|
||||
updateCriteria(criteria, false);
|
||||
const oldFieldName = criteria.fieldName;
|
||||
|
||||
setOperatorOptions(criteria.fieldName)
|
||||
if(operatorOptions.length)
|
||||
criteria.fieldName = newValue ? newValue.fieldName : null;
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// decide if we should clear out the values or not. //
|
||||
//////////////////////////////////////////////////////
|
||||
if (criteria.fieldName == null || isFieldTypeDifferent(oldFieldName, criteria.fieldName))
|
||||
{
|
||||
setOperatorSelectedValue(operatorOptions[0]);
|
||||
setOperatorInputValue(operatorOptions[0].label);
|
||||
criteria.values = getDefaultCriteriaValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// update the operator options, and the operator on this criteria //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
setOperatorOptions(criteria.fieldName);
|
||||
if (operatorOptions.length)
|
||||
{
|
||||
if (isFieldTypeDifferent(oldFieldName, criteria.fieldName))
|
||||
{
|
||||
criteria.operator = operatorOptions[0].value;
|
||||
setOperatorSelectedValue(operatorOptions[0]);
|
||||
setOperatorInputValue(operatorOptions[0].label);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
criteria.operator = null;
|
||||
setOperatorSelectedValue(null);
|
||||
setOperatorInputValue("");
|
||||
}
|
||||
|
||||
updateCriteria(criteria, false);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////
|
||||
@ -314,7 +338,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
const handleValueChange = (event: React.ChangeEvent | SyntheticEvent, valueIndex: number | "all" = 0, newValue?: any) =>
|
||||
{
|
||||
// @ts-ignore
|
||||
const value = newValue ? newValue : event ? event.target.value : null;
|
||||
const value = newValue !== undefined ? newValue : event ? event.target.value : null;
|
||||
|
||||
if(!criteria.values)
|
||||
{
|
||||
@ -323,7 +347,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
|
||||
if(valueIndex == "all")
|
||||
{
|
||||
criteria.values= value;
|
||||
criteria.values = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -333,6 +357,22 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
updateCriteria(criteria, true);
|
||||
};
|
||||
|
||||
const isFieldTypeDifferent = (fieldNameA: string, fieldNameB: string): boolean =>
|
||||
{
|
||||
const [fieldA] = FilterUtils.getField(tableMetaData, fieldNameA);
|
||||
const [fieldB] = FilterUtils.getField(tableMetaData, fieldNameB);
|
||||
if (fieldA?.type !== fieldB.type)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
if (fieldA.possibleValueSourceName !== fieldB.possibleValueSourceName)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
};
|
||||
|
||||
function isFieldOptionEqual(option: any, value: any)
|
||||
{
|
||||
return option.fieldName === value.fieldName;
|
||||
@ -465,6 +505,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
renderOption={(props, option, state) => renderFieldOption(props, option, state)}
|
||||
autoSelect={true}
|
||||
autoHighlight={true}
|
||||
slotProps={{popper: {style: {padding: 0, width: "250px"}}}}
|
||||
/>
|
||||
</Box>
|
||||
<Box display="inline-block" width={200}>
|
||||
@ -481,6 +522,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, criteria, booleanOp
|
||||
getOptionLabel={(option: any) => option.label}
|
||||
autoSelect={true}
|
||||
autoHighlight={true}
|
||||
slotProps={{popper: {style: {padding: 0, maxHeight: "unset", width: "200px"}}}}
|
||||
/*disabled={criteria.fieldName == null}*/
|
||||
/>
|
||||
</Tooltip>
|
||||
|
@ -25,11 +25,16 @@ import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QField
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import Box from "@mui/material/Box";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import InputAdornment from "@mui/material/InputAdornment/InputAdornment";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import React, {SyntheticEvent} from "react";
|
||||
import React, {SyntheticEvent, useReducer} from "react";
|
||||
import DynamicSelect from "qqq/components/forms/DynamicSelect";
|
||||
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
|
||||
import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster";
|
||||
import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -45,31 +50,64 @@ FilterCriteriaRowValues.defaultProps = {
|
||||
|
||||
function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler}: Props): JSX.Element
|
||||
{
|
||||
if(!operatorOption)
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
if (!operatorOption)
|
||||
{
|
||||
return <br />
|
||||
return <br />;
|
||||
}
|
||||
|
||||
const makeTextField = (valueIndex: number = 0, label = "Value", idPrefix="value-") =>
|
||||
const getTypeForTextField = (): string =>
|
||||
{
|
||||
let type = "search"
|
||||
const inputLabelProps: any = {};
|
||||
let type = "search";
|
||||
|
||||
if(field.type == QFieldType.INTEGER)
|
||||
if (field.type == QFieldType.INTEGER)
|
||||
{
|
||||
type = "number";
|
||||
}
|
||||
else if(field.type == QFieldType.DATE)
|
||||
else if (field.type == QFieldType.DATE)
|
||||
{
|
||||
type = "date";
|
||||
inputLabelProps.shrink = true;
|
||||
}
|
||||
else if(field.type == QFieldType.DATE_TIME)
|
||||
else if (field.type == QFieldType.DATE_TIME)
|
||||
{
|
||||
type = "datetime-local";
|
||||
}
|
||||
|
||||
return (type);
|
||||
};
|
||||
|
||||
const makeTextField = (valueIndex: number = 0, label = "Value", idPrefix = "value-") =>
|
||||
{
|
||||
let type = getTypeForTextField();
|
||||
const inputLabelProps: any = {};
|
||||
|
||||
if (field.type == QFieldType.DATE || field.type == QFieldType.DATE_TIME)
|
||||
{
|
||||
inputLabelProps.shrink = true;
|
||||
}
|
||||
|
||||
let value = criteria.values[valueIndex];
|
||||
if (field.type == QFieldType.DATE_TIME && value && String(value).indexOf("Z") > -1)
|
||||
{
|
||||
value = ValueUtils.formatDateTimeValueForForm(value);
|
||||
}
|
||||
|
||||
const clearValue = (event: React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement>, index: number) =>
|
||||
{
|
||||
valueChangeHandler(event, index, "");
|
||||
document.getElementById(`${idPrefix}${criteria.id}`).focus();
|
||||
};
|
||||
|
||||
const inputProps: any = {};
|
||||
inputProps.endAdornment = (
|
||||
<InputAdornment position="end">
|
||||
<IconButton sx={{visibility: value ? "visible" : "hidden"}} onClick={(event) => clearValue(event, valueIndex)}>
|
||||
<Icon>close</Icon>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
);
|
||||
|
||||
return <TextField
|
||||
id={`${idPrefix}${criteria.id}`}
|
||||
label={label}
|
||||
@ -77,17 +115,40 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
|
||||
autoComplete="off"
|
||||
type={type}
|
||||
onChange={(event) => valueChangeHandler(event, valueIndex)}
|
||||
value={criteria.values[valueIndex]}
|
||||
value={value}
|
||||
InputLabelProps={inputLabelProps}
|
||||
InputProps={inputProps}
|
||||
fullWidth
|
||||
// todo - x to clear value?
|
||||
/>
|
||||
/>;
|
||||
};
|
||||
|
||||
function saveNewPasterValues(newValues: any[])
|
||||
{
|
||||
if (criteria.values)
|
||||
{
|
||||
criteria.values = [...criteria.values, ...newValues];
|
||||
}
|
||||
else
|
||||
{
|
||||
criteria.values = newValues;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// we are somehow getting some empty-strings as first-value leaking through. they aren't cool, so, remove them if we find them //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (criteria.values.length > 0 && criteria.values[0] == "")
|
||||
{
|
||||
criteria.values = criteria.values.splice(1);
|
||||
}
|
||||
|
||||
valueChangeHandler(null, "all", criteria.values);
|
||||
forceUpdate();
|
||||
}
|
||||
|
||||
switch (operatorOption.valueMode)
|
||||
{
|
||||
case ValueMode.NONE:
|
||||
return <br />
|
||||
return <br />;
|
||||
case ValueMode.SINGLE:
|
||||
return makeTextField();
|
||||
case ValueMode.SINGLE_DATE:
|
||||
@ -100,30 +161,36 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
|
||||
{ makeTextField(0, "From", "from-") }
|
||||
</Box>
|
||||
<Box width="50%" display="inline-block">
|
||||
{ makeTextField(1, "To", "to-") }
|
||||
{makeTextField(1, "To", "to-")}
|
||||
</Box>
|
||||
</Box>;
|
||||
case ValueMode.MULTI:
|
||||
let values = criteria.values;
|
||||
if(values && values.length == 1 && values[0] == "")
|
||||
if (values && values.length == 1 && values[0] == "")
|
||||
{
|
||||
values = [];
|
||||
}
|
||||
return <Autocomplete
|
||||
renderInput={(params) => (<TextField {...params} variant="standard" label="Values" />)}
|
||||
options={[]}
|
||||
multiple
|
||||
freeSolo // todo - no debounce after enter?
|
||||
selectOnFocus
|
||||
clearOnBlur
|
||||
limitTags={5}
|
||||
value={values}
|
||||
onChange={(event, value) => valueChangeHandler(event, "all", value)}
|
||||
/>
|
||||
// todo - need the Paste button
|
||||
return <Box display="flex" alignItems="flex-end">
|
||||
<Autocomplete
|
||||
renderInput={(params) => (<TextField {...params} variant="standard" label="Values" />)}
|
||||
options={[]}
|
||||
multiple
|
||||
freeSolo // todo - no debounce after enter?
|
||||
selectOnFocus
|
||||
clearOnBlur
|
||||
fullWidth
|
||||
limitTags={5}
|
||||
value={values}
|
||||
onChange={(event, value) => valueChangeHandler(event, "all", value)}
|
||||
/>
|
||||
<Box>
|
||||
<FilterCriteriaPaster type={getTypeForTextField()} onSave={(newValues: any[]) => saveNewPasterValues(newValues)} />
|
||||
</Box>
|
||||
</Box>;
|
||||
case ValueMode.PVS_SINGLE:
|
||||
console.log("Doing pvs single: " + criteria.values);
|
||||
let selectedPossibleValue = null;
|
||||
if(criteria.values && criteria.values.length > 0)
|
||||
if (criteria.values && criteria.values.length > 0)
|
||||
{
|
||||
selectedPossibleValue = criteria.values[0];
|
||||
}
|
||||
@ -131,22 +198,38 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
|
||||
<DynamicSelect
|
||||
tableName={table.name}
|
||||
fieldName={field.name}
|
||||
overrideId={field.name + "-single-" + criteria.id}
|
||||
key={field.name + "-single-" + criteria.id}
|
||||
fieldLabel="Value"
|
||||
initialValue={selectedPossibleValue?.id}
|
||||
initialDisplayValue={selectedPossibleValue?.label}
|
||||
inForm={false}
|
||||
onChange={(value: any) => valueChangeHandler(null, 0, value)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>;
|
||||
case ValueMode.PVS_MULTI:
|
||||
// todo - values not sticking when re-opening filter panel
|
||||
console.log("Doing pvs multi: " + criteria.values);
|
||||
let initialValues: any[] = [];
|
||||
if (criteria.values && criteria.values.length > 0)
|
||||
{
|
||||
if (criteria.values.length == 1 && criteria.values[0] == "")
|
||||
{
|
||||
// we never want a tag that's just ""...
|
||||
}
|
||||
else
|
||||
{
|
||||
initialValues = criteria.values;
|
||||
}
|
||||
}
|
||||
return <Box mb={-1.5}>
|
||||
<DynamicSelect
|
||||
tableName={table.name}
|
||||
fieldName={field.name}
|
||||
overrideId={field.name + "-multi-" + criteria.id}
|
||||
key={field.name + "-multi-" + criteria.id}
|
||||
isMultiple
|
||||
fieldLabel="Values"
|
||||
initialValues={criteria.values || []}
|
||||
initialValues={initialValues}
|
||||
inForm={false}
|
||||
onChange={(value: any) => valueChangeHandler(null, "all", value)}
|
||||
/>
|
||||
|
Reference in New Issue
Block a user