mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
Add export to RecordGridWidget
This commit is contained in:
@ -155,22 +155,22 @@ export class AddNewRecordButton extends LabelComponent
|
||||
export class ExportDataButton extends LabelComponent
|
||||
{
|
||||
callbackToExport: any;
|
||||
label: string;
|
||||
tooltipTitle: string;
|
||||
isDisabled: boolean;
|
||||
|
||||
constructor(callbackToExport: any, isDisabled = false, label: string = "Export")
|
||||
constructor(callbackToExport: any, isDisabled = false, tooltipTitle: string = "Export")
|
||||
{
|
||||
super();
|
||||
this.callbackToExport = callbackToExport;
|
||||
this.isDisabled = isDisabled;
|
||||
this.label = label;
|
||||
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="Export"><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={() => this.callbackToExport()} disabled={this.isDisabled}><Icon>save_alt</Icon></Button></Tooltip>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
@ -25,9 +25,11 @@ import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
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, HeaderLink, LabelComponent} from "qqq/components/widgets/Widget";
|
||||
import Widget, {AddNewRecordButton, ExportDataButton, HeaderLink, 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";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -42,7 +44,9 @@ const qController = Client.getInstance();
|
||||
function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
|
||||
{
|
||||
const [rows, setRows] = useState([]);
|
||||
const [records, setRecords] = useState([] as QRecord[])
|
||||
const [columns, setColumns] = useState([]);
|
||||
const [allColumns, setAllColumns] = useState([])
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() =>
|
||||
@ -68,6 +72,11 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
|
||||
const childTablePath = data.tablePath ? data.tablePath + (data.tablePath.endsWith("/") ? "" : "/") : data.tablePath;
|
||||
const columns = DataGridUtils.setupGridColumns(tableMetaData, childTablePath, null, "bySection");
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// capture all-columns to use for the export (before we might splice some away from the on-screen display) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
setAllColumns(JSON.parse(JSON.stringify(columns)));
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// do not not show the foreign-key column of the parent table //
|
||||
////////////////////////////////////////////////////////////////
|
||||
@ -84,16 +93,67 @@ 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++)
|
||||
{
|
||||
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";
|
||||
HtmlUtils.download(fileName, csv);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// view all link //
|
||||
///////////////////
|
||||
const labelAdditionalComponentsLeft: LabelComponent[] = []
|
||||
if(data && data.viewAllLink)
|
||||
{
|
||||
labelAdditionalComponentsLeft.push(new HeaderLink("View All", data.viewAllLink));
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// export button //
|
||||
///////////////////
|
||||
let isExportDisabled = true;
|
||||
let tooltipTitle = "Export";
|
||||
if (data && data.childTableMetaData && data.queryOutput && data.queryOutput.records && data.queryOutput.records.length > 0)
|
||||
{
|
||||
isExportDisabled = false;
|
||||
|
||||
if(data.totalRows && data.queryOutput.records.length < data.totalRows)
|
||||
{
|
||||
tooltipTitle = "Export these " + data.queryOutput.records.length + " records."
|
||||
if(data.viewAllLink)
|
||||
{
|
||||
tooltipTitle += "\nClick View All to export all records.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labelAdditionalComponentsLeft.push(new ExportDataButton(() => exportCallback(), isExportDisabled, tooltipTitle))
|
||||
|
||||
////////////////////
|
||||
// add new button //
|
||||
////////////////////
|
||||
const labelAdditionalComponentsRight: LabelComponent[] = []
|
||||
if(data && data.canAddChildRecord)
|
||||
{
|
||||
|
@ -26,6 +26,7 @@ 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 HtmlUtils from "qqq/utils/HtmlUtils";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
|
||||
interface Props
|
||||
@ -37,23 +38,8 @@ interface Props
|
||||
}
|
||||
|
||||
TableWidget.defaultProps = {
|
||||
foo: null,
|
||||
};
|
||||
|
||||
function download(filename: string, text: string)
|
||||
{
|
||||
var element = document.createElement("a");
|
||||
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
|
||||
element.setAttribute("download", filename);
|
||||
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
function TableWidget(props: Props): JSX.Element
|
||||
{
|
||||
const [isExportDisabled, setIsExportDisabled] = useState(true);
|
||||
@ -115,7 +101,7 @@ function TableWidget(props: Props): JSX.Element
|
||||
console.log(csv);
|
||||
|
||||
const fileName = (props.widgetData.label ?? props.widgetMetaData.label) + " " + ValueUtils.formatDateTimeForFileName(new Date()) + ".csv";
|
||||
download(fileName, csv);
|
||||
HtmlUtils.download(fileName, csv);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -27,7 +27,6 @@ import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJo
|
||||
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
|
||||
import {TablePagination} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Grid from "@mui/material/Grid";
|
||||
@ -38,6 +37,7 @@ import {DataGridPro, GridSortModel} from "@mui/x-data-grid-pro";
|
||||
import FormData from "form-data";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import DataGridUtils from "qqq/utils/DataGridUtils";
|
||||
import HtmlUtils from "qqq/utils/HtmlUtils";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
|
||||
@ -54,21 +54,6 @@ ColumnStats.defaultProps = {
|
||||
|
||||
const qController = Client.getInstance();
|
||||
|
||||
// todo - merge w/ same function in TableWidget
|
||||
function download(filename: string, text: string)
|
||||
{
|
||||
var element = document.createElement("a");
|
||||
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
|
||||
element.setAttribute("download", filename);
|
||||
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
function ColumnStats({tableMetaData, fieldMetaData, fieldTableName, filter}: Props): JSX.Element
|
||||
{
|
||||
const [statusString, setStatusString] = useState("Calculating statistics...");
|
||||
@ -202,7 +187,7 @@ function ColumnStats({tableMetaData, fieldMetaData, fieldTableName, filter}: Pro
|
||||
}
|
||||
|
||||
const fileName = tableMetaData.label + " - " + fieldMetaData.label + " Column Stats " + ValueUtils.formatDateTimeForFileName(new Date()) + ".csv";
|
||||
download(fileName, csv);
|
||||
HtmlUtils.download(fileName, csv);
|
||||
}
|
||||
|
||||
function Loading()
|
||||
|
62
src/qqq/utils/HtmlUtils.ts
Normal file
62
src/qqq/utils/HtmlUtils.ts
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility functions for basic html/webpage/browser things.
|
||||
*******************************************************************************/
|
||||
export default class HtmlUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Since our pages are set (w/ style on the HTML element) to smooth scroll,
|
||||
** if you ever want to do an "auto" scroll (e.g., instant, not smooth), you can
|
||||
** call this method, which will remove that style, and then put it back.
|
||||
*******************************************************************************/
|
||||
static autoScroll = (top: number, left: number = 0) =>
|
||||
{
|
||||
let htmlElement = document.querySelector("html");
|
||||
const initialScrollBehavior = htmlElement.style.scrollBehavior;
|
||||
htmlElement.style.scrollBehavior = "auto";
|
||||
setTimeout(() =>
|
||||
{
|
||||
window.scrollTo({top: top, left: left, behavior: "auto"});
|
||||
htmlElement.style.scrollBehavior = initialScrollBehavior;
|
||||
});
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
** Download a client-side generated file (e.g., csv).
|
||||
*******************************************************************************/
|
||||
static download = (filename: string, text: string) =>
|
||||
{
|
||||
var element = document.createElement("a");
|
||||
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
|
||||
element.setAttribute("download", filename);
|
||||
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
};
|
||||
|
||||
}
|
Reference in New Issue
Block a user