mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-19 05:40:44 +00:00
QQQ-32 udpating styles on query; record list & view sections; record labels; apps in nav; updated breadcrumb; updated navigation
This commit is contained in:
264
src/qqq/pages/app-home/index.tsx
Normal file
264
src/qqq/pages/app-home/index.tsx
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 React, {useEffect, useState} from "react";
|
||||
import {Link, useLocation} from "react-router-dom";
|
||||
|
||||
// Material Dashboard 2 PRO React TS examples components
|
||||
import QClient from "qqq/utils/QClient";
|
||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||
import {QAppMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppMetaData";
|
||||
import BaseLayout from "qqq/components/BaseLayout";
|
||||
import MDBox from "components/MDBox";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import MiniStatisticsCard from "examples/Cards/StatisticsCards/MiniStatisticsCard";
|
||||
import {Icon} from "@mui/material";
|
||||
import MDTypography from "components/MDTypography";
|
||||
import Card from "@mui/material/Card";
|
||||
import ComplexStatisticsCard from "examples/Cards/StatisticsCards/ComplexStatisticsCard";
|
||||
import ReportsLineChart from "examples/Charts/LineCharts/ReportsLineChart";
|
||||
import DefaultLineChart from "examples/Charts/LineCharts/DefaultLineChart";
|
||||
import MDBadgeDot from "components/MDBadgeDot";
|
||||
import ReportsBarChart from "examples/Charts/BarCharts/ReportsBarChart";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType";
|
||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||
import DefaultInfoCard from "examples/Cards/InfoCards/DefaultInfoCard";
|
||||
import ProcessLinkCard from "qqq/components/ProcessLinkCard";
|
||||
|
||||
const qController = QClient.getInstance();
|
||||
|
||||
interface Props
|
||||
{
|
||||
app?: QAppMetaData;
|
||||
}
|
||||
|
||||
function AppHome({app}: Props): JSX.Element
|
||||
{
|
||||
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||
const [tables, setTables] = useState([] as QTableMetaData[]);
|
||||
const [processes, setProcesses] = useState([] as QProcessMetaData[]);
|
||||
const [childApps, setChildApps] = useState([] as QAppMetaData[]);
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
(async () =>
|
||||
{
|
||||
const newQInstance = await qController.loadMetaData();
|
||||
setQInstance(newQInstance);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!qInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const newTables: QTableMetaData[] = [];
|
||||
const newProcesses: QProcessMetaData[] = [];
|
||||
const newChildApps: QAppMetaData[] = [];
|
||||
|
||||
app.children.forEach((child) =>
|
||||
{
|
||||
switch (child.type)
|
||||
{
|
||||
// todo - filter out hidden
|
||||
case QAppNodeType.TABLE:
|
||||
newTables.push(qInstance.tables.get(child.name));
|
||||
break;
|
||||
case QAppNodeType.PROCESS:
|
||||
newProcesses.push(qInstance.processes.get(child.name));
|
||||
break;
|
||||
case QAppNodeType.APP:
|
||||
newChildApps.push(qInstance.apps.get(child.name));
|
||||
break;
|
||||
default:
|
||||
console.log(`Unexpected child type: ${child.type}`);
|
||||
}
|
||||
});
|
||||
|
||||
setTables(newTables);
|
||||
setProcesses(newProcesses);
|
||||
setChildApps(newChildApps);
|
||||
}, [qInstance, location]);
|
||||
|
||||
const reportsBarChartData = {
|
||||
labels: ["M", "T", "W", "T", "F", "S", "S"],
|
||||
datasets: {label: "Sales", data: [50, 20, 10, 22, 50, 10, 40]},
|
||||
};
|
||||
|
||||
interface Types {
|
||||
labels: string[];
|
||||
datasets: {
|
||||
label: string;
|
||||
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||
data: number[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const demoLineChartData: Types = {
|
||||
labels: ["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Facebook Ads",
|
||||
color: "info",
|
||||
data: [50, 100, 200, 190, 400, 350, 500, 450, 700],
|
||||
},
|
||||
{
|
||||
label: "Google Ads",
|
||||
color: "dark",
|
||||
data: [10, 30, 40, 120, 150, 220, 280, 250, 280],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout>
|
||||
<MDBox mt={4}>
|
||||
<Grid container spacing={3}>
|
||||
|
||||
<Grid item xs={6} lg={6}>
|
||||
<Grid container spacing={3}>
|
||||
|
||||
<Grid item xs={6} lg={6}>
|
||||
<MDBox mb={3}>
|
||||
<MDBox mb={3}>
|
||||
<ReportsBarChart
|
||||
color="info"
|
||||
title="Packages Shipped"
|
||||
description="Total outbound shipments"
|
||||
date="Updated at 7:04 a.m. ET"
|
||||
chart={reportsBarChartData}
|
||||
/>
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6} lg={6}>
|
||||
<DefaultLineChart
|
||||
title="Revenue"
|
||||
description={(
|
||||
<MDBox display="flex" justifyContent="space-between">
|
||||
<MDBox display="flex" ml={-1}>
|
||||
<MDBadgeDot color="info" size="sm" badgeContent="UPS" />
|
||||
<MDBadgeDot color="dark" size="sm" badgeContent="FedEx" />
|
||||
</MDBox>
|
||||
<MDBox mt={-4} mr={-1} position="absolute" right="1.5rem" />
|
||||
</MDBox>
|
||||
)}
|
||||
chart={demoLineChartData}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6} lg={6}>
|
||||
{
|
||||
tables.length ? (
|
||||
<MDBox mb={3}>
|
||||
<Card id="basic-info" sx={{overflow: "visible"}}>
|
||||
<MDBox p={3}>
|
||||
<MDTypography variant="h5">Tables</MDTypography>
|
||||
</MDBox>
|
||||
<Grid container spacing={3} padding={3} paddingTop={0}>
|
||||
{tables.map((table) => (
|
||||
<Grid key={table.name} item xs={12} md={12} lg={6}>
|
||||
<Link to={table.name}>
|
||||
<MDBox mb={3}>
|
||||
<MiniStatisticsCard
|
||||
title={{fontWeight: "bold", text: table.label}}
|
||||
count="17,013"
|
||||
percentage={{color: "info", text: "total records"}}
|
||||
icon={{color: "info", component: <Icon>{table.iconName || app.iconName}</Icon>}}
|
||||
direction="right"
|
||||
/>
|
||||
</MDBox>
|
||||
</Link>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Card>
|
||||
</MDBox>
|
||||
) : null
|
||||
}
|
||||
|
||||
{
|
||||
processes.length ? (
|
||||
<MDBox mb={3}>
|
||||
<Card id="basic-info" sx={{overflow: "visible"}}>
|
||||
<MDBox p={3}>
|
||||
<MDTypography variant="h5">Processes</MDTypography>
|
||||
</MDBox>
|
||||
<Grid container spacing={3} padding={3} paddingTop={3}>
|
||||
{processes.map((process) => (
|
||||
<Grid key={process.name} item xs={12} md={12} lg={6}>
|
||||
<Link to={process.name}>
|
||||
<ProcessLinkCard
|
||||
icon={process.iconName || app.iconName}
|
||||
title={process.label}
|
||||
/>
|
||||
</Link>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Card>
|
||||
</MDBox>
|
||||
) : null
|
||||
}
|
||||
|
||||
{
|
||||
childApps.length ? (
|
||||
<MDBox mb={3}>
|
||||
<Card id="basic-info" sx={{overflow: "visible"}}>
|
||||
<Grid container spacing={3} padding={3} paddingTop={3}>
|
||||
{childApps.map((childApp) => (
|
||||
<Grid key={childApp.name} item xs={12} md={12} lg={6}>
|
||||
<Link to={childApp.name}>
|
||||
<DefaultInfoCard
|
||||
icon={childApp.iconName || app.iconName}
|
||||
title={childApp.label}
|
||||
/>
|
||||
</Link>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Card>
|
||||
</MDBox>
|
||||
) : null
|
||||
}
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</MDBox>
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
AppHome.defaultProps = {
|
||||
app: null,
|
||||
};
|
||||
|
||||
export default AppHome;
|
@ -1,16 +1,22 @@
|
||||
/**
|
||||
=========================================================
|
||||
* Material Dashboard 2 PRO React TS - v1.0.0
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
|
||||
|
||||
Coded by www.creative-tim.com
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @mui material components
|
||||
@ -22,15 +28,22 @@ import MDBox from "components/MDBox";
|
||||
// Settings page components
|
||||
import EntityForm from "qqq/components/EntityForm";
|
||||
import BaseLayout from "qqq/components/BaseLayout";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {useParams} from "react-router-dom";
|
||||
|
||||
function EntityCreate(): JSX.Element
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
}
|
||||
|
||||
function EntityCreate({table}: Props): JSX.Element
|
||||
{
|
||||
return (
|
||||
<BaseLayout>
|
||||
<MDBox mt={4}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} lg={12}>
|
||||
<EntityForm />
|
||||
<EntityForm table={table} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MDBox>
|
||||
@ -38,4 +51,8 @@ function EntityCreate(): JSX.Element
|
||||
);
|
||||
}
|
||||
|
||||
EntityCreate.defaultProps = {
|
||||
table: null,
|
||||
};
|
||||
|
||||
export default EntityCreate;
|
||||
|
@ -1,16 +1,22 @@
|
||||
/**
|
||||
=========================================================
|
||||
* Material Dashboard 2 PRO React TS - v1.0.0
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
|
||||
|
||||
Coded by www.creative-tim.com
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @mui material components
|
||||
@ -23,8 +29,15 @@ import MDBox from "components/MDBox";
|
||||
import BaseLayout from "qqq/components/BaseLayout";
|
||||
import {useParams} from "react-router-dom";
|
||||
import EntityForm from "qqq/components/EntityForm";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import EntityList from "qqq/pages/entity-list";
|
||||
|
||||
function EntityEdit(): JSX.Element
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
}
|
||||
|
||||
function EntityEdit({table}: Props): JSX.Element
|
||||
{
|
||||
const {id} = useParams();
|
||||
|
||||
@ -36,7 +49,7 @@ function EntityEdit(): JSX.Element
|
||||
<MDBox mb={3}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<EntityForm id={id} />
|
||||
<EntityForm table={table} id={id} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MDBox>
|
||||
@ -47,4 +60,8 @@ function EntityEdit(): JSX.Element
|
||||
);
|
||||
}
|
||||
|
||||
EntityEdit.defaultProps = {
|
||||
table: null,
|
||||
};
|
||||
|
||||
export default EntityEdit;
|
||||
|
@ -1,26 +1,39 @@
|
||||
/**
|
||||
=========================================================
|
||||
* Material Dashboard 2 PRO React TS - v1.0.0
|
||||
=========================================================
|
||||
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
|
||||
Coded by www.creative-tim.com
|
||||
=========================================================
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
/* eslint-disable react/no-unstable-nested-components */
|
||||
|
||||
import React, {useEffect, useReducer, useState} from "react";
|
||||
import {useParams, useSearchParams} from "react-router-dom";
|
||||
import React, {
|
||||
SyntheticEvent,
|
||||
useCallback,
|
||||
useEffect, useReducer, useRef, useState,
|
||||
} from "react";
|
||||
import {
|
||||
Link, useNavigate, useParams, useSearchParams,
|
||||
} from "react-router-dom";
|
||||
|
||||
// @mui material components
|
||||
import Card from "@mui/material/Card";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Link from "@mui/material/Link";
|
||||
import {Alert, TablePagination} from "@mui/material";
|
||||
import {Alert, Pagination, TablePagination} from "@mui/material";
|
||||
import {
|
||||
DataGridPro,
|
||||
GridCallbackDetails,
|
||||
@ -40,6 +53,7 @@ import {
|
||||
GridToolbarExportContainer,
|
||||
GridToolbarFilterButton,
|
||||
GridExportMenuItemProps,
|
||||
MuiEvent,
|
||||
} from "@mui/x-data-grid-pro";
|
||||
|
||||
// Material Dashboard 2 PRO React TS components
|
||||
@ -64,11 +78,13 @@ import Footer from "../../components/Footer";
|
||||
import QProcessUtils from "../../utils/QProcessUtils";
|
||||
|
||||
import "./styles.css";
|
||||
import {QActionsMenuButton, QCreateNewButton} from "qqq/components/QButtons";
|
||||
import QValueUtils from "qqq/utils/QValueUtils";
|
||||
import LinearProgress from "@mui/material/LinearProgress";
|
||||
|
||||
const COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT = "qqq.columnVisibility";
|
||||
const COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT = "qqq.columnSort";
|
||||
|
||||
// Declaring props types for DefaultCell
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
@ -98,7 +114,6 @@ function EntityList({table}: Props): JSX.Element
|
||||
defaultVisibility = JSON.parse(localStorage.getItem(columnVisibilityLocalStorageKey));
|
||||
}
|
||||
|
||||
const [buttonText, setButtonText] = useState("");
|
||||
const [tableState, setTableState] = useState("");
|
||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
||||
const [, setFiltersMenu] = useState(null);
|
||||
@ -117,6 +132,9 @@ function EntityList({table}: Props): JSX.Element
|
||||
const [tableLabel, setTableLabel] = useState("");
|
||||
const [columnSortModel, setColumnSortModel] = useState(defaultSort);
|
||||
const [columnVisibilityModel, setColumnVisibilityModel] = useState(defaultVisibility);
|
||||
const [gridMouseDownX, setGridMouseDownX] = useState(0);
|
||||
const [gridMouseDownY, setGridMouseDownY] = useState(0);
|
||||
const instance = useRef({timer: null});
|
||||
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
@ -192,14 +210,15 @@ function EntityList({table}: Props): JSX.Element
|
||||
|
||||
const updateTable = () =>
|
||||
{
|
||||
setRows([]);
|
||||
(async () =>
|
||||
{
|
||||
const newTableMetaData = await qController.loadTableMetaData(tableName);
|
||||
setTableMetaData(newTableMetaData);
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
setTableMetaData(tableMetaData);
|
||||
if (columnSortModel.length === 0)
|
||||
{
|
||||
columnSortModel.push({
|
||||
field: newTableMetaData.primaryKeyField,
|
||||
field: tableMetaData.primaryKeyField,
|
||||
sort: "desc",
|
||||
});
|
||||
setColumnSortModel(columnSortModel);
|
||||
@ -209,8 +228,7 @@ function EntityList({table}: Props): JSX.Element
|
||||
|
||||
const count = await qController.count(tableName, qFilter);
|
||||
setTotalRecords(count);
|
||||
setButtonText(`new ${newTableMetaData.label}`);
|
||||
setTableLabel(newTableMetaData.label);
|
||||
setTableLabel(tableMetaData.label);
|
||||
|
||||
const columns = [] as GridColDef[];
|
||||
|
||||
@ -233,47 +251,65 @@ function EntityList({table}: Props): JSX.Element
|
||||
throw error;
|
||||
});
|
||||
|
||||
const fields = [...tableMetaData.fields.values()];
|
||||
|
||||
const rows = [] as any[];
|
||||
results.forEach((record) =>
|
||||
{
|
||||
rows.push(Object.fromEntries(record.values.entries()));
|
||||
const row: any = {};
|
||||
fields.forEach((field) =>
|
||||
{
|
||||
row[field.name] = QValueUtils.getDisplayValue(field, record);
|
||||
});
|
||||
|
||||
rows.push(row);
|
||||
});
|
||||
|
||||
const sortedKeys = [...newTableMetaData.fields.keys()].sort();
|
||||
const sortedKeys = [...tableMetaData.fields.keys()].sort();
|
||||
sortedKeys.forEach((key) =>
|
||||
{
|
||||
const field = newTableMetaData.fields.get(key);
|
||||
const field = tableMetaData.fields.get(key);
|
||||
|
||||
let columnType = "string";
|
||||
let columnWidth = 200;
|
||||
switch (field.type)
|
||||
{
|
||||
case QFieldType.DECIMAL:
|
||||
case QFieldType.INTEGER:
|
||||
columnType = "number";
|
||||
columnWidth = 100;
|
||||
|
||||
if (key === tableMetaData.primaryKeyField && field.label.length < 3)
|
||||
{
|
||||
columnWidth = 75;
|
||||
}
|
||||
|
||||
break;
|
||||
case QFieldType.DATE:
|
||||
columnType = "date";
|
||||
columnWidth = 100;
|
||||
break;
|
||||
case QFieldType.DATE_TIME:
|
||||
columnType = "dateTime";
|
||||
columnWidth = 200;
|
||||
break;
|
||||
case QFieldType.BOOLEAN:
|
||||
columnType = "boolean";
|
||||
columnWidth = 75;
|
||||
break;
|
||||
default:
|
||||
// noop
|
||||
// noop - leave as string
|
||||
}
|
||||
|
||||
const column = {
|
||||
field: field.name,
|
||||
type: columnType,
|
||||
headerName: field.label,
|
||||
width: 200,
|
||||
width: columnWidth,
|
||||
};
|
||||
|
||||
if (key === newTableMetaData.primaryKeyField)
|
||||
if (key === tableMetaData.primaryKeyField)
|
||||
{
|
||||
column.width = 75;
|
||||
columns.splice(0, 0, column);
|
||||
}
|
||||
else
|
||||
@ -299,11 +335,49 @@ function EntityList({table}: Props): JSX.Element
|
||||
setRowsPerPage(size);
|
||||
};
|
||||
|
||||
const handleRowClick = (params: GridRowParams) =>
|
||||
const navigate = useNavigate();
|
||||
const handleRowClick = (params: GridRowParams, event: MuiEvent<React.MouseEvent>, details: GridCallbackDetails) =>
|
||||
{
|
||||
document.location.href = `/${tableName}/${params.id}`;
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// strategy for when to trigger or not trigger a row click: //
|
||||
// To avoid a drag-event that highlighted text in a cell: //
|
||||
// - we capture the x & y upon mouse-down - then compare them in this method (which fires when the mouse is up) //
|
||||
// if they are more than 5 pixels away from the mouse-down, then assume it's a drag, not a click. //
|
||||
// - avoid clicking the row upon double-click, by setting a 500ms timer here - and in the onDoubleClick handler, //
|
||||
// cancelling the timer. //
|
||||
// - also avoid a click, then click-again-and-start-dragging, by always cancelling the timer in mouse-down. //
|
||||
// All in, these seem to have good results - the only downside being the half-second delay... //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
navigate(`${params.id}`);
|
||||
/*
|
||||
const diff = Math.max(Math.abs(event.clientX - gridMouseDownX), Math.abs(event.clientY - gridMouseDownY));
|
||||
if (diff < 5)
|
||||
{
|
||||
clearTimeout(instance.current.timer);
|
||||
instance.current.timer = setTimeout(() =>
|
||||
{
|
||||
navigate(`${params.id}`);
|
||||
}, 500);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log(`row-click mouse-up happened ${diff} x or y pixels away from the mouse-down - so not considering it a click.`);
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
const handleGridMouseDown = useCallback((event: any) =>
|
||||
{
|
||||
setGridMouseDownX(event.clientX);
|
||||
setGridMouseDownY(event.clientY);
|
||||
clearTimeout(instance.current.timer);
|
||||
}, []);
|
||||
|
||||
const handleGridDoubleClick = useCallback((event: any) =>
|
||||
{
|
||||
clearTimeout(instance.current.timer);
|
||||
}, []);
|
||||
|
||||
const handleFilterChange = (filterModel: GridFilterModel) =>
|
||||
{
|
||||
setFilterModel(filterModel);
|
||||
@ -377,6 +451,8 @@ function EntityList({table}: Props): JSX.Element
|
||||
format: string;
|
||||
}
|
||||
|
||||
// todo - figure out what's up here...
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
function ExportMenuItem(props: QExportMenuItemProps)
|
||||
{
|
||||
const {format, hideMenu} = props;
|
||||
@ -476,7 +552,7 @@ function EntityList({table}: Props): JSX.Element
|
||||
|
||||
const bulkLoadClicked = () =>
|
||||
{
|
||||
document.location.href = `/processes/${tableName}.bulkInsert`;
|
||||
navigate(`${tableName}.bulkInsert`);
|
||||
};
|
||||
|
||||
const bulkEditClicked = () =>
|
||||
@ -486,7 +562,7 @@ function EntityList({table}: Props): JSX.Element
|
||||
setAlertContent("No records were selected to Bulk Edit.");
|
||||
return;
|
||||
}
|
||||
document.location.href = `/processes/${tableName}.bulkEdit${getRecordsQueryString()}`;
|
||||
navigate(`${tableName}.bulkEdit${getRecordsQueryString()}`);
|
||||
};
|
||||
|
||||
const bulkDeleteClicked = () =>
|
||||
@ -496,12 +572,21 @@ function EntityList({table}: Props): JSX.Element
|
||||
setAlertContent("No records were selected to Bulk Delete.");
|
||||
return;
|
||||
}
|
||||
document.location.href = `/processes/${tableName}.bulkDelete${getRecordsQueryString()}`;
|
||||
navigate(`${tableName}.bulkDelete${getRecordsQueryString()}`);
|
||||
};
|
||||
|
||||
const processClicked = (process: QProcessMetaData) =>
|
||||
{
|
||||
// todo - let the process specify that it needs initial rows - err if none selected.
|
||||
// alternatively, let a process itself have an initial screen to select rows...
|
||||
navigate(`${process.name}${getRecordsQueryString()}`);
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const defaultLabelDisplayedRows = ({from, to, count}) => `${from.toLocaleString()}–${to.toLocaleString()} of ${count !== -1 ? count.toLocaleString() : `more than ${to.toLocaleString()}`}`;
|
||||
const defaultLabelDisplayedRows = ({from, to, count}) => `Showing ${from.toLocaleString()} to ${to.toLocaleString()} of ${count !== -1 ? `${count.toLocaleString()} records` : `more than ${to.toLocaleString()} records`}`;
|
||||
|
||||
// todo - figure out what's up here...
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
function CustomPagination()
|
||||
{
|
||||
return (
|
||||
@ -518,23 +603,28 @@ function EntityList({table}: Props): JSX.Element
|
||||
);
|
||||
}
|
||||
|
||||
// todo - figure out what's up here...
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
function Loading()
|
||||
{
|
||||
return (
|
||||
<LinearProgress color="info" />
|
||||
);
|
||||
}
|
||||
|
||||
// todo - figure out what's up here...
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
function CustomToolbar()
|
||||
{
|
||||
const [bulkActionsMenuAnchor, setBulkActionsMenuAnchor] = useState(null as HTMLElement);
|
||||
const bulkActionsMenuOpen = Boolean(bulkActionsMenuAnchor);
|
||||
|
||||
const openBulkActionsMenu = (event: React.MouseEvent<HTMLElement>) =>
|
||||
function gtcMouseDown(e: React.MouseEvent<HTMLDivElement>)
|
||||
{
|
||||
setBulkActionsMenuAnchor(event.currentTarget);
|
||||
};
|
||||
|
||||
const closeBulkActionsMenu = () =>
|
||||
{
|
||||
setBulkActionsMenuAnchor(null);
|
||||
};
|
||||
console.log(e.target);
|
||||
}
|
||||
|
||||
return (
|
||||
<GridToolbarContainer>
|
||||
<GridToolbarContainer
|
||||
onMouseDown={(e) => gtcMouseDown(e)}
|
||||
>
|
||||
<div>
|
||||
<Button
|
||||
id="refresh-button"
|
||||
@ -551,29 +641,6 @@ function EntityList({table}: Props): JSX.Element
|
||||
<ExportMenuItem format="csv" />
|
||||
<ExportMenuItem format="xlsx" />
|
||||
</GridToolbarExportContainer>
|
||||
<div>
|
||||
<Button
|
||||
id="bulk-actions-button"
|
||||
onClick={openBulkActionsMenu}
|
||||
aria-controls={bulkActionsMenuOpen ? "basic-menu" : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={bulkActionsMenuOpen ? "true" : undefined}
|
||||
startIcon={<Icon>table_rows</Icon>}
|
||||
>
|
||||
Bulk Actions
|
||||
</Button>
|
||||
<Menu
|
||||
id="bulk-actions-menu"
|
||||
open={bulkActionsMenuOpen}
|
||||
anchorEl={bulkActionsMenuAnchor}
|
||||
onClose={closeBulkActionsMenu}
|
||||
MenuListProps={{"aria-labelledby": "bulk-actions-button"}}
|
||||
>
|
||||
<MenuItem onClick={bulkLoadClicked}>Bulk Load</MenuItem>
|
||||
<MenuItem onClick={bulkEditClicked}>Bulk Edit</MenuItem>
|
||||
<MenuItem onClick={bulkDeleteClicked}>Bulk Delete</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
selectFullFilterState === "checked" && (
|
||||
@ -621,29 +688,38 @@ function EntityList({table}: Props): JSX.Element
|
||||
anchorEl={actionsMenu}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
horizontal: "right",
|
||||
}}
|
||||
open={Boolean(actionsMenu)}
|
||||
onClose={closeActionsMenu}
|
||||
keepMounted
|
||||
>
|
||||
<MenuItem onClick={bulkLoadClicked}>Bulk Load</MenuItem>
|
||||
<MenuItem onClick={bulkEditClicked}>Bulk Edit</MenuItem>
|
||||
<MenuItem onClick={bulkDeleteClicked}>Bulk Delete</MenuItem>
|
||||
<MenuItem divider />
|
||||
{tableProcesses.map((process) => (
|
||||
<MenuItem key={process.name}>
|
||||
<Link href={`/processes/${process.name}${getRecordsQueryString()}`}>{process.label}</Link>
|
||||
</MenuItem>
|
||||
<MenuItem key={process.name} onClick={() => processClicked(process)}>{process.label}</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setLoading(true);
|
||||
updateTable();
|
||||
}, [pageNumber, rowsPerPage, tableState, columnSortModel, filterModel]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
document.documentElement.scrollTop = 0;
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
}, [pageNumber, rowsPerPage]);
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Navbar />
|
||||
@ -668,39 +744,27 @@ function EntityList({table}: Props): JSX.Element
|
||||
<MDAlert color="success" dismissible>
|
||||
{`${tableLabel} successfully deleted`}
|
||||
</MDAlert>
|
||||
) : ("")
|
||||
) : null
|
||||
}
|
||||
<MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}>
|
||||
{buttonText ? (
|
||||
<Link href={`/${tableName}/create`}>
|
||||
<MDButton variant="gradient" color="info">
|
||||
{
|
||||
buttonText
|
||||
}
|
||||
</MDButton>
|
||||
</Link>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<MDBox display="flex" justifyContent="flex-end" alignItems="flex-start" mb={2}>
|
||||
|
||||
<MDBox display="flex">
|
||||
<MDBox display="flex" width="150px">
|
||||
{tableProcesses.length > 0 && (
|
||||
<MDButton
|
||||
variant={actionsMenu ? "contained" : "outlined"}
|
||||
color="dark"
|
||||
onClick={openActionsMenu}
|
||||
>
|
||||
actions
|
||||
<Icon>keyboard_arrow_down</Icon>
|
||||
</MDButton>
|
||||
<QActionsMenuButton isOpen={actionsMenu} onClickHandler={openActionsMenu} />
|
||||
)}
|
||||
{renderActionsMenu}
|
||||
</MDBox>
|
||||
|
||||
<QCreateNewButton />
|
||||
|
||||
</MDBox>
|
||||
<Card>
|
||||
{/* with these turned on, the toolbar & pagination controls become very flaky...
|
||||
onMouseDown={(e) => handleGridMouseDown(e)} onDoubleClick={(e) => handleGridDoubleClick(e)} */}
|
||||
<MDBox height="100%">
|
||||
<DataGridPro
|
||||
components={{Toolbar: CustomToolbar, Pagination: CustomPagination}}
|
||||
components={{Toolbar: CustomToolbar, Pagination: CustomPagination, LoadingOverlay: Loading}}
|
||||
pinnedColumns={{left: ["__check__", "id"]}}
|
||||
pagination
|
||||
paginationMode="server"
|
||||
sortingMode="server"
|
||||
@ -715,7 +779,7 @@ function EntityList({table}: Props): JSX.Element
|
||||
rowCount={totalRecords}
|
||||
onPageSizeChange={handleRowsPerPageChange}
|
||||
onRowClick={handleRowClick}
|
||||
density="compact"
|
||||
density="standard"
|
||||
loading={loading}
|
||||
onFilterModelChange={handleFilterChange}
|
||||
columnVisibilityModel={columnVisibilityModel}
|
||||
|
@ -1,23 +1,31 @@
|
||||
/**
|
||||
=========================================================
|
||||
* Material Dashboard 2 PRO React TS - v1.0.0
|
||||
=========================================================
|
||||
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
|
||||
Coded by www.creative-tim.com
|
||||
=========================================================
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// react components
|
||||
import {useParams, useSearchParams} from "react-router-dom";
|
||||
import {useLocation, useNavigate, useSearchParams} from "react-router-dom";
|
||||
import React, {useReducer, useState} from "react";
|
||||
|
||||
// @material-ui core components
|
||||
import Card from "@mui/material/Card";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Link from "@mui/material/Link";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
@ -26,7 +34,6 @@ import DialogActions from "@mui/material/DialogActions";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
// qqq imports
|
||||
import {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||
|
||||
// Material Dashboard 2 PRO React TS components
|
||||
@ -34,31 +41,46 @@ import MDBox from "components/MDBox";
|
||||
import MDTypography from "components/MDTypography";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import MDAlert from "components/MDAlert";
|
||||
import MDButton from "../../../../../components/MDButton";
|
||||
import QProcessUtils from "../../../../utils/QProcessUtils";
|
||||
import QClient from "qqq/utils/QClient";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QActionsMenuButton, QDeleteButton, QEditButton} from "qqq/components/QButtons";
|
||||
import QValueUtils from "qqq/utils/QValueUtils";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import QRecordSidebar from "qqq/components/QRecordSidebar";
|
||||
import QTableUtils from "qqq/utils/QTableUtils";
|
||||
|
||||
const qController = QClient.getInstance();
|
||||
|
||||
// Declaring props types for ViewForm
|
||||
interface Props
|
||||
{
|
||||
id: string;
|
||||
id: string;
|
||||
table?: QTableMetaData;
|
||||
}
|
||||
|
||||
function ViewContents({id}: Props): JSX.Element
|
||||
function ViewContents({id, table}: Props): JSX.Element
|
||||
{
|
||||
const {tableName} = useParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const pathParts = location.pathname.split("/");
|
||||
const tableName = table ? table.name : pathParts[pathParts.length - 2];
|
||||
|
||||
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
|
||||
const [nameValues, setNameValues] = useState([] as JSX.Element[]);
|
||||
const [sectionFieldElements, setSectionFieldElements] = useState(null as Map<string, JSX.Element[]>);
|
||||
const [t1Section, setT1Section] = useState(null as JSX.Element);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [tableMetaData, setTableMetaData] = useState(null);
|
||||
const [record, setRecord] = useState(null as QRecord);
|
||||
const [tableSections, setTableSections] = useState(null as any);
|
||||
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
||||
const [actionsMenu, setActionsMenu] = useState(null);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||
@ -70,28 +92,66 @@ function ViewContents({id}: Props): JSX.Element
|
||||
|
||||
(async () =>
|
||||
{
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
//////////////////////////////////////////
|
||||
// load the table meta-data (if needed) //
|
||||
//////////////////////////////////////////
|
||||
const tableMetaData = table || await qController.loadTableMetaData(tableName);
|
||||
setTableMetaData(tableMetaData);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// load top-level meta-data (e.g., to find processes for table) //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
const metaData = await qController.loadMetaData();
|
||||
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName));
|
||||
|
||||
const foundRecord = await qController.get(tableName, id);
|
||||
/////////////////////
|
||||
// load the record //
|
||||
/////////////////////
|
||||
const record = await qController.get(tableName, id);
|
||||
setRecord(record);
|
||||
|
||||
nameValues.push(
|
||||
<MDBox key={tableMetaData.primaryKeyField} display="flex" py={1} pr={2}>
|
||||
<MDTypography variant="button" fontWeight="bold" textTransform="capitalize">
|
||||
{tableMetaData.primaryKeyField}
|
||||
:
|
||||
</MDTypography>
|
||||
<MDTypography variant="button" fontWeight="regular" color="text">
|
||||
|
||||
{id}
|
||||
</MDTypography>
|
||||
</MDBox>,
|
||||
);
|
||||
/////////////////////////////////////////////////
|
||||
// define the sections, e.g., for the left-bar //
|
||||
/////////////////////////////////////////////////
|
||||
const tableSections = QTableUtils.getSectionsForRecordSidebar(tableMetaData);
|
||||
setTableSections(tableSections);
|
||||
|
||||
const sortedKeys = [...foundRecord.values.keys()].sort();
|
||||
////////////////////////////////////////////////////
|
||||
// make elements with the values for each section //
|
||||
////////////////////////////////////////////////////
|
||||
const sectionFieldElements = new Map();
|
||||
for (let i = 0; i < tableSections.length; i++)
|
||||
{
|
||||
const section = tableSections[i];
|
||||
sectionFieldElements.set(
|
||||
section.name,
|
||||
<MDBox key={section.name} display="flex" flexDirection="column" py={1} pr={2}>
|
||||
{
|
||||
section.fieldNames.map((fieldName: string) => (
|
||||
<MDBox key={fieldName} flexDirection="row" pr={2}>
|
||||
<MDTypography variant="button" fontWeight="bold">
|
||||
{tableMetaData.fields.get(fieldName).label}
|
||||
:
|
||||
</MDTypography>
|
||||
<MDTypography variant="button" fontWeight="regular" color="text">
|
||||
|
||||
{QValueUtils.getDisplayValue(tableMetaData.fields.get(fieldName), record)}
|
||||
</MDTypography>
|
||||
</MDBox>
|
||||
))
|
||||
}
|
||||
</MDBox>,
|
||||
);
|
||||
|
||||
if (section.tier === "T1")
|
||||
{
|
||||
setT1Section(sectionFieldElements.get(section.name));
|
||||
}
|
||||
}
|
||||
setSectionFieldElements(sectionFieldElements);
|
||||
|
||||
// todo - delete this
|
||||
const sortedKeys = [...record.values.keys()].sort();
|
||||
sortedKeys.forEach((key) =>
|
||||
{
|
||||
if (key !== tableMetaData.primaryKeyField)
|
||||
@ -103,15 +163,15 @@ function ViewContents({id}: Props): JSX.Element
|
||||
:
|
||||
</MDTypography>
|
||||
<MDTypography variant="button" fontWeight="regular" color="text">
|
||||
|
||||
{foundRecord.values.get(key)}
|
||||
|
||||
{QValueUtils.getDisplayValue(tableMetaData.fields.get(key), record)}
|
||||
</MDTypography>
|
||||
</MDBox>,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
setNameValues(nameValues);
|
||||
|
||||
forceUpdate();
|
||||
})();
|
||||
}
|
||||
@ -134,32 +194,45 @@ function ViewContents({id}: Props): JSX.Element
|
||||
await qController.delete(tableName, id)
|
||||
.then(() =>
|
||||
{
|
||||
window.location.href = `/${tableName}?deleteSuccess=true`;
|
||||
const path = `${pathParts.slice(0, -1).join("/")}?deleteSuccess=true`;
|
||||
navigate(path);
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
const editPath = `/${tableName}/${id}/edit`;
|
||||
function processClicked(process: QProcessMetaData)
|
||||
{
|
||||
const path = `${pathParts.slice(0, -1).join("/")}/${process.name}?recordIds=${id}`;
|
||||
navigate(path);
|
||||
}
|
||||
|
||||
const renderActionsMenu = (
|
||||
<Menu
|
||||
anchorEl={actionsMenu}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
horizontal: "right",
|
||||
}}
|
||||
open={Boolean(actionsMenu)}
|
||||
onClose={closeActionsMenu}
|
||||
keepMounted
|
||||
>
|
||||
<MenuItem onClick={() => navigate("edit")}>Edit</MenuItem>
|
||||
<MenuItem onClick={() =>
|
||||
{
|
||||
setActionsMenu(null);
|
||||
handleClickConfirmOpen();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</MenuItem>
|
||||
<MenuItem divider />
|
||||
{tableProcesses.map((process) => (
|
||||
<MenuItem key={process.name}>
|
||||
<Link href={`/processes/${process.name}?recordIds=${id}`}>{process.label}</Link>
|
||||
</MenuItem>
|
||||
<MenuItem key={process.name} onClick={() => processClicked(process)}>{process.label}</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
@ -179,77 +252,89 @@ function ViewContents({id}: Props): JSX.Element
|
||||
</MDAlert>
|
||||
) : ("")
|
||||
}
|
||||
<Card id="basic-info" sx={{overflow: "visible"}}>
|
||||
<MDBox p={3}>
|
||||
<MDBox display="flex" justifyContent="space-between">
|
||||
<MDTypography variant="h5">
|
||||
Viewing
|
||||
{" "}
|
||||
{tableMetaData?.label}
|
||||
{" "}
|
||||
(
|
||||
{id}
|
||||
)
|
||||
</MDTypography>
|
||||
{tableProcesses.length > 0 && (
|
||||
<MDButton
|
||||
variant={actionsMenu ? "contained" : "outlined"}
|
||||
color="dark"
|
||||
onClick={openActionsMenu}
|
||||
>
|
||||
actions
|
||||
<Icon>keyboard_arrow_down</Icon>
|
||||
</MDButton>
|
||||
)}
|
||||
{renderActionsMenu}
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
<MDBox p={3}>{nameValues}</MDBox>
|
||||
<MDBox component="form" pb={3} px={3}>
|
||||
<Grid key="tres" container spacing={3}>
|
||||
<MDBox ml="auto" mr={3}>
|
||||
<MDButton
|
||||
variant="gradient"
|
||||
color="primary"
|
||||
size="small"
|
||||
onClick={handleClickConfirmOpen}
|
||||
>
|
||||
delete
|
||||
{" "}
|
||||
{tableMetaData?.label}
|
||||
</MDButton>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClickConfirmClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Confirm Deletion</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete this record?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClickConfirmClose}>No</Button>
|
||||
<Button onClick={handleDelete} autoFocus>
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</MDBox>
|
||||
<MDBox>
|
||||
<MDButton variant="gradient" color="dark" size="small">
|
||||
<Link href={editPath}>
|
||||
{`edit ${tableMetaData?.label}`}
|
||||
</Link>
|
||||
</MDButton>
|
||||
</MDBox>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} lg={3}>
|
||||
<QRecordSidebar tableSections={tableSections} />
|
||||
</Grid>
|
||||
<Grid item xs={12} lg={9}>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} mb={3}>
|
||||
<Card>
|
||||
<MDBox display="flex" p={3} pb={1}>
|
||||
<MDBox mr={1.5}>
|
||||
<Avatar sx={{bgcolor: "rgb(26, 115, 232)"}}>
|
||||
<Icon>
|
||||
{tableMetaData?.iconName}
|
||||
</Icon>
|
||||
</Avatar>
|
||||
</MDBox>
|
||||
<MDBox display="flex" justifyContent="space-between" width="100%" alignItems="center">
|
||||
<MDTypography variant="h5">
|
||||
{tableMetaData && record ? `Viewing ${tableMetaData?.label}: ${record?.recordLabel}` : ""}
|
||||
</MDTypography>
|
||||
{tableProcesses.length > 0 && (
|
||||
<QActionsMenuButton isOpen={actionsMenu} onClickHandler={openActionsMenu} />
|
||||
)}
|
||||
{renderActionsMenu}
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
{t1Section ? (<MDBox p={3} pt={0}>{t1Section}</MDBox>) : null}
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MDBox>
|
||||
</Card>
|
||||
{tableSections && sectionFieldElements ? tableSections.map(({
|
||||
icon, label, name, fieldNames, tier,
|
||||
}: any) => (tier !== "T1"
|
||||
? (
|
||||
<MDBox mb={3} key={name}>
|
||||
<Card key={name} id={name} sx={{overflow: "visible"}}>
|
||||
<MDTypography variant="h5" p={3} pb={1}>
|
||||
{label}
|
||||
</MDTypography>
|
||||
<MDBox p={3} pt={0} flexDirection="column">{sectionFieldElements.get(name)}</MDBox>
|
||||
</Card>
|
||||
</MDBox>
|
||||
) : null)) : null}
|
||||
|
||||
<MDBox component="form" p={3}>
|
||||
<Grid container justifyContent="flex-end" spacing={3}>
|
||||
<QDeleteButton onClickHandler={handleClickConfirmOpen} />
|
||||
<QEditButton />
|
||||
</Grid>
|
||||
</MDBox>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Delete confirmation Dialog */}
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClickConfirmClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Confirm Deletion</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete this record?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClickConfirmClose}>No</Button>
|
||||
<Button onClick={handleDelete} autoFocus>
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</MDBox>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
ViewContents.defaultProps = {
|
||||
table: null,
|
||||
};
|
||||
|
||||
export default ViewContents;
|
||||
|
@ -1,16 +1,22 @@
|
||||
/**
|
||||
=========================================================
|
||||
* Material Dashboard 2 PRO React TS - v1.0.0
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
|
||||
|
||||
Coded by www.creative-tim.com
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
/*
|
||||
* 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 {useParams} from "react-router-dom";
|
||||
@ -24,22 +30,25 @@ import MDBox from "components/MDBox";
|
||||
// Settings page components
|
||||
import BaseLayout from "qqq/components/BaseLayout";
|
||||
import ViewContents from "./components/ViewContents";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import EntityList from "qqq/pages/entity-list";
|
||||
|
||||
function EntityView(): JSX.Element
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
}
|
||||
|
||||
function EntityView({table}: Props): JSX.Element
|
||||
{
|
||||
const {id} = useParams();
|
||||
|
||||
return (
|
||||
<BaseLayout>
|
||||
<MDBox mt={4}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} lg={12}>
|
||||
<MDBox>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<MDBox mb={3}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<ViewContents id={id} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ViewContents id={id} />
|
||||
</MDBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -48,4 +57,8 @@ function EntityView(): JSX.Element
|
||||
);
|
||||
}
|
||||
|
||||
EntityView.defaultProps = {
|
||||
table: null,
|
||||
};
|
||||
|
||||
export default EntityView;
|
||||
|
@ -1,16 +1,22 @@
|
||||
/**
|
||||
=========================================================
|
||||
* Material Dashboard 2 PRO React TS - v1.0.0
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
|
||||
|
||||
Coded by www.creative-tim.com
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
/*
|
||||
* 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 React, {useEffect, useState, Fragment} from "react";
|
||||
@ -31,9 +37,7 @@ import MDButton from "components/MDButton";
|
||||
|
||||
// Material Dashboard 2 PRO React TS examples components
|
||||
import DashboardLayout from "examples/LayoutContainers/DashboardLayout";
|
||||
import DashboardNavbar from "examples/Navbars/DashboardNavbar";
|
||||
|
||||
// ProcessRun layout schemas for form and form fields
|
||||
import * as Yup from "yup";
|
||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||
import {QFrontendStepMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFrontendStepMetaData";
|
||||
@ -52,10 +56,18 @@ import {CircularProgress, TablePagination} from "@mui/material";
|
||||
import QDynamicForm from "../../components/QDynamicForm";
|
||||
import MDTypography from "../../../components/MDTypography";
|
||||
import Footer from "examples/Footer";
|
||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||
import Navbar from "qqq/components/Navbar";
|
||||
|
||||
function ProcessRun(): JSX.Element
|
||||
interface Props
|
||||
{
|
||||
const {processName} = useParams();
|
||||
process?: QProcessMetaData;
|
||||
}
|
||||
|
||||
function ProcessRun({process}: Props): JSX.Element
|
||||
{
|
||||
const processNameParam = useParams().processName;
|
||||
const processName = process === null ? processNameParam : process.name;
|
||||
|
||||
///////////////////
|
||||
// process state //
|
||||
@ -686,7 +698,7 @@ function ProcessRun(): JSX.Element
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<DashboardNavbar />
|
||||
<Navbar />
|
||||
<MDBox py={3} mb={20}>
|
||||
<Grid
|
||||
container
|
||||
@ -788,4 +800,8 @@ function ProcessRun(): JSX.Element
|
||||
);
|
||||
}
|
||||
|
||||
ProcessRun.defaultProps = {
|
||||
process: null,
|
||||
};
|
||||
|
||||
export default ProcessRun;
|
||||
|
Reference in New Issue
Block a user