mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-22 07:08:44 +00:00
Compare commits
5 Commits
snapshot-f
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
bcade32ed1 | |||
8071c54ccd | |||
334871988b | |||
ddb055bc81 | |||
66ddf4cb57 |
2200
package-lock.json
generated
2200
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,13 +6,14 @@
|
||||
"@auth0/auth0-react": "1.10.2",
|
||||
"@emotion/react": "11.7.1",
|
||||
"@emotion/styled": "11.6.0",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.90",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.94",
|
||||
"@mui/icons-material": "5.4.1",
|
||||
"@mui/material": "5.11.1",
|
||||
"@mui/styles": "5.11.1",
|
||||
"@mui/system": "5.11.1",
|
||||
"@mui/x-data-grid": "5.17.23",
|
||||
"@mui/x-data-grid-pro": "5.17.23",
|
||||
"@mui/x-date-pickers": "7.1.1",
|
||||
"@mui/x-license-pro": "5.12.3",
|
||||
"@react-jvectormap/core": "1.0.1",
|
||||
"@react-jvectormap/unitedstates": "1.0.1",
|
||||
@ -26,6 +27,7 @@
|
||||
"chroma-js": "2.4.2",
|
||||
"cmdk": "0.2.0",
|
||||
"datejs": "1.0.0-rc3",
|
||||
"dayjs": "1.11.10",
|
||||
"downshift": "3.2.10",
|
||||
"faker": "5.5.3",
|
||||
"form-data": "4.0.0",
|
||||
|
22
src/App.tsx
22
src/App.tsx
@ -79,7 +79,7 @@ export default function App()
|
||||
Client.setUnauthorizedCallback(() =>
|
||||
{
|
||||
logout();
|
||||
})
|
||||
});
|
||||
|
||||
const shouldStoreNewToken = (newToken: string, oldToken: string): boolean =>
|
||||
{
|
||||
@ -114,10 +114,10 @@ export default function App()
|
||||
// remove the exp & iat values from what we compare - as they are always different from auth0 //
|
||||
// note, this is only deleting them from what we compare, not from what we'd store. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
delete newJSON["exp"]
|
||||
delete newJSON["iat"]
|
||||
delete oldJSON["exp"]
|
||||
delete oldJSON["iat"]
|
||||
delete newJSON["exp"];
|
||||
delete newJSON["iat"];
|
||||
delete oldJSON["exp"];
|
||||
delete oldJSON["iat"];
|
||||
|
||||
const different = JSON.stringify(newJSON) !== JSON.stringify(oldJSON);
|
||||
if (different)
|
||||
@ -128,7 +128,7 @@ export default function App()
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log("Caught in shouldStoreNewToken: " + e)
|
||||
console.log("Caught in shouldStoreNewToken: " + e);
|
||||
}
|
||||
|
||||
return (true);
|
||||
@ -185,7 +185,7 @@ export default function App()
|
||||
{
|
||||
console.log(`Error loading token: ${JSON.stringify(e)}`);
|
||||
qController.clearAuthenticationMetaDataLocalStorage();
|
||||
localStorage.removeItem("accessToken")
|
||||
localStorage.removeItem("accessToken");
|
||||
removeCookie(SESSION_UUID_COOKIE_NAME, {path: "/"});
|
||||
logout();
|
||||
return;
|
||||
@ -550,7 +550,7 @@ export default function App()
|
||||
});
|
||||
}
|
||||
|
||||
const pathToLabelMap: {[path: string]: string} = {}
|
||||
const pathToLabelMap: { [path: string]: string } = {};
|
||||
for (let i = 0; i < appRoutesList.length; i++)
|
||||
{
|
||||
const route = appRoutesList[i];
|
||||
@ -575,11 +575,11 @@ export default function App()
|
||||
console.error(e);
|
||||
if (e instanceof QException)
|
||||
{
|
||||
if ((e as QException).status === "401")
|
||||
if ((e as QException).status === 401)
|
||||
{
|
||||
console.log("Exception is a QException with status = 401. Clearing some of localStorage & cookies");
|
||||
qController.clearAuthenticationMetaDataLocalStorage();
|
||||
localStorage.removeItem("accessToken")
|
||||
localStorage.removeItem("accessToken");
|
||||
removeCookie(SESSION_UUID_COOKIE_NAME, {path: "/"});
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
@ -656,7 +656,7 @@ export default function App()
|
||||
|
||||
const [pageHeader, setPageHeader] = useState("" as string | JSX.Element);
|
||||
const [accentColor, setAccentColor] = useState("#0062FF");
|
||||
const [accentColorLight, setAccentColorLight] = useState("#C0D6F7")
|
||||
const [accentColorLight, setAccentColorLight] = useState("#C0D6F7");
|
||||
const [tableMetaData, setTableMetaData] = useState(null);
|
||||
const [tableProcesses, setTableProcesses] = useState(null);
|
||||
const [dotMenuOpen, setDotMenuOpen] = useState(false);
|
||||
|
@ -34,10 +34,10 @@ import ToggleButton from "@mui/material/ToggleButton";
|
||||
import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import QContext from "QContext";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -217,7 +217,7 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element
|
||||
{
|
||||
if (e instanceof QException)
|
||||
{
|
||||
if ((e as QException).status === "403")
|
||||
if ((e as QException).status === 403)
|
||||
{
|
||||
setStatusString("You do not have permission to view audits");
|
||||
return;
|
||||
|
@ -63,7 +63,7 @@ interface Props
|
||||
disabledFields: { [key: string]: boolean } | string[];
|
||||
isCopy?: boolean;
|
||||
onSubmitCallback?: (values: any) => void;
|
||||
overrideHeading?: string;
|
||||
overrideHeading?: string
|
||||
}
|
||||
|
||||
EntityForm.defaultProps = {
|
||||
@ -128,28 +128,23 @@ function EntityForm(props: Props): JSX.Element
|
||||
{
|
||||
try
|
||||
{
|
||||
const parts = hashParts[i].split("=");
|
||||
if (parts.length > 1)
|
||||
const parts = hashParts[i].split("=")
|
||||
if (parts.length > 1 && parts[0] == "defaultValues")
|
||||
{
|
||||
const name = parts[0].replace(/^#/, "");
|
||||
const value = parts[1];
|
||||
if (name == "defaultValues")
|
||||
{
|
||||
defaultValues = JSON.parse(decodeURIComponent(value)) as { [key: string]: any };
|
||||
defaultValues = JSON.parse(decodeURIComponent(parts[1])) as { [key: string]: any };
|
||||
}
|
||||
|
||||
if (name == "disabledFields")
|
||||
if (parts.length > 1 && parts[0] == "disabledFields")
|
||||
{
|
||||
disabledFields = JSON.parse(decodeURIComponent(value)) as { [key: string]: any };
|
||||
}
|
||||
disabledFields = JSON.parse(decodeURIComponent(parts[1])) as { [key: string]: any };
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
}
|
||||
{}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -239,7 +234,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
const metaData = await qController.loadMetaData();
|
||||
const widgetMetaData = metaData.widgets.get(widgetName);
|
||||
|
||||
const newChildListWidgetData: { [name: string]: ChildRecordListData } = Object.assign({}, childListWidgetData);
|
||||
const newChildListWidgetData: {[name: string]: ChildRecordListData} = Object.assign({}, childListWidgetData)
|
||||
if(!newChildListWidgetData[widgetName].queryOutput.records)
|
||||
{
|
||||
newChildListWidgetData[widgetName].queryOutput.records = [];
|
||||
@ -248,7 +243,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
switch(action)
|
||||
{
|
||||
case "insert":
|
||||
newChildListWidgetData[widgetName].queryOutput.records.push({values: values});
|
||||
newChildListWidgetData[widgetName].queryOutput.records.push({values: values})
|
||||
break;
|
||||
case "edit":
|
||||
newChildListWidgetData[widgetName].queryOutput.records[rowIndex] = {values: values};
|
||||
@ -260,7 +255,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
newChildListWidgetData[widgetName].totalRows = newChildListWidgetData[widgetName].queryOutput.records.length;
|
||||
setChildListWidgetData(newChildListWidgetData);
|
||||
|
||||
const newRenderedWidgetSections = Object.assign({}, renderedWidgetSections);
|
||||
const newRenderedWidgetSections = Object.assign({}, renderedWidgetSections)
|
||||
newRenderedWidgetSections[widgetName] = getWidgetSection(widgetMetaData, newChildListWidgetData[widgetName]);
|
||||
setRenderedWidgetSections(newRenderedWidgetSections);
|
||||
forceUpdate();
|
||||
@ -298,11 +293,11 @@ function EntityForm(props: Props): JSX.Element
|
||||
return <div>Error: No form fields in section {section.name}</div>;
|
||||
}
|
||||
|
||||
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"];
|
||||
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"]
|
||||
|
||||
if(omitWrapper)
|
||||
{
|
||||
return <QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} />;
|
||||
return <QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} />
|
||||
}
|
||||
|
||||
return <Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}} elevation={cardElevation}>
|
||||
@ -315,7 +310,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
<QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Card>;
|
||||
</Card>
|
||||
}
|
||||
|
||||
|
||||
@ -337,7 +332,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
addNewRecordCallback={() => openAddChildRecord(widgetMetaData.name, widgetData)}
|
||||
editRecordCallback={(rowIndex) => openEditChildRecord(widgetMetaData.name, widgetData, rowIndex)}
|
||||
deleteRecordCallback={(rowIndex) => deleteChildRecord(widgetMetaData.name, widgetData, rowIndex)}
|
||||
/>;
|
||||
/>
|
||||
}
|
||||
|
||||
|
||||
@ -352,7 +347,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
}
|
||||
else
|
||||
{
|
||||
return renderedWidgetSections[section.widgetName] ?? <Box>Loading {section.label}...</Box>;
|
||||
return renderedWidgetSections[section.widgetName] ?? <Box>Loading {section.label}...</Box>
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,7 +368,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
/////////////////////////////////////////////////
|
||||
const tableSections = TableUtils.getSectionsForRecordSidebar(tableMetaData, [...tableMetaData.fields.keys()], (section: QTableSection) =>
|
||||
{
|
||||
return section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList" && metaData.widgets.get(section.widgetName)?.defaultValues?.has("manageAssociationName");
|
||||
return section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList" && metaData.widgets.get(section.widgetName)?.defaultValues?.has("manageAssociationName")
|
||||
});
|
||||
setTableSections(tableSections);
|
||||
|
||||
@ -549,7 +544,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
}
|
||||
|
||||
const hasFields = section.fieldNames && section.fieldNames.length > 0;
|
||||
const hasChildRecordListWidget = section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList";
|
||||
const hasChildRecordListWidget = section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList"
|
||||
if(!hasFields && !hasChildRecordListWidget)
|
||||
{
|
||||
continue;
|
||||
@ -706,7 +701,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(fieldMetaData.type === QFieldType.DATE_TIME && valuesToPost[fieldName])
|
||||
{
|
||||
console.log(`DateTime ${fieldName}: Initial value: [${initialValues[fieldName]}] -> [${valuesToPost[fieldName]}]`);
|
||||
console.log(`DateTime ${fieldName}: Initial value: [${initialValues[fieldName]}] -> [${valuesToPost[fieldName]}]`)
|
||||
if (initialValues[fieldName] == valuesToPost[fieldName])
|
||||
{
|
||||
console.log(" - Is the same, so, deleting from the post");
|
||||
@ -739,11 +734,11 @@ function EntityForm(props: Props): JSX.Element
|
||||
}
|
||||
}
|
||||
|
||||
const associationsToPost: any = {};
|
||||
const associationsToPost: any = {}
|
||||
let haveAssociationsToPost = false;
|
||||
for (let name of Object.keys(childListWidgetData))
|
||||
{
|
||||
const manageAssociationName = metaData.widgets.get(name)?.defaultValues?.get("manageAssociationName");
|
||||
const manageAssociationName = metaData.widgets.get(name)?.defaultValues?.get("manageAssociationName")
|
||||
if(!manageAssociationName)
|
||||
{
|
||||
console.log(`Cannot send association data to backend - missing a manageAssociationName defaultValue in widget meta data for widget name ${name}`);
|
||||
@ -855,15 +850,15 @@ function EntityForm(props: Props): JSX.Element
|
||||
|
||||
const getSectionHelp = (section: QTableSection) =>
|
||||
{
|
||||
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"];
|
||||
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"]
|
||||
const formattedHelpContent = <HelpContent helpContents={section.helpContents} roles={helpRoles} helpContentKey={`table:${tableMetaData.name};section:${section.name}`} />;
|
||||
|
||||
return formattedHelpContent && (
|
||||
<Box px={"1.5rem"} fontSize={"0.875rem"}>
|
||||
{formattedHelpContent}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
if (notAllowedError)
|
||||
{
|
||||
@ -1012,7 +1007,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
|
||||
function ScrollToFirstError(): JSX.Element
|
||||
{
|
||||
const {submitCount, isValid} = useFormikContext();
|
||||
const {submitCount, isValid} = useFormikContext()
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -1042,8 +1037,8 @@ function ScrollToFirstError(): JSX.Element
|
||||
}
|
||||
firstErrorMessage.scrollIntoView({block: "center"});
|
||||
|
||||
}, 100);
|
||||
}, [submitCount, isValid]);
|
||||
}, 100)
|
||||
}, [submitCount, isValid])
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
const RENAME_OPTION = "Rename...";
|
||||
const DELETE_OPTION = "Delete...";
|
||||
const CLEAR_OPTION = "New View";
|
||||
const NEW_REPORT_OPTION = "Create Report from Current View";
|
||||
const dropdownOptions = [DUPLICATE_OPTION, RENAME_OPTION, DELETE_OPTION, CLEAR_OPTION];
|
||||
|
||||
const {accentColor, accentColorLight} = useContext(QContext);
|
||||
|
||||
@ -187,26 +187,10 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
case DELETE_OPTION:
|
||||
setIsDeleteFilter(true)
|
||||
break;
|
||||
case NEW_REPORT_OPTION:
|
||||
createNewReport();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
function createNewReport()
|
||||
{
|
||||
const defaultValues: {[key: string]: any} = {};
|
||||
defaultValues.tableName = tableMetaData.name;
|
||||
defaultValues.queryFilterJson = JSON.stringify(view.queryFilter, null, 3);
|
||||
defaultValues.columnsJson = JSON.stringify(view.queryColumns, null, 3);
|
||||
navigate(`${metaData.getTablePathByName("savedReport")}/create#defaultValues=${encodeURIComponent(JSON.stringify(defaultValues))}`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** fired when save or delete button saved on confirmation dialogs
|
||||
@ -392,7 +376,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
const hasStorePermission = metaData?.processes.has("storeSavedView");
|
||||
const hasDeletePermission = metaData?.processes.has("deleteSavedView");
|
||||
const hasQueryPermission = metaData?.processes.has("querySavedView");
|
||||
const hasSavedReportsPermission = metaData?.tables.has("savedReport");
|
||||
|
||||
const tooltipMaxWidth = (maxWidth: string) =>
|
||||
{
|
||||
@ -446,7 +429,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
hasDeletePermission && currentSavedView != null &&
|
||||
hasStorePermission && currentSavedView != null &&
|
||||
<Tooltip {...menuTooltipAttribs} title="Delete this saved view.">
|
||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}>
|
||||
<ListItemIcon><Icon>delete</Icon></ListItemIcon>
|
||||
@ -462,15 +445,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
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>
|
||||
Create Report from Current View
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
}
|
||||
<Divider/>
|
||||
<MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem>
|
||||
{
|
||||
|
@ -19,13 +19,12 @@
|
||||
*/
|
||||
|
||||
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
||||
import {Skeleton} from "@mui/material";
|
||||
import {Alert, Skeleton} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Tab from "@mui/material/Tab";
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import parse from "html-react-parser";
|
||||
import React, {useContext, useEffect, useReducer, useState} from "react";
|
||||
import QContext from "QContext";
|
||||
import MDTypography from "qqq/components/legacy/MDTypography";
|
||||
import TabPanel from "qqq/components/misc/TabPanel";
|
||||
@ -47,10 +46,11 @@ import USMapWidget from "qqq/components/widgets/misc/USMapWidget";
|
||||
import ParentWidget from "qqq/components/widgets/ParentWidget";
|
||||
import MultiStatisticsCard from "qqq/components/widgets/statistics/MultiStatisticsCard";
|
||||
import StatisticsCard from "qqq/components/widgets/statistics/StatisticsCard";
|
||||
import Widget, {HeaderIcon, WIDGET_DROPDOWN_SELECTION_LOCAL_STORAGE_KEY_ROOT, LabelComponent} from "qqq/components/widgets/Widget";
|
||||
import Widget, {HeaderIcon, LabelComponent, WIDGET_DROPDOWN_SELECTION_LOCAL_STORAGE_KEY_ROOT} from "qqq/components/widgets/Widget";
|
||||
import WidgetBlock from "qqq/components/widgets/WidgetBlock";
|
||||
import ProcessRun from "qqq/pages/processes/ProcessRun";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import React, {useContext, useEffect, useReducer, useState} from "react";
|
||||
import TableWidget from "./tables/TableWidget";
|
||||
|
||||
|
||||
@ -93,7 +93,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
||||
let selectedTabKey: string = null;
|
||||
if (parentWidgetMetaData && wrapWidgetsInTabPanels)
|
||||
{
|
||||
selectedTabKey = `qqq.widgets.selectedTabs.${parentWidgetMetaData.name}`
|
||||
selectedTabKey = `qqq.widgets.selectedTabs.${parentWidgetMetaData.name}`;
|
||||
if (localStorage.getItem(selectedTabKey))
|
||||
{
|
||||
initialSelectedTab = Number(localStorage.getItem(selectedTabKey));
|
||||
@ -191,7 +191,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
||||
const metaDataToUse = (thisWidgetHasDropdowns) ? widgetMetaData : parentWidgetMetaData;
|
||||
for (let i = 0; i < metaDataToUse.dropdowns.length; i++)
|
||||
{
|
||||
const dropdownName = metaDataToUse.dropdowns[i].possibleValueSourceName;
|
||||
const dropdownName = metaDataToUse.dropdowns[i].possibleValueSourceName ?? metaDataToUse.dropdowns[i].name;
|
||||
const localStorageKey = `${WIDGET_DROPDOWN_SELECTION_LOCAL_STORAGE_KEY_ROOT}.${metaDataToUse.name}.${dropdownName}`;
|
||||
const json = JSON.parse(localStorage.getItem(localStorageKey));
|
||||
if (json)
|
||||
@ -286,6 +286,21 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
widgetMetaData.type === "alert" && widgetData[i]?.html && (
|
||||
<Widget
|
||||
omitPadding={true}
|
||||
widgetMetaData={widgetMetaData}
|
||||
widgetData={widgetData[i]}
|
||||
reloadWidgetCallback={(data) => reloadWidget(i, data)}
|
||||
isChild={areChildren}
|
||||
labelAdditionalComponentsRight={labelAdditionalComponentsRight}
|
||||
labelAdditionalComponentsLeft={labelAdditionalComponentsLeft}
|
||||
>
|
||||
<Alert severity={widgetData[i]?.alertType?.toLowerCase()}>{parse(widgetData[i]?.html)}</Alert>
|
||||
</Widget>
|
||||
)
|
||||
}
|
||||
{
|
||||
widgetMetaData.type === "usaMap" && (
|
||||
<USMapWidget
|
||||
@ -582,7 +597,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
||||
</TabPanel>);
|
||||
}
|
||||
|
||||
return (<React.Fragment key={`${widgetMetaData.name}-${i}`}>{renderedWidget}</React.Fragment>)
|
||||
return (<React.Fragment key={`${widgetMetaData.name}-${i}`}>{renderedWidget}</React.Fragment>);
|
||||
})
|
||||
}
|
||||
</>
|
||||
@ -590,7 +605,8 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
||||
|
||||
const tabs = widgetMetaDataList && wrapWidgetsInTabPanels ?
|
||||
<Tabs
|
||||
sx={{m: 0, mb: 1.5, ml: -2, mr: -2, mt: -3,
|
||||
sx={{
|
||||
m: 0, mb: 1.5, ml: -2, mr: -2, mt: -3,
|
||||
"& .MuiTabs-scroller": {
|
||||
ml: 0
|
||||
}
|
||||
@ -603,7 +619,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
||||
<Tab key={widgetMetaData.name} label={widgetMetaData.label} />
|
||||
))}
|
||||
</Tabs>
|
||||
: <></>
|
||||
: <></>;
|
||||
|
||||
return (
|
||||
widgetCount > 0 ? (
|
||||
|
@ -28,14 +28,14 @@ import Icon from "@mui/material/Icon";
|
||||
import Tooltip from "@mui/material/Tooltip/Tooltip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import parse from "html-react-parser";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import {NavigateFunction, useNavigate} from "react-router-dom";
|
||||
import QContext from "QContext";
|
||||
import colors from "qqq/assets/theme/base/colors";
|
||||
import HelpContent, {hasHelpContent} from "qqq/components/misc/HelpContent";
|
||||
import WidgetDropdownMenu, {DropdownOption} from "qqq/components/widgets/components/WidgetDropdownMenu";
|
||||
import {WidgetUtils} from "qqq/components/widgets/WidgetUtils";
|
||||
import HtmlUtils from "qqq/utils/HtmlUtils";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import {NavigateFunction, useNavigate} from "react-router-dom";
|
||||
|
||||
export interface WidgetData
|
||||
{
|
||||
@ -240,6 +240,13 @@ export class Dropdown extends LabelComponent
|
||||
if (localStorageOption)
|
||||
{
|
||||
const id = localStorageOption.id;
|
||||
|
||||
if (this.dropdownMetaData.type == "DATE_PICKER")
|
||||
{
|
||||
defaultValue = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (let i = 0; i < this.options.length; i++)
|
||||
{
|
||||
if (this.options[i].id == id)
|
||||
@ -250,6 +257,7 @@ export class Dropdown extends LabelComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log(`Error getting default value for dropdown [${this.dropdownName}] from local storage`, e);
|
||||
@ -299,6 +307,7 @@ export class Dropdown extends LabelComponent
|
||||
<Box mb={2} sx={{float: "right"}}>
|
||||
<WidgetDropdownMenu
|
||||
name={this.dropdownName}
|
||||
type={this.dropdownMetaData.type}
|
||||
defaultValue={defaultValue}
|
||||
sx={{marginLeft: "1rem"}}
|
||||
label={label}
|
||||
@ -622,7 +631,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
|
||||
setUsingLabelAsTitle(props.widgetData.isLabelPageTitle);
|
||||
}
|
||||
|
||||
const helpRoles = ["ALL_SCREENS"]
|
||||
const helpRoles = ["ALL_SCREENS"];
|
||||
const slotName = "label";
|
||||
const showHelp = helpHelpActive || hasHelpContent(props.widgetMetaData?.helpContent?.get(slotName), helpRoles);
|
||||
|
||||
|
@ -31,7 +31,7 @@ export default function TextBlock({widgetMetaData, data}: StandardBlockComponent
|
||||
{
|
||||
return (
|
||||
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="">
|
||||
<span>{data.values.text}</span>
|
||||
<span style={{fontSize: "1.000rem"}}>{data.values.text}</span>
|
||||
</BlockElementWrapper>
|
||||
);
|
||||
}
|
||||
|
@ -19,18 +19,23 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Collapse, Theme, InputAdornment} from "@mui/material";
|
||||
import {CalendarTodayOutlined} from "@mui/icons-material";
|
||||
import {Collapse, InputAdornment, Theme} from "@mui/material";
|
||||
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 TextField from "@mui/material/TextField";
|
||||
import {SxProps} from "@mui/system";
|
||||
import {DatePicker, DateValidationError, LocalizationProvider, PickerChangeHandlerContext, PickerValidDate} from "@mui/x-date-pickers";
|
||||
import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs";
|
||||
import dayjs from "dayjs";
|
||||
import {Field, Form, Formik} from "formik";
|
||||
import React, {useState} from "react";
|
||||
import QContext from "QContext";
|
||||
import colors from "qqq/assets/theme/base/colors";
|
||||
import MDInput from "qqq/components/legacy/MDInput";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
|
||||
|
||||
export interface DropdownOption
|
||||
@ -45,6 +50,7 @@ export interface DropdownOption
|
||||
interface Props
|
||||
{
|
||||
name: string;
|
||||
type?: string;
|
||||
defaultValue?: any;
|
||||
label?: string;
|
||||
startIcon?: string;
|
||||
@ -96,7 +102,7 @@ function makeBackendValuesFromFrontendValues(frontendDefaultValues: StartAndEndD
|
||||
return (backendTimeValues);
|
||||
}
|
||||
|
||||
function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disableClearable, allowBackAndForth, backAndForthInverted, dropdownOptions, onChangeCallback, sx}: Props): JSX.Element
|
||||
function WidgetDropdownMenu({name, type, defaultValue, label, startIcon, width, disableClearable, allowBackAndForth, backAndForthInverted, dropdownOptions, onChangeCallback, sx}: Props): JSX.Element
|
||||
{
|
||||
const [customTimesVisible, setCustomTimesVisible] = useState(defaultValue && defaultValue.id && defaultValue.id.startsWith("custom,"));
|
||||
const [customTimeValuesFrontend, setCustomTimeValuesFrontend] = useState(parseCustomTimeValuesFromDefaultValue(defaultValue) as StartAndEndDate);
|
||||
@ -105,16 +111,27 @@ function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disabl
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const [dateValue, setDateValue] = useState(defaultValue);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const [backDisabled, setBackDisabled] = useState(false);
|
||||
const [forthDisabled, setForthDisabled] = useState(false);
|
||||
|
||||
const {accentColor} = useContext(QContext);
|
||||
|
||||
const doForceOpen = (event: React.MouseEvent<HTMLDivElement>) =>
|
||||
{
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (type == "DATE_PICKER")
|
||||
{
|
||||
handleOnChange(null, defaultValue, null);
|
||||
}
|
||||
}, [defaultValue]);
|
||||
|
||||
function getSelectedIndex(value: DropdownOption)
|
||||
{
|
||||
let currentIndex = null;
|
||||
@ -129,9 +146,19 @@ function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disabl
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
const navigateBackAndForth = (event: React.MouseEvent, direction: -1 | 1) =>
|
||||
|
||||
const navigateBackAndForth = (event: React.MouseEvent, direction: -1 | 1, type: string) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
|
||||
if (type == "DATE_PICKER")
|
||||
{
|
||||
let currentDate = new Date(dateValue);
|
||||
currentDate.setDate(currentDate.getDate() + direction);
|
||||
handleOnChange(null, currentDate, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let currentIndex = getSelectedIndex(value);
|
||||
|
||||
if (currentIndex == null)
|
||||
@ -156,9 +183,26 @@ function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disabl
|
||||
};
|
||||
|
||||
|
||||
const handleDatePickerOnChange = (value: PickerValidDate, context: PickerChangeHandlerContext<DateValidationError>) =>
|
||||
{
|
||||
if (value.isValid())
|
||||
{
|
||||
handleOnChange(null, value.toDate(), null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleOnChange = (event: any, newValue: any, reason: string) =>
|
||||
{
|
||||
if (type == "DATE_PICKER")
|
||||
{
|
||||
setDateValue(newValue);
|
||||
newValue = {"id": new Date(newValue).toLocaleDateString()};
|
||||
}
|
||||
else
|
||||
{
|
||||
setValue(newValue);
|
||||
}
|
||||
|
||||
const isTimeframeCustom = name == "timeframe" && newValue && newValue.id == "custom";
|
||||
setCustomTimesVisible(isTimeframeCustom);
|
||||
@ -252,16 +296,52 @@ function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disabl
|
||||
let optionPaddingLeftRems = 0.75;
|
||||
if (startIcon)
|
||||
{
|
||||
optionPaddingLeftRems += allowBackAndForth ? 1.5 : 1.75
|
||||
optionPaddingLeftRems += allowBackAndForth ? 1.5 : 1.75;
|
||||
}
|
||||
if (allowBackAndForth)
|
||||
{
|
||||
optionPaddingLeftRems += 2.5;
|
||||
}
|
||||
|
||||
if (type == "DATE_PICKER")
|
||||
{
|
||||
return (
|
||||
<Box sx={{
|
||||
...sx,
|
||||
background: "white",
|
||||
width: "250px",
|
||||
borderRadius: "0.75rem !important",
|
||||
border: `1px solid ${colors.grayLines.main}`,
|
||||
"& *": {cursor: "pointer"}
|
||||
}} display="flex" alignItems="center" onClick={(event) => doForceOpen(event)}>
|
||||
{allowBackAndForth && <IconButton sx={{padding: 0, margin: "8px"}} onClick={(event) => navigateBackAndForth(event, backAndForthInverted ? 1 : -1, type)} disabled={backDisabled}><Icon>navigate_before</Icon></IconButton>}
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<DatePicker
|
||||
sx={{paddingRight: "8px"}}
|
||||
defaultValue={dayjs(defaultValue)}
|
||||
name={name}
|
||||
value={dayjs(dateValue)}
|
||||
onChange={handleDatePickerOnChange}
|
||||
slots={{
|
||||
openPickerIcon: CalendarTodayOutlined
|
||||
}}
|
||||
slotProps={{
|
||||
openPickerIcon: {sx: {fontSize: "1.25rem !important", color: "#757575"}},
|
||||
actionBar: {actions: ["today"]},
|
||||
textField: {variant: "standard", InputProps: {sx: {fontSize: "16px", color: "#495057"}, disableUnderline: true}}
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
{allowBackAndForth && <IconButton onClick={(event) => navigateBackAndForth(event, backAndForthInverted ? -1 : 1, type)} disabled={forthDisabled}><Icon>navigate_next</Icon></IconButton>}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (
|
||||
dropdownOptions ? (
|
||||
<Box sx={{whiteSpace: "nowrap", display: "flex",
|
||||
<Box sx={{
|
||||
whiteSpace: "nowrap", display: "flex",
|
||||
"& .MuiPopperUnstyled-root": {
|
||||
border: `1px solid ${colors.grayLines.main}`,
|
||||
borderTop: "none",
|
||||
@ -301,14 +381,14 @@ function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disabl
|
||||
renderInput={(params: any) =>
|
||||
<>
|
||||
<Box sx={{width: `${width}px`, background: "white", borderRadius: isOpen ? "0.75rem 0.75rem 0 0" : "0.75rem", border: `1px solid ${colors.grayLines.main}`, "& *": {cursor: "pointer"}}} display="flex" alignItems="center" onClick={(event) => doForceOpen(event)}>
|
||||
{allowBackAndForth && <IconButton onClick={(event) => navigateBackAndForth(event, backAndForthInverted ? 1 : -1)} disabled={backDisabled}><Icon>navigate_before</Icon></IconButton>}
|
||||
{allowBackAndForth && <IconButton onClick={(event) => navigateBackAndForth(event, backAndForthInverted ? 1 : -1, type)} disabled={backDisabled}><Icon>navigate_before</Icon></IconButton>}
|
||||
<TextField {...params} placeholder={label} sx={{
|
||||
"& .MuiInputBase-input": {
|
||||
fontSize: fontSize
|
||||
}
|
||||
}} InputProps={{...params.InputProps, startAdornment: startAdornment/*, endAdornment: endAdornment*/}}
|
||||
/>
|
||||
{allowBackAndForth && <IconButton onClick={(event) => navigateBackAndForth(event, backAndForthInverted ? -1 : 1)} disabled={forthDisabled}><Icon>navigate_next</Icon></IconButton>}
|
||||
{allowBackAndForth && <IconButton onClick={(event) => navigateBackAndForth(event, backAndForthInverted ? -1 : 1, type)} disabled={forthDisabled}><Icon>navigate_next</Icon></IconButton>}
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
@ -331,5 +411,6 @@ function WidgetDropdownMenu({name, defaultValue, label, startIcon, width, disabl
|
||||
) : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetDropdownMenu;
|
||||
|
@ -119,7 +119,7 @@ export default function DataBagViewer({dataBagId}: Props): JSX.Element
|
||||
{
|
||||
if (e instanceof QException)
|
||||
{
|
||||
if ((e as QException).status === "404")
|
||||
if ((e as QException).status === 404)
|
||||
{
|
||||
setNotFoundMessage("Data bag data could not be found.");
|
||||
return;
|
||||
|
@ -169,7 +169,7 @@ export default function ScriptViewer({scriptId, associatedScriptTableName, assoc
|
||||
{
|
||||
if (e instanceof QException)
|
||||
{
|
||||
if ((e as QException).status === "404")
|
||||
if ((e as QException).status === 404)
|
||||
{
|
||||
setNotFoundMessage("Script code could not be found.");
|
||||
return;
|
||||
|
@ -21,8 +21,8 @@
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import {Theme} from "@mui/material/styles";
|
||||
import {ReactNode} from "react";
|
||||
import colors from "qqq/assets/theme/base/colors";
|
||||
import {ReactNode} from "react";
|
||||
|
||||
// Declaring prop types for DataTableBodyCell
|
||||
interface Props
|
||||
@ -49,7 +49,7 @@ function DataTableBodyCell({noBorder, align, children}: Props): JSX.Element
|
||||
"@media (max-width: 1440px)": {
|
||||
fontSize: "0.875rem"
|
||||
},
|
||||
"&:nth-child(1)": {
|
||||
"&:nth-of-type(1)": {
|
||||
paddingLeft: "1rem"
|
||||
},
|
||||
"&:last-child": {
|
||||
|
@ -23,9 +23,9 @@ import Box from "@mui/material/Box";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import {Theme} from "@mui/material/styles";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import {ReactNode} from "react";
|
||||
import colors from "qqq/assets/theme/base/colors";
|
||||
import {useMaterialUIController} from "qqq/context";
|
||||
import {ReactNode} from "react";
|
||||
|
||||
// Declaring props types for DataTableHeadCell
|
||||
interface Props
|
||||
@ -50,7 +50,7 @@ function DataTableHeadCell({width, children, sorted, align, tooltip, ...rest}: P
|
||||
px={1.5}
|
||||
sx={({palette: {light}, borders: {borderWidth}}: Theme) => ({
|
||||
borderBottom: `${borderWidth[1]} solid ${colors.grayLines.main}`,
|
||||
"&:nth-child(1)": {
|
||||
"&:nth-of-type(1)": {
|
||||
paddingLeft: "1rem"
|
||||
},
|
||||
"&:last-child": {
|
||||
|
@ -226,19 +226,8 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
setShowFullHelpText(!showFullHelpText);
|
||||
};
|
||||
|
||||
const download = (processValues: {[key: string]: string}) =>
|
||||
const download = (url: string, fileName: string) =>
|
||||
{
|
||||
let url;
|
||||
let fileName = processValues.downloadFileName;
|
||||
if(processValues.serverFilePath)
|
||||
{
|
||||
url = `/download/${encodeURIComponent(processValues.downloadFileName)}?filePath=${encodeURIComponent(processValues.serverFilePath)}`;
|
||||
}
|
||||
else if(processValues.storageTableName && processValues.storageReference)
|
||||
{
|
||||
url = `/download/${encodeURIComponent(processValues.downloadFileName)}?storageTableName=${encodeURIComponent(processValues.storageTableName)}&storageReference=${encodeURIComponent(processValues.storageReference)}`;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - this could be simplified, i think? //
|
||||
// it was originally built like this when we had to submit full access token to backend... //
|
||||
@ -427,10 +416,10 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
//////////////////////////////////////////////////
|
||||
step.components && (step.components.map((component: QFrontendComponent, index: number) =>
|
||||
{
|
||||
let helpRoles = ["PROCESS_SCREEN", "ALL_SCREENS"]
|
||||
let helpRoles = ["PROCESS_SCREEN", "ALL_SCREENS"];
|
||||
if (component.type == QComponentType.BULK_EDIT_FORM)
|
||||
{
|
||||
helpRoles = ["EDIT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"]
|
||||
helpRoles = ["EDIT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"];
|
||||
}
|
||||
|
||||
return (
|
||||
@ -567,7 +556,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
Download
|
||||
</Box>
|
||||
<Box display="flex" py={1} pr={2}>
|
||||
<MDTypography variant="button" fontWeight="bold" onClick={() => download(processValues)} sx={{cursor: "pointer"}}>
|
||||
<MDTypography variant="button" fontWeight="bold" onClick={() => download(`/download/${processValues.downloadFileName}?filePath=${processValues.serverFilePath}`, processValues.downloadFileName)} sx={{cursor: "pointer"}}>
|
||||
<Box display="flex" alignItems="center" gap={1} py={1} pr={2}>
|
||||
<Icon fontSize="large">download_for_offline</Icon>
|
||||
{processValues.downloadFileName}
|
||||
@ -1079,7 +1068,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
|
||||
const handlePermissionDenied = (e: any): boolean =>
|
||||
{
|
||||
if ((e as QException).status === "403")
|
||||
if ((e as QException).status === 403)
|
||||
{
|
||||
setProcessError(`You do not have permission to run this ${isReport ? "report" : "process"}.`, true);
|
||||
return (true);
|
||||
|
@ -28,9 +28,6 @@ import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Snackbar from "@mui/material/Snackbar";
|
||||
import React, {useContext, useReducer, useState} from "react";
|
||||
import AceEditor from "react-ace";
|
||||
import {useParams} from "react-router-dom";
|
||||
import QContext from "QContext";
|
||||
import ScriptViewer from "qqq/components/widgets/misc/ScriptViewer";
|
||||
import BaseLayout from "qqq/layouts/BaseLayout";
|
||||
@ -41,6 +38,9 @@ import "ace-builds/src-noconflict/mode-java";
|
||||
import "ace-builds/src-noconflict/mode-javascript";
|
||||
import "ace-builds/src-noconflict/mode-json";
|
||||
import "ace-builds/src-noconflict/theme-github";
|
||||
import React, {useContext, useReducer, useState} from "react";
|
||||
import AceEditor from "react-ace";
|
||||
import {useParams} from "react-router-dom";
|
||||
import "ace-builds/src-noconflict/ext-language_tools";
|
||||
|
||||
const qController = Client.getInstance();
|
||||
@ -121,7 +121,7 @@ function RecordDeveloperView({table}: Props): JSX.Element
|
||||
{
|
||||
if (e instanceof QException)
|
||||
{
|
||||
if ((e as QException).status === "404")
|
||||
if ((e as QException).status === 404)
|
||||
{
|
||||
setNotFoundMessage(`${tableMetaData.label} ${id} could not be found.`);
|
||||
return;
|
||||
|
@ -447,13 +447,13 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
||||
|
||||
if (e instanceof QException)
|
||||
{
|
||||
if ((e as QException).status === "404")
|
||||
if ((e as QException).status === 404)
|
||||
{
|
||||
setNotFoundMessage(`${tableMetaData.label} ${id} could not be found.`);
|
||||
historyPurge(location.pathname);
|
||||
return;
|
||||
}
|
||||
else if ((e as QException).status === "403")
|
||||
else if ((e as QException).status === 403)
|
||||
{
|
||||
setNotFoundMessage(`You do not have permission to view ${tableMetaData.label} records`);
|
||||
historyPurge(location.pathname);
|
||||
|
@ -158,6 +158,7 @@ but we've turned off the click-to-sort function, so remove hand cursor */
|
||||
white-space: normal;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.MuiDataGrid-filterForm
|
||||
{
|
||||
align-items: flex-end;
|
||||
@ -173,10 +174,12 @@ but we've turned off the click-to-sort function, so remove hand cursor */
|
||||
{
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.MuiDataGrid-filterForm .MuiDataGrid-filterFormValueInput
|
||||
{
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.MuiDataGrid-filterForm .MuiDataGrid-filterFormOperatorInput
|
||||
{
|
||||
width: 150px;
|
||||
@ -187,6 +190,7 @@ but we've turned off the click-to-sort function, so remove hand cursor */
|
||||
{
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.MuiDataGrid-filterForm .MuiDataGrid-filterFormValueInput .MuiAutocomplete-root .MuiAutocomplete-endAdornment svg
|
||||
{
|
||||
height: 0.625em;
|
||||
@ -208,13 +212,28 @@ but we've turned off the click-to-sort function, so remove hand cursor */
|
||||
}
|
||||
|
||||
/* clears the ‘X’ from Internet Explorer */
|
||||
input[type=search]::-ms-clear { display: none; width : 0; height: 0; }
|
||||
input[type=search]::-ms-reveal { display: none; width : 0; height: 0; }
|
||||
input[type=search]::-ms-clear
|
||||
{
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
input[type=search]::-ms-reveal
|
||||
{
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* clears the ‘X’ from Chrome */
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-results-button,
|
||||
input[type="search"]::-webkit-search-results-decoration { display: none; }
|
||||
input[type="search"]::-webkit-search-results-decoration
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Shrink the big margin-bottom on modal processes */
|
||||
.modalProcess > .MuiBox-root > .MuiBox-root
|
||||
@ -270,6 +289,7 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
|
||||
color: initial !important;
|
||||
border: 1px solid rgb(206, 212, 218);
|
||||
}
|
||||
|
||||
.MuiDataGrid-filterForm .MuiAutocomplete-tag .MuiSvgIcon-root
|
||||
{
|
||||
color: initial !important;
|
||||
@ -640,6 +660,7 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
|
||||
border: 1px solid #BDBDBD;
|
||||
border-radius: 0.5rem !important;
|
||||
}
|
||||
|
||||
.MuiToggleButtonGroup-root .MuiButtonBase-root
|
||||
{
|
||||
text-transform: none;
|
||||
@ -650,11 +671,19 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
|
||||
border: none;
|
||||
flex: 1 1 0px;
|
||||
}
|
||||
|
||||
.MuiToggleButtonGroup-root .MuiButtonBase-root.Mui-selected
|
||||
{
|
||||
background: rgba(117, 117, 117, 0.20);
|
||||
}
|
||||
|
||||
.MuiToggleButtonGroup-root .MuiButtonBase-root.Mui-disabled
|
||||
{
|
||||
border: none;
|
||||
}
|
||||
|
||||
.MuiPickersDay-root.Mui-selected, .MuiPickersDay-root.MuiPickersDay-dayWithMargin:hover
|
||||
{
|
||||
color: white;
|
||||
background-color: #0062FF !important;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class Client
|
||||
{
|
||||
console.log(`Caught Exception: ${JSON.stringify(exception)}`);
|
||||
|
||||
if(exception && exception.status == "401" && Client.unauthorizedCallback)
|
||||
if (exception && exception.status == 401 && Client.unauthorizedCallback)
|
||||
{
|
||||
console.log("This is a 401 - calling the unauthorized callback.");
|
||||
Client.unauthorizedCallback();
|
||||
|
@ -219,6 +219,16 @@ class ValueUtils
|
||||
|
||||
if (field.type === QFieldType.DATE_TIME)
|
||||
{
|
||||
if(displayValue && displayValue != rawValue)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// if the date-time actually has a displayValue set, and it isn't just the //
|
||||
// raw-value being copied into the display value by whoever called us, then //
|
||||
// return the display value. //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
return displayValue;
|
||||
}
|
||||
|
||||
if (!rawValue)
|
||||
{
|
||||
return ("");
|
||||
@ -270,6 +280,7 @@ class ValueUtils
|
||||
{
|
||||
date = new Date(date);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return (`${date.toString("yyyy-MM-dd hh:mm:ss")} ${date.getHours() < 12 ? "AM" : "PM"} ${date.getTimezone()}`);
|
||||
}
|
||||
|
Reference in New Issue
Block a user