mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 05:10:45 +00:00
Add add-child capability to recordGridWidget; making new standard Widget that others can(should) use
This commit is contained in:
@ -1,10 +1,13 @@
|
|||||||
/// <reference types="cypress-wait-for-stable-dom" />
|
/// <reference types="cypress-wait-for-stable-dom" />
|
||||||
|
|
||||||
|
import QLib from "./lib/qLib";
|
||||||
|
|
||||||
describe("table query screen", () =>
|
describe("table query screen", () =>
|
||||||
{
|
{
|
||||||
|
|
||||||
before(() =>
|
before(() =>
|
||||||
{
|
{
|
||||||
|
QLib.init(cy);
|
||||||
|
|
||||||
cy.intercept("GET", "/metaData/authentication", {fixture: "metaData/authentication.json"}).as("authenticationMetaData");
|
cy.intercept("GET", "/metaData/authentication", {fixture: "metaData/authentication.json"}).as("authenticationMetaData");
|
||||||
cy.intercept("GET", "/metaData", {fixture: "metaData/index.json"}).as("metaData");
|
cy.intercept("GET", "/metaData", {fixture: "metaData/index.json"}).as("metaData");
|
||||||
cy.intercept("GET", "/metaData/table/person", {fixture: "metaData/table/person.json"}).as("personMetaData");
|
cy.intercept("GET", "/metaData/table/person", {fixture: "metaData/table/person.json"}).as("personMetaData");
|
||||||
@ -29,14 +32,13 @@ describe("table query screen", () =>
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.only("can add query filters", () =>
|
it("can add query filters", () =>
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// go to table, wait for filter to run, and rows to appear //
|
// go to table, wait for filter to run, and rows to appear //
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
cy.visit("https://localhost:3000/peopleApp/greetingsApp/person");
|
cy.visit("https://localhost:3000/peopleApp/greetingsApp/person");
|
||||||
cy.wait(["@personQuery", "@personCount"]);
|
QLib.waitForQueryScreen();
|
||||||
cy.get(".MuiDataGrid-virtualScrollerRenderZone").children().should("have.length.greaterThan", 3);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// open the filter window, enter a value, wait for query to re-run //
|
// open the filter window, enter a value, wait for query to re-run //
|
||||||
@ -72,6 +74,29 @@ describe("table query screen", () =>
|
|||||||
cy.contains(".MuiDataGrid-toolbarContainer .MuiBadge-root", "1").should("not.exist");
|
cy.contains(".MuiDataGrid-toolbarContainer .MuiBadge-root", "1").should("not.exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.only("can do a boolean or query", () =>
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// go to table, wait for filter to run //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
cy.visit("https://localhost:3000/peopleApp/greetingsApp/person");
|
||||||
|
QLib.waitForQueryScreen();
|
||||||
|
|
||||||
|
QLib.buildEntityListQueryFilter([
|
||||||
|
{fieldLabel: "First Name", operator: "contains", textValue: "Dar"},
|
||||||
|
{fieldLabel: "First Name", operator: "contains", textValue: "Jam"}
|
||||||
|
], "or");
|
||||||
|
|
||||||
|
let expectedFilterContents0 = JSON.stringify({fieldName: "firstName", operator: "CONTAINS", values: ["Dar"]});
|
||||||
|
let expectedFilterContents1 = JSON.stringify({fieldName: "firstName", operator: "CONTAINS", values: ["Jam"]});
|
||||||
|
cy.wait("@personQuery").its("request.body").should((body) =>
|
||||||
|
{
|
||||||
|
expect(body).to.contain(expectedFilterContents0);
|
||||||
|
expect(body).to.contain(expectedFilterContents1);
|
||||||
|
expect(body).to.contain("asdf");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// tests to add:
|
// tests to add:
|
||||||
// - filter boolean OR
|
// - filter boolean OR
|
||||||
// - sort column
|
// - sort column
|
||||||
|
86
cypress/e2e/lib/qLib.ts
Normal file
86
cypress/e2e/lib/qLib.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class QLib
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
private static cy: Cypress.cy;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
public static init(cy: Cypress.cy)
|
||||||
|
{
|
||||||
|
QLib.cy = cy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Wait for a query to finish on the entity-list screen. specifically, wait for
|
||||||
|
** personQuery & personCount requests, and wait for the data grid to have rows.
|
||||||
|
*******************************************************************************/
|
||||||
|
public static waitForQueryScreen()
|
||||||
|
{
|
||||||
|
QLib.cy.wait(["@personQuery", "@personCount"]);
|
||||||
|
QLib.cy.get(".MuiDataGrid-virtualScrollerRenderZone").children().should("have.length.greaterThan", 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Open the Filters drop down, and build a query
|
||||||
|
*******************************************************************************/
|
||||||
|
public static buildEntityListQueryFilter(input: QueryFilterInput | QueryFilterInput[], booleanOperator: ("and" | "or") = "and")
|
||||||
|
{
|
||||||
|
QLib.cy.contains("Filters").click();
|
||||||
|
|
||||||
|
if ((input as QueryFilterInput).fieldLabel)
|
||||||
|
{
|
||||||
|
const queryFilterInput = input as QueryFilterInput;
|
||||||
|
QLib.addSingleQueryFilterInput(queryFilterInput, 0, booleanOperator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const inputArray = input as QueryFilterInput[];
|
||||||
|
inputArray.forEach((qfi, index) => QLib.addSingleQueryFilterInput(qfi, index, booleanOperator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** private helper - adds 1 query filter input.
|
||||||
|
*******************************************************************************/
|
||||||
|
private static addSingleQueryFilterInput(queryFilterInput: QueryFilterInput, index: number, booleanOperator: ("and" | "or"))
|
||||||
|
{
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
QLib.cy.contains("Add filter").click();
|
||||||
|
QLib.cy.get(".MuiDataGrid-filterForm").eq(index).find(".MuiDataGrid-filterFormLinkOperatorInput SELECT").select(booleanOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLib.cy.get(".MuiDataGrid-filterForm").eq(index).find(".MuiDataGrid-filterFormColumnInput SELECT").select(queryFilterInput.fieldLabel);
|
||||||
|
QLib.cy.get(".MuiDataGrid-filterForm").eq(index).find(".MuiDataGrid-filterFormOperatorInput SELECT").select(queryFilterInput.operator);
|
||||||
|
QLib.cy.get(".MuiDataGrid-filterForm").eq(index).find(".MuiDataGrid-filterFormValueInput INPUT").type(queryFilterInput.textValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueryFilterInput
|
||||||
|
{
|
||||||
|
fieldLabel?: string;
|
||||||
|
fieldName?: string;
|
||||||
|
operator?: string;
|
||||||
|
textValue?: string;
|
||||||
|
}
|
@ -21,6 +21,7 @@
|
|||||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
||||||
import {Skeleton} from "@mui/material";
|
import {Skeleton} from "@mui/material";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import parse from "html-react-parser";
|
import parse from "html-react-parser";
|
||||||
@ -37,6 +38,7 @@ import QuickSightChart from "qqq/pages/dashboards/Widgets/QuickSightChart";
|
|||||||
import RecordGridWidget from "qqq/pages/dashboards/Widgets/RecordGridWidget";
|
import RecordGridWidget from "qqq/pages/dashboards/Widgets/RecordGridWidget";
|
||||||
import StepperCard from "qqq/pages/dashboards/Widgets/StepperCard";
|
import StepperCard from "qqq/pages/dashboards/Widgets/StepperCard";
|
||||||
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
||||||
|
import Widget from "qqq/pages/dashboards/Widgets/Widget";
|
||||||
import ProcessRun from "qqq/pages/process-run";
|
import ProcessRun from "qqq/pages/process-run";
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
@ -116,7 +118,7 @@ function DashboardWidgets({widgetMetaDataList, entityPrimaryKey, omitWrappingGri
|
|||||||
const renderWidget = (widgetMetaData: QWidgetMetaData, i: number): JSX.Element =>
|
const renderWidget = (widgetMetaData: QWidgetMetaData, i: number): JSX.Element =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<MDBox sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
<MDBox key={i} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px", width: "100%", height: "100%"}}>
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "parentWidget" && (
|
widgetMetaData.type === "parentWidget" && (
|
||||||
<ParentWidget
|
<ParentWidget
|
||||||
@ -165,11 +167,8 @@ function DashboardWidgets({widgetMetaDataList, entityPrimaryKey, omitWrappingGri
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "html" && (
|
widgetMetaData.type === "html" && (
|
||||||
<Card sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
<Widget label={widgetMetaData.label}>
|
||||||
<MDBox padding="1rem">
|
<Box px={1} pt={0} pb={2}>
|
||||||
<MDTypography variant="h5" textTransform="capitalize">
|
|
||||||
{widgetMetaData.label}
|
|
||||||
</MDTypography>
|
|
||||||
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
||||||
{
|
{
|
||||||
widgetData && widgetData[i] && widgetData[i].html ? (
|
widgetData && widgetData[i] && widgetData[i].html ? (
|
||||||
@ -177,8 +176,8 @@ function DashboardWidgets({widgetMetaDataList, entityPrimaryKey, omitWrappingGri
|
|||||||
) : <Skeleton />
|
) : <Skeleton />
|
||||||
}
|
}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</Box>
|
||||||
</Card>
|
</Widget>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -233,6 +232,7 @@ function DashboardWidgets({widgetMetaDataList, entityPrimaryKey, omitWrappingGri
|
|||||||
<RecordGridWidget
|
<RecordGridWidget
|
||||||
title={widgetMetaData.label}
|
title={widgetMetaData.label}
|
||||||
data={widgetData[i]}
|
data={widgetData[i]}
|
||||||
|
reloadWidgetCallback={reloadWidget}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,11 @@ import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QF
|
|||||||
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
|
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
|
||||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
|
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
|
||||||
|
import {QPossibleValue} from "@kingsrook/qqq-frontend-core/lib/model/QPossibleValue";
|
||||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
import {Alert} from "@mui/material";
|
import {Alert} from "@mui/material";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@mui/material/Avatar";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
@ -49,10 +51,23 @@ import QValueUtils from "qqq/utils/QValueUtils";
|
|||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
id?: string;
|
id?: string;
|
||||||
|
isModal: boolean;
|
||||||
table?: QTableMetaData;
|
table?: QTableMetaData;
|
||||||
|
closeModalHandler?: (event: object, reason: string) => void;
|
||||||
|
defaultValues: { [key: string]: string };
|
||||||
|
disabledFields: { [key: string]: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
function EntityForm({table, id}: Props): JSX.Element
|
EntityForm.defaultProps = {
|
||||||
|
id: null,
|
||||||
|
isModal: false,
|
||||||
|
table: null,
|
||||||
|
closeModalHandler: null,
|
||||||
|
defaultValues: {},
|
||||||
|
disabledFields: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
function EntityForm({table, isModal, id, closeModalHandler, defaultValues, disabledFields}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const qController = QClient.getInstance();
|
const qController = QClient.getInstance();
|
||||||
const tableNameParam = useParams().tableName;
|
const tableNameParam = useParams().tableName;
|
||||||
@ -126,39 +141,89 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
// if doing an edit, fetch the record and pre-populate the form values from it //
|
// if doing an edit, fetch the record and pre-populate the form values from it //
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
let record: QRecord = null;
|
let record: QRecord = null;
|
||||||
|
let defaultDisplayValues = new Map<string, string>();
|
||||||
if (id !== null)
|
if (id !== null)
|
||||||
{
|
{
|
||||||
record = await qController.get(tableName, id);
|
record = await qController.get(tableName, id);
|
||||||
setRecord(record);
|
setRecord(record);
|
||||||
setFormTitle(`Edit ${tableMetaData?.label}: ${record?.recordLabel}`);
|
setFormTitle(`Edit ${tableMetaData?.label}: ${record?.recordLabel}`);
|
||||||
setPageHeader(`Edit ${tableMetaData?.label}: ${record?.recordLabel}`);
|
|
||||||
|
if (!isModal)
|
||||||
|
{
|
||||||
|
setPageHeader(`Edit ${tableMetaData?.label}: ${record?.recordLabel}`);
|
||||||
|
}
|
||||||
|
|
||||||
tableMetaData.fields.forEach((fieldMetaData, key) =>
|
tableMetaData.fields.forEach((fieldMetaData, key) =>
|
||||||
{
|
{
|
||||||
initialValues[key] = record.values.get(key);
|
initialValues[key] = record.values.get(key);
|
||||||
if(fieldMetaData.type == QFieldType.DATE_TIME)
|
|
||||||
{
|
|
||||||
initialValues[key] = QValueUtils.formatDateTimeValueForForm(record.values.get(key));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setFormValues(formValues);
|
//? safe to delete? setFormValues(formValues);
|
||||||
|
|
||||||
if(!tableMetaData.capabilities.has(Capability.TABLE_UPDATE))
|
if (!tableMetaData.capabilities.has(Capability.TABLE_UPDATE))
|
||||||
{
|
{
|
||||||
setNoCapabilityError("You may not edit records in this table");
|
setNoCapabilityError("You may not edit records in this table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
///////////////////////////////////////////
|
||||||
|
// else handle preparing to do an insert //
|
||||||
|
///////////////////////////////////////////
|
||||||
setFormTitle(`Creating New ${tableMetaData?.label}`);
|
setFormTitle(`Creating New ${tableMetaData?.label}`);
|
||||||
setPageHeader(`Creating New ${tableMetaData?.label}`);
|
|
||||||
|
|
||||||
if(!tableMetaData.capabilities.has(Capability.TABLE_INSERT))
|
if (!isModal)
|
||||||
|
{
|
||||||
|
setPageHeader(`Creating New ${tableMetaData?.label}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tableMetaData.capabilities.has(Capability.TABLE_INSERT))
|
||||||
{
|
{
|
||||||
setNoCapabilityError("You may not create records in this table");
|
setNoCapabilityError("You may not create records in this table");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if default values were supplied for a new record, then populate initialValues, for formik. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(defaultValues)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < fieldArray.length; i++)
|
||||||
|
{
|
||||||
|
const fieldMetaData = fieldArray[i];
|
||||||
|
const fieldName = fieldMetaData.name;
|
||||||
|
if (defaultValues[fieldName])
|
||||||
|
{
|
||||||
|
initialValues[fieldName] = defaultValues[fieldName];
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// we need to set the initialDisplayValue for possible value fields with a default value //
|
||||||
|
// so, look them up here now if needed //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if (fieldMetaData.possibleValueSourceName)
|
||||||
|
{
|
||||||
|
const results: QPossibleValue[] = await qController.possibleValues(tableName, fieldName, null, [initialValues[fieldName]]);
|
||||||
|
if (results && results.length > 0)
|
||||||
|
{
|
||||||
|
defaultDisplayValues.set(fieldName, results[0].label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// make sure all initialValues are properly formatted for the form //
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
for (let i = 0; i < fieldArray.length; i++)
|
||||||
|
{
|
||||||
|
const fieldMetaData = fieldArray[i];
|
||||||
|
if (fieldMetaData.type == QFieldType.DATE_TIME && initialValues[fieldMetaData.name])
|
||||||
|
{
|
||||||
|
initialValues[fieldMetaData.name] = QValueUtils.formatDateTimeValueForForm(initialValues[fieldMetaData.name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setInitialValues(initialValues);
|
setInitialValues(initialValues);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
@ -168,7 +233,15 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
dynamicFormFields,
|
dynamicFormFields,
|
||||||
formValidations,
|
formValidations,
|
||||||
} = DynamicFormUtils.getFormData(fieldArray);
|
} = DynamicFormUtils.getFormData(fieldArray);
|
||||||
DynamicFormUtils.addPossibleValueProps(dynamicFormFields, fieldArray, tableName, record?.displayValues);
|
DynamicFormUtils.addPossibleValueProps(dynamicFormFields, fieldArray, tableName, record ? record.displayValues : defaultDisplayValues);
|
||||||
|
|
||||||
|
if(disabledFields)
|
||||||
|
{
|
||||||
|
for (let fieldName in disabledFields)
|
||||||
|
{
|
||||||
|
dynamicFormFields[fieldName].isEditable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
// group the formFields by section //
|
// group the formFields by section //
|
||||||
@ -181,12 +254,7 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
const section = tableSections[i];
|
const section = tableSections[i];
|
||||||
const sectionDynamicFormFields: any[] = [];
|
const sectionDynamicFormFields: any[] = [];
|
||||||
|
|
||||||
if(section.isHidden)
|
if (section.isHidden || !section.fieldNames)
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!section.fieldNames)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -271,8 +339,15 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
.update(tableName, id, values)
|
.update(tableName, id, values)
|
||||||
.then((record) =>
|
.then((record) =>
|
||||||
{
|
{
|
||||||
const path = `${location.pathname.replace(/\/edit$/, "")}?updateSuccess=true`;
|
if (isModal)
|
||||||
navigate(path);
|
{
|
||||||
|
closeModalHandler(null, "recordUpdated");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const path = `${location.pathname.replace(/\/edit$/, "")}?updateSuccess=true`;
|
||||||
|
navigate(path);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) =>
|
.catch((error) =>
|
||||||
{
|
{
|
||||||
@ -287,8 +362,15 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
.create(tableName, values)
|
.create(tableName, values)
|
||||||
.then((record) =>
|
.then((record) =>
|
||||||
{
|
{
|
||||||
const path = `${location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField))}?createSuccess=true`;
|
if (isModal)
|
||||||
navigate(path);
|
{
|
||||||
|
closeModalHandler(null, "recordCreated");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const path = `${location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField))}?createSuccess=true`;
|
||||||
|
navigate(path);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) =>
|
.catch((error) =>
|
||||||
{
|
{
|
||||||
@ -300,111 +382,128 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
|
|
||||||
const formId = id != null ? `edit-${tableMetaData?.name}-form` : `create-${tableMetaData?.name}-form`;
|
const formId = id != null ? `edit-${tableMetaData?.name}-form` : `create-${tableMetaData?.name}-form`;
|
||||||
|
|
||||||
if(noCapabilityError)
|
let body;
|
||||||
|
if (noCapabilityError)
|
||||||
{
|
{
|
||||||
return <MDBox mb={3}>
|
body = (
|
||||||
<Grid container spacing={3}>
|
<MDBox mb={3}>
|
||||||
<Grid item xs={12}>
|
<Grid container spacing={3}>
|
||||||
<MDBox mb={3}>
|
<Grid item xs={12}>
|
||||||
<Alert severity="error">{noCapabilityError}</Alert>
|
|
||||||
</MDBox>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</MDBox>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MDBox mb={3}>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
{alertContent ? (
|
|
||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<Alert severity="error">{alertContent}</Alert>
|
<Alert severity="error">{noCapabilityError}</Alert>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
) : ("")}
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</MDBox>
|
||||||
<Grid container spacing={3}>
|
);
|
||||||
<Grid item xs={12} lg={3}>
|
}
|
||||||
<QRecordSidebar tableSections={tableSections} />
|
else
|
||||||
|
{
|
||||||
|
const cardElevation = isModal ? 3 : 1;
|
||||||
|
body = (
|
||||||
|
<MDBox mb={3}>
|
||||||
|
<Grid container spacing={3}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{alertContent ? (
|
||||||
|
<MDBox mb={3}>
|
||||||
|
<Alert severity="error">{alertContent}</Alert>
|
||||||
|
</MDBox>
|
||||||
|
) : ("")}
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} lg={9}>
|
<Grid container spacing={3}>
|
||||||
|
{
|
||||||
|
!isModal &&
|
||||||
|
<Grid item xs={12} lg={3}>
|
||||||
|
<QRecordSidebar tableSections={tableSections} />
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
<Grid item xs={12} lg={isModal ? 12 : 9}>
|
||||||
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
validationSchema={validations}
|
validationSchema={validations}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
{({
|
{({
|
||||||
values,
|
values,
|
||||||
errors,
|
errors,
|
||||||
touched,
|
touched,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
}) => (
|
}) => (
|
||||||
<Form id={formId} autoComplete="off">
|
<Form id={formId} autoComplete="off">
|
||||||
|
|
||||||
<MDBox pb={3} pt={0}>
|
<MDBox pb={3} pt={0}>
|
||||||
<Card id={`${t1sectionName}`} sx={{overflow: "visible", pb: 2, scrollMarginTop: "100px"}}>
|
<Card id={`${t1sectionName}`} sx={{overflow: "visible", pb: 2, scrollMarginTop: "100px"}} elevation={cardElevation}>
|
||||||
<MDBox display="flex" p={3} pb={1}>
|
<MDBox display="flex" p={3} pb={1}>
|
||||||
<MDBox mr={1.5}>
|
<MDBox mr={1.5}>
|
||||||
<Avatar sx={{bgcolor: colors.info.main}}>
|
<Avatar sx={{bgcolor: colors.info.main}}>
|
||||||
<Icon>
|
<Icon>
|
||||||
{tableMetaData?.iconName}
|
{tableMetaData?.iconName}
|
||||||
</Icon>
|
</Icon>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
</MDBox>
|
||||||
|
<MDBox display="flex" alignItems="center">
|
||||||
|
<MDTypography variant="h5">{formTitle}</MDTypography>
|
||||||
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox display="flex" alignItems="center">
|
{
|
||||||
<MDTypography variant="h5">{formTitle}</MDTypography>
|
t1sectionName && formFields ? (
|
||||||
</MDBox>
|
<MDBox pb={1} px={3}>
|
||||||
</MDBox>
|
<MDBox p={3} width="100%">
|
||||||
{
|
{getFormSection(values, touched, formFields.get(t1sectionName), errors)}
|
||||||
t1sectionName && formFields ? (
|
</MDBox>
|
||||||
<MDBox pb={1} px={3}>
|
|
||||||
<MDBox p={3} width="100%">
|
|
||||||
{getFormSection(values, touched, formFields.get(t1sectionName), errors)}
|
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
) : null
|
||||||
) : null
|
}
|
||||||
}
|
|
||||||
</Card>
|
|
||||||
</MDBox>
|
|
||||||
{formFields && nonT1Sections.length ? nonT1Sections.map((section: QTableSection) => (
|
|
||||||
<MDBox key={`edit-card-${section.name}`} pb={3}>
|
|
||||||
<Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}}>
|
|
||||||
<MDTypography variant="h5" p={3} pb={1}>
|
|
||||||
{section.label}
|
|
||||||
</MDTypography>
|
|
||||||
<MDBox pb={1} px={3}>
|
|
||||||
<MDBox p={3} width="100%">
|
|
||||||
{
|
|
||||||
getFormSection(values, touched, formFields.get(section.name), errors)
|
|
||||||
}
|
|
||||||
</MDBox>
|
|
||||||
</MDBox>
|
|
||||||
</Card>
|
</Card>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
)) : null}
|
{formFields && nonT1Sections.length ? nonT1Sections.map((section: QTableSection) => (
|
||||||
|
<MDBox key={`edit-card-${section.name}`} pb={3}>
|
||||||
|
<Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}} elevation={cardElevation}>
|
||||||
|
<MDTypography variant="h5" p={3} pb={1}>
|
||||||
|
{section.label}
|
||||||
|
</MDTypography>
|
||||||
|
<MDBox pb={1} px={3}>
|
||||||
|
<MDBox p={3} width="100%">
|
||||||
|
{getFormSection(values, touched, formFields.get(section.name), errors)}
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
</Card>
|
||||||
|
</MDBox>
|
||||||
|
)) : null}
|
||||||
|
|
||||||
<MDBox component="div" p={3}>
|
<MDBox component="div" p={3}>
|
||||||
<Grid container justifyContent="flex-end" spacing={3}>
|
<Grid container justifyContent="flex-end" spacing={3}>
|
||||||
<QCancelButton onClickHandler={handleCancelClicked} disabled={isSubmitting} />
|
<QCancelButton onClickHandler={isModal ? closeModalHandler : handleCancelClicked} disabled={isSubmitting} />
|
||||||
<QSaveButton disabled={isSubmitting} />
|
<QSaveButton disabled={isSubmitting} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
|
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</MDBox>
|
||||||
</MDBox>
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
if (isModal)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<Box sx={{position: "absolute", overflowY: "auto", maxHeight: "100%", width: "100%"}}>
|
||||||
|
<Card sx={{my: 5, mx: "auto", p: 6, pb: 0, maxWidth: "1024px"}}>
|
||||||
|
{body}
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityForm.defaultProps = {
|
|
||||||
id: null,
|
|
||||||
table: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EntityForm;
|
export default EntityForm;
|
||||||
|
@ -133,6 +133,7 @@ function QDynamicForm(props: Props): JSX.Element
|
|||||||
<QDynamicSelect
|
<QDynamicSelect
|
||||||
tableName={field.possibleValueProps.tableName}
|
tableName={field.possibleValueProps.tableName}
|
||||||
fieldName={fieldName}
|
fieldName={fieldName}
|
||||||
|
isEditable={field.isEditable}
|
||||||
fieldLabel={field.label}
|
fieldLabel={field.label}
|
||||||
initialValue={values[fieldName]}
|
initialValue={values[fieldName]}
|
||||||
initialDisplayValue={field.possibleValueProps.initialDisplayValue}
|
initialDisplayValue={field.possibleValueProps.initialDisplayValue}
|
||||||
|
@ -259,7 +259,7 @@ function CarrierPerformance(): JSX.Element
|
|||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<SimpleStatisticsCard
|
<SimpleStatisticsCard
|
||||||
title={qInstance?.widgets.get("TotalShipmentsStatisticsCard").label}
|
title={qInstance?.widgets?.get("TotalShipmentsStatisticsCard").label}
|
||||||
data={totalShipmentsData}
|
data={totalShipmentsData}
|
||||||
increaseIsGood={true}
|
increaseIsGood={true}
|
||||||
dropdown={{
|
dropdown={{
|
||||||
@ -271,7 +271,7 @@ function CarrierPerformance(): JSX.Element
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<SimpleStatisticsCard
|
<SimpleStatisticsCard
|
||||||
title={qInstance?.widgets.get("SuccessfulDeliveriesStatisticsCard").label}
|
title={qInstance?.widgets?.get("SuccessfulDeliveriesStatisticsCard").label}
|
||||||
data={successfulDeliveriesData}
|
data={successfulDeliveriesData}
|
||||||
increaseIsGood={true}
|
increaseIsGood={true}
|
||||||
dropdown={{
|
dropdown={{
|
||||||
@ -284,7 +284,7 @@ function CarrierPerformance(): JSX.Element
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<SimpleStatisticsCard
|
<SimpleStatisticsCard
|
||||||
title={qInstance?.widgets.get("ServiceFailuresStatisticsCard").label}
|
title={qInstance?.widgets?.get("ServiceFailuresStatisticsCard").label}
|
||||||
data={serviceFailuresData}
|
data={serviceFailuresData}
|
||||||
increaseIsGood={false}
|
increaseIsGood={false}
|
||||||
dropdown={{
|
dropdown={{
|
||||||
@ -300,7 +300,7 @@ function CarrierPerformance(): JSX.Element
|
|||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} sm={6} lg={8}>
|
<Grid item xs={12} sm={6} lg={8}>
|
||||||
<DefaultLineChart
|
<DefaultLineChart
|
||||||
title={qInstance?.widgets.get("CarrierVolumeLineChart").label}
|
title={qInstance?.widgets?.get("CarrierVolumeLineChart").label}
|
||||||
data={carrierVolumeData}
|
data={carrierVolumeData}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -265,7 +265,7 @@ function Overview(): JSX.Element
|
|||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<BarChart
|
<BarChart
|
||||||
color="info"
|
color="info"
|
||||||
title={qInstance?.widgets.get("TotalShipmentsByDayBarChart").label}
|
title={qInstance?.widgets?.get("TotalShipmentsByDayBarChart").label}
|
||||||
description={shipmentsByDayDescription}
|
description={shipmentsByDayDescription}
|
||||||
date="Updated 3 minutes ago"
|
date="Updated 3 minutes ago"
|
||||||
data={shipmentsByDayData}
|
data={shipmentsByDayData}
|
||||||
@ -275,7 +275,7 @@ function Overview(): JSX.Element
|
|||||||
<Grid item xs={12} md={6} lg={4}>
|
<Grid item xs={12} md={6} lg={4}>
|
||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<PieChartCard
|
<PieChartCard
|
||||||
title={qInstance?.widgets.get("YTDShipmentsByCarrierPieChart").label}
|
title={qInstance?.widgets?.get("YTDShipmentsByCarrierPieChart").label}
|
||||||
description={shipmentsByCarrierDescription}
|
description={shipmentsByCarrierDescription}
|
||||||
data={shipmentsByCarrierData}
|
data={shipmentsByCarrierData}
|
||||||
/>
|
/>
|
||||||
@ -285,7 +285,7 @@ function Overview(): JSX.Element
|
|||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<SmallLineChart
|
<SmallLineChart
|
||||||
color="dark"
|
color="dark"
|
||||||
title={qInstance?.widgets.get("TotalShipmentsByMonthLineChart").label}
|
title={qInstance?.widgets?.get("TotalShipmentsByMonthLineChart").label}
|
||||||
description={shipmentsByMonthDescription}
|
description={shipmentsByMonthDescription}
|
||||||
date=""
|
date=""
|
||||||
chart={shipmentsByMonthData}
|
chart={shipmentsByMonthData}
|
||||||
|
@ -21,32 +21,28 @@
|
|||||||
|
|
||||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
import Box from "@mui/material/Box";
|
import {DataGridPro} from "@mui/x-data-grid-pro";
|
||||||
import Card from "@mui/material/Card";
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import {DataGridPro, GridValidRowModel} from "@mui/x-data-grid-pro";
|
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {Link} from "react-router-dom";
|
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
|
||||||
import DataGridUtils from "qqq/utils/DataGridUtils";
|
import DataGridUtils from "qqq/utils/DataGridUtils";
|
||||||
|
import Widget, {AddNewRecordButton, HeaderLink} from "./Widget";
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
title: string
|
title: string;
|
||||||
data: any;
|
data: any;
|
||||||
|
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordGridWidget.defaultProps = {
|
RecordGridWidget.defaultProps = {};
|
||||||
};
|
|
||||||
|
|
||||||
function RecordGridWidget({title, data}: Props): JSX.Element
|
function RecordGridWidget({title, data, reloadWidgetCallback}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const [rows, setRows] = useState([]);
|
const [rows, setRows] = useState([]);
|
||||||
const [columns, setColumns] = useState([])
|
const [columns, setColumns] = useState([]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(data && data.childTableMetaData && data.queryOutput)
|
if (data && data.childTableMetaData && data.queryOutput)
|
||||||
{
|
{
|
||||||
const records: QRecord[] = [];
|
const records: QRecord[] = [];
|
||||||
const queryOutputRecords = data.queryOutput.records;
|
const queryOutputRecords = data.queryOutput.records;
|
||||||
@ -60,28 +56,26 @@ function RecordGridWidget({title, data}: Props): JSX.Element
|
|||||||
|
|
||||||
const tableMetaData = new QTableMetaData(data.childTableMetaData);
|
const tableMetaData = new QTableMetaData(data.childTableMetaData);
|
||||||
const {rows, columnsToRender} = DataGridUtils.makeRows(records, tableMetaData);
|
const {rows, columnsToRender} = DataGridUtils.makeRows(records, tableMetaData);
|
||||||
const columns = DataGridUtils.setupGridColumns(tableMetaData, columnsToRender, data.tablePath);
|
|
||||||
|
const childTablePath = data.tablePath + (data.tablePath.endsWith("/") ? "" : "/")
|
||||||
|
const columns = DataGridUtils.setupGridColumns(tableMetaData, columnsToRender, childTablePath);
|
||||||
|
|
||||||
setRows(rows);
|
setRows(rows);
|
||||||
setColumns(columns);
|
setColumns(columns);
|
||||||
}
|
}
|
||||||
}, [data])
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{width: "100%"}}>
|
<Widget
|
||||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
label={title}
|
||||||
<Typography variant="h5" fontWeight="medium" p={3}>
|
labelAdditionalComponentsLeft={[
|
||||||
{title}
|
new HeaderLink("View All", data.viewAllLink)
|
||||||
</Typography>
|
]}
|
||||||
{
|
labelAdditionalComponentsRight={[
|
||||||
data.viewAllLink &&
|
new AddNewRecordButton(data.childTableMetaData, data.defaultValuesForNewChildRecords)
|
||||||
<Typography variant="body2" p={3}>
|
]}
|
||||||
<Link to={data.viewAllLink}>
|
reloadWidgetCallback={reloadWidgetCallback}
|
||||||
View All
|
>
|
||||||
</Link>
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
<DataGridPro
|
<DataGridPro
|
||||||
autoHeight
|
autoHeight
|
||||||
rows={rows}
|
rows={rows}
|
||||||
@ -115,8 +109,8 @@ function RecordGridWidget({title, data}: Props): JSX.Element
|
|||||||
// sortingOrder={[ "asc", "desc" ]}
|
// sortingOrder={[ "asc", "desc" ]}
|
||||||
// sortModel={columnSortModel}
|
// sortModel={columnSortModel}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Widget>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RecordGridWidget;
|
export default RecordGridWidget;
|
||||||
|
194
src/qqq/pages/dashboards/Widgets/Widget.tsx
Normal file
194
src/qqq/pages/dashboards/Widgets/Widget.tsx
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import Card from "@mui/material/Card";
|
||||||
|
import Modal from "@mui/material/Modal";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import React, {useState} from "react";
|
||||||
|
import {Link} from "react-router-dom";
|
||||||
|
import EntityForm from "qqq/components/EntityForm";
|
||||||
|
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
label: string;
|
||||||
|
labelAdditionalComponentsLeft: [LabelComponent];
|
||||||
|
labelAdditionalComponentsRight: [LabelComponent];
|
||||||
|
children: JSX.Element;
|
||||||
|
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget.defaultProps = {
|
||||||
|
label: null,
|
||||||
|
labelAdditionalComponentsLeft: [],
|
||||||
|
labelAdditionalComponentsRight: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LabelComponent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class HeaderLink extends LabelComponent
|
||||||
|
{
|
||||||
|
label: string;
|
||||||
|
to: string
|
||||||
|
|
||||||
|
constructor(label: string, to: string)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.label = label;
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class AddNewRecordButton extends LabelComponent
|
||||||
|
{
|
||||||
|
table: QTableMetaData;
|
||||||
|
label:string;
|
||||||
|
defaultValues: any;
|
||||||
|
disabledFields: any;
|
||||||
|
|
||||||
|
constructor(table: QTableMetaData, defaultValues: any, label: string = "Add new", disabledFields: any = defaultValues)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.table = table;
|
||||||
|
this.label = label;
|
||||||
|
this.defaultValues = defaultValues;
|
||||||
|
this.disabledFields = disabledFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Widget(props: React.PropsWithChildren<Props>): JSX.Element
|
||||||
|
{
|
||||||
|
const [showEditForm, setShowEditForm] = useState(null as any);
|
||||||
|
|
||||||
|
function openEditForm(table: QTableMetaData, id: any = null, defaultValues: any, disabledFields: any)
|
||||||
|
{
|
||||||
|
const showEditForm: any = {};
|
||||||
|
showEditForm.table = table;
|
||||||
|
showEditForm.id = id;
|
||||||
|
showEditForm.defaultValues = defaultValues;
|
||||||
|
showEditForm.disabledFields = disabledFields;
|
||||||
|
setShowEditForm(showEditForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeEditForm = (event: object, reason: string) =>
|
||||||
|
{
|
||||||
|
if (reason === "backdropClick")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason === "recordUpdated" || reason === "recordCreated")
|
||||||
|
{
|
||||||
|
if(props.reloadWidgetCallback)
|
||||||
|
{
|
||||||
|
props.reloadWidgetCallback(0, "ok");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowEditForm(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderComponent(component: LabelComponent)
|
||||||
|
{
|
||||||
|
if(component instanceof HeaderLink)
|
||||||
|
{
|
||||||
|
const link = component as HeaderLink
|
||||||
|
return (
|
||||||
|
<Typography variant="body2" p={2} display="inline">
|
||||||
|
{link.to ? <Link to={link.to}>{link.label}</Link> : null}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component instanceof AddNewRecordButton)
|
||||||
|
{
|
||||||
|
const addNewRecordButton = component as AddNewRecordButton
|
||||||
|
return (
|
||||||
|
<Typography variant="body2" p={2} pr={1} display="inline">
|
||||||
|
<Button onClick={() => openEditForm(addNewRecordButton.table, null, addNewRecordButton.defaultValues, addNewRecordButton.disabledFields)}>{addNewRecordButton.label}</Button>
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card sx={{width: "100%"}}>
|
||||||
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||||
|
<Box py={2}>
|
||||||
|
<Typography variant="h6" fontWeight="medium" p={3} display="inline">
|
||||||
|
{props.label}
|
||||||
|
</Typography>
|
||||||
|
{
|
||||||
|
props.labelAdditionalComponentsLeft.map((component, i) =>
|
||||||
|
{
|
||||||
|
return (<span key={i}>{renderComponent(component)}</span>);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
<Box pr={1}>
|
||||||
|
{
|
||||||
|
props.labelAdditionalComponentsRight.map((component, i) =>
|
||||||
|
{
|
||||||
|
return (<span key={i}>{renderComponent(component)}</span>);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{props.children}
|
||||||
|
</Card>
|
||||||
|
{
|
||||||
|
showEditForm &&
|
||||||
|
<Modal open={showEditForm as boolean} onClose={(event, reason) => closeEditForm(event, reason)}>
|
||||||
|
<div className="modalEditForm">
|
||||||
|
<EntityForm
|
||||||
|
isModal={true}
|
||||||
|
closeModalHandler={closeEditForm}
|
||||||
|
table={showEditForm.table}
|
||||||
|
id={showEditForm.id}
|
||||||
|
defaultValues={showEditForm.defaultValues}
|
||||||
|
disabledFields={showEditForm.disabledFields} />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Widget;
|
@ -273,9 +273,9 @@ function EntityView({table, launchProcess}: Props): JSX.Element
|
|||||||
{
|
{
|
||||||
sectionFieldElements.set(section.name,
|
sectionFieldElements.set(section.name,
|
||||||
<Grid id={section.name} key={section.name} item lg={12} xs={12} sx={{display: "flex", alignItems: "stretch", scrollMarginTop: "100px"}}>
|
<Grid id={section.name} key={section.name} item lg={12} xs={12} sx={{display: "flex", alignItems: "stretch", scrollMarginTop: "100px"}}>
|
||||||
<MDBox mb={3} width="100%">
|
<MDBox width="100%">
|
||||||
<Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}}>
|
<Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}}>
|
||||||
<MDTypography variant="h5" p={3} pb={1}>
|
<MDTypography variant="h6" p={3} pb={1}>
|
||||||
{section.label}
|
{section.label}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDBox p={3} pt={0} flexDirection="column">
|
<MDBox p={3} pt={0} flexDirection="column">
|
||||||
@ -466,9 +466,9 @@ function EntityView({table, launchProcess}: Props): JSX.Element
|
|||||||
{nonT1TableSections.length > 0 ? nonT1TableSections.map(({
|
{nonT1TableSections.length > 0 ? nonT1TableSections.map(({
|
||||||
iconName, label, name, fieldNames, tier,
|
iconName, label, name, fieldNames, tier,
|
||||||
}: any) => (
|
}: any) => (
|
||||||
<>
|
<React.Fragment key={name}>
|
||||||
{sectionFieldElements.get(name)}
|
{sectionFieldElements.get(name)}
|
||||||
</>
|
</React.Fragment>
|
||||||
)) : null}
|
)) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
<MDBox component="form" p={3}>
|
<MDBox component="form" p={3}>
|
||||||
|
@ -275,7 +275,7 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds
|
|||||||
<Grid m={3} mt={9} container>
|
<Grid m={3} mt={9} container>
|
||||||
<Grid item xs={0} lg={3} />
|
<Grid item xs={0} lg={3} />
|
||||||
<Grid item xs={12} lg={6}>
|
<Grid item xs={12} lg={6}>
|
||||||
<Card>
|
<Card elevation={5}>
|
||||||
<MDBox p={3}>
|
<MDBox p={3}>
|
||||||
<MDTypography variant="h5" component="div">
|
<MDTypography variant="h5" component="div">
|
||||||
Working
|
Working
|
||||||
|
Reference in New Issue
Block a user