diff --git a/src/qqq/components/widgets/Widget.tsx b/src/qqq/components/widgets/Widget.tsx index da59312..66e749d 100644 --- a/src/qqq/components/widgets/Widget.tsx +++ b/src/qqq/components/widgets/Widget.tsx @@ -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 (
Unsupported component type
) - } -} - - -/******************************************************************************* - ** - *******************************************************************************/ -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 ( - - {this.to ? {this.label} : null} - - ); - } + return (
Unsupported component type
); + }; } @@ -141,8 +118,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 => { @@ -151,35 +128,7 @@ export class AddNewRecordButton extends LabelComponent ); - } -} - - -/******************************************************************************* - ** - *******************************************************************************/ -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 ( - - - - ); - } + }; } @@ -227,7 +176,7 @@ export class Dropdown extends LabelComponent /> ); - } + }; } @@ -251,7 +200,7 @@ export class ReloadControl extends LabelComponent ); - } + }; } @@ -372,7 +321,7 @@ function Widget(props: React.PropsWithChildren): 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; @@ -394,7 +343,7 @@ function Widget(props: React.PropsWithChildren): JSX.Element } } - reloadWidget(dropdownData) + reloadWidget(dropdownData); } } @@ -422,7 +371,7 @@ function Widget(props: React.PropsWithChildren): JSX.Element { console.log(`No reload widget callback in ${props.widgetMetaData.label}`); } - } + }; const toggleFullScreenWidget = () => { @@ -434,14 +383,14 @@ function Widget(props: React.PropsWithChildren): 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 // @@ -450,6 +399,7 @@ function Widget(props: React.PropsWithChildren): 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); @@ -530,6 +480,7 @@ function Widget(props: React.PropsWithChildren): JSX.Element }) ) } + {props.labelAdditionalElementsLeft} { diff --git a/src/qqq/components/widgets/misc/RecordGridWidget.tsx b/src/qqq/components/widgets/misc/RecordGridWidget.tsx index 6f02ba8..1f97113 100644 --- a/src/qqq/components/widgets/misc/RecordGridWidget.tsx +++ b/src/qqq/components/widgets/misc/RecordGridWidget.tsx @@ -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( + + View All + + ) } /////////////////// @@ -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( + + + + ); + } //////////////////// // add new button // @@ -184,7 +213,7 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element - { 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( + + + + ); + } return ( props.reloadWidgetCallback(data)} footerHTML={props.widgetData?.footerHTML} isChild={props.isChild} - labelAdditionalComponentsLeft={props.widgetMetaData?.showExportButton ? [new ExportDataButton(() => exportCallback(), isExportDisabled)] : []} + labelAdditionalElementsLeft={labelAdditionalElementsLeft} >