Merged feature/remove-old-auth-header into feature/CE-604-complete-shipment-sla-updates-and-local-tnt-rules

This commit is contained in:
2023-10-27 16:04:50 -05:00
13 changed files with 441 additions and 281 deletions

View File

@ -30,7 +30,7 @@ import Tooltip from "@mui/material/Tooltip/Tooltip";
import Typography from "@mui/material/Typography";
import parse from "html-react-parser";
import React, {useEffect, useState} from "react";
import {Link, NavigateFunction, useNavigate} from "react-router-dom";
import {NavigateFunction, useNavigate} from "react-router-dom";
import colors from "qqq/components/legacy/colors";
import DropdownMenu, {DropdownOption} from "qqq/components/widgets/components/DropdownMenu";
@ -46,6 +46,7 @@ export interface WidgetData
dropdownNeedsSelectedText?: string;
hasPermission?: boolean;
errorLoading?: boolean;
[other: string]: any;
}
@ -53,6 +54,7 @@ export interface WidgetData
interface Props
{
labelAdditionalComponentsLeft: LabelComponent[];
labelAdditionalElementsLeft: JSX.Element[];
labelAdditionalComponentsRight: LabelComponent[];
widgetMetaData?: QWidgetMetaData;
widgetData?: WidgetData;
@ -70,6 +72,7 @@ Widget.defaultProps = {
widgetMetaData: {},
widgetData: {},
labelAdditionalComponentsLeft: [],
labelAdditionalElementsLeft: [],
labelAdditionalComponentsRight: [],
};
@ -88,34 +91,8 @@ export class LabelComponent
{
render = (args: LabelComponentRenderArgs): JSX.Element =>
{
return (<div>Unsupported component type</div>)
}
}
/*******************************************************************************
**
*******************************************************************************/
export class HeaderLink extends LabelComponent
{
label: string;
to: string
constructor(label: string, to: string)
{
super();
this.label = label;
this.to = to;
}
render = (args: LabelComponentRenderArgs): JSX.Element =>
{
return (
<Typography variant="body2" p={2} display="inline" fontSize=".875rem" pt="0" position="relative" top="-0.25rem">
{this.to ? <Link to={this.to}>{this.label}</Link> : null}
</Typography>
);
}
return (<div>Unsupported component type</div>);
};
}
@ -184,8 +161,8 @@ export class AddNewRecordButton extends LabelComponent
openEditForm = (navigate: any, table: QTableMetaData, id: any = null, defaultValues: any, disabledFields: any) =>
{
navigate(`#/createChild=${table.name}/defaultValues=${JSON.stringify(defaultValues)}/disabledFields=${JSON.stringify(disabledFields)}`)
}
navigate(`#/createChild=${table.name}/defaultValues=${JSON.stringify(defaultValues)}/disabledFields=${JSON.stringify(disabledFields)}`);
};
render = (args: LabelComponentRenderArgs): JSX.Element =>
{
@ -194,35 +171,7 @@ export class AddNewRecordButton extends LabelComponent
<Button sx={{mt: 0.75}} onClick={() => this.openEditForm(args.navigate, this.table, null, this.defaultValues, this.disabledFields)}>{this.label}</Button>
</Typography>
);
}
}
/*******************************************************************************
**
*******************************************************************************/
export class ExportDataButton extends LabelComponent
{
callbackToExport: any;
tooltipTitle: string;
isDisabled: boolean;
constructor(callbackToExport: any, isDisabled = false, tooltipTitle: string = "Export")
{
super();
this.callbackToExport = callbackToExport;
this.isDisabled = isDisabled;
this.tooltipTitle = tooltipTitle;
}
render = (args: LabelComponentRenderArgs): JSX.Element =>
{
return (
<Typography variant="body2" py={2} px={0} display="inline" position="relative" top="-0.375rem">
<Tooltip title={this.tooltipTitle}><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={() => this.callbackToExport()} disabled={this.isDisabled}><Icon>save_alt</Icon></Button></Tooltip>
</Typography>
);
}
};
}
@ -270,7 +219,7 @@ export class Dropdown extends LabelComponent
/>
</Box>
);
}
};
}
@ -294,7 +243,7 @@ export class ReloadControl extends LabelComponent
<Tooltip title="Refresh"><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={() => this.callback()}><Icon>refresh</Icon></Button></Tooltip>
</Typography>
);
}
};
}
@ -415,7 +364,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
if (index < 0)
{
throw(`Could not find table name for label ${tableName}`);
throw (`Could not find table name for label ${tableName}`);
}
dropdownData[index] = (changedData) ? changedData.id : null;
@ -437,7 +386,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
}
}
reloadWidget(dropdownData)
reloadWidget(dropdownData);
}
}
@ -465,7 +414,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
{
console.log(`No reload widget callback in ${props.widgetMetaData.label}`);
}
}
};
const toggleFullScreenWidget = () =>
{
@ -477,14 +426,14 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
{
setFullScreenWidgetClassName("fullScreenWidget");
}
}
};
const hasPermission = props.widgetData?.hasPermission === undefined || props.widgetData?.hasPermission === true;
const isSet = (v: any): boolean =>
{
return (v !== null && v !== undefined);
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// to avoid taking up the space of the Box with the label and icon and label-components (since it has a height), only output that box if we need any of the components //
@ -493,6 +442,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
if (hasPermission)
{
needLabelBox ||= (labelComponentsLeft && labelComponentsLeft.length > 0);
needLabelBox ||= (props.labelAdditionalElementsLeft && props.labelAdditionalElementsLeft.length > 0);
needLabelBox ||= (labelComponentsRight && labelComponentsRight.length > 0);
needLabelBox ||= isSet(props.widgetMetaData?.icon);
needLabelBox ||= isSet(props.widgetData?.label);
@ -577,6 +527,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
})
)
}
{props.labelAdditionalElementsLeft}
</Box>
<Box>
{

View File

@ -22,10 +22,14 @@
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
import Button from "@mui/material/Button";
import Icon from "@mui/material/Icon";
import Tooltip from "@mui/material/Tooltip/Tooltip";
import Typography from "@mui/material/Typography";
import {DataGridPro, GridCallbackDetails, GridRowParams, MuiEvent} from "@mui/x-data-grid-pro";
import React, {useEffect, useState} from "react";
import {useNavigate} from "react-router-dom";
import Widget, {AddNewRecordButton, ExportDataButton, HeaderLink, LabelComponent} from "qqq/components/widgets/Widget";
import {useNavigate, Link} from "react-router-dom";
import Widget, {AddNewRecordButton, LabelComponent} from "qqq/components/widgets/Widget";
import DataGridUtils from "qqq/utils/DataGridUtils";
import HtmlUtils from "qqq/utils/HtmlUtils";
import Client from "qqq/utils/qqq/Client";
@ -47,6 +51,8 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
const [records, setRecords] = useState([] as QRecord[])
const [columns, setColumns] = useState([]);
const [allColumns, setAllColumns] = useState([])
const [csv, setCsv] = useState(null as string);
const [fileName, setFileName] = useState(null as string);
const navigate = useNavigate();
useEffect(() =>
@ -75,6 +81,7 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// capture all-columns to use for the export (before we might splice some away from the on-screen display) //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
const allColumns = [... columns];
setAllColumns(JSON.parse(JSON.stringify(columns)));
////////////////////////////////////////////////////////////////
@ -95,39 +102,42 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
setRows(rows);
setRecords(records)
setColumns(columns);
}
}, [data]);
const exportCallback = () =>
{
let csv = "";
for (let i = 0; i < allColumns.length; i++)
{
csv += `${i > 0 ? "," : ""}"${ValueUtils.cleanForCsv(allColumns[i].headerName)}"`
}
csv += "\n";
for (let i = 0; i < records.length; i++)
{
for (let j = 0; j < allColumns.length; j++)
let csv = "";
for (let i = 0; i < allColumns.length; i++)
{
const value = records[i].displayValues.get(allColumns[j].field) ?? records[i].values.get(allColumns[j].field)
csv += `${j > 0 ? "," : ""}"${ValueUtils.cleanForCsv(value)}"`
csv += `${i > 0 ? "," : ""}"${ValueUtils.cleanForCsv(allColumns[i].headerName)}"`
}
csv += "\n";
}
const fileName = (data?.label ?? widgetMetaData.label) + " " + ValueUtils.formatDateTimeForFileName(new Date()) + ".csv";
HtmlUtils.download(fileName, csv);
}
for (let i = 0; i < records.length; i++)
{
for (let j = 0; j < allColumns.length; j++)
{
const value = records[i].displayValues.get(allColumns[j].field) ?? records[i].values.get(allColumns[j].field)
csv += `${j > 0 ? "," : ""}"${ValueUtils.cleanForCsv(value)}"`
}
csv += "\n";
}
const fileName = (data?.label ?? widgetMetaData.label) + " " + ValueUtils.formatDateTimeForFileName(new Date()) + ".csv";
setCsv(csv);
setFileName(fileName);
}
}, [data]);
///////////////////
// view all link //
///////////////////
const labelAdditionalComponentsLeft: LabelComponent[] = []
const labelAdditionalElementsLeft: JSX.Element[] = [];
if(data && data.viewAllLink)
{
labelAdditionalComponentsLeft.push(new HeaderLink("View All", data.viewAllLink));
labelAdditionalElementsLeft.push(
<Typography variant="body2" p={2} display="inline" fontSize=".875rem" pt="0" position="relative" top="-0.25rem">
<Link to={data.viewAllLink}>View All</Link>
</Typography>
)
}
///////////////////
@ -149,7 +159,26 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
}
}
labelAdditionalComponentsLeft.push(new ExportDataButton(() => exportCallback(), isExportDisabled, tooltipTitle))
const onExportClick = () =>
{
if(csv)
{
HtmlUtils.download(fileName, csv);
}
else
{
alert("There is no data available to export.")
}
}
if(widgetMetaData?.showExportButton)
{
labelAdditionalElementsLeft.push(
<Typography key={1} variant="body2" py={2} px={0} display="inline" position="relative" top="-0.375rem">
<Tooltip title={tooltipTitle}><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={onExportClick} disabled={isExportDisabled}><Icon>save_alt</Icon></Button></Tooltip>
</Typography>
);
}
////////////////////
// add new button //
@ -184,7 +213,7 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
<Widget
widgetMetaData={widgetMetaData}
widgetData={data}
labelAdditionalComponentsLeft={labelAdditionalComponentsLeft}
labelAdditionalElementsLeft={labelAdditionalElementsLeft}
labelAdditionalComponentsRight={labelAdditionalComponentsRight}
>
<DataGridPro

