mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 13:20:43 +00:00
Merged feature/remove-old-auth-header into feature/CE-604-complete-shipment-sla-updates-and-local-tnt-rules
This commit is contained in:
@ -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>
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
Reference in New Issue
Block a user