QQQ-41: added app sections, wired all dashboards, implemented widgets that could be, upped version
62374
package-lock.json
generated
225
package.json
@ -1,114 +1,115 @@
|
|||||||
{
|
{
|
||||||
"name": "qqq-frontend-material-dashboard",
|
"name": "qqq-frontend-material-dashboard",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "React + TypeScript version of Material Dashboard 2 PRO by Creative Tim",
|
"description": "QQQ Default Dashboard",
|
||||||
"proxy": "http://localhost:8000",
|
"proxy": "http://localhost:8000",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asseinfo/react-kanban": "2.2.0",
|
"@asseinfo/react-kanban": "2.2.0",
|
||||||
"@auth0/auth0-react": "1.10.2",
|
"@auth0/auth0-react": "1.10.2",
|
||||||
"@emotion/cache": "11.7.1",
|
"@emotion/cache": "11.7.1",
|
||||||
"@emotion/react": "11.7.1",
|
"@emotion/react": "11.7.1",
|
||||||
"@emotion/styled": "11.6.0",
|
"@emotion/styled": "11.6.0",
|
||||||
"@fullcalendar/daygrid": "5.10.0",
|
"@fullcalendar/daygrid": "5.10.0",
|
||||||
"@fullcalendar/interaction": "5.10.0",
|
"@fullcalendar/interaction": "5.10.0",
|
||||||
"@fullcalendar/react": "5.10.0",
|
"@fullcalendar/react": "5.10.0",
|
||||||
"@fullcalendar/timegrid": "5.10.0",
|
"@fullcalendar/timegrid": "5.10.0",
|
||||||
"@kingsrook/qqq-frontend-core": "1.0.15",
|
"@kingsrook/qqq-frontend-core": "1.0.17",
|
||||||
"@mui/icons-material": "5.4.1",
|
"@mui/icons-material": "5.4.1",
|
||||||
"@mui/material": "5.4.1",
|
"@mui/material": "5.4.1",
|
||||||
"@mui/styled-engine": "5.4.1",
|
"@mui/styled-engine": "5.4.1",
|
||||||
"@mui/x-data-grid": "5.13.0",
|
"@mui/x-data-grid": "5.13.0",
|
||||||
"@mui/x-data-grid-pro": "5.13.0",
|
"@mui/x-data-grid-pro": "5.13.0",
|
||||||
"@mui/x-license-pro": "5.12.3",
|
"@mui/x-license-pro": "5.12.3",
|
||||||
"@react-jvectormap/core": "1.0.1",
|
"@react-jvectormap/core": "1.0.1",
|
||||||
"@react-jvectormap/unitedstates": "1.0.1",
|
"@react-jvectormap/unitedstates": "1.0.1",
|
||||||
"@react-jvectormap/world": "1.0.0",
|
"@react-jvectormap/world": "1.0.0",
|
||||||
"@testing-library/jest-dom": "5.16.2",
|
"@testing-library/jest-dom": "5.16.2",
|
||||||
"@testing-library/react": "12.1.2",
|
"@testing-library/react": "12.1.2",
|
||||||
"@testing-library/user-event": "13.5.0",
|
"@testing-library/user-event": "13.5.0",
|
||||||
"@types/jest": "27.4.0",
|
"@types/jest": "27.4.0",
|
||||||
"@types/node": "16.11.21",
|
"@types/node": "16.11.21",
|
||||||
"@types/react": "17.0.38",
|
"@types/react": "17.0.38",
|
||||||
"@types/react-dom": "17.0.11",
|
"@types/react-dom": "17.0.11",
|
||||||
"chart.js": "3.4.1",
|
"chart.js": "3.4.1",
|
||||||
"chroma-js": "2.4.2",
|
"chroma-js": "2.4.2",
|
||||||
"datejs": "1.0.0-rc3",
|
"datejs": "1.0.0-rc3",
|
||||||
"dropzone": "5.9.2",
|
"dropzone": "5.9.2",
|
||||||
"flatpickr": "4.6.9",
|
"flatpickr": "4.6.9",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"formik": "2.2.9",
|
"formik": "2.2.9",
|
||||||
"html-react-parser": "1.4.8",
|
"html-react-parser": "1.4.8",
|
||||||
"http-proxy-middleware": "2.0.6",
|
"http-proxy-middleware": "2.0.6",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-chartjs-2": "3.0.4",
|
"react-chartjs-2": "3.0.4",
|
||||||
"react-cookie": "4.1.1",
|
"react-cookie": "4.1.1",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-flatpickr": "3.10.7",
|
"react-flatpickr": "3.10.7",
|
||||||
"react-github-btn": "1.2.1",
|
"react-github-btn": "1.2.1",
|
||||||
"react-images-viewer": "1.7.1",
|
"react-html-parser": "2.0.2",
|
||||||
"react-quill": "1.3.5",
|
"react-images-viewer": "1.7.1",
|
||||||
"react-router": "6.3.0",
|
"react-quill": "1.3.5",
|
||||||
"react-router-dom": "6.2.1",
|
"react-router": "6.3.0",
|
||||||
"react-scripts": "5.0.0",
|
"react-router-dom": "6.2.1",
|
||||||
"react-table": "7.7.0",
|
"react-scripts": "5.0.0",
|
||||||
"stylis": "4.0.13",
|
"react-table": "7.7.0",
|
||||||
"stylis-plugin-rtl": "2.1.1",
|
"stylis": "4.0.13",
|
||||||
"ts-md5": "1.2.11",
|
"stylis-plugin-rtl": "2.1.1",
|
||||||
"uuid": "8.3.2",
|
"ts-md5": "1.2.11",
|
||||||
"web-vitals": "2.1.4",
|
"uuid": "8.3.2",
|
||||||
"yup": "0.32.11"
|
"web-vitals": "2.1.4",
|
||||||
},
|
"yup": "0.32.11"
|
||||||
"scripts": {
|
},
|
||||||
"build": "react-scripts build",
|
"scripts": {
|
||||||
"clean": "rm -rf node_modules package-lock.json",
|
"build": "react-scripts build",
|
||||||
"eject": "react-scripts eject",
|
"clean": "rm -rf node_modules package-lock.json",
|
||||||
"geff-ham": "rm -rf node_modules/ && rm -rf package-lock-bak.json && npm install --legacy-peer-deps && npm start",
|
"eject": "react-scripts eject",
|
||||||
"install-legacy-peer-deps": "npm install --legacy-peer-deps",
|
"geff-ham": "rm -rf node_modules/ && rm -rf package-lock-bak.json && npm install --legacy-peer-deps && npm start",
|
||||||
"prepublishOnly": "tsc -p ./ --outDir lib/",
|
"install-legacy-peer-deps": "npm install --legacy-peer-deps",
|
||||||
"start": "react-scripts start",
|
"prepublishOnly": "tsc -p ./ --outDir lib/",
|
||||||
"test": "react-scripts test",
|
"start": "react-scripts start",
|
||||||
"cypress:open": "cypress open"
|
"test": "react-scripts test",
|
||||||
},
|
"cypress:open": "cypress open"
|
||||||
"eslintConfig": {
|
},
|
||||||
"extends": [
|
"eslintConfig": {
|
||||||
"react-app",
|
"extends": [
|
||||||
"react-app/jest"
|
"react-app",
|
||||||
]
|
"react-app/jest"
|
||||||
},
|
]
|
||||||
"browserslist": {
|
},
|
||||||
"production": [
|
"browserslist": {
|
||||||
">0.2%",
|
"production": [
|
||||||
"not dead",
|
">0.2%",
|
||||||
"not op_mini all"
|
"not dead",
|
||||||
],
|
"not op_mini all"
|
||||||
"development": [
|
],
|
||||||
"last 1 chrome version",
|
"development": [
|
||||||
"last 1 firefox version",
|
"last 1 chrome version",
|
||||||
"last 1 safari version"
|
"last 1 firefox version",
|
||||||
]
|
"last 1 safari version"
|
||||||
},
|
]
|
||||||
"devDependencies": {
|
},
|
||||||
"@types/chroma-js": "2.1.3",
|
"devDependencies": {
|
||||||
"@types/dropzone": "5.7.4",
|
"@types/chroma-js": "2.1.3",
|
||||||
"@types/react-flatpickr": "3.8.5",
|
"@types/dropzone": "5.7.4",
|
||||||
"@types/react-table": "7.7.9",
|
"@types/react-flatpickr": "3.8.5",
|
||||||
"@types/uuid": "8.3.4",
|
"@types/react-table": "7.7.9",
|
||||||
"@typescript-eslint/eslint-plugin": "5.10.2",
|
"@types/uuid": "8.3.4",
|
||||||
"@typescript-eslint/parser": "5.10.2",
|
"@typescript-eslint/eslint-plugin": "5.10.2",
|
||||||
"cypress": "10.3.1",
|
"@typescript-eslint/parser": "5.10.2",
|
||||||
"eslint": "8.8.0",
|
"cypress": "10.3.1",
|
||||||
"eslint-config-airbnb": "19.0.4",
|
"eslint": "8.8.0",
|
||||||
"eslint-import-resolver-typescript": "2.5.0",
|
"eslint-config-airbnb": "19.0.4",
|
||||||
"eslint-plugin-import": "2.25.4",
|
"eslint-import-resolver-typescript": "2.5.0",
|
||||||
"eslint-plugin-jsx-a11y": "6.5.1",
|
"eslint-plugin-import": "2.25.4",
|
||||||
"eslint-plugin-react": "7.28.0",
|
"eslint-plugin-jsx-a11y": "6.5.1",
|
||||||
"eslint-plugin-react-hooks": "4.3.0",
|
"eslint-plugin-react": "7.28.0",
|
||||||
"typescript": "^4.7.3"
|
"eslint-plugin-react-hooks": "4.3.0",
|
||||||
},
|
"typescript": "^4.7.3"
|
||||||
"main": "qqq-frontend-material-dashboard.js",
|
},
|
||||||
"module": "lib/qqq-frontend-material-dashboard.js",
|
"main": "qqq-frontend-material-dashboard.js",
|
||||||
"files": [
|
"module": "lib/qqq-frontend-material-dashboard.js",
|
||||||
"lib",
|
"files": [
|
||||||
"package.json"
|
"lib",
|
||||||
]
|
"package.json"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
BIN
public/carrier-logos/axlehire.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/carrier-logos/cdl.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
public/carrier-logos/dhl.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/carrier-logos/fedex.jpeg
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
public/carrier-logos/fedex.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
public/carrier-logos/lso.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
public/carrier-logos/ontrac.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
public/carrier-logos/ups.png
Normal file
After Width: | Height: | Size: 146 KiB |
BIN
public/ups.png
Normal file
After Width: | Height: | Size: 146 KiB |
BIN
public/warehouses/edison.jpg
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
public/warehouses/patterson.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
public/warehouses/stockton.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
23
src/App.tsx
@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {useAuth0} from "@auth0/auth0-react";
|
import {useAuth0} from "@auth0/auth0-react";
|
||||||
|
import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException";
|
||||||
import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType";
|
import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType";
|
||||||
import {QAppTreeNode} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppTreeNode";
|
import {QAppTreeNode} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppTreeNode";
|
||||||
import {QBrandingMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QBrandingMetaData";
|
import {QBrandingMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QBrandingMetaData";
|
||||||
@ -87,10 +88,8 @@ LicenseInfo.setLicenseKey(process.env.REACT_APP_MATERIAL_UI_LICENSE_KEY);
|
|||||||
|
|
||||||
export default function App()
|
export default function App()
|
||||||
{
|
{
|
||||||
const [, setCookie] = useCookies([SESSION_ID_COOKIE_NAME]);
|
const [, setCookie, removeCookie] = useCookies([SESSION_ID_COOKIE_NAME]);
|
||||||
const {
|
const {user, getAccessTokenSilently, getIdTokenClaims, logout} = useAuth0();
|
||||||
user, getAccessTokenSilently, getIdTokenClaims, logout, loginWithRedirect,
|
|
||||||
} = useAuth0();
|
|
||||||
const [loadingToken, setLoadingToken] = useState(false);
|
const [loadingToken, setLoadingToken] = useState(false);
|
||||||
const [isFullyAuthenticated, setIsFullyAuthenticated] = useState(false);
|
const [isFullyAuthenticated, setIsFullyAuthenticated] = useState(false);
|
||||||
const [profileRoutes, setProfileRoutes] = useState({});
|
const [profileRoutes, setProfileRoutes] = useState({});
|
||||||
@ -117,7 +116,9 @@ export default function App()
|
|||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
console.log(`Error loading token: ${JSON.stringify(e)}`);
|
console.log(`Error loading token: ${JSON.stringify(e)}`);
|
||||||
|
removeCookie(SESSION_ID_COOKIE_NAME);
|
||||||
logout();
|
logout();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [loadingToken]);
|
}, [loadingToken]);
|
||||||
@ -159,7 +160,7 @@ export default function App()
|
|||||||
}
|
}
|
||||||
|
|
||||||
const childList: any[] = [];
|
const childList: any[] = [];
|
||||||
if(app.children)
|
if (app.children)
|
||||||
{
|
{
|
||||||
app.children.forEach((child: QAppTreeNode) =>
|
app.children.forEach((child: QAppTreeNode) =>
|
||||||
{
|
{
|
||||||
@ -213,7 +214,7 @@ export default function App()
|
|||||||
const path = `${parentPath}/${app.name}`;
|
const path = `${parentPath}/${app.name}`;
|
||||||
if (app.type === QAppNodeType.APP)
|
if (app.type === QAppNodeType.APP)
|
||||||
{
|
{
|
||||||
if(app.children)
|
if (app.children)
|
||||||
{
|
{
|
||||||
app.children.forEach((child: QAppTreeNode) =>
|
app.children.forEach((child: QAppTreeNode) =>
|
||||||
{
|
{
|
||||||
@ -348,10 +349,14 @@ export default function App()
|
|||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
console.log(e);
|
if (e instanceof QException)
|
||||||
if (e.toString().indexOf("status code 401") !== -1)
|
|
||||||
{
|
{
|
||||||
logout();
|
if ((e as QException).message.indexOf("status code 401") !== -1)
|
||||||
|
{
|
||||||
|
removeCookie(SESSION_ID_COOKIE_NAME);
|
||||||
|
logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||||
import {QSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QSection";
|
|
||||||
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 {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";
|
||||||
@ -30,7 +30,7 @@ import Grid from "@mui/material/Grid";
|
|||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {Form, Formik} from "formik";
|
import {Form, Formik} from "formik";
|
||||||
import React, {useReducer, useState} from "react";
|
import React, {useReducer, useState} from "react";
|
||||||
import {useParams, useNavigate, useLocation} from "react-router-dom";
|
import {useLocation, useNavigate, useParams} from "react-router-dom";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import {QCancelButton, QSaveButton} from "qqq/components/QButtons";
|
import {QCancelButton, QSaveButton} from "qqq/components/QButtons";
|
||||||
import QDynamicForm from "qqq/components/QDynamicForm";
|
import QDynamicForm from "qqq/components/QDynamicForm";
|
||||||
@ -58,7 +58,7 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
const [initialValues, setInitialValues] = useState({} as { [key: string]: string });
|
const [initialValues, setInitialValues] = useState({} as { [key: string]: string });
|
||||||
const [formFields, setFormFields] = useState(null as Map<string, any>);
|
const [formFields, setFormFields] = useState(null as Map<string, any>);
|
||||||
const [t1sectionName, setT1SectionName] = useState(null as string);
|
const [t1sectionName, setT1SectionName] = useState(null as string);
|
||||||
const [nonT1Sections, setNonT1Sections] = useState([] as QSection[]);
|
const [nonT1Sections, setNonT1Sections] = useState([] as QTableSection[]);
|
||||||
|
|
||||||
const [alertContent, setAlertContent] = useState("");
|
const [alertContent, setAlertContent] = useState("");
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
const [formValues, setFormValues] = useState({} as { [key: string]: string });
|
const [formValues, setFormValues] = useState({} as { [key: string]: string });
|
||||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
||||||
const [record, setRecord] = useState(null as QRecord);
|
const [record, setRecord] = useState(null as QRecord);
|
||||||
const [tableSections, setTableSections] = useState(null as QSection[]);
|
const [tableSections, setTableSections] = useState(null as QTableSection[]);
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -144,7 +144,7 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
const dynamicFormFieldsBySection = new Map<string, any>();
|
const dynamicFormFieldsBySection = new Map<string, any>();
|
||||||
let t1sectionName;
|
let t1sectionName;
|
||||||
const nonT1Sections: QSection[] = [];
|
const nonT1Sections: QTableSection[] = [];
|
||||||
for (let i = 0; i < tableSections.length; i++)
|
for (let i = 0; i < tableSections.length; i++)
|
||||||
{
|
{
|
||||||
const section = tableSections[i];
|
const section = tableSections[i];
|
||||||
@ -325,7 +325,7 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
</Card>
|
</Card>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
{formFields && nonT1Sections.length ? nonT1Sections.map((section: QSection) => (
|
{formFields && nonT1Sections.length ? nonT1Sections.map((section: QTableSection) => (
|
||||||
<MDBox key={`edit-card-${section.name}`} pb={3}>
|
<MDBox key={`edit-card-${section.name}`} pb={3}>
|
||||||
<Card id={section.name} sx={{overflow: "visible"}}>
|
<Card id={section.name} sx={{overflow: "visible"}}>
|
||||||
<MDTypography variant="h5" p={3} pb={1}>
|
<MDTypography variant="h5" p={3} pb={1}>
|
||||||
|
@ -26,16 +26,18 @@ import {ReactNode} from "react";
|
|||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
interface Props {
|
interface Props
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
{
|
||||||
title: string;
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
percentage?: {
|
title: string;
|
||||||
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark" | "white";
|
percentage?: {
|
||||||
amount: string | number;
|
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark" | "white";
|
||||||
label: string;
|
amount: string | number;
|
||||||
};
|
label: string;
|
||||||
icon: ReactNode;
|
};
|
||||||
[key: string]: any;
|
icon: ReactNode;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProcessLinkCard({
|
function ProcessLinkCard({
|
||||||
@ -44,7 +46,7 @@ function ProcessLinkCard({
|
|||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<MDBox display="flex" justifyContent="space-between" pt={1} px={2}>
|
<MDBox display="flex" justifyContent="space-between" pt={3} px={2}>
|
||||||
<MDBox
|
<MDBox
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
bgColor={color}
|
bgColor={color}
|
||||||
|
@ -26,12 +26,14 @@ import {Link} from "react-router-dom";
|
|||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
interface Props {
|
interface Props
|
||||||
icon: ReactNode;
|
{
|
||||||
title: string;
|
icon: ReactNode;
|
||||||
route: string | string[];
|
title: string;
|
||||||
light?: boolean;
|
route: string | string[];
|
||||||
[key: string]: any;
|
light?: boolean;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ucFirst = (input: string): string =>
|
const ucFirst = (input: string): string =>
|
||||||
@ -107,23 +109,16 @@ function QBreadcrumbs({
|
|||||||
</MDTypography>
|
</MDTypography>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
<MDTypography
|
|
||||||
variant="button"
|
|
||||||
fontWeight="regular"
|
|
||||||
textTransform="capitalize"
|
|
||||||
color={light ? "white" : "dark"}
|
|
||||||
sx={{lineHeight: 0}}
|
|
||||||
>
|
|
||||||
{routeToLabel(title)}
|
|
||||||
</MDTypography>
|
|
||||||
</MuiBreadcrumbs>
|
</MuiBreadcrumbs>
|
||||||
<MDTypography
|
<MDTypography
|
||||||
|
pt={1}
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
textTransform="capitalize"
|
textTransform="capitalize"
|
||||||
variant="h6"
|
variant="h5"
|
||||||
color={light ? "white" : "dark"}
|
color={light ? "white" : "dark"}
|
||||||
noWrap
|
noWrap
|
||||||
>
|
>
|
||||||
|
{routeToLabel(title)}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {QSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QSection";
|
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {Theme} from "@mui/material/styles";
|
import {Theme} from "@mui/material/styles";
|
||||||
@ -27,8 +27,9 @@ import React from "react";
|
|||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
interface Props {
|
interface Props
|
||||||
tableSections: QSection[];
|
{
|
||||||
|
tableSections: QTableSection[];
|
||||||
light?: boolean;
|
light?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ function QRecordSidebar({tableSections, light}: Props): JSX.Element
|
|||||||
<Card sx={{borderRadius: ({borders: {borderRadius}}) => borderRadius.lg, position: "sticky", top: "1%"}}>
|
<Card sx={{borderRadius: ({borders: {borderRadius}}) => borderRadius.lg, position: "sticky", top: "1%"}}>
|
||||||
<MDBox component="ul" display="flex" flexDirection="column" p={2} m={0} sx={{listStyle: "none"}}>
|
<MDBox component="ul" display="flex" flexDirection="column" p={2} m={0} sx={{listStyle: "none"}}>
|
||||||
{
|
{
|
||||||
tableSections ? tableSections.map((section: QSection, key: number) => (
|
tableSections ? tableSections.map((section: QTableSection, key: number) => (
|
||||||
<MDBox key={`section-${section.name}`} component="li" pt={key === 0 ? 0 : 1}>
|
<MDBox key={`section-${section.name}`} component="li" pt={key === 0 ? 0 : 1}>
|
||||||
<MDTypography
|
<MDTypography
|
||||||
component="a"
|
component="a"
|
||||||
|
@ -23,11 +23,11 @@ import Divider from "@mui/material/Divider";
|
|||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import Link from "@mui/material/Link";
|
import Link from "@mui/material/Link";
|
||||||
import List from "@mui/material/List";
|
import List from "@mui/material/List";
|
||||||
import {useEffect, useState, ReactNode, useReducer} from "react";
|
import {ReactNode, useEffect, useReducer, useState} from "react";
|
||||||
import {useLocation, NavLink} from "react-router-dom";
|
import {NavLink, useLocation} from "react-router-dom";
|
||||||
import MDBox from "components/MDBox";
|
import MDBox from "components/MDBox";
|
||||||
import MDTypography from "components/MDTypography";
|
import MDTypography from "components/MDTypography";
|
||||||
import {useMaterialUIController, setMiniSidenav, setTransparentSidenav, setWhiteSidenav,} from "context";
|
import {setMiniSidenav, setTransparentSidenav, setWhiteSidenav, useMaterialUIController,} from "context";
|
||||||
import sidenavLogoLabel from "examples/Sidenav/styles/sidenav";
|
import sidenavLogoLabel from "examples/Sidenav/styles/sidenav";
|
||||||
import AuthenticationButton from "qqq/components/Buttons/AuthenticationButton";
|
import AuthenticationButton from "qqq/components/Buttons/AuthenticationButton";
|
||||||
import SidenavCollapse from "qqq/components/Sidenav/SidenavCollapse";
|
import SidenavCollapse from "qqq/components/Sidenav/SidenavCollapse";
|
||||||
@ -334,6 +334,12 @@ function Sidenav({color, icon, logo, companyName, routes, ...rest}: Props): JSX.
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<List>{renderRoutes}</List>
|
<List>{renderRoutes}</List>
|
||||||
|
<Divider
|
||||||
|
light={
|
||||||
|
(!darkMode && !whiteSidenav && !transparentSidenav) ||
|
||||||
|
(darkMode && !transparentSidenav && whiteSidenav)
|
||||||
|
}
|
||||||
|
/>
|
||||||
<AuthenticationButton />
|
<AuthenticationButton />
|
||||||
</SidenavRoot>
|
</SidenavRoot>
|
||||||
);
|
);
|
||||||
|
65
src/qqq/components/Temporary/DataTable/DataTableBodyCell.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 {Theme} from "@mui/material/styles";
|
||||||
|
import {ReactNode} from "react";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
|
||||||
|
// Declaring prop types for DataTableBodyCell
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
children: ReactNode;
|
||||||
|
noBorder?: boolean;
|
||||||
|
align?: "left" | "right" | "center";
|
||||||
|
}
|
||||||
|
|
||||||
|
function DataTableBodyCell({noBorder, align, children}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MDBox
|
||||||
|
component="td"
|
||||||
|
textAlign={align}
|
||||||
|
py={1.5}
|
||||||
|
px={3}
|
||||||
|
sx={({palette: {light}, typography: {size}, borders: {borderWidth}}: Theme) => ({
|
||||||
|
fontSize: size.sm,
|
||||||
|
borderBottom: noBorder ? "none" : `${borderWidth[1]} solid ${light.main}`,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<MDBox
|
||||||
|
display="inline-block"
|
||||||
|
width="max-content"
|
||||||
|
color="text"
|
||||||
|
sx={{verticalAlign: "middle"}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaring default props for DataTableBodyCell
|
||||||
|
DataTableBodyCell.defaultProps = {
|
||||||
|
noBorder: false,
|
||||||
|
align: "left",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataTableBodyCell;
|
107
src/qqq/components/Temporary/DataTable/DataTableHeadCell.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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 Icon from "@mui/material/Icon";
|
||||||
|
import {Theme} from "@mui/material/styles";
|
||||||
|
import {ReactNode} from "react";
|
||||||
|
import {useMaterialUIController} from "context";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
|
||||||
|
// Declaring props types for DataTableHeadCell
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
width?: string | number;
|
||||||
|
children: ReactNode;
|
||||||
|
sorted?: false | "none" | "asce" | "desc";
|
||||||
|
align?: "left" | "right" | "center";
|
||||||
|
}
|
||||||
|
|
||||||
|
function DataTableHeadCell({width, children, sorted, align, ...rest}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
const [controller] = useMaterialUIController();
|
||||||
|
const {darkMode} = controller;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MDBox
|
||||||
|
component="th"
|
||||||
|
width={width}
|
||||||
|
py={1.5}
|
||||||
|
px={3}
|
||||||
|
sx={({palette: {light}, borders: {borderWidth}}: Theme) => ({
|
||||||
|
borderBottom: `${borderWidth[1]} solid ${light.main}`,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<MDBox
|
||||||
|
{...rest}
|
||||||
|
position="relative"
|
||||||
|
textAlign={align}
|
||||||
|
color={darkMode ? "white" : "secondary"}
|
||||||
|
opacity={0.7}
|
||||||
|
sx={({typography: {size, fontWeightBold}}: Theme) => ({
|
||||||
|
fontSize: size.xxs,
|
||||||
|
fontWeight: fontWeightBold,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
cursor: sorted && "pointer",
|
||||||
|
userSelect: sorted && "none",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
{sorted && (
|
||||||
|
<MDBox
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
right={align !== "right" ? "16px" : 0}
|
||||||
|
left={align === "right" ? "-5px" : "unset"}
|
||||||
|
sx={({typography: {size}}: any) => ({
|
||||||
|
fontSize: size.lg,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<MDBox
|
||||||
|
position="absolute"
|
||||||
|
top={-6}
|
||||||
|
color={sorted === "asce" ? "text" : "secondary"}
|
||||||
|
opacity={sorted === "asce" ? 1 : 0.5}
|
||||||
|
>
|
||||||
|
<Icon>arrow_drop_up</Icon>
|
||||||
|
</MDBox>
|
||||||
|
<MDBox
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
color={sorted === "desc" ? "text" : "secondary"}
|
||||||
|
opacity={sorted === "desc" ? 1 : 0.5}
|
||||||
|
>
|
||||||
|
<Icon>arrow_drop_down</Icon>
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaring default props for DataTableHeadCell
|
||||||
|
DataTableHeadCell.defaultProps = {
|
||||||
|
width: "auto",
|
||||||
|
sorted: "none",
|
||||||
|
align: "left",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataTableHeadCell;
|
360
src/qqq/components/Temporary/DataTable/index.tsx
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
/* 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 Autocomplete from "@mui/material/Autocomplete";
|
||||||
|
import Icon from "@mui/material/Icon";
|
||||||
|
import Table from "@mui/material/Table";
|
||||||
|
import TableBody from "@mui/material/TableBody";
|
||||||
|
import TableContainer from "@mui/material/TableContainer";
|
||||||
|
import TableRow from "@mui/material/TableRow";
|
||||||
|
import parse from "html-react-parser";
|
||||||
|
import {useEffect, useMemo, useState} from "react";
|
||||||
|
import {useAsyncDebounce, useGlobalFilter, usePagination, useSortBy, useTable} from "react-table";
|
||||||
|
import DefaultCell from "layouts/dashboards/sales/components/DefaultCell";
|
||||||
|
import DataTableBodyCell from "qqq/components/Temporary/DataTable/DataTableBodyCell";
|
||||||
|
import DataTableHeadCell from "qqq/components/Temporary/DataTable/DataTableHeadCell";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
import MDInput from "qqq/components/Temporary/MDInput";
|
||||||
|
import MDPagination from "qqq/components/Temporary/MDPagination";
|
||||||
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
import ImageCell from "qqq/pages/dashboards/Tables/ImageCell";
|
||||||
|
|
||||||
|
export interface TableDataInput
|
||||||
|
{
|
||||||
|
columns: { [key: string]: any }[];
|
||||||
|
rows: { [key: string]: any }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaring props types for DataTable
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
entriesPerPage?:
|
||||||
|
| false
|
||||||
|
| {
|
||||||
|
defaultValue: number;
|
||||||
|
entries: number[];
|
||||||
|
};
|
||||||
|
canSearch?: boolean;
|
||||||
|
showTotalEntries?: boolean;
|
||||||
|
table: TableDataInput;
|
||||||
|
pagination?: {
|
||||||
|
variant: "contained" | "gradient";
|
||||||
|
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark" | "light";
|
||||||
|
};
|
||||||
|
isSorted?: boolean;
|
||||||
|
noEndBorder?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DataTable({
|
||||||
|
entriesPerPage,
|
||||||
|
canSearch,
|
||||||
|
showTotalEntries,
|
||||||
|
table,
|
||||||
|
pagination,
|
||||||
|
isSorted,
|
||||||
|
noEndBorder,
|
||||||
|
}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
let defaultValue: any;
|
||||||
|
let entries: any[];
|
||||||
|
|
||||||
|
if (entriesPerPage)
|
||||||
|
{
|
||||||
|
defaultValue = entriesPerPage.defaultValue ? entriesPerPage.defaultValue : "10";
|
||||||
|
entries = entriesPerPage.entries ? entriesPerPage.entries : ["10", "25", "50", "100"];
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = useMemo<any>(() => table.columns, [table]);
|
||||||
|
const data = useMemo<any>(() => table.rows, [table]);
|
||||||
|
|
||||||
|
if (!columns || !data)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableInstance = useTable(
|
||||||
|
{columns, data, initialState: {pageIndex: 0}},
|
||||||
|
useGlobalFilter,
|
||||||
|
useSortBy,
|
||||||
|
usePagination
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
prepareRow,
|
||||||
|
rows,
|
||||||
|
page,
|
||||||
|
pageOptions,
|
||||||
|
canPreviousPage,
|
||||||
|
canNextPage,
|
||||||
|
gotoPage,
|
||||||
|
nextPage,
|
||||||
|
previousPage,
|
||||||
|
setPageSize,
|
||||||
|
setGlobalFilter,
|
||||||
|
state: {pageIndex, pageSize, globalFilter},
|
||||||
|
}: any = tableInstance;
|
||||||
|
|
||||||
|
// Set the default value for the entries per page when component mounts
|
||||||
|
useEffect(() => setPageSize(defaultValue || 10), [defaultValue]);
|
||||||
|
|
||||||
|
// Set the entries per page value based on the select value
|
||||||
|
const setEntriesPerPage = (value: any) => setPageSize(value);
|
||||||
|
|
||||||
|
// Render the paginations
|
||||||
|
const renderPagination = pageOptions.map((option: any) => (
|
||||||
|
<MDPagination
|
||||||
|
item
|
||||||
|
key={option}
|
||||||
|
onClick={() => gotoPage(Number(option))}
|
||||||
|
active={pageIndex === option}
|
||||||
|
>
|
||||||
|
{option + 1}
|
||||||
|
</MDPagination>
|
||||||
|
));
|
||||||
|
|
||||||
|
// Handler for the input to set the pagination index
|
||||||
|
const handleInputPagination = ({target: {value}}: any) =>
|
||||||
|
value > pageOptions.length || value < 0 ? gotoPage(0) : gotoPage(Number(value));
|
||||||
|
|
||||||
|
// Customized page options starting from 1
|
||||||
|
const customizedPageOptions = pageOptions.map((option: any) => option + 1);
|
||||||
|
|
||||||
|
// Setting value for the pagination input
|
||||||
|
const handleInputPaginationValue = ({target: value}: any) => gotoPage(Number(value.value - 1));
|
||||||
|
|
||||||
|
// Search input value state
|
||||||
|
const [search, setSearch] = useState(globalFilter);
|
||||||
|
|
||||||
|
// Search input state handle
|
||||||
|
const onSearchChange = useAsyncDebounce((value) =>
|
||||||
|
{
|
||||||
|
setGlobalFilter(value || undefined);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// A function that sets the sorted value for the table
|
||||||
|
const setSortedValue = (column: any) =>
|
||||||
|
{
|
||||||
|
let sortedValue;
|
||||||
|
|
||||||
|
if (isSorted && column.isSorted)
|
||||||
|
{
|
||||||
|
sortedValue = column.isSortedDesc ? "desc" : "asce";
|
||||||
|
}
|
||||||
|
else if (isSorted)
|
||||||
|
{
|
||||||
|
sortedValue = "none";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sortedValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setting the entries starting point
|
||||||
|
const entriesStart = pageIndex === 0 ? pageIndex + 1 : pageIndex * pageSize + 1;
|
||||||
|
|
||||||
|
// Setting the entries ending point
|
||||||
|
let entriesEnd;
|
||||||
|
|
||||||
|
if (pageIndex === 0)
|
||||||
|
{
|
||||||
|
entriesEnd = pageSize;
|
||||||
|
}
|
||||||
|
else if (pageIndex === pageOptions.length - 1)
|
||||||
|
{
|
||||||
|
entriesEnd = rows.length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entriesEnd = pageSize * (pageIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableContainer sx={{boxShadow: "none"}}>
|
||||||
|
{entriesPerPage || canSearch ? (
|
||||||
|
<MDBox display="flex" justifyContent="space-between" alignItems="center" p={3}>
|
||||||
|
{entriesPerPage && (
|
||||||
|
<MDBox display="flex" alignItems="center">
|
||||||
|
<Autocomplete
|
||||||
|
disableClearable
|
||||||
|
value={pageSize.toString()}
|
||||||
|
options={entries}
|
||||||
|
onChange={(event, newValue) =>
|
||||||
|
{
|
||||||
|
setEntriesPerPage(parseInt(newValue, 10));
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
sx={{width: "5rem"}}
|
||||||
|
renderInput={(params) => <MDInput {...params} />}
|
||||||
|
/>
|
||||||
|
<MDTypography variant="caption" color="secondary">
|
||||||
|
entries per page
|
||||||
|
</MDTypography>
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
{canSearch && (
|
||||||
|
<MDBox width="12rem" ml="auto">
|
||||||
|
<MDInput
|
||||||
|
placeholder="Search..."
|
||||||
|
value={search}
|
||||||
|
size="small"
|
||||||
|
fullWidth
|
||||||
|
onChange={({currentTarget}: any) =>
|
||||||
|
{
|
||||||
|
setSearch(search);
|
||||||
|
onSearchChange(currentTarget.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
</MDBox>
|
||||||
|
) : null}
|
||||||
|
<Table {...getTableProps()}>
|
||||||
|
<MDBox component="thead">
|
||||||
|
{headerGroups.map((headerGroup: any, i: number) => (
|
||||||
|
<TableRow key={i} {...headerGroup.getHeaderGroupProps()}>
|
||||||
|
{headerGroup.headers.map((column: any) => (
|
||||||
|
column.type !== "hidden" && (
|
||||||
|
<DataTableHeadCell
|
||||||
|
key={i++}
|
||||||
|
{...column.getHeaderProps(isSorted && column.getSortByToggleProps())}
|
||||||
|
width={column.width ? column.width : "auto"}
|
||||||
|
align={column.align ? column.align : "left"}
|
||||||
|
sorted={setSortedValue(column)}
|
||||||
|
>
|
||||||
|
{column.render("header")}
|
||||||
|
</DataTableHeadCell>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</MDBox>
|
||||||
|
<TableBody {...getTableBodyProps()}>
|
||||||
|
{page.map((row: any, key: any) =>
|
||||||
|
{
|
||||||
|
prepareRow(row);
|
||||||
|
return (
|
||||||
|
<TableRow key={key} {...row.getRowProps()}>
|
||||||
|
{row.cells.map((cell: any) => (
|
||||||
|
cell.column.type !== "hidden" && (
|
||||||
|
<DataTableBodyCell
|
||||||
|
key={key}
|
||||||
|
noBorder={noEndBorder && rows.length - 1 === key}
|
||||||
|
align={cell.column.align ? cell.column.align : "left"}
|
||||||
|
{...cell.getCellProps()}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
cell.column.type === "default" && (
|
||||||
|
cell.value && "number" === typeof cell.value ? (
|
||||||
|
<DefaultCell>{cell.value.toLocaleString()}</DefaultCell>
|
||||||
|
) : (<DefaultCell>{cell.render("Cell")}</DefaultCell>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cell.column.type === "html" && (
|
||||||
|
<DefaultCell>{parse(cell.value)}</DefaultCell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cell.column.type === "image" && row.values["imageTotal"] && (
|
||||||
|
<ImageCell imageUrl={row.values["imageUrl"]} label={row.values["imageLabel"]} total={row.values["imageTotal"].toLocaleString()} totalType={row.values["imageTotalType"]} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cell.column.type === "image" && !row.values["imageTotal"] && (
|
||||||
|
<ImageCell imageUrl={row.values["imageUrl"]} label={row.values["imageLabel"]} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</DataTableBodyCell>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<MDBox
|
||||||
|
display="flex"
|
||||||
|
flexDirection={{xs: "column", sm: "row"}}
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems={{xs: "flex-start", sm: "center"}}
|
||||||
|
p={!showTotalEntries && pageOptions.length === 1 ? 0 : 3}
|
||||||
|
>
|
||||||
|
{showTotalEntries && (
|
||||||
|
<MDBox mb={{xs: 3, sm: 0}}>
|
||||||
|
<MDTypography variant="button" color="secondary" fontWeight="regular">
|
||||||
|
Showing {entriesStart} to {entriesEnd} of {rows.length} entries
|
||||||
|
</MDTypography>
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
{pageOptions.length > 1 && (
|
||||||
|
<MDPagination
|
||||||
|
variant={pagination.variant ? pagination.variant : "gradient"}
|
||||||
|
color={pagination.color ? pagination.color : "info"}
|
||||||
|
>
|
||||||
|
{canPreviousPage && (
|
||||||
|
<MDPagination item onClick={() => previousPage()}>
|
||||||
|
<Icon sx={{fontWeight: "bold"}}>chevron_left</Icon>
|
||||||
|
</MDPagination>
|
||||||
|
)}
|
||||||
|
{renderPagination.length > 6 ? (
|
||||||
|
<MDBox width="5rem" mx={1}>
|
||||||
|
<MDInput
|
||||||
|
inputProps={{type: "number", min: 1, max: customizedPageOptions.length}}
|
||||||
|
value={customizedPageOptions[pageIndex]}
|
||||||
|
onChange={(event: any) =>
|
||||||
|
{
|
||||||
|
handleInputPagination(event);
|
||||||
|
handleInputPaginationValue(event);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</MDBox>
|
||||||
|
) : (
|
||||||
|
renderPagination
|
||||||
|
)}
|
||||||
|
{canNextPage && (
|
||||||
|
<MDPagination item onClick={() => nextPage()}>
|
||||||
|
<Icon sx={{fontWeight: "bold"}}>chevron_right</Icon>
|
||||||
|
</MDPagination>
|
||||||
|
)}
|
||||||
|
</MDPagination>
|
||||||
|
)}
|
||||||
|
</MDBox>
|
||||||
|
</TableContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaring default props for DataTable
|
||||||
|
DataTable.defaultProps = {
|
||||||
|
entriesPerPage: {defaultValue: 10, entries: ["5", "10", "15", "20", "25"]},
|
||||||
|
canSearch: false,
|
||||||
|
showTotalEntries: true,
|
||||||
|
pagination: {variant: "gradient", color: "info"},
|
||||||
|
isSorted: true,
|
||||||
|
noEndBorder: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataTable;
|
@ -26,7 +26,7 @@ import MDTypography from "components/MDTypography";
|
|||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
variant?: "gradient" | "contained";
|
variant?: "gradient" | "contained";
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
color?: string;
|
||||||
size?: "xs" | "sm" | "md" | "lg";
|
size?: "xs" | "sm" | "md" | "lg";
|
||||||
badgeContent: string;
|
badgeContent: string;
|
||||||
font?:
|
font?:
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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 {styled, Theme} from "@mui/material/styles";
|
||||||
|
import MDButton from "qqq/components/Temporary/MDButton";
|
||||||
|
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export default styled(MDButton)(({theme, ownerState}: { theme?: Theme; ownerState: any }) =>
|
||||||
|
{
|
||||||
|
const {borders, functions, typography, palette} = theme;
|
||||||
|
const {variant, paginationSize, active} = ownerState;
|
||||||
|
|
||||||
|
const {borderColor} = borders;
|
||||||
|
const {pxToRem} = functions;
|
||||||
|
const {fontWeightRegular, size: fontSize} = typography;
|
||||||
|
const {light} = palette;
|
||||||
|
|
||||||
|
// width, height, minWidth and minHeight values
|
||||||
|
let sizeValue = pxToRem(36);
|
||||||
|
|
||||||
|
if (paginationSize === "small")
|
||||||
|
{
|
||||||
|
sizeValue = pxToRem(30);
|
||||||
|
}
|
||||||
|
else if (paginationSize === "large")
|
||||||
|
{
|
||||||
|
sizeValue = pxToRem(46);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
borderColor,
|
||||||
|
margin: `0 ${pxToRem(2)}`,
|
||||||
|
pointerEvents: active ? "none" : "auto",
|
||||||
|
fontWeight: fontWeightRegular,
|
||||||
|
fontSize: fontSize.sm,
|
||||||
|
width: sizeValue,
|
||||||
|
minWidth: sizeValue,
|
||||||
|
height: sizeValue,
|
||||||
|
minHeight: sizeValue,
|
||||||
|
|
||||||
|
"&:hover, &:focus, &:active": {
|
||||||
|
transform: "none",
|
||||||
|
boxShadow: (variant !== "gradient" || variant !== "contained") && "none !important",
|
||||||
|
opacity: "1 !important",
|
||||||
|
},
|
||||||
|
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: light.main,
|
||||||
|
borderColor,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
104
src/qqq/components/Temporary/MDPagination/index.tsx
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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 {createContext, FC, forwardRef, ReactNode, useContext, useMemo} from "react";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
import MDPaginationItemRoot from "qqq/components/Temporary/MDPagination/MDPaginationItemRoot";
|
||||||
|
|
||||||
|
// The Pagination main context
|
||||||
|
const Context = createContext<any>(null);
|
||||||
|
|
||||||
|
// Declare props types for MDPagination
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
item?: boolean;
|
||||||
|
variant?: "gradient" | "contained";
|
||||||
|
color?:
|
||||||
|
| "white"
|
||||||
|
| "primary"
|
||||||
|
| "secondary"
|
||||||
|
| "info"
|
||||||
|
| "success"
|
||||||
|
| "warning"
|
||||||
|
| "error"
|
||||||
|
| "light"
|
||||||
|
| "dark";
|
||||||
|
size?: "small" | "medium" | "large";
|
||||||
|
active?: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MDPagination: FC<Props | any> = forwardRef(
|
||||||
|
({item, variant, color, size, active, children, ...rest}, ref) =>
|
||||||
|
{
|
||||||
|
const context: any = useContext(Context);
|
||||||
|
const paginationSize = context ? context.size : undefined;
|
||||||
|
|
||||||
|
const providerValue = useMemo(
|
||||||
|
() => ({
|
||||||
|
variant,
|
||||||
|
color,
|
||||||
|
size,
|
||||||
|
}),
|
||||||
|
[variant, color, size]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider value={providerValue}>
|
||||||
|
{item ? (
|
||||||
|
<MDPaginationItemRoot
|
||||||
|
{...rest}
|
||||||
|
ref={ref}
|
||||||
|
variant={active ? context.variant : "outlined"}
|
||||||
|
color={active ? context.color : "secondary"}
|
||||||
|
iconOnly
|
||||||
|
circular
|
||||||
|
ownerState={{variant, active, paginationSize}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MDPaginationItemRoot>
|
||||||
|
) : (
|
||||||
|
<MDBox
|
||||||
|
display="flex"
|
||||||
|
justifyContent="flex-end"
|
||||||
|
alignItems="center"
|
||||||
|
sx={{listStyle: "none"}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
</Context.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Declaring default props for MDPagination
|
||||||
|
MDPagination.defaultProps = {
|
||||||
|
item: false,
|
||||||
|
variant: "gradient",
|
||||||
|
color: "info",
|
||||||
|
size: "medium",
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MDPagination;
|
@ -25,19 +25,19 @@ import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/
|
|||||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
import {Icon} from "@mui/material";
|
import {Icon} from "@mui/material";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
|
import Divider from "@mui/material/Divider";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {Link, useLocation} from "react-router-dom";
|
import {Link, useLocation} from "react-router-dom";
|
||||||
import BaseLayout from "qqq/components/BaseLayout";
|
import BaseLayout from "qqq/components/BaseLayout";
|
||||||
import ProcessLinkCard from "qqq/components/ProcessLinkCard";
|
import ProcessLinkCard from "qqq/components/ProcessLinkCard";
|
||||||
import DefaultInfoCard from "qqq/components/Temporary/DefaultInfoCard";
|
|
||||||
import MDBadgeDot from "qqq/components/Temporary/MDBadgeDot";
|
import MDBadgeDot from "qqq/components/Temporary/MDBadgeDot";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import MiniStatisticsCard from "qqq/components/Temporary/MiniStatisticsCard";
|
import MiniStatisticsCard from "qqq/components/Temporary/MiniStatisticsCard";
|
||||||
import BarChart from "qqq/pages/dashboards/Widgets/BarChart";
|
import BarChart from "qqq/pages/dashboards/Widgets/BarChart";
|
||||||
import QuickSightChart from "qqq/pages/dashboards/Widgets/QuickSightChart";
|
import QuickSightChart from "qqq/pages/dashboards/Widgets/QuickSightChart";
|
||||||
import SmallLineChart from "qqq/pages/dashboards/Widgets/SmallLineChart";
|
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
import LineChart from "../dashboards/Widgets/LineChart";
|
import LineChart from "../dashboards/Widgets/LineChart";
|
||||||
|
|
||||||
@ -118,7 +118,6 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
});
|
});
|
||||||
setTableCounts(tableCounts);
|
setTableCounts(tableCounts);
|
||||||
|
|
||||||
console.log(app.widgets);
|
|
||||||
if (app.widgets)
|
if (app.widgets)
|
||||||
{
|
{
|
||||||
const widgets: any[] = [];
|
const widgets: any[] = [];
|
||||||
@ -127,8 +126,7 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
widgets[i] = {};
|
widgets[i] = {};
|
||||||
setTimeout(async () =>
|
setTimeout(async () =>
|
||||||
{
|
{
|
||||||
const widget = await qController.widget(app.widgets[i]);
|
widgets[i] = await qController.widget(app.widgets[i]);
|
||||||
widgets[i] = widget;
|
|
||||||
setUpdatedTableCounts(new Date());
|
setUpdatedTableCounts(new Date());
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
@ -136,47 +134,52 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
}, [qInstance, location]);
|
}, [qInstance, location]);
|
||||||
|
|
||||||
/*
|
|
||||||
const charts = [
|
|
||||||
{
|
|
||||||
type: "barChart",
|
|
||||||
title: "Parcel Invoice Lines per Month",
|
|
||||||
barChartData: {
|
|
||||||
labels: ["Feb 22", "Mar 22", "Apr 22", "May 22", "Jun 22", "Jul 22", "Aug 22"],
|
|
||||||
datasets: {label: "Parcel Invoice Lines", data: [50000, 22000, 11111, 22333, 40404, 9876, 2355]},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "lineChart",
|
|
||||||
title: "Total Charges by Carrier per Month",
|
|
||||||
lineChartData: {
|
|
||||||
labels: ["Feb 22", "Mar 22", "Apr 22", "May 22", "Jun 22", "Jul 22", "Aug 22"],
|
|
||||||
datasets: [
|
|
||||||
{label: "UPS", color: "info", data: [50000, 22000, 11111, 22333, 40404, 9876, 2355]},
|
|
||||||
{label: "FedEx", color: "dark", data: [5000, 22000, 31111, 32333, 20404, 19876, 24355]},
|
|
||||||
{label: "LSO", color: "error", data: [500, 2200, 1111, 2333, 404, 17876, 2355]},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
*/
|
|
||||||
|
|
||||||
console.log(`Widgets: ${widgets} and tables: ${tables}`);
|
|
||||||
|
|
||||||
const haveWidgets = widgets && widgets.length;
|
|
||||||
const widgetCount = widgets ? widgets.length : 0;
|
const widgetCount = widgets ? widgets.length : 0;
|
||||||
|
|
||||||
// eslint-disable-next-line no-nested-ternary
|
// eslint-disable-next-line no-nested-ternary
|
||||||
const tileSizeLg = (widgetCount === 0 ? 3 : widgetCount === 1 ? 4 : 6);
|
const tileSizeLg = (widgetCount === 0 ? 3 : widgetCount === 1 ? 4 : 6);
|
||||||
|
|
||||||
|
const handleDropdownOnChange = (value: string, index: number) =>
|
||||||
|
{
|
||||||
|
alert(value);
|
||||||
|
|
||||||
|
setTimeout(async () =>
|
||||||
|
{
|
||||||
|
widgets[index] = await qController.widget(app.widgets[index]);
|
||||||
|
}, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<MDBox mt={4}>
|
<MDBox mt={4}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
{
|
{
|
||||||
widgetCount > 0 ? (
|
widgetCount > 0 ? (
|
||||||
<Grid item xs={12} lg={widgetCount === 1 ? 3 : 6}>
|
<Grid item xs={12} lg={12}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
|
{
|
||||||
|
widgets.map((chart, i) => (
|
||||||
|
<Grid key={`${i}`} item xs={12} lg={12}>
|
||||||
|
<MDBox mb={3}>
|
||||||
|
{
|
||||||
|
chart.type === "table" && (
|
||||||
|
<TableCard
|
||||||
|
color="info"
|
||||||
|
title={chart.title}
|
||||||
|
data={chart}
|
||||||
|
dropdownOptions={chart.dropdownOptions}
|
||||||
|
dropdownOnChange={handleDropdownOnChange}
|
||||||
|
widgetIndex={i}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</MDBox>
|
||||||
|
</Grid>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} lg={widgetCount === 1 ? 3 : 6}>
|
||||||
{
|
{
|
||||||
widgets.map((chart, i) => (
|
widgets.map((chart, i) => (
|
||||||
<Grid key={`${i}`} item xs={12} lg={widgetCount === 1 ? 12 : 6}>
|
<Grid key={`${i}`} item xs={12} lg={widgetCount === 1 ? 12 : 6}>
|
||||||
@ -192,7 +195,7 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
color="info"
|
color="info"
|
||||||
title={chart.title}
|
title={chart.title}
|
||||||
date={`As of ${new Date().toDateString()}`}
|
date={`As of ${new Date().toDateString()}`}
|
||||||
chart={chart.barChartData}
|
data={chart.chartData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -226,85 +229,86 @@ function AppHome({app}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
tables.length > 0 || processes.length > 0 || childApps.length > 0 ? (
|
app.sections ? (
|
||||||
// eslint-disable-next-line no-nested-ternary
|
|
||||||
<Grid item xs={12} lg={widgetCount === 0 ? 12 : widgetCount === 1 ? 9 : 6}>
|
<Grid item xs={12} lg={widgetCount === 0 ? 12 : widgetCount === 1 ? 9 : 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={tileSizeLg}>
|
|
||||||
<Link to={table.name}>
|
|
||||||
<MDBox mb={3}>
|
|
||||||
<MiniStatisticsCard
|
|
||||||
title={{fontWeight: "bold", text: table.label}}
|
|
||||||
count={!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "..." : tableCounts.get(table.name).value.toLocaleString()}
|
|
||||||
percentage={{color: "info", text: (!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "" : (tableCounts.get(table.name).value === 1 ? "total record" : "total records"))}}
|
|
||||||
icon={{color: "info", component: <Icon>{table.iconName || app.iconName}</Icon>}}
|
|
||||||
direction="right"
|
|
||||||
/>
|
|
||||||
</MDBox>
|
|
||||||
</Link>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Card>
|
|
||||||
</MDBox>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{app.sections.map((section) => (
|
||||||
processes.length ? (
|
<MDBox key={section.name} mb={3}>
|
||||||
<MDBox mb={3}>
|
<Card sx={{overflow: "visible"}}>
|
||||||
<Card id="basic-info" sx={{overflow: "visible"}}>
|
<MDBox p={3}>
|
||||||
<MDBox p={3}>
|
<MDTypography variant="h5">{section.label}</MDTypography>
|
||||||
<MDTypography variant="h5">Processes</MDTypography>
|
</MDBox>
|
||||||
</MDBox>
|
{
|
||||||
<Grid container spacing={3} padding={3} paddingTop={3}>
|
section.processes ? (
|
||||||
{processes.map((process) => (
|
<MDBox p={3} pl={5} pt={0}>
|
||||||
<Grid key={process.name} item xs={12} md={12} lg={tileSizeLg}>
|
<MDTypography variant="h6">Actions</MDTypography>
|
||||||
<Link to={process.name}>
|
</MDBox>
|
||||||
<ProcessLinkCard
|
) : null
|
||||||
icon={process.iconName || app.iconName}
|
}
|
||||||
title={process.label}
|
{
|
||||||
/>
|
section.processes ? (
|
||||||
</Link>
|
<Grid container spacing={3} padding={3} paddingTop={0}>
|
||||||
</Grid>
|
{
|
||||||
))}
|
section.processes.map((processName) =>
|
||||||
</Grid>
|
{
|
||||||
</Card>
|
let process = app.childMap.get(processName);
|
||||||
</MDBox>
|
return (
|
||||||
) : null
|
<Grid key={process.name} item xs={12} md={12} lg={tileSizeLg}>
|
||||||
}
|
<Link to={process.name}>
|
||||||
|
<ProcessLinkCard
|
||||||
{
|
icon={process.iconName || app.iconName}
|
||||||
childApps.length ? (
|
title={process.label}
|
||||||
<MDBox mb={3}>
|
/>
|
||||||
<Card id="basic-info" sx={{overflow: "visible"}}>
|
</Link>
|
||||||
<MDBox p={3}>
|
</Grid>
|
||||||
<MDTypography variant="h5">Apps</MDTypography>
|
);
|
||||||
</MDBox>
|
})
|
||||||
<Grid container spacing={3} padding={3} paddingTop={3}>
|
}
|
||||||
{childApps.map((childApp) => (
|
</Grid>
|
||||||
<Grid key={childApp.name} item xs={12} md={12} lg={tileSizeLg}>
|
) : null
|
||||||
<Link to={childApp.name}>
|
}
|
||||||
<DefaultInfoCard
|
{
|
||||||
icon={childApp.iconName || app.iconName}
|
section.processes && section.tables ? (
|
||||||
title={childApp.label}
|
<Divider />
|
||||||
/>
|
) : null
|
||||||
</Link>
|
}
|
||||||
</Grid>
|
{
|
||||||
))}
|
section.tables ? (
|
||||||
</Grid>
|
<MDBox p={3} pl={5} pb={0} pt={0}>
|
||||||
</Card>
|
<MDTypography variant="h6">Data</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
section.tables ? (
|
||||||
|
<Grid container spacing={3} padding={3} paddingBottom={0} paddingTop={0}>
|
||||||
|
{
|
||||||
|
section.tables.map((tableName) =>
|
||||||
|
{
|
||||||
|
let table = app.childMap.get(tableName);
|
||||||
|
return (
|
||||||
|
<Grid key={table.name} item xs={12} md={12} lg={tileSizeLg}>
|
||||||
|
<Link to={table.name}>
|
||||||
|
<MDBox mb={3}>
|
||||||
|
<MiniStatisticsCard
|
||||||
|
title={{fontWeight: "bold", text: table.label}}
|
||||||
|
count={!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "..." : tableCounts.get(table.name).value.toLocaleString()}
|
||||||
|
percentage={{color: "info", text: (!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "" : (tableCounts.get(table.name).value === 1 ? "total record" : "total records"))}}
|
||||||
|
icon={{color: "info", component: <Icon>{table.iconName || app.iconName}</Icon>}}
|
||||||
|
direction="right"
|
||||||
|
/>
|
||||||
|
</MDBox>
|
||||||
|
</Link>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Grid>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</Card>
|
||||||
|
</MDBox>
|
||||||
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
@ -19,35 +19,36 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Title} from "@mui/icons-material";
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import {Icon} from "@mui/material";
|
import {Icon} from "@mui/material";
|
||||||
import Card from "@mui/material/Card";
|
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Menu from "@mui/material/Menu";
|
import Menu from "@mui/material/Menu";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import React, {useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import DefaultLineChart from "examples/Charts/LineCharts/DefaultLineChart";
|
import DashboardLayout from "qqq/components/DashboardLayout";
|
||||||
import DashboardLayout from "examples/LayoutContainers/DashboardLayout";
|
|
||||||
import DataTable from "examples/Tables/DataTable";
|
|
||||||
import Footer from "qqq/components/Footer";
|
import Footer from "qqq/components/Footer";
|
||||||
import Navbar from "qqq/components/Navbar";
|
import Navbar from "qqq/components/Navbar";
|
||||||
import MDBadgeDot from "qqq/components/Temporary/MDBadgeDot";
|
import {TableDataInput} from "qqq/components/Temporary/DataTable";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
|
||||||
import ShipmentsByWarehouseTable from "qqq/pages/dashboards/Tables/ShipmentsByWarehouseTable";
|
import ShipmentsByWarehouseTable from "qqq/pages/dashboards/Tables/ShipmentsByWarehouseTable";
|
||||||
import carrierSpendData from "qqq/pages/dashboards/Widgets/Data/CarrierSpendData";
|
import {GenericChartData} from "qqq/pages/dashboards/Widgets/Data/GenericChartData";
|
||||||
import carrierVolumeLineChartData from "qqq/pages/dashboards/Widgets/Data/CarrierVolumeLineChartData";
|
|
||||||
import smallShipmentsByWarehouseData from "qqq/pages/dashboards/Widgets/Data/SmallShipmentsByWarehouseData";
|
import smallShipmentsByWarehouseData from "qqq/pages/dashboards/Widgets/Data/SmallShipmentsByWarehouseData";
|
||||||
import timeInTransitBarChartData from "qqq/pages/dashboards/Widgets/Data/TimeInTransitBarChartData";
|
import DefaultLineChart, {DefaultLineChartData} from "qqq/pages/dashboards/Widgets/DefaultLineChart";
|
||||||
import ShipmentsByCarrierPieChart from "qqq/pages/dashboards/Widgets/ShipmentsByChannelPieChart";
|
import HorizontalBarChart from "qqq/pages/dashboards/Widgets/HorizontalBarChart";
|
||||||
|
import {PieChartData} from "qqq/pages/dashboards/Widgets/PieChart";
|
||||||
|
import PieChartCard from "qqq/pages/dashboards/Widgets/PieChartCard";
|
||||||
import SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard";
|
import SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard";
|
||||||
import HorizontalBarChart from "./Widgets/HorizontalBarChart";
|
import {StatisticsCardData} from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
||||||
|
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
||||||
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
|
const qController = QClient.getInstance();
|
||||||
|
|
||||||
|
|
||||||
function CarrierPerformance(): JSX.Element
|
function CarrierPerformance(): JSX.Element
|
||||||
{
|
{
|
||||||
const openArrowIcon = "arrow_drop_down";
|
const openArrowIcon = "arrow_drop_down";
|
||||||
const closeArrowIcon = "arrow_drop_up";
|
const closeArrowIcon = "arrow_drop_up";
|
||||||
|
|
||||||
const [shipmentsDropdownValue, setShipmentsDropdownValue] = useState<string>("Last 30 Days");
|
const [shipmentsDropdownValue, setShipmentsDropdownValue] = useState<string>("Last 30 Days");
|
||||||
const [deliveriesDropdownValue, setDeliveriesDropdownValue] = useState<string>("Last 30 Days");
|
const [deliveriesDropdownValue, setDeliveriesDropdownValue] = useState<string>("Last 30 Days");
|
||||||
const [failuresDropdownValue, setFailuresDropdownValue] = useState<string>("Last 30 Days");
|
const [failuresDropdownValue, setFailuresDropdownValue] = useState<string>("Last 30 Days");
|
||||||
@ -64,7 +65,7 @@ function CarrierPerformance(): JSX.Element
|
|||||||
{
|
{
|
||||||
setShipmentsDropdown(currentTarget);
|
setShipmentsDropdown(currentTarget);
|
||||||
setShipmentsDropdownIcon(closeArrowIcon);
|
setShipmentsDropdownIcon(closeArrowIcon);
|
||||||
}
|
};
|
||||||
const closeShipmentsDropdown = ({currentTarget}: any) =>
|
const closeShipmentsDropdown = ({currentTarget}: any) =>
|
||||||
{
|
{
|
||||||
setShipmentsDropdown(null);
|
setShipmentsDropdown(null);
|
||||||
@ -74,8 +75,8 @@ function CarrierPerformance(): JSX.Element
|
|||||||
const openDeliveriesDropdown = ({currentTarget}: any) =>
|
const openDeliveriesDropdown = ({currentTarget}: any) =>
|
||||||
{
|
{
|
||||||
setDeliveriesDropdown(currentTarget);
|
setDeliveriesDropdown(currentTarget);
|
||||||
setDeliveriesDropdownIcon(closeArrowIcon)
|
setDeliveriesDropdownIcon(closeArrowIcon);
|
||||||
}
|
};
|
||||||
const closeDeliveriesDropdown = ({currentTarget}: any) =>
|
const closeDeliveriesDropdown = ({currentTarget}: any) =>
|
||||||
{
|
{
|
||||||
setDeliveriesDropdown(null);
|
setDeliveriesDropdown(null);
|
||||||
@ -85,8 +86,8 @@ function CarrierPerformance(): JSX.Element
|
|||||||
const openFailuresDropdown = ({currentTarget}: any) =>
|
const openFailuresDropdown = ({currentTarget}: any) =>
|
||||||
{
|
{
|
||||||
setFailuresDropdown(currentTarget);
|
setFailuresDropdown(currentTarget);
|
||||||
setFailuresDropdownIcon(closeArrowIcon)
|
setFailuresDropdownIcon(closeArrowIcon);
|
||||||
}
|
};
|
||||||
const closeFailuresDropdown = ({currentTarget}: any) =>
|
const closeFailuresDropdown = ({currentTarget}: any) =>
|
||||||
{
|
{
|
||||||
setFailuresDropdown(null);
|
setFailuresDropdown(null);
|
||||||
@ -94,7 +95,140 @@ function CarrierPerformance(): JSX.Element
|
|||||||
setFailuresDropdownIcon(openArrowIcon);
|
setFailuresDropdownIcon(openArrowIcon);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dropdown menu template for the DefaultStatisticsCard
|
|
||||||
|
const [totalShipmentsData, setTotalShipmentsData] = useState({} as StatisticsCardData);
|
||||||
|
const [successfulDeliveriesData, setSuccessfulDeliveriesData] = useState({} as StatisticsCardData);
|
||||||
|
const [serviceFailuresData, setServiceFailuresData] = useState({} as StatisticsCardData);
|
||||||
|
|
||||||
|
const [shipmentsByCarrierTitle, setShipmentsByCarrierTitle] = useState("");
|
||||||
|
const [shipmentsByCarrierDescription, setShipmentsByCarrierDescription] = useState("");
|
||||||
|
const [shipmentsByCarrierData, setShipmentsByCarrierData] = useState({} as PieChartData);
|
||||||
|
|
||||||
|
const [carrierVolumeTitle, setCarrierVolumeTitle] = useState("");
|
||||||
|
const [carrierVolumeData, setCarrierVolumeData] = useState({} as DefaultLineChartData);
|
||||||
|
|
||||||
|
const [spendByCarrierTitle, setSpendByCarrierTitle] = useState("");
|
||||||
|
const [spendByCarrierData, setSpendByCarrierData] = useState({columns: [], rows: []} as TableDataInput);
|
||||||
|
|
||||||
|
const [timeInTransitTitle, setTimeInTransitTitle] = useState("");
|
||||||
|
const [timeInTransitData, setTimeInTransitData] = useState({} as GenericChartData);
|
||||||
|
|
||||||
|
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||||
|
const [dataLoaded, setDataLoaded] = useState(false);
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// load meta data first //
|
||||||
|
//////////////////////////
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const newQInstance = await qController.loadMetaData();
|
||||||
|
setQInstance(newQInstance);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// once meta data has loaded, load widgets' data //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if (!qInstance || dataLoaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDataLoaded(true);
|
||||||
|
loadYTDShipmentsByCarrierData();
|
||||||
|
loadTotalShipmentsData();
|
||||||
|
loadSuccessfulDeliveriesData();
|
||||||
|
loadServiceFailuresData();
|
||||||
|
loadCarrierVolumeData();
|
||||||
|
loadSpendByCarrierData();
|
||||||
|
loadTimeInTransitData();
|
||||||
|
|
||||||
|
}, [qInstance]);
|
||||||
|
|
||||||
|
|
||||||
|
function loadTotalShipmentsData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("TotalShipmentsStatisticsCard");
|
||||||
|
setTotalShipmentsData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSuccessfulDeliveriesData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("SuccessfulDeliveriesStatisticsCard");
|
||||||
|
setSuccessfulDeliveriesData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadServiceFailuresData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("ServiceFailuresStatisticsCard");
|
||||||
|
setServiceFailuresData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCarrierVolumeData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("CarrierVolumeLineChart");
|
||||||
|
setCarrierVolumeTitle(widgetData.title);
|
||||||
|
setCarrierVolumeData(widgetData.chartData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadYTDShipmentsByCarrierData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("YTDShipmentsByCarrierPieChart");
|
||||||
|
setShipmentsByCarrierTitle(widgetData.title);
|
||||||
|
setShipmentsByCarrierDescription(widgetData.description);
|
||||||
|
setShipmentsByCarrierData(widgetData.chartData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSpendByCarrierData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("YTDSpendByCarrierTable");
|
||||||
|
setSpendByCarrierTitle(widgetData.title);
|
||||||
|
setSpendByCarrierData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTimeInTransitData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("TimeInTransitBarChart");
|
||||||
|
setTimeInTransitTitle(widgetData.title);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
// todo: need to make it so all charts can use multiple datasets //
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
const data = {
|
||||||
|
labels: widgetData.chartData.labels,
|
||||||
|
datasets: [
|
||||||
|
widgetData.chartData.dataset
|
||||||
|
]
|
||||||
|
};
|
||||||
|
setTimeInTransitData(data);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
const renderMenu = (state: any, open: any, close: any, icon: string) => (
|
const renderMenu = (state: any, open: any, close: any, icon: string) => (
|
||||||
<span style={{whiteSpace: "nowrap"}}>
|
<span style={{whiteSpace: "nowrap"}}>
|
||||||
<Icon onClick={open} fontSize={"medium"} style={{cursor: "pointer", float: "right"}}>{icon}</Icon>
|
<Icon onClick={open} fontSize={"medium"} style={{cursor: "pointer", float: "right"}}>{icon}</Icon>
|
||||||
@ -121,13 +255,8 @@ 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="total shipments"
|
data={totalShipmentsData}
|
||||||
count="50,234"
|
increaseIsGood={true}
|
||||||
percentage={{
|
|
||||||
color: "success",
|
|
||||||
value: "+5%",
|
|
||||||
label: "since last month",
|
|
||||||
}}
|
|
||||||
dropdown={{
|
dropdown={{
|
||||||
action: openShipmentsDropdown,
|
action: openShipmentsDropdown,
|
||||||
menu: renderMenu(shipmentsDropdown, openShipmentsDropdown, closeShipmentsDropdown, shipmentsDropdownIcon),
|
menu: renderMenu(shipmentsDropdown, openShipmentsDropdown, closeShipmentsDropdown, shipmentsDropdownIcon),
|
||||||
@ -137,29 +266,20 @@ function CarrierPerformance(): JSX.Element
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<SimpleStatisticsCard
|
<SimpleStatisticsCard
|
||||||
title="Successful deliveries"
|
data={successfulDeliveriesData}
|
||||||
count="49,234"
|
increaseIsGood={true}
|
||||||
percentage={{
|
|
||||||
color: "success",
|
|
||||||
value: "+12%",
|
|
||||||
label: "since last month",
|
|
||||||
}}
|
|
||||||
dropdown={{
|
dropdown={{
|
||||||
action: openDeliveriesDropdown,
|
action: openDeliveriesDropdown,
|
||||||
menu: renderMenu(deliveriesDropdown, openDeliveriesDropdown, closeDeliveriesDropdown, deliveriesDropdownIcon),
|
menu: renderMenu(deliveriesDropdown, openDeliveriesDropdown, closeDeliveriesDropdown, deliveriesDropdownIcon),
|
||||||
value: deliveriesDropdownValue,
|
value: deliveriesDropdownValue,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<SimpleStatisticsCard
|
<SimpleStatisticsCard
|
||||||
title="service failures"
|
data={serviceFailuresData}
|
||||||
count="832"
|
increaseIsGood={false}
|
||||||
percentage={{
|
|
||||||
color: "error",
|
|
||||||
value: "+1.2%",
|
|
||||||
label: "since last month",
|
|
||||||
}}
|
|
||||||
dropdown={{
|
dropdown={{
|
||||||
action: openFailuresDropdown,
|
action: openFailuresDropdown,
|
||||||
menu: renderMenu(failuresDropdown, openFailuresDropdown, closeFailuresDropdown, failuresDropdownIcon),
|
menu: renderMenu(failuresDropdown, openFailuresDropdown, closeFailuresDropdown, failuresDropdownIcon),
|
||||||
@ -171,54 +291,35 @@ function CarrierPerformance(): JSX.Element
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} sm={6} lg={4}>
|
|
||||||
<ShipmentsByCarrierPieChart />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={6} lg={8}>
|
<Grid item xs={12} sm={6} lg={8}>
|
||||||
<DefaultLineChart
|
<DefaultLineChart
|
||||||
title="Carrier Volume by Month"
|
title={carrierVolumeTitle}
|
||||||
description={
|
data={carrierVolumeData}
|
||||||
<MDBox display="flex" justifyContent="space-between">
|
|
||||||
<MDBox display="flex" ml={-1}>
|
|
||||||
<MDBadgeDot color="dark" size="sm" badgeContent="AxleHire" />
|
|
||||||
<MDBadgeDot color="info" size="sm" badgeContent="CDL" />
|
|
||||||
<MDBadgeDot color="primary" size="sm" badgeContent="DHL" />
|
|
||||||
<MDBadgeDot color="success" size="sm" badgeContent="FedEx" />
|
|
||||||
<MDBadgeDot color="error" size="sm" badgeContent="LSO" />
|
|
||||||
<MDBadgeDot color="secondary" size="sm" badgeContent="OnTrac" />
|
|
||||||
<MDBadgeDot color="warning" size="sm" badgeContent="UPS" />
|
|
||||||
</MDBox>
|
|
||||||
</MDBox>
|
|
||||||
}
|
|
||||||
chart={carrierVolumeLineChartData}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} md={6} lg={4}>
|
||||||
|
<MDBox mb={3}>
|
||||||
|
<PieChartCard
|
||||||
|
title={shipmentsByCarrierTitle}
|
||||||
|
description={shipmentsByCarrierDescription}
|
||||||
|
data={shipmentsByCarrierData}
|
||||||
|
/>
|
||||||
|
</MDBox>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<Grid container spacing={3} mb={3}>
|
<Grid container spacing={3} mb={3}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Card>
|
<TableCard
|
||||||
<MDBox pt={3} px={3}>
|
title={spendByCarrierTitle}
|
||||||
<MDTypography variant="h6" fontWeight="medium">
|
data={spendByCarrierData}
|
||||||
Spend by Carrier YTD
|
/>
|
||||||
</MDTypography>
|
|
||||||
</MDBox>
|
|
||||||
<MDBox py={1}>
|
|
||||||
<DataTable
|
|
||||||
table={carrierSpendData}
|
|
||||||
entriesPerPage={false}
|
|
||||||
showTotalEntries={false}
|
|
||||||
isSorted={false}
|
|
||||||
noEndBorder
|
|
||||||
/>
|
|
||||||
</MDBox>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} lg={8}>
|
<Grid item xs={12} lg={8}>
|
||||||
<HorizontalBarChart title="Time in Transit Last 30 Days" chart={timeInTransitBarChartData} />
|
<HorizontalBarChart height={250} title={timeInTransitTitle} data={timeInTransitData} />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} lg={4}>
|
<Grid item xs={12} lg={4}>
|
||||||
<ShipmentsByWarehouseTable title="Shipments by Warehouse" rows={smallShipmentsByWarehouseData} />
|
<ShipmentsByWarehouseTable title="Shipments by Warehouse" rows={smallShipmentsByWarehouseData} />
|
||||||
|
@ -19,28 +19,215 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
import DashboardLayout from "qqq/components/DashboardLayout";
|
import DashboardLayout from "qqq/components/DashboardLayout";
|
||||||
import Footer from "qqq/components/Footer";
|
import Footer from "qqq/components/Footer";
|
||||||
import Navbar from "qqq/components/Navbar";
|
import Navbar from "qqq/components/Navbar";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import edisonWarehouse from "qqq/images/warehouses/edison_nj.jpg";
|
|
||||||
import pattersonWarehouse from "qqq/images/warehouses/patterson.jpg";
|
|
||||||
import stocktonWarehouse from "qqq/images/warehouses/stockton.jpg";
|
|
||||||
import BarChart from "qqq/pages/dashboards/Widgets/BarChart";
|
import BarChart from "qqq/pages/dashboards/Widgets/BarChart";
|
||||||
import shipmentsByDayBarChartData from "qqq/pages/dashboards/Widgets/Data/ShipmentsByDayBarChartData";
|
import {GenericChartDataSingleDataset} from "qqq/pages/dashboards/Widgets/Data/GenericChartDataSingleDataset";
|
||||||
import shipmentsByMonthLineChartData from "qqq/pages/dashboards/Widgets/Data/ShipmentsByMonthLineChartData";
|
import LocationCard, {LocationCardData} from "qqq/pages/dashboards/Widgets/LocationCard";
|
||||||
import ShipmentsByCarrierPieChart from "qqq/pages/dashboards/Widgets/ShipmentsByChannelPieChart";
|
import {PieChartData} from "qqq/pages/dashboards/Widgets/PieChart";
|
||||||
|
import PieChartCard from "qqq/pages/dashboards/Widgets/PieChartCard";
|
||||||
import ShipmentsByWarehouse from "qqq/pages/dashboards/Widgets/ShipmentsByWarehouse";
|
import ShipmentsByWarehouse from "qqq/pages/dashboards/Widgets/ShipmentsByWarehouse";
|
||||||
import SmallLineChart from "qqq/pages/dashboards/Widgets/SmallLineChart";
|
import SmallLineChart from "qqq/pages/dashboards/Widgets/SmallLineChart";
|
||||||
import StatisticsCard from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
import StatisticsCard, {StatisticsCardData} from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
||||||
import WarehouseCard from "qqq/pages/dashboards/Widgets/WarehouseCard";
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
|
const qController = QClient.getInstance();
|
||||||
|
|
||||||
function Overview(): JSX.Element
|
function Overview(): JSX.Element
|
||||||
{
|
{
|
||||||
|
//////////////////////////////////
|
||||||
|
// shipments by day widget data //
|
||||||
|
//////////////////////////////////
|
||||||
|
const [shipmentsByDayTitle, setShipmentsByDayTitle] = useState("");
|
||||||
|
const [shipmentsByDayDescription, setShipmentsByDayDescription] = useState("");
|
||||||
|
const [shipmentsByDayData, setShipmentsByDayData] = useState({} as GenericChartDataSingleDataset);
|
||||||
|
|
||||||
|
const [shipmentsByMonthTitle, setShipmentsByMonthTitle] = useState("");
|
||||||
|
const [shipmentsByMonthDescription, setShipmentsByMonthDescription] = useState("");
|
||||||
|
const [shipmentsByMonthData, setShipmentsByMonthData] = useState({} as GenericChartDataSingleDataset);
|
||||||
|
|
||||||
|
const [shipmentsByCarrierTitle, setShipmentsByCarrierTitle] = useState("");
|
||||||
|
const [shipmentsByCarrierDescription, setShipmentsByCarrierDescription] = useState("");
|
||||||
|
const [shipmentsByCarrierData, setShipmentsByCarrierData] = useState({} as PieChartData);
|
||||||
|
|
||||||
|
const [todaysShipmentsData, setTodaysShipmentsData] = useState({} as StatisticsCardData);
|
||||||
|
const [shipmentsInTransitData, setShipmentsInTransitData] = useState({} as StatisticsCardData);
|
||||||
|
const [openOrdersData, setOpenOrdersData] = useState({} as StatisticsCardData);
|
||||||
|
const [shippingExceptionsData, setShippingExceptionsData] = useState({} as StatisticsCardData);
|
||||||
|
|
||||||
|
const [warehouseData, setWarehouseData] = useState([] as LocationCardData[]);
|
||||||
|
|
||||||
|
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// load meta data first //
|
||||||
|
//////////////////////////
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const newQInstance = await qController.loadMetaData();
|
||||||
|
setQInstance(newQInstance);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// once meta data has loaded, load widgets' data //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if (!qInstance)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadShipmentsByDayData();
|
||||||
|
loadShipmentsByMonthData();
|
||||||
|
loadYTDShipmentsByCarrierData();
|
||||||
|
|
||||||
|
loadTodaysShipmentsData();
|
||||||
|
loadShipmentsInTransitData();
|
||||||
|
loadOpenOrdersData();
|
||||||
|
loadShippingExceptionsData();
|
||||||
|
|
||||||
|
loadWarehouseData();
|
||||||
|
|
||||||
|
}, [qInstance]);
|
||||||
|
|
||||||
|
|
||||||
|
function loadShipmentsByDayData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("TotalShipmentsByDayBarChart");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// calculate average and number of days over that average //
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
let dataValues = widgetData.chartData.dataset.data;
|
||||||
|
let totalShipments = 0;
|
||||||
|
for (let i = 0; i < dataValues.length; i++)
|
||||||
|
{
|
||||||
|
totalShipments += dataValues[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let daysOverAverage = 0;
|
||||||
|
let average = Math.floor(totalShipments / 7);
|
||||||
|
for (let i = 0; i < dataValues.length; i++)
|
||||||
|
{
|
||||||
|
if (dataValues[i] > average)
|
||||||
|
{
|
||||||
|
daysOverAverage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = "Over the last week there have been <strong>" + daysOverAverage.toLocaleString() + (daysOverAverage == 1 ? " day" : " days") + "</strong> with total shipments greater than the daily average of <strong>" + average.toLocaleString() + " shipments</strong>.";
|
||||||
|
setShipmentsByDayTitle(widgetData.title);
|
||||||
|
setShipmentsByDayData(widgetData.chartData);
|
||||||
|
setShipmentsByDayDescription(description);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadShipmentsByMonthData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("TotalShipmentsByMonthLineChart");
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
// calculate if 'increasing or decreasing' //
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
let dataValues = widgetData.chartData.dataset.data;
|
||||||
|
let firstHalf = 0;
|
||||||
|
let secondHalf = 0;
|
||||||
|
for (let i = 0; i < dataValues.length; i++)
|
||||||
|
{
|
||||||
|
if (i < dataValues.length / 2)
|
||||||
|
{
|
||||||
|
firstHalf += dataValues[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
secondHalf += dataValues[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = "Total shipments have been <strong>" + ((secondHalf >= firstHalf) ? "increasing" : "decreasing") + "</strong> over the last eight months.";
|
||||||
|
setShipmentsByMonthTitle(widgetData.title);
|
||||||
|
setShipmentsByMonthDescription(description);
|
||||||
|
setShipmentsByMonthData(widgetData.chartData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadYTDShipmentsByCarrierData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("YTDShipmentsByCarrierPieChart");
|
||||||
|
setShipmentsByCarrierTitle(widgetData.title);
|
||||||
|
setShipmentsByCarrierDescription(widgetData.description);
|
||||||
|
setShipmentsByCarrierData(widgetData.chartData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadTodaysShipmentsData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("TodaysShipmentsStatisticsCard");
|
||||||
|
setTodaysShipmentsData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadShipmentsInTransitData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("ShipmentsInTransitStatisticsCard");
|
||||||
|
setShipmentsInTransitData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadOpenOrdersData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("OpenOrdersStatisticsCard");
|
||||||
|
setOpenOrdersData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadShippingExceptionsData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("ShippingExceptionsStatisticsCard");
|
||||||
|
setShippingExceptionsData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadWarehouseData()
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const widgetData = await qController.widget("WarehouseLocationCards");
|
||||||
|
setWarehouseData(widgetData);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const actionButtons = (
|
const actionButtons = (
|
||||||
<>
|
<>
|
||||||
<Tooltip title="Refresh" placement="bottom">
|
<Tooltip title="Refresh" placement="bottom">
|
||||||
@ -74,30 +261,30 @@ function Overview(): JSX.Element
|
|||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<BarChart
|
<BarChart
|
||||||
color="info"
|
color="info"
|
||||||
title="Total Shipments by Day"
|
title={shipmentsByDayTitle}
|
||||||
description={
|
description={shipmentsByDayDescription}
|
||||||
<span>Over the last week there have been <strong>3 days</strong> with total shipments <strong>greater than</strong> the daily average of <strong>564 shipments</strong>.</span>
|
|
||||||
}
|
|
||||||
date="Updated 3 minutes ago"
|
date="Updated 3 minutes ago"
|
||||||
chart={shipmentsByDayBarChartData}
|
data={shipmentsByDayData}
|
||||||
/>
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={6} lg={4}>
|
<Grid item xs={12} md={6} lg={4}>
|
||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<ShipmentsByCarrierPieChart />
|
<PieChartCard
|
||||||
|
title={shipmentsByCarrierTitle}
|
||||||
|
description={shipmentsByCarrierDescription}
|
||||||
|
data={shipmentsByCarrierData}
|
||||||
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={6} lg={4}>
|
<Grid item xs={12} md={6} lg={4}>
|
||||||
<MDBox mb={3}>
|
<MDBox mb={3}>
|
||||||
<SmallLineChart
|
<SmallLineChart
|
||||||
color="dark"
|
color="dark"
|
||||||
title="shipments by month"
|
title={shipmentsByMonthTitle}
|
||||||
description={
|
description={shipmentsByMonthDescription}
|
||||||
<span>Total shipments have been <strong>increasing</strong> over the last eight months.</span>
|
date="Just updatederp"
|
||||||
}
|
chart={shipmentsByMonthData}
|
||||||
date="Just updated"
|
|
||||||
chart={shipmentsByMonthLineChartData}
|
|
||||||
/>
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -109,13 +296,8 @@ function Overview(): JSX.Element
|
|||||||
<MDBox mb={1.5}>
|
<MDBox mb={1.5}>
|
||||||
<StatisticsCard
|
<StatisticsCard
|
||||||
icon="widgets"
|
icon="widgets"
|
||||||
title="Today's Shipments"
|
data={todaysShipmentsData}
|
||||||
count="2,813"
|
increaseIsGood={true}
|
||||||
percentage={{
|
|
||||||
color: "success",
|
|
||||||
amount: "+15%",
|
|
||||||
label: "than lask week",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -123,13 +305,8 @@ function Overview(): JSX.Element
|
|||||||
<MDBox mb={1.5}>
|
<MDBox mb={1.5}>
|
||||||
<StatisticsCard
|
<StatisticsCard
|
||||||
icon="local_shipping"
|
icon="local_shipping"
|
||||||
title="Shipments In Transit"
|
data={shipmentsInTransitData}
|
||||||
count="1,023"
|
increaseIsGood={true}
|
||||||
percentage={{
|
|
||||||
color: "success",
|
|
||||||
amount: "+1%",
|
|
||||||
label: "than yesterday",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -138,13 +315,8 @@ function Overview(): JSX.Element
|
|||||||
<StatisticsCard
|
<StatisticsCard
|
||||||
color="warning"
|
color="warning"
|
||||||
icon="receipt"
|
icon="receipt"
|
||||||
title="Open Orders"
|
data={openOrdersData}
|
||||||
count="213"
|
increaseIsGood={true}
|
||||||
percentage={{
|
|
||||||
color: "error",
|
|
||||||
amount: "+3%",
|
|
||||||
label: "than last week",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -153,13 +325,8 @@ function Overview(): JSX.Element
|
|||||||
<StatisticsCard
|
<StatisticsCard
|
||||||
color="error"
|
color="error"
|
||||||
icon="error"
|
icon="error"
|
||||||
title="Shipping Exceptions"
|
data={shippingExceptionsData}
|
||||||
count="28"
|
increaseIsGood={false}
|
||||||
percentage={{
|
|
||||||
color: "success",
|
|
||||||
amount: "-12%",
|
|
||||||
label: "than yesterday",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -167,48 +334,18 @@ function Overview(): JSX.Element
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox mt={2}>
|
<MDBox mt={2}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} md={6} lg={4}>
|
{
|
||||||
<MDBox mt={3}>
|
warehouseData && warehouseData.map((data) => (
|
||||||
<WarehouseCard
|
<Grid item xs={12} md={6} lg={4} key={data.title}>
|
||||||
image={edisonWarehouse}
|
<MDBox mt={3}>
|
||||||
title="Edison, NJ"
|
<LocationCard
|
||||||
description={
|
locationData={data}
|
||||||
<span>The Edison, NJ warehouse currently has <strong>38 open orders</strong> and <strong>39 ASNs</strong> are expected in the next week.</span>
|
action={actionButtons}
|
||||||
}
|
/>
|
||||||
price="99% SLA"
|
</MDBox>
|
||||||
location="Edison, NJ"
|
</Grid>
|
||||||
action={actionButtons}
|
))
|
||||||
/>
|
}
|
||||||
</MDBox>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={6} lg={4}>
|
|
||||||
<MDBox mt={3}>
|
|
||||||
<WarehouseCard
|
|
||||||
image={pattersonWarehouse}
|
|
||||||
title="Patterson, CA"
|
|
||||||
description={
|
|
||||||
<span>The Patterson, CA warehouse shipped <strong>32,032</strong> this year. The delivery SLA is <strong>97.3%</strong>, up <strong>0.8%</strong> from last week.</span>
|
|
||||||
}
|
|
||||||
price="98% SLA"
|
|
||||||
location="Patterson, CA"
|
|
||||||
action={actionButtons}
|
|
||||||
/>
|
|
||||||
</MDBox>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={6} lg={4}>
|
|
||||||
<MDBox mt={3}>
|
|
||||||
<WarehouseCard
|
|
||||||
image={stocktonWarehouse}
|
|
||||||
title="Stockton, CA"
|
|
||||||
description={
|
|
||||||
<span>The Stockton, CA warehouse shipped <strong>2,032</strong> packages yesterday. Last week's failed shipments were down by <strong>12%</strong>.</span>
|
|
||||||
}
|
|
||||||
price="95% SLA"
|
|
||||||
location="Stockton, CA"
|
|
||||||
action={actionButtons}
|
|
||||||
/>
|
|
||||||
</MDBox>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
|
57
src/qqq/pages/dashboards/Tables/ImageCell.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Declaring props types for ProductCell
|
||||||
|
import MDAvatar from "qqq/components/Temporary/MDAvatar";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
imageUrl: string;
|
||||||
|
label: string;
|
||||||
|
total?: string | number;
|
||||||
|
totalType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImageCell({imageUrl, label, total, totalType}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MDBox display="flex" alignItems="center" pr={2}>
|
||||||
|
<MDBox mr={2}>
|
||||||
|
<MDAvatar src={imageUrl} alt={label} />
|
||||||
|
</MDBox>
|
||||||
|
<MDBox display="flex" flexDirection="column">
|
||||||
|
<MDTypography variant="button" fontWeight="medium">
|
||||||
|
{label}
|
||||||
|
</MDTypography>
|
||||||
|
<MDTypography variant="button" fontWeight="regular" color="secondary">
|
||||||
|
<MDTypography component="span" variant="button" fontWeight="regular" color="success">
|
||||||
|
{total}
|
||||||
|
</MDTypography>{" "}
|
||||||
|
{totalType}
|
||||||
|
</MDTypography>
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageCell;
|
@ -22,31 +22,124 @@
|
|||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {useMemo, ReactNode} from "react";
|
import parse from "html-react-parser";
|
||||||
|
import {useMemo} from "react";
|
||||||
import {Bar} from "react-chartjs-2";
|
import {Bar} from "react-chartjs-2";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import configs from "qqq/pages/dashboards/Widgets/Configs/BarChartConfig";
|
import {GenericChartDataSingleDataset} from "qqq/pages/dashboards/Widgets/Data/GenericChartDataSingleDataset";
|
||||||
|
|
||||||
// Declaring props types for ReportsBarChart
|
|
||||||
interface Props {
|
///////////////////////////
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark";
|
// options for bar chart //
|
||||||
title: string;
|
///////////////////////////
|
||||||
description?: string | ReactNode;
|
const options = {
|
||||||
date: string;
|
responsive: true,
|
||||||
chart: {
|
maintainAspectRatio: false,
|
||||||
labels: string[];
|
plugins: {
|
||||||
datasets: {
|
legend: {
|
||||||
label: string;
|
display: false,
|
||||||
data: number[];
|
},
|
||||||
};
|
},
|
||||||
};
|
interaction: {
|
||||||
[key: string]: any;
|
intersect: false,
|
||||||
|
mode: "index",
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: true,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
color: "rgba(255, 255, 255, .2)",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
suggestedMin: 0,
|
||||||
|
suggestedMax: 500,
|
||||||
|
beginAtZero: true,
|
||||||
|
padding: 10,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: true,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
color: "rgba(255, 255, 255, .2)",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
color: "#f8f9fa",
|
||||||
|
padding: 10,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// define properties and defaults //
|
||||||
|
////////////////////////////////////
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark";
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
date: string;
|
||||||
|
data: GenericChartDataSingleDataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
function BarChart({color, title, description, date, chart}: Props): JSX.Element
|
BarChart.defaultProps = {
|
||||||
|
color: "dark",
|
||||||
|
description: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
function getChartData(labels: any, dataset: any)
|
||||||
{
|
{
|
||||||
const {data, options} = configs(chart.labels || [], chart.datasets || {});
|
return {
|
||||||
|
chartData: {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: dataset?.label,
|
||||||
|
tension: 0.4,
|
||||||
|
borderWidth: 0,
|
||||||
|
borderRadius: 4,
|
||||||
|
borderSkipped: false,
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
data: dataset?.data,
|
||||||
|
maxBarThickness: 6,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function BarChart({color, title, description, date, data}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
// enrich data with expected customizations and styles //
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
const {chartData} = getChartData(data?.labels, data?.dataset);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{height: "100%"}}>
|
<Card sx={{height: "100%"}}>
|
||||||
@ -63,17 +156,17 @@ function BarChart({color, title, description, date, chart}: Props): JSX.Element
|
|||||||
mt={-5}
|
mt={-5}
|
||||||
height="12.5rem"
|
height="12.5rem"
|
||||||
>
|
>
|
||||||
<Bar data={data} options={options} />
|
<Bar data={chartData} options={options} />
|
||||||
</MDBox>
|
</MDBox>
|
||||||
),
|
),
|
||||||
[chart, color]
|
[data, color]
|
||||||
)}
|
)}
|
||||||
<MDBox pt={3} pb={1} px={1}>
|
<MDBox pt={3} pb={1} px={1}>
|
||||||
<MDTypography variant="h6" textTransform="capitalize">
|
<MDTypography variant="h6" textTransform="capitalize">
|
||||||
{title}
|
{title}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
||||||
{description}
|
{parse(description)}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MDBox display="flex" alignItems="center">
|
<MDBox display="flex" alignItems="center">
|
||||||
@ -90,10 +183,4 @@ function BarChart({color, title, description, date, chart}: Props): JSX.Element
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting default values for the props of ReportsBarChart
|
|
||||||
BarChart.defaultProps = {
|
|
||||||
color: "dark",
|
|
||||||
description: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BarChart;
|
export default BarChart;
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function configs(labels: any, datasets: any)
|
function configs(labels: any, datasets: any)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
data: {
|
dataderp: {
|
||||||
labels,
|
labels,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 DefaultCell from "layouts/dashboards/sales/components/DefaultCell";
|
|
||||||
import ProductCell from "layouts/dashboards/sales/components/ProductCell";
|
|
||||||
import RefundsCell from "layouts/dashboards/sales/components/RefundsCell";
|
|
||||||
import axlehire from "qqq/images/carrier-logos/axlehire.png"
|
|
||||||
import cdl from "qqq/images/carrier-logos/cdl.png"
|
|
||||||
import dhl from "qqq/images/carrier-logos/dhl.png"
|
|
||||||
import fedex from "qqq/images/carrier-logos/fedex.png"
|
|
||||||
import lso from "qqq/images/carrier-logos/lso.png"
|
|
||||||
import ontrac from "qqq/images/carrier-logos/ontrac.png"
|
|
||||||
import ups from "qqq/images/carrier-logos/ups.png"
|
|
||||||
|
|
||||||
const carrierSpendData = {
|
|
||||||
columns: [
|
|
||||||
{Header: "carrier", accessor: "product", width: "55%"},
|
|
||||||
{Header: "total YTD", accessor: "value"},
|
|
||||||
{Header: "monthly average", accessor: "adsSpent", align: "center"},
|
|
||||||
{Header: "service failures", accessor: "refunds", align: "center"},
|
|
||||||
],
|
|
||||||
|
|
||||||
rows: [
|
|
||||||
{
|
|
||||||
product: <ProductCell image={axlehire} name="AxleHire" orders="921" />,
|
|
||||||
value: <DefaultCell>$140,925</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$24,531</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={121} icon={{color: "success", name: "keyboard_arrow_up"}} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
product: <ProductCell image={cdl} name="CDL" orders="2,421" />,
|
|
||||||
value: <DefaultCell>$40,600</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$9,430</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={54} icon={{color: "success", name: "keyboard_arrow_up"}} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
product: <ProductCell image={dhl} name="DHL" orders="1,391" />,
|
|
||||||
value: <DefaultCell>$90,233</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$18.30</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={54} icon={{color: "success", name: "keyboard_arrow_up"}} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
product: <ProductCell image={fedex} name="FedEx" orders="12,821" />,
|
|
||||||
value: <DefaultCell>$80,250</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$4,200</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={40} icon={{color: "error", name: "keyboard_arrow_down"}} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
product: <ProductCell image={lso} name="LSO" orders="5,921" />,
|
|
||||||
value: <DefaultCell>$91,300</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$7,364</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={5} icon={{color: "error", name: "keyboard_arrow_down"}} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
product: <ProductCell image={ontrac} name="OnTrac" orders="5,921" />,
|
|
||||||
value: <DefaultCell>$77,300</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$4,064</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={5} icon={{color: "error", name: "keyboard_arrow_down"}} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
product: <ProductCell image={ups} name="UPS" orders="8,232" />,
|
|
||||||
value: <DefaultCell>$130,992</DefaultCell>,
|
|
||||||
adsSpent: <DefaultCell>$9,500</DefaultCell>,
|
|
||||||
refunds: <RefundsCell value={13} icon={{color: "success", name: "keyboard_arrow_up"}} />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default carrierSpendData;
|
|
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface Types {
|
|
||||||
labels: string[];
|
|
||||||
datasets: {
|
|
||||||
label: string;
|
|
||||||
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
|
||||||
data: number[];
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const carrierVolumeLineChartData: Types = {
|
|
||||||
labels: ["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: " AxleHire",
|
|
||||||
color: "dark",
|
|
||||||
data: [500, 200, 110, 150, 440, 670, 100, 150, 300],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: " CDL",
|
|
||||||
color: "info",
|
|
||||||
data: [1000, 3000, 4000, 1200, 1500, 2200, 2800, 3500, 4500],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: " DHL",
|
|
||||||
color: "primary",
|
|
||||||
data: [3489, 5932, 4332, 8234, 9239, 10823, 9483, 11909, 11808],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: " FedEx",
|
|
||||||
color: "success",
|
|
||||||
data: [20388, 21008, 19323, 17934, 18399, 22090, 23909, 25800, 28833],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: " LSO",
|
|
||||||
color: "error",
|
|
||||||
data: [100, 300, 400, 1200, 1500, 2200, 2800, 2500, 2800],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: " OnTrac",
|
|
||||||
color: "secondary",
|
|
||||||
data: [3489, 5932, 4332, 8234, 9239, 10823, 9483, 11909, 11808],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: " UPS",
|
|
||||||
color: "warning",
|
|
||||||
data: [19348, 18008, 20844, 16034, 24000, 23480, 26809, 27888, 27909],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
"warning"
|
|
||||||
|
|
||||||
|
|
||||||
export default carrierVolumeLineChartData;
|
|
@ -19,9 +19,17 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const shipmentsByDayBarChartData = {
|
|
||||||
labels: ["M", "T", "W", "T", "F", "S", "S"],
|
|
||||||
datasets: {label: "Sales", data: [503, 202, 1001, 354, 659, 938, 350]},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default shipmentsByDayBarChartData;
|
//////////////////////////////////////
|
||||||
|
// structure of expected chart data //
|
||||||
|
//////////////////////////////////////
|
||||||
|
export interface GenericChartData
|
||||||
|
{
|
||||||
|
description?: string;
|
||||||
|
labels: string[];
|
||||||
|
datasets: {
|
||||||
|
label: string;
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
data: number[];
|
||||||
|
}[];
|
||||||
|
}
|
@ -19,10 +19,16 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const shipmentsByMonthLineChartData =
|
|
||||||
{
|
|
||||||
labels: ["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
|
||||||
datasets: {label: "Mobile apps", data: [50, 40, 300, 320, 500, 350, 200, 230, 500]},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default shipmentsByMonthLineChartData;
|
//////////////////////////////////////
|
||||||
|
// structure of expected chart data //
|
||||||
|
//////////////////////////////////////
|
||||||
|
export interface GenericChartDataSingleDataset
|
||||||
|
{
|
||||||
|
labels: string[];
|
||||||
|
dataset: {
|
||||||
|
label: string;
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
data: number[];
|
||||||
|
};
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 MDBadgeDot from "components/MDBadgeDot";
|
|
||||||
import MDBox from "components/MDBox";
|
|
||||||
|
|
||||||
const shipmentsByCarrierPieChartData = {
|
|
||||||
labels: [" AxleHire", " CDL", " DHL", " FedEx", " LSO", " OnTrac", " UPS"],
|
|
||||||
datasets: {
|
|
||||||
label: "Projects",
|
|
||||||
backgroundColors: ["dark", "info", "primary", "success", "error", "secondary", "warning"],
|
|
||||||
data: [523, 1139, 1933, 3248, 993, 103, 2439]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default shipmentsByCarrierPieChartData;
|
|
230
src/qqq/pages/dashboards/Widgets/DefaultLineChart.tsx
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* 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 Card from "@mui/material/Card";
|
||||||
|
import Icon from "@mui/material/Icon";
|
||||||
|
import React, {ReactNode, useMemo} from "react";
|
||||||
|
import {Line} from "react-chartjs-2";
|
||||||
|
import colors from "assets/theme/base/colors";
|
||||||
|
import MDBadgeDot from "qqq/components/Temporary/MDBadgeDot";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// structure of default line chart data //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
export interface DefaultLineChartData
|
||||||
|
{
|
||||||
|
labels: string[];
|
||||||
|
lineLabels?: string[];
|
||||||
|
datasets: {
|
||||||
|
label: string;
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
data: number[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// display options //
|
||||||
|
/////////////////////
|
||||||
|
const options = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
mode: "index",
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: true,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
color: "#c1c4ce5c",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
padding: 10,
|
||||||
|
color: "#9ca2b7",
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: true,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: true,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
color: "#c1c4ce5c",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
color: "#9ca2b7",
|
||||||
|
padding: 10,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// inputs and defaults //
|
||||||
|
/////////////////////////
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
icon?: {
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
component: ReactNode;
|
||||||
|
};
|
||||||
|
title?: string;
|
||||||
|
height?: string | number;
|
||||||
|
data: DefaultLineChartData;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLineChart.defaultProps = {
|
||||||
|
icon: {color: "info", component: ""},
|
||||||
|
title: "",
|
||||||
|
height: "19.125rem",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function DefaultLineChart({icon, title, height, data}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
const allBackgroundColors = ["info", "warning", "primary", "success", "error", "secondary", "dark"];
|
||||||
|
if (data && data.datasets)
|
||||||
|
{
|
||||||
|
data.datasets.forEach((ds, index) =>
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
ds.color = allBackgroundColors[index];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartDatasets = data && data.datasets
|
||||||
|
? data.datasets.map((dataset) => ({
|
||||||
|
...dataset,
|
||||||
|
tension: 0,
|
||||||
|
pointRadius: 3,
|
||||||
|
borderWidth: 4,
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
fill: true,
|
||||||
|
pointBackgroundColor: colors[dataset.color]
|
||||||
|
? colors[dataset.color || "dark"].main
|
||||||
|
: colors.dark.main,
|
||||||
|
borderColor: colors[dataset.color]
|
||||||
|
? colors[dataset.color || "dark"].main
|
||||||
|
: colors.dark.main,
|
||||||
|
maxBarThickness: 6,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
let fullData = {};
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
fullData = {
|
||||||
|
labels: data.labels,
|
||||||
|
datasets: chartDatasets
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderChart = (
|
||||||
|
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
||||||
|
{title ? (
|
||||||
|
<MDBox display="flex" px={0} pt={0}>
|
||||||
|
{icon.component && (
|
||||||
|
<MDBox
|
||||||
|
width="4rem"
|
||||||
|
height="4rem"
|
||||||
|
bgColor={icon.color || "info"}
|
||||||
|
variant="gradient"
|
||||||
|
coloredShadow={icon.color || "info"}
|
||||||
|
borderRadius="xl"
|
||||||
|
display="flex"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
color="white"
|
||||||
|
mt={-5}
|
||||||
|
mr={2}
|
||||||
|
>
|
||||||
|
<Icon fontSize="medium">{icon.component}</Icon>
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
<MDBox mt={icon.component ? -2 : 0}>
|
||||||
|
{title && <MDTypography variant="h6">{title}</MDTypography>}
|
||||||
|
<MDBox mb={2}>
|
||||||
|
<MDTypography component="div" variant="button" color="text">
|
||||||
|
<MDBox display="flex" justifyContent="space-between">
|
||||||
|
<MDBox display="flex" ml={-1}>
|
||||||
|
{
|
||||||
|
data && data.lineLabels ? (
|
||||||
|
(data.lineLabels.map((label: string, index: number) => (
|
||||||
|
|
||||||
|
<MDBox key={index}>
|
||||||
|
<MDBadgeDot color={allBackgroundColors[index]} size="sm" badgeContent={label} />
|
||||||
|
</MDBox>
|
||||||
|
)
|
||||||
|
))) : null
|
||||||
|
}
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
</MDTypography>
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
</MDBox>
|
||||||
|
) : null}
|
||||||
|
{useMemo(
|
||||||
|
() => (
|
||||||
|
<MDBox height={height}>
|
||||||
|
<Line data={fullData} options={options} />
|
||||||
|
</MDBox>
|
||||||
|
),
|
||||||
|
|
||||||
|
[data, height]
|
||||||
|
)}
|
||||||
|
</MDBox>
|
||||||
|
);
|
||||||
|
|
||||||
|
return title ? <Card>{renderChart}</Card> : renderChart;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DefaultLineChart;
|
@ -26,8 +26,72 @@ import {Bar} from "react-chartjs-2";
|
|||||||
import colors from "qqq/components/Temporary/colors";
|
import colors from "qqq/components/Temporary/colors";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import configs from "qqq/pages/dashboards/Widgets/Configs/HorizontalBarChartConfigs"
|
import {GenericChartData} from "qqq/pages/dashboards/Widgets/Data/GenericChartData";
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////
|
||||||
|
// configuation //
|
||||||
|
//////////////////
|
||||||
|
const options = {
|
||||||
|
indexAxis: "y",
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: true,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
color: "#c1c4ce5c",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
padding: 10,
|
||||||
|
color: "#9ca2b7",
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: false,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: true,
|
||||||
|
color: "#c1c4ce5c",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
color: "#9ca2b7",
|
||||||
|
padding: 10,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// inputs and defaults //
|
||||||
|
/////////////////////////
|
||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
icon?: {
|
icon?: {
|
||||||
@ -37,22 +101,15 @@ interface Props
|
|||||||
title?: string;
|
title?: string;
|
||||||
description?: string | ReactNode;
|
description?: string | ReactNode;
|
||||||
height?: string | number;
|
height?: string | number;
|
||||||
chart: {
|
data: GenericChartData;
|
||||||
labels: string[];
|
|
||||||
datasets: {
|
|
||||||
label: string;
|
|
||||||
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
|
||||||
data: number[];
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function HorizontalBarChart({icon, title, description, height, chart}: Props): JSX.Element
|
function HorizontalBarChart({icon, title, description, height, data}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const chartDatasets = chart.datasets
|
const chartDatasets = data.datasets
|
||||||
? chart.datasets.map((dataset) => ({
|
? data.datasets.map((dataset) => ({
|
||||||
...dataset,
|
...dataset,
|
||||||
weight: 5,
|
weight: 5,
|
||||||
borderWidth: 0,
|
borderWidth: 0,
|
||||||
@ -65,7 +122,14 @@ function HorizontalBarChart({icon, title, description, height, chart}: Props): J
|
|||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const {data, options} = configs(chart.labels || [], chartDatasets);
|
let fullData = {};
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
fullData = {
|
||||||
|
labels: data.labels,
|
||||||
|
datasets: chartDatasets
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const renderChart = (
|
const renderChart = (
|
||||||
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
||||||
@ -102,10 +166,10 @@ function HorizontalBarChart({icon, title, description, height, chart}: Props): J
|
|||||||
{useMemo(
|
{useMemo(
|
||||||
() => (
|
() => (
|
||||||
<MDBox height={height}>
|
<MDBox height={height}>
|
||||||
<Bar data={data} options={options} />
|
<Bar data={fullData} options={options} />
|
||||||
</MDBox>
|
</MDBox>
|
||||||
),
|
),
|
||||||
[chart, height]
|
[data, height]
|
||||||
)}
|
)}
|
||||||
</MDBox>
|
</MDBox>
|
||||||
);
|
);
|
||||||
|
@ -21,32 +21,122 @@
|
|||||||
|
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {useMemo, ReactNode} from "react";
|
import {ReactNode, useMemo} from "react";
|
||||||
import {Line} from "react-chartjs-2";
|
import {Line} from "react-chartjs-2";
|
||||||
import colors from "qqq/components/Temporary/colors";
|
import colors from "qqq/components/Temporary/colors";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import configs from "qqq/pages/dashboards/Widgets/Configs/LineChartConfigs";
|
import configs from "qqq/pages/dashboards/Widgets/Configs/LineChartConfigs";
|
||||||
|
|
||||||
interface Props {
|
|
||||||
icon?: {
|
///////////////////////////////////////////
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
// structure of expected line chart data //
|
||||||
component: ReactNode;
|
///////////////////////////////////////////
|
||||||
};
|
export interface LineChartData
|
||||||
title?: string;
|
{
|
||||||
description?: string | ReactNode;
|
labels: string[];
|
||||||
height?: string | number;
|
datasets: {
|
||||||
chart: {
|
|
||||||
labels: string[];
|
|
||||||
datasets: {
|
|
||||||
label: string;
|
label: string;
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
data: number[];
|
data: number[];
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// line chart options //
|
||||||
|
////////////////////////
|
||||||
|
const options = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
mode: "index",
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: true,
|
||||||
|
drawOnChartArea: true,
|
||||||
|
drawTicks: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
color: "rgba(255, 255, 255, .2)",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
color: "#f8f9fa",
|
||||||
|
padding: 10,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
display: false,
|
||||||
|
drawOnChartArea: false,
|
||||||
|
drawTicks: false,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
color: "#f8f9fa",
|
||||||
|
padding: 10,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: 300,
|
||||||
|
family: "Roboto",
|
||||||
|
style: "normal",
|
||||||
|
lineHeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// define input properties and defaults //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
icon?: {
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
component: ReactNode;
|
||||||
|
};
|
||||||
|
title?: string;
|
||||||
|
description?: string | ReactNode;
|
||||||
|
height?: string | number;
|
||||||
|
chart: {
|
||||||
|
labels: string[];
|
||||||
|
datasets: {
|
||||||
|
label: string;
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
data: number[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LineChart.defaultProps = {
|
||||||
|
icon: {color: "info", component: ""},
|
||||||
|
title: "",
|
||||||
|
description: "",
|
||||||
|
height: "19.125rem",
|
||||||
|
};
|
||||||
|
|
||||||
function LineChart({icon, title, description, height, chart}: Props): JSX.Element
|
function LineChart({icon, title, description, height, chart}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const chartDatasets = chart.datasets
|
const chartDatasets = chart.datasets
|
||||||
@ -67,7 +157,7 @@ function LineChart({icon, title, description, height, chart}: Props): JSX.Elemen
|
|||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const {data, options} = configs(chart.labels || [], chartDatasets);
|
const {data} = configs(chart.labels || [], chartDatasets);
|
||||||
|
|
||||||
const renderChart = (
|
const renderChart = (
|
||||||
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
||||||
@ -115,11 +205,5 @@ function LineChart({icon, title, description, height, chart}: Props): JSX.Elemen
|
|||||||
return title || description ? <Card>{renderChart}</Card> : renderChart;
|
return title || description ? <Card>{renderChart}</Card> : renderChart;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineChart.defaultProps = {
|
|
||||||
icon: {color: "info", component: ""},
|
|
||||||
title: "",
|
|
||||||
description: "",
|
|
||||||
height: "19.125rem",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LineChart;
|
export default LineChart;
|
||||||
|
@ -22,22 +22,39 @@
|
|||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
|
import parse from "html-react-parser";
|
||||||
import {ReactNode} from "react";
|
import {ReactNode} from "react";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
interface Props {
|
//////////////////////////////////////////
|
||||||
image: string;
|
// structure of location card data //
|
||||||
title: string;
|
//////////////////////////////////////////
|
||||||
description: string | ReactNode;
|
export interface LocationCardData
|
||||||
price: string;
|
{
|
||||||
location: ReactNode;
|
imageUrl: string;
|
||||||
action?: ReactNode | boolean;
|
title: string;
|
||||||
[key: string]: any;
|
description: string;
|
||||||
|
footerText: string;
|
||||||
|
location: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function WarehouseCard({image, title, description, price, location, action}: Props): JSX.Element
|
interface Props
|
||||||
{
|
{
|
||||||
|
locationData: LocationCardData;
|
||||||
|
action?: ReactNode | boolean;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationCard.defaultProps = {
|
||||||
|
action: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
function LocationCard({locationData, action}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
const {imageUrl, title, description, footerText, location} = locationData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<MDBox
|
<MDBox
|
||||||
@ -50,7 +67,7 @@ function WarehouseCard({image, title, description, price, location, action}: Pro
|
|||||||
>
|
>
|
||||||
<MDBox
|
<MDBox
|
||||||
component="img"
|
component="img"
|
||||||
src={image}
|
src={imageUrl}
|
||||||
alt={title}
|
alt={title}
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
shadow="md"
|
shadow="md"
|
||||||
@ -68,7 +85,7 @@ function WarehouseCard({image, title, description, price, location, action}: Pro
|
|||||||
left={0}
|
left={0}
|
||||||
top="0"
|
top="0"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundImage: `url(${image})`,
|
backgroundImage: `url(${locationData.imageUrl})`,
|
||||||
transform: "scale(0.94)",
|
transform: "scale(0.94)",
|
||||||
filter: "blur(12px)",
|
filter: "blur(12px)",
|
||||||
backgroundSize: "cover",
|
backgroundSize: "cover",
|
||||||
@ -83,7 +100,7 @@ function WarehouseCard({image, title, description, price, location, action}: Pro
|
|||||||
{title}
|
{title}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDTypography variant="body2" color="text" sx={{mt: 1.5, mb: 1}}>
|
<MDTypography variant="body2" color="text" sx={{mt: 1.5, mb: 1}}>
|
||||||
{description}
|
{parse(description)}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -97,11 +114,11 @@ function WarehouseCard({image, title, description, price, location, action}: Pro
|
|||||||
lineHeight={1}
|
lineHeight={1}
|
||||||
>
|
>
|
||||||
<MDTypography variant="body2" fontWeight="regular" color="text">
|
<MDTypography variant="body2" fontWeight="regular" color="text">
|
||||||
{price}
|
{footerText}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDBox color="text" display="flex" alignItems="center">
|
<MDBox color="text" display="flex" alignItems="center">
|
||||||
<Icon color="inherit" sx={{m: 0.5}}>
|
<Icon color="inherit" sx={{m: 0.5}}>
|
||||||
place
|
place
|
||||||
</Icon>
|
</Icon>
|
||||||
<MDTypography variant="button" fontWeight="light" color="text">
|
<MDTypography variant="button" fontWeight="light" color="text">
|
||||||
{location}
|
{location}
|
||||||
@ -112,8 +129,4 @@ function WarehouseCard({image, title, description, price, location, action}: Pro
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WarehouseCard.defaultProps = {
|
export default LocationCard;
|
||||||
action: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WarehouseCard;
|
|
@ -21,11 +21,26 @@
|
|||||||
|
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {useMemo, ReactNode} from "react";
|
import parse from "html-react-parser";
|
||||||
|
import {ReactNode, useMemo} from "react";
|
||||||
import {Pie} from "react-chartjs-2";
|
import {Pie} from "react-chartjs-2";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import configs from "qqq/pages/dashboards/Widgets/Configs/PieChartConfigs"
|
import configs from "qqq/pages/dashboards/Widgets/Configs/PieChartConfigs";
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// structure of expected bar chart data //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
export interface PieChartData
|
||||||
|
{
|
||||||
|
labels: string[];
|
||||||
|
dataset: {
|
||||||
|
label: string;
|
||||||
|
backgroundColors?: string[];
|
||||||
|
data: number[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Declaring props types for PieChart
|
// Declaring props types for PieChart
|
||||||
interface Props
|
interface Props
|
||||||
@ -35,23 +50,16 @@ interface Props
|
|||||||
component: ReactNode;
|
component: ReactNode;
|
||||||
};
|
};
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string | ReactNode;
|
description?: string;
|
||||||
height?: string | number;
|
height?: string | number;
|
||||||
chart: {
|
chart: PieChartData;
|
||||||
labels: string[];
|
|
||||||
datasets: {
|
|
||||||
label: string;
|
|
||||||
backgroundColors: string[];
|
|
||||||
data: number[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PieChart({icon, title, description, height, chart}: Props): JSX.Element
|
function PieChart({icon, title, description, height, chart}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const {data, options} = configs(chart.labels || [], chart.datasets || {});
|
const {data, options} = configs(chart?.labels || [], chart?.dataset || {});
|
||||||
|
|
||||||
const renderChart = (
|
const renderChart = (
|
||||||
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2}>
|
||||||
@ -79,7 +87,7 @@ function PieChart({icon, title, description, height, chart}: Props): JSX.Element
|
|||||||
{title && <MDTypography variant="h6">{title}</MDTypography>}
|
{title && <MDTypography variant="h6">{title}</MDTypography>}
|
||||||
<MDBox mb={2}>
|
<MDBox mb={2}>
|
||||||
<MDTypography component="div" variant="button" color="text">
|
<MDTypography component="div" variant="button" color="text">
|
||||||
{description}
|
{parse(description)}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
|
@ -22,61 +22,61 @@
|
|||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import {useMaterialUIController} from "context";
|
import parse from "html-react-parser";
|
||||||
import MDBadgeDot from "qqq/components/Temporary/MDBadgeDot";
|
import MDBadgeDot from "qqq/components/Temporary/MDBadgeDot";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import shipmentsByCarrierPieChartData from "qqq/pages/dashboards/Widgets/Data/ShipmentsByCarrierPieChartData";
|
import PieChart, {PieChartData} from "qqq/pages/dashboards/Widgets/PieChart";
|
||||||
import PieChart from "qqq/pages/dashboards/Widgets/PieChart";
|
|
||||||
|
|
||||||
function ShipmentsByCarrierPieChart(): JSX.Element
|
// Declaring props types for PieChart
|
||||||
|
interface Props
|
||||||
{
|
{
|
||||||
const [controller] = useMaterialUIController();
|
title?: string;
|
||||||
const {darkMode} = controller;
|
description?: string;
|
||||||
|
data: PieChartData;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PieChartCard({title, description, data}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
const allBackgroundColors = ["info", "warning", "primary", "success", "error", "secondary", "dark"];
|
||||||
|
|
||||||
|
if (data && data.dataset)
|
||||||
|
{
|
||||||
|
data.dataset.backgroundColors = allBackgroundColors;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{height: "100%"}}>
|
<Card sx={{height: "100%"}}>
|
||||||
<MDBox display="flex" justifyContent="space-between" alignItems="center" pt={2} px={2}>
|
<MDBox display="flex" justifyContent="space-between" alignItems="center" pt={2} px={2}>
|
||||||
<MDTypography variant="h6">Shipments By Carrier Year To Date</MDTypography>
|
<MDTypography variant="h6">{title}</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox mt={3}>
|
<MDBox mt={3}>
|
||||||
<Grid container alignItems="center">
|
<Grid container alignItems="center">
|
||||||
<Grid item xs={7}>
|
<Grid item xs={7}>
|
||||||
<PieChart chart={shipmentsByCarrierPieChartData} height="9.5rem" />
|
<PieChart chart={data} height="9.5rem" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={5}>
|
<Grid item xs={5}>
|
||||||
<MDBox pr={1}>
|
<MDBox pr={1}>
|
||||||
<MDBox>
|
{
|
||||||
<MDBadgeDot color="dark" size="sm" badgeContent="AxleHire" />
|
data && data.labels ? (
|
||||||
</MDBox>
|
(data.labels.map((label: string, index: number) => (
|
||||||
<MDBox>
|
<MDBox key={index}>
|
||||||
<MDBadgeDot color="info" size="sm" badgeContent="CDL" />
|
<MDBadgeDot color={allBackgroundColors[index]} size="sm" badgeContent={label} />
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox>
|
)
|
||||||
<MDBadgeDot color="primary" size="sm" badgeContent="DHL" />
|
))) : null
|
||||||
</MDBox>
|
}
|
||||||
<MDBox>
|
|
||||||
<MDBadgeDot color="success" size="sm" badgeContent="FedEx" />
|
|
||||||
</MDBox>
|
|
||||||
<MDBox>
|
|
||||||
<MDBadgeDot color="error" size="sm" badgeContent="LSO" />
|
|
||||||
</MDBox>
|
|
||||||
<MDBox>
|
|
||||||
<MDBadgeDot color="secondary" size="sm" badgeContent="OnTrac" />
|
|
||||||
</MDBox>
|
|
||||||
<MDBox>
|
|
||||||
<MDBadgeDot color="warning" size="sm" badgeContent="UPS" />
|
|
||||||
</MDBox>
|
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<MDBox pb={2} px={2} display="flex" flexDirection={{xs: "column", sm: "row"}} mt="auto" >
|
<MDBox pb={2} px={2} display="flex" flexDirection={{xs: "column", sm: "row"}} mt="auto">
|
||||||
<MDTypography variant="button" color="text" fontWeight="light">
|
<MDTypography variant="button" color="text" fontWeight="light">
|
||||||
<strong>Fedex and UPS</strong> delivered the majority of shipments with a combined percentage of <strong>55%</strong>.
|
{parse(description)}
|
||||||
The fewest shipments were delivered by <strong>AxleHire and OnTrac</strong> combining for <strong>6%</strong>.
|
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -86,4 +86,4 @@ function ShipmentsByCarrierPieChart(): JSX.Element
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ShipmentsByCarrierPieChart;
|
export default PieChartCard;
|
@ -22,30 +22,46 @@
|
|||||||
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 {ReactNode} from "react";
|
import {ReactNode} from "react";
|
||||||
import {useMaterialUIController} from "context";
|
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
import {StatisticsCardData} from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
||||||
|
|
||||||
interface Props {
|
interface Props
|
||||||
title: string;
|
{
|
||||||
count: string | number;
|
data: StatisticsCardData;
|
||||||
percentage?: {
|
increaseIsGood: boolean;
|
||||||
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark" | "white";
|
dropdown?: {
|
||||||
value: string | number;
|
action: (...args: any) => void;
|
||||||
label: string;
|
menu: ReactNode;
|
||||||
};
|
value: string;
|
||||||
dropdown?: {
|
};
|
||||||
action: (...args: any) => void;
|
|
||||||
menu: ReactNode;
|
[key: string]: any;
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SimpleStatisticsCard({title, count, percentage, dropdown}: Props): JSX.Element
|
function SimpleStatisticsCard({data, increaseIsGood, dropdown}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const [controller] = useMaterialUIController();
|
const {title, count, percentageAmount, percentageLabel} = data;
|
||||||
const {darkMode} = controller;
|
|
||||||
|
let percentageString = "";
|
||||||
|
if (percentageAmount)
|
||||||
|
{
|
||||||
|
percentageString = percentageAmount.toLocaleString() + "%";
|
||||||
|
if (percentageAmount > 0)
|
||||||
|
{
|
||||||
|
percentageString = "+" + percentageString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let percentColor: string;
|
||||||
|
if (increaseIsGood)
|
||||||
|
{
|
||||||
|
percentColor = (percentageAmount > 0) ? "success" : "warning";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
percentColor = (percentageAmount < 0) ? "success" : "warning";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
@ -63,17 +79,21 @@ function SimpleStatisticsCard({title, count, percentage, dropdown}: Props): JSX.
|
|||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox lineHeight={1}>
|
<MDBox lineHeight={1}>
|
||||||
<MDTypography variant="h5" fontWeight="bold">
|
{
|
||||||
{count}
|
count ? (
|
||||||
</MDTypography>
|
<MDTypography variant="h5" fontWeight="bold">
|
||||||
<MDTypography variant="button" fontWeight="bold" color={percentage.color}>
|
{count.toLocaleString()}
|
||||||
{percentage.value}
|
</MDTypography>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
<MDTypography variant="button" fontWeight="bold" color={percentColor}>
|
||||||
|
{percentageString}
|
||||||
<MDTypography
|
<MDTypography
|
||||||
variant="button"
|
variant="button"
|
||||||
fontWeight="regular"
|
fontWeight="regular"
|
||||||
color={darkMode ? "text" : "secondary"}
|
color={"secondary"}
|
||||||
>
|
>
|
||||||
{percentage.label}
|
{percentageLabel}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
|
@ -22,30 +22,42 @@
|
|||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {useMemo, ReactNode} from "react";
|
import parse from "html-react-parser";
|
||||||
|
import {useMemo} from "react";
|
||||||
import {Line} from "react-chartjs-2";
|
import {Line} from "react-chartjs-2";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import configs from "qqq/pages/dashboards/Widgets/Configs/LineChartConfigs"
|
import configs from "qqq/pages/dashboards/Widgets/Configs/LineChartConfigs";
|
||||||
|
|
||||||
interface Props {
|
//////////////////////////////////////////
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark";
|
// structure of expected bar chart data //
|
||||||
title: string;
|
//////////////////////////////////////////
|
||||||
description?: string | ReactNode;
|
export interface SmallLineChartData
|
||||||
date: string;
|
{
|
||||||
chart: {
|
labels: string[];
|
||||||
labels: string[];
|
dataset: {
|
||||||
datasets: {
|
|
||||||
label: string;
|
label: string;
|
||||||
data: number[];
|
data: number[];
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark";
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
date: string;
|
||||||
|
chart: SmallLineChartData;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SmallLineChart({color, title, description, date, chart}: Props): JSX.Element
|
function SmallLineChart({color, title, description, date, chart}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const {data, options} = configs(chart.labels || [], chart.datasets || {});
|
const {data, options} = configs(chart?.labels || [], chart?.dataset || {});
|
||||||
|
|
||||||
|
console.log(`DATA: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{height: "100%"}}>
|
<Card sx={{height: "100%"}}>
|
||||||
@ -72,7 +84,7 @@ function SmallLineChart({color, title, description, date, chart}: Props): JSX.El
|
|||||||
{title}
|
{title}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
||||||
{description}
|
{parse(description)}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MDBox display="flex" alignItems="center">
|
<MDBox display="flex" alignItems="center">
|
||||||
|
@ -26,22 +26,67 @@ import {ReactNode} from "react";
|
|||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
// Declaring props types for CompleStatisticsCard
|
///////////////////////////////////////////
|
||||||
interface Props {
|
// structure of expected stats card data //
|
||||||
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
///////////////////////////////////////////
|
||||||
title: string;
|
export interface StatisticsCardData
|
||||||
count: string | number;
|
{
|
||||||
percentage?: {
|
title: string;
|
||||||
color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "dark" | "white";
|
count: number;
|
||||||
amount: string | number;
|
percentageAmount: number;
|
||||||
label: string;
|
percentageLabel: string;
|
||||||
};
|
|
||||||
icon: ReactNode;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatisticsCard({color, title, count, percentage, icon}: Props): JSX.Element
|
/////////////////////////
|
||||||
|
// inputs and defaults //
|
||||||
|
/////////////////////////
|
||||||
|
interface Props
|
||||||
{
|
{
|
||||||
|
data: StatisticsCardData;
|
||||||
|
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
|
||||||
|
icon: ReactNode;
|
||||||
|
increaseIsGood: boolean;
|
||||||
|
dropdown?: {
|
||||||
|
action: (...args: any) => void;
|
||||||
|
menu: ReactNode;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatisticsCard.defaultProps = {
|
||||||
|
color: "info",
|
||||||
|
increaseIsGood: true
|
||||||
|
};
|
||||||
|
|
||||||
|
function StatisticsCard({data, color, icon, increaseIsGood}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
const {title, count, percentageAmount, percentageLabel} = data;
|
||||||
|
|
||||||
|
let percentageString = "";
|
||||||
|
if (percentageAmount)
|
||||||
|
{
|
||||||
|
percentageString = percentageAmount.toLocaleString() + "%";
|
||||||
|
if (percentageAmount > 0)
|
||||||
|
{
|
||||||
|
percentageString = "+" + percentageString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let percentColor = "dark";
|
||||||
|
if (percentageAmount !== 0)
|
||||||
|
{
|
||||||
|
if (increaseIsGood)
|
||||||
|
{
|
||||||
|
percentColor = (percentageAmount > 0) ? "success" : "warning";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
percentColor = (percentageAmount < 0) ? "success" : "warning";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<MDBox display="flex" justifyContent="space-between" pt={1} px={2}>
|
<MDBox display="flex" justifyContent="space-between" pt={1} px={2}>
|
||||||
@ -66,34 +111,34 @@ function StatisticsCard({color, title, count, percentage, icon}: Props): JSX.Ele
|
|||||||
<MDTypography variant="button" fontWeight="light" color="text">
|
<MDTypography variant="button" fontWeight="light" color="text">
|
||||||
{title}
|
{title}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDTypography variant="h4">{count}</MDTypography>
|
{
|
||||||
|
count !== undefined ? (
|
||||||
|
<MDTypography variant="h4">{count.toLocaleString()}</MDTypography>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MDBox pb={2} px={2}>
|
{
|
||||||
<MDTypography component="p" variant="button" color="text" display="flex">
|
percentageAmount !== undefined && percentageAmount !== 0 ? (
|
||||||
<MDTypography
|
<MDBox pb={2} px={2}>
|
||||||
component="span"
|
<MDTypography component="p" variant="button" color="text" display="flex">
|
||||||
variant="button"
|
<MDTypography
|
||||||
fontWeight="bold"
|
component="span"
|
||||||
color={percentage.color}
|
variant="button"
|
||||||
>
|
fontWeight="bold"
|
||||||
{percentage.amount}
|
color={percentColor}
|
||||||
</MDTypography>
|
>
|
||||||
{percentage.label}
|
{percentageString}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
{percentageLabel}
|
||||||
|
</MDTypography>
|
||||||
|
</MDBox>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StatisticsCard.defaultProps = {
|
|
||||||
color: "info",
|
|
||||||
percentage: {
|
|
||||||
color: "success",
|
|
||||||
text: "",
|
|
||||||
label: "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StatisticsCard;
|
export default StatisticsCard;
|
||||||
|
142
src/qqq/pages/dashboards/Widgets/TableCard.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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 {Icon} from "@mui/material";
|
||||||
|
import Card from "@mui/material/Card";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
import Menu from "@mui/material/Menu";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import DataTable, {TableDataInput} from "qqq/components/Temporary/DataTable";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// inputs and defaults //
|
||||||
|
/////////////////////////
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
title: string;
|
||||||
|
data: TableDataInput;
|
||||||
|
dropdownOptions?: string[];
|
||||||
|
dropdownOnChange?: (selectedValue: string, widgetIndex: number) => void;
|
||||||
|
widgetIndex?: number;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableCard({title, data, dropdownOptions, dropdownOnChange, widgetIndex}: Props): JSX.Element
|
||||||
|
{
|
||||||
|
const openArrowIcon = "arrow_drop_down";
|
||||||
|
const closeArrowIcon = "arrow_drop_up";
|
||||||
|
const [dropdown, setDropdown] = useState<string | null>(null);
|
||||||
|
const [dropdownValue, setDropdownValue] = useState<string>("");
|
||||||
|
const [dropdownIcon, setDropdownIcon] = useState<string>(openArrowIcon);
|
||||||
|
|
||||||
|
const openDropdown = ({currentTarget}: any) =>
|
||||||
|
{
|
||||||
|
setDropdown(currentTarget);
|
||||||
|
setDropdownIcon(closeArrowIcon);
|
||||||
|
};
|
||||||
|
const closeDropdown = ({currentTarget}: any) =>
|
||||||
|
{
|
||||||
|
setDropdown(null);
|
||||||
|
setDropdownValue(currentTarget.innerText || dropdownValue);
|
||||||
|
setDropdownIcon(openArrowIcon);
|
||||||
|
alert(widgetIndex);
|
||||||
|
dropdownOnChange(currentTarget.innerText || dropdownValue, widgetIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderMenu = (state: any, open: any, close: any, icon: string) => (
|
||||||
|
dropdownOptions && (
|
||||||
|
<span style={{whiteSpace: "nowrap"}}>
|
||||||
|
<Icon onClick={open} fontSize={"medium"} style={{cursor: "pointer", float: "right"}}>{icon}</Icon>
|
||||||
|
<Menu
|
||||||
|
anchorEl={state}
|
||||||
|
transformOrigin={{vertical: "top", horizontal: "center"}}
|
||||||
|
open={Boolean(state)}
|
||||||
|
onClose={close}
|
||||||
|
keepMounted
|
||||||
|
disableAutoFocusItem
|
||||||
|
>
|
||||||
|
{
|
||||||
|
dropdownOptions.map((option) =>
|
||||||
|
<MenuItem key={option} onClick={close}>{option}</MenuItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
console.log(dropdownOptions);
|
||||||
|
if (dropdownOptions)
|
||||||
|
{
|
||||||
|
setDropdownValue(dropdownOptions[0]);
|
||||||
|
}
|
||||||
|
}, [dropdownOptions]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={7}>
|
||||||
|
<MDBox pt={3} px={3}>
|
||||||
|
<MDTypography variant="h6" fontWeight="medium">
|
||||||
|
{title}
|
||||||
|
</MDTypography>
|
||||||
|
</MDBox>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
{dropdownOptions && (
|
||||||
|
<MDBox p={2} width="100%" textAlign="right" lineHeight={1}>
|
||||||
|
<MDTypography
|
||||||
|
variant="caption"
|
||||||
|
color="secondary"
|
||||||
|
fontWeight="regular"
|
||||||
|
sx={{cursor: "pointer"}}
|
||||||
|
onClick={openDropdown}
|
||||||
|
>
|
||||||
|
{dropdownValue}
|
||||||
|
</MDTypography>
|
||||||
|
{renderMenu(dropdown, openDropdown, closeDropdown, dropdownIcon)}
|
||||||
|
</MDBox>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<MDBox py={1}>
|
||||||
|
<DataTable
|
||||||
|
table={data}
|
||||||
|
entriesPerPage={false}
|
||||||
|
showTotalEntries={false}
|
||||||
|
isSorted={false}
|
||||||
|
noEndBorder
|
||||||
|
/>
|
||||||
|
</MDBox>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TableCard;
|
@ -20,8 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||||
import {QSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QSection";
|
|
||||||
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 {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@mui/material/Avatar";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
@ -71,10 +71,10 @@ function ViewContents({id, table}: Props): JSX.Element
|
|||||||
const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false);
|
const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false);
|
||||||
const [tableMetaData, setTableMetaData] = useState(null);
|
const [tableMetaData, setTableMetaData] = useState(null);
|
||||||
const [record, setRecord] = useState(null as QRecord);
|
const [record, setRecord] = useState(null as QRecord);
|
||||||
const [tableSections, setTableSections] = useState([] as QSection[]);
|
const [tableSections, setTableSections] = useState([] as QTableSection[]);
|
||||||
const [t1SectionName, setT1SectionName] = useState(null as string);
|
const [t1SectionName, setT1SectionName] = useState(null as string);
|
||||||
const [t1SectionElement, setT1SectionElement] = useState(null as JSX.Element);
|
const [t1SectionElement, setT1SectionElement] = useState(null as JSX.Element);
|
||||||
const [nonT1TableSections, setNonT1TableSections] = useState([] as QSection[]);
|
const [nonT1TableSections, setNonT1TableSections] = useState([] as QTableSection[]);
|
||||||
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
||||||
const [actionsMenu, setActionsMenu] = useState(null);
|
const [actionsMenu, setActionsMenu] = useState(null);
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {QSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QSection";
|
|
||||||
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";
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Utility class for working with QQQ Tables
|
** Utility class for working with QQQ Tables
|
||||||
@ -28,7 +28,7 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class QTableUtils
|
class QTableUtils
|
||||||
{
|
{
|
||||||
public static getSectionsForRecordSidebar(tableMetaData: QTableMetaData): QSection[]
|
public static getSectionsForRecordSidebar(tableMetaData: QTableMetaData): QTableSection[]
|
||||||
{
|
{
|
||||||
if (tableMetaData.sections)
|
if (tableMetaData.sections)
|
||||||
{
|
{
|
||||||
@ -36,7 +36,7 @@ class QTableUtils
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ([new QSection({
|
return ([new QTableSection({
|
||||||
iconName: "description", label: "All Fields", name: "allFields", fieldNames: [...tableMetaData.fields.keys()],
|
iconName: "description", label: "All Fields", name: "allFields", fieldNames: [...tableMetaData.fields.keys()],
|
||||||
})]);
|
})]);
|
||||||
}
|
}
|
||||||
|