View File

@ -21,11 +21,15 @@
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
import Button from "@mui/material/Button";
import Icon from "@mui/material/Icon";
import Tooltip from "@mui/material/Tooltip/Tooltip";
import Typography from "@mui/material/Typography";
// @ts-ignore
import {htmlToText} from "html-to-text";
import React, {useEffect, useState} from "react";
import TableCard from "qqq/components/widgets/tables/TableCard";
import Widget, {ExportDataButton, WidgetData} from "qqq/components/widgets/Widget";
import Widget, {WidgetData} from "qqq/components/widgets/Widget";
import HtmlUtils from "qqq/utils/HtmlUtils";
import ValueUtils from "qqq/utils/qqq/ValueUtils";
@ -43,6 +47,8 @@ TableWidget.defaultProps = {
function TableWidget(props: Props): JSX.Element
{
const [isExportDisabled, setIsExportDisabled] = useState(false); // hmm, would like true here, but it broke...
const [csv, setCsv] = useState(null as string);
const [fileName, setFileName] = useState(null as string);
const rows = props.widgetData?.rows;
const columns = props.widgetData?.columns;
@ -56,14 +62,8 @@ function TableWidget(props: Props): JSX.Element
}
setIsExportDisabled(isExportDisabled);
}, [props.widgetMetaData, props.widgetData]);
const exportCallback = () =>
{
if (props.widgetData && rows && columns)
{
console.log(props.widgetData);
let csv = "";
for (let j = 0; j < columns.length; j++)
{
@ -98,16 +98,37 @@ function TableWidget(props: Props): JSX.Element
csv += "\n";
}
console.log(csv);
setCsv(csv);
const fileName = (props.widgetData.label ?? props.widgetMetaData.label) + " " + ValueUtils.formatDateTimeForFileName(new Date()) + ".csv";
setFileName(fileName)
console.log(`useEffect, setting fileName ${fileName}`);
}
}, [props.widgetMetaData, props.widgetData]);
const onExportClick = () =>
{
if(csv)
{
HtmlUtils.download(fileName, csv);
}
else
{
alert("There is no data available to export.");
alert("There is no data available to export.")
}
};
}
const labelAdditionalElementsLeft: JSX.Element[] = [];
if(props.widgetMetaData?.showExportButton)
{
labelAdditionalElementsLeft.push(
<Typography key={1} variant="body2" py={2} px={0} display="inline" position="relative" top="-0.375rem">
<Tooltip title="Export"><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={onExportClick} disabled={false}><Icon>save_alt</Icon></Button></Tooltip>
</Typography>
);
}
return (
<Widget
@ -116,7 +137,7 @@ function TableWidget(props: Props): JSX.Element
reloadWidgetCallback={(data) => props.reloadWidgetCallback(data)}
footerHTML={props.widgetData?.footerHTML}
isChild={props.isChild}
labelAdditionalComponentsLeft={props.widgetMetaData?.showExportButton ? [new ExportDataButton(() => exportCallback(), isExportDisabled)] : []}
labelAdditionalElementsLeft={labelAdditionalElementsLeft}
>
<TableCard
noRowsFoundHTML={props.widgetData?.noRowsFoundHTML}