mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-22 23:28:44 +00:00
CE-1115 pre-QA commit on saved report UI, including:
- redo pivots so editing is in a modal - add form validations - field rules for clearing one field when another changes
This commit is contained in:
@ -23,7 +23,9 @@
|
||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {Box} from "@mui/material";
|
||||
import Autocomplete, {AutocompleteRenderOptionState} from "@mui/material/Autocomplete";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import React, {ReactNode, useState} from "react";
|
||||
|
||||
@ -33,14 +35,16 @@ interface FieldAutoCompleteProps
|
||||
metaData: QInstance;
|
||||
tableMetaData: QTableMetaData;
|
||||
handleFieldChange: (event: any, newValue: any, reason: string) => void;
|
||||
defaultValue?: {field: QFieldMetaData, table: QTableMetaData, fieldName: string};
|
||||
defaultValue?: { field: QFieldMetaData, table: QTableMetaData, fieldName: string };
|
||||
autoFocus?: boolean;
|
||||
forceOpen?: boolean;
|
||||
hiddenFieldNames?: string[];
|
||||
availableFieldNames?: string[];
|
||||
variant?: "standard" | "filled" | "outlined"
|
||||
label?: string
|
||||
textFieldSX?: any
|
||||
variant?: "standard" | "filled" | "outlined";
|
||||
label?: string;
|
||||
textFieldSX?: any;
|
||||
autocompleteSlotProps?: any;
|
||||
hasError?: boolean;
|
||||
}
|
||||
|
||||
FieldAutoComplete.defaultProps =
|
||||
@ -53,6 +57,8 @@ FieldAutoComplete.defaultProps =
|
||||
variant: "standard",
|
||||
label: "Field",
|
||||
textFieldSX: null,
|
||||
autocompleteSlotProps: null,
|
||||
hasError: false,
|
||||
};
|
||||
|
||||
function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: any[], isJoinTable: boolean, hiddenFieldNames: string[], availableFieldNames: string[], selectedFieldName: string)
|
||||
@ -62,12 +68,12 @@ function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: a
|
||||
{
|
||||
const fieldName = isJoinTable ? `${tableMetaData.name}.${sortedFields[i].name}` : sortedFields[i].name;
|
||||
|
||||
if(hiddenFieldNames && hiddenFieldNames.indexOf(fieldName) > -1 && fieldName != selectedFieldName)
|
||||
if (hiddenFieldNames && hiddenFieldNames.indexOf(fieldName) > -1 && fieldName != selectedFieldName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(availableFieldNames && availableFieldNames.indexOf(fieldName) == -1)
|
||||
if (availableFieldNames && availableFieldNames.indexOf(fieldName) == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -80,9 +86,9 @@ function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: a
|
||||
/*******************************************************************************
|
||||
** Component for rendering a list of field names from a table as an auto-complete.
|
||||
*******************************************************************************/
|
||||
export default function FieldAutoComplete({id, metaData, tableMetaData, handleFieldChange, defaultValue, autoFocus, forceOpen, hiddenFieldNames, availableFieldNames, variant, label, textFieldSX}: FieldAutoCompleteProps): JSX.Element
|
||||
export default function FieldAutoComplete({id, metaData, tableMetaData, handleFieldChange, defaultValue, autoFocus, forceOpen, hiddenFieldNames, availableFieldNames, variant, label, textFieldSX, autocompleteSlotProps, hasError}: FieldAutoCompleteProps): JSX.Element
|
||||
{
|
||||
const [selectedFieldName, setSelectedFieldName] = useState(defaultValue ? defaultValue.fieldName : null)
|
||||
const [selectedFieldName, setSelectedFieldName] = useState(defaultValue ? defaultValue.fieldName : null);
|
||||
|
||||
const fieldOptions: any[] = [];
|
||||
makeFieldOptionsForTable(tableMetaData, fieldOptions, false, hiddenFieldNames, availableFieldNames, selectedFieldName);
|
||||
@ -149,8 +155,8 @@ export default function FieldAutoComplete({id, metaData, tableMetaData, handleFi
|
||||
// seems like, if we always add the open attribute, then if its false or null, then the autocomplete //
|
||||
// doesn't open at all... so, only add the attribute at all, if forceOpen is true //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const alsoOpen: {[key: string]: any} = {}
|
||||
if(forceOpen)
|
||||
const alsoOpen: { [key: string]: any } = {};
|
||||
if (forceOpen)
|
||||
{
|
||||
alsoOpen["open"] = forceOpen;
|
||||
}
|
||||
@ -161,14 +167,24 @@ export default function FieldAutoComplete({id, metaData, tableMetaData, handleFi
|
||||
*******************************************************************************/
|
||||
function onChange(event: any, newValue: any, reason: string)
|
||||
{
|
||||
setSelectedFieldName(newValue ? newValue.fieldName : null)
|
||||
setSelectedFieldName(newValue ? newValue.fieldName : null);
|
||||
handleFieldChange(event, newValue, reason);
|
||||
}
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
id={id}
|
||||
renderInput={(params) => (<TextField {...params} autoFocus={autoFocus} label={label} variant={variant} sx={textFieldSX} autoComplete="off" type="search" InputProps={{...params.InputProps}} />)}
|
||||
renderInput={(params) =>
|
||||
{
|
||||
const inputProps = params.InputProps;
|
||||
const originalEndAdornment = inputProps.endAdornment;
|
||||
inputProps.endAdornment = <Box>
|
||||
{hasError && <Icon color="error">error_outline</Icon>}
|
||||
{originalEndAdornment}
|
||||
</Box>;
|
||||
|
||||
return (<TextField {...params} autoFocus={autoFocus} label={label} variant={variant} sx={textFieldSX} autoComplete="off" type="search" InputProps={inputProps} />)
|
||||
}}
|
||||
// @ts-ignore
|
||||
defaultValue={defaultValue}
|
||||
options={fieldOptions}
|
||||
@ -179,7 +195,7 @@ export default function FieldAutoComplete({id, metaData, tableMetaData, handleFi
|
||||
renderOption={(props, option, state) => renderFieldOption(props, option, state)}
|
||||
autoSelect={true}
|
||||
autoHighlight={true}
|
||||
slotProps={{popper: {className: "filterCriteriaRowColumnPopper", style: {padding: 0, width: "250px"}}}}
|
||||
slotProps={autocompleteSlotProps ?? {}}
|
||||
{...alsoOpen}
|
||||
/>
|
||||
|
||||
|
@ -431,9 +431,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
open={Boolean(savedViewsMenu)}
|
||||
onClose={closeSavedViewsMenu}
|
||||
keepMounted
|
||||
PaperProps={{style: {maxHeight: "calc(100vh - 200px)", minHeight: "200px"}}}
|
||||
PaperProps={{style: {maxHeight: "calc(100vh - 200px)", minWidth: "300px"}}}
|
||||
>
|
||||
<MenuItem sx={{width: "300px"}} disabled style={{"opacity": "initial"}}><b>View Actions</b></MenuItem>
|
||||
{
|
||||
isQueryScreen &&
|
||||
<MenuItem sx={{width: "300px"}} disabled style={{"opacity": "initial"}}><b>View Actions</b></MenuItem>
|
||||
}
|
||||
{
|
||||
isQueryScreen && hasStorePermission &&
|
||||
<Tooltip {...menuTooltipAttribs} title={<>Save your current filters, columns and settings, for quick re-use at a later time.<br /><br />You will be prompted to enter a name if you choose this option.</>}>
|
||||
@ -471,6 +474,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen &&
|
||||
<Tooltip {...menuTooltipAttribs} title="Create a new view of this table, resetting the filters and columns to their defaults.">
|
||||
<MenuItem onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>
|
||||
<ListItemIcon><Icon>monitor</Icon></ListItemIcon>
|
||||
@ -479,7 +483,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
hasSavedReportsPermission &&
|
||||
isQueryScreen && hasSavedReportsPermission &&
|
||||
<Tooltip {...menuTooltipAttribs} title="Create a new Saved Report using your current view of this table as a starting point.">
|
||||
<MenuItem onClick={() => handleDropdownOptionClick(NEW_REPORT_OPTION)}>
|
||||
<ListItemIcon><Icon>article</Icon></ListItemIcon>
|
||||
@ -487,7 +491,9 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
}
|
||||
<Divider/>
|
||||
{
|
||||
isQueryScreen && <Divider/>
|
||||
}
|
||||
<MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem>
|
||||
{
|
||||
savedViews && savedViews.length > 0 ? (
|
||||
@ -497,7 +503,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</MenuItem>
|
||||
)
|
||||
): (
|
||||
<MenuItem>
|
||||
<MenuItem disabled sx={{opacity: "1 !important"}}>
|
||||
<i>You do not have any saved views for this table.</i>
|
||||
</MenuItem>
|
||||
)
|
||||
@ -606,7 +612,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
}
|
||||
</ul>
|
||||
</>}>
|
||||
<Button disableRipple={true} sx={linkButtonStyle} onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>{queryScreenUsage} Save View As…</Button>
|
||||
<Button disableRipple={true} sx={linkButtonStyle} onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>Save View As…</Button>
|
||||
</Tooltip>
|
||||
|
||||
{/* vertical rule */}
|
||||
@ -618,7 +624,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</>
|
||||
}
|
||||
{
|
||||
currentSavedView && viewIsModified && <>
|
||||
isQueryScreen && currentSavedView && viewIsModified && <>
|
||||
<Tooltip {...tooltipMaxWidth("24rem")} sx={{cursor: "pointer"}} title={<>
|
||||
<b>Unsaved Changes</b>
|
||||
<ul style={{padding: "0.5rem 1rem"}}>
|
||||
@ -637,6 +643,34 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleSavedViewRecordOnClick(currentSavedView)}>Reset All Changes</Button>
|
||||
</>
|
||||
}
|
||||
{
|
||||
!isQueryScreen && currentSavedView &&
|
||||
<Box>
|
||||
<Box display="inline-block" fontSize="0.875rem" fontWeight="500" sx={{position: "relative", top: "-1px"}}>
|
||||
{currentSavedView.values.get("label")}
|
||||
</Box>
|
||||
|
||||
{
|
||||
viewIsModified &&
|
||||
<>
|
||||
<Tooltip {...tooltipMaxWidth("24rem")} sx={{cursor: "pointer"}} title={<>
|
||||
<b>Changes</b>
|
||||
<ul style={{padding: "0.5rem 1rem"}}>
|
||||
{
|
||||
viewDiffs.map((s: string, i: number) => <li key={i}>{s}</li>)
|
||||
}
|
||||
</ul></>}>
|
||||
<Box display="inline" ml="0.25rem" mr="0.25rem" sx={{...linkButtonStyle, p: 0, cursor: "default", position: "relative", top: "-1px"}}>with {viewDiffs.length} Change{viewDiffs.length == 1 ? "" : "s"}</Box>
|
||||
</Tooltip>
|
||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleSavedViewRecordOnClick(currentSavedView)}>Reset Changes</Button>
|
||||
</>
|
||||
}
|
||||
|
||||
{/* vertical rule */}
|
||||
<Box display="inline-block" ml="0.25rem" borderLeft={`1px solid ${colors.grayLines.main}`} height="1rem" width="1px" position="relative" />
|
||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>Reset to New View</Button>
|
||||
</Box>
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
{
|
||||
|
Reference in New Issue
Block a user