mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 05:10:45 +00:00
Merged feature/banners into dev
This commit is contained in:
@ -6,7 +6,7 @@
|
|||||||
"@auth0/auth0-react": "1.10.2",
|
"@auth0/auth0-react": "1.10.2",
|
||||||
"@emotion/react": "11.7.1",
|
"@emotion/react": "11.7.1",
|
||||||
"@emotion/styled": "11.6.0",
|
"@emotion/styled": "11.6.0",
|
||||||
"@kingsrook/qqq-frontend-core": "1.0.114",
|
"@kingsrook/qqq-frontend-core": "1.0.117",
|
||||||
"@mui/icons-material": "5.4.1",
|
"@mui/icons-material": "5.4.1",
|
||||||
"@mui/material": "5.11.1",
|
"@mui/material": "5.11.1",
|
||||||
"@mui/styles": "5.11.1",
|
"@mui/styles": "5.11.1",
|
||||||
|
2
pom.xml
2
pom.xml
@ -66,7 +66,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.kingsrook.qqq</groupId>
|
<groupId>com.kingsrook.qqq</groupId>
|
||||||
<artifactId>qqq-backend-core</artifactId>
|
<artifactId>qqq-backend-core</artifactId>
|
||||||
<version>0.21.0</version>
|
<version>0.25.0-integration-sprint-62-20250307-205536</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
20
src/App.tsx
20
src/App.tsx
@ -29,6 +29,7 @@ import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstan
|
|||||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@mui/material/Avatar";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
import CssBaseline from "@mui/material/CssBaseline";
|
import CssBaseline from "@mui/material/CssBaseline";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {ThemeProvider} from "@mui/material/styles";
|
import {ThemeProvider} from "@mui/material/styles";
|
||||||
@ -38,6 +39,7 @@ import jwt_decode from "jwt-decode";
|
|||||||
import QContext from "QContext";
|
import QContext from "QContext";
|
||||||
import Sidenav from "qqq/components/horseshoe/sidenav/SideNav";
|
import Sidenav from "qqq/components/horseshoe/sidenav/SideNav";
|
||||||
import theme from "qqq/components/legacy/Theme";
|
import theme from "qqq/components/legacy/Theme";
|
||||||
|
import {getBannerClassName, getBannerStyles, getBanner, makeBannerContent} from "qqq/components/misc/Banners";
|
||||||
import {setMiniSidenav, setOpenConfigurator, useMaterialUIController} from "qqq/context";
|
import {setMiniSidenav, setOpenConfigurator, useMaterialUIController} from "qqq/context";
|
||||||
import AppHome from "qqq/pages/apps/Home";
|
import AppHome from "qqq/pages/apps/Home";
|
||||||
import NoApps from "qqq/pages/apps/NoApps";
|
import NoApps from "qqq/pages/apps/NoApps";
|
||||||
@ -691,6 +693,23 @@ export default function App()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function banner(): JSX.Element | null
|
||||||
|
{
|
||||||
|
const banner = getBanner(metaData?.branding, "QFMD_TOP_OF_SITE");
|
||||||
|
|
||||||
|
if (!banner)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<Box className={getBannerClassName(banner)} sx={{display: "flex", justifyContent: "center", padding: "0.5rem", position: "sticky", top: "0", zIndex: 1, ...getBannerStyles(banner)}}>
|
||||||
|
{makeBannerContent(banner)}
|
||||||
|
</Box>);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
appRoutes && (
|
appRoutes && (
|
||||||
@ -718,6 +737,7 @@ export default function App()
|
|||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<CommandMenu metaData={metaData} />
|
<CommandMenu metaData={metaData} />
|
||||||
|
{banner()}
|
||||||
<Sidenav
|
<Sidenav
|
||||||
color={sidenavColor}
|
color={sidenavColor}
|
||||||
icon={branding.icon}
|
icon={branding.icon}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.frontend.materialdashboard.model.metadata;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.branding.BannerSlot;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public enum MaterialDashboardBannerSlots implements BannerSlot
|
||||||
|
{
|
||||||
|
QFMD_TOP_OF_SITE,
|
||||||
|
QFMD_TOP_OF_BODY,
|
||||||
|
QFMD_SIDE_NAV_UNDER_LOGO
|
||||||
|
}
|
@ -34,6 +34,7 @@ import SideNavList from "qqq/components/horseshoe/sidenav/SideNavList";
|
|||||||
import SidenavRoot from "qqq/components/horseshoe/sidenav/SideNavRoot";
|
import SidenavRoot from "qqq/components/horseshoe/sidenav/SideNavRoot";
|
||||||
import sidenavLogoLabel from "qqq/components/horseshoe/sidenav/styles/SideNav";
|
import sidenavLogoLabel from "qqq/components/horseshoe/sidenav/styles/SideNav";
|
||||||
import MDTypography from "qqq/components/legacy/MDTypography";
|
import MDTypography from "qqq/components/legacy/MDTypography";
|
||||||
|
import {getBannerClassName, getBannerStyles, getBanner, makeBannerContent} from "qqq/components/misc/Banners";
|
||||||
import {setMiniSidenav, setTransparentSidenav, setWhiteSidenav, useMaterialUIController,} from "qqq/context";
|
import {setMiniSidenav, setTransparentSidenav, setWhiteSidenav, useMaterialUIController,} from "qqq/context";
|
||||||
|
|
||||||
|
|
||||||
@ -300,6 +301,30 @@ function Sidenav({color, icon, logo, appName, branding, routes, ...rest}: Props)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function EnvironmentBanner({branding}: { branding: QBrandingMetaData }): JSX.Element | null
|
||||||
|
{
|
||||||
|
// deprecated!
|
||||||
|
if (branding && branding.environmentBannerText)
|
||||||
|
{
|
||||||
|
return <Box mt={2} bgcolor={branding.environmentBannerColor} borderRadius={2}>
|
||||||
|
{branding.environmentBannerText}
|
||||||
|
</Box>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const banner = getBanner(branding, "QFMD_SIDE_NAV_UNDER_LOGO");
|
||||||
|
if (banner)
|
||||||
|
{
|
||||||
|
return <Box className={getBannerClassName(banner)} mt={2} borderRadius={2} sx={getBannerStyles(banner)}>
|
||||||
|
{makeBannerContent(banner)}
|
||||||
|
</Box>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidenavRoot
|
<SidenavRoot
|
||||||
{...rest}
|
{...rest}
|
||||||
@ -330,12 +355,7 @@ function Sidenav({color, icon, logo, appName, branding, routes, ...rest}: Props)
|
|||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
{
|
<EnvironmentBanner branding={branding} />
|
||||||
branding && branding.environmentBannerText &&
|
|
||||||
<Box mt={2} bgcolor={branding.environmentBannerColor} borderRadius={2}>
|
|
||||||
{branding.environmentBannerText}
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Divider
|
<Divider
|
||||||
light={
|
light={
|
||||||
|
@ -97,6 +97,7 @@ export default styled(Drawer)(({theme, ownerState}: { theme?: Theme | any; owner
|
|||||||
margin: "0",
|
margin: "0",
|
||||||
borderRadius: "0",
|
borderRadius: "0",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
top: "unset",
|
||||||
|
|
||||||
...(miniSidenav ? drawerCloseStyles() : drawerOpenStyles()),
|
...(miniSidenav ? drawerCloseStyles() : drawerOpenStyles()),
|
||||||
},
|
},
|
||||||
|
97
src/qqq/components/misc/Banners.tsx
Normal file
97
src/qqq/components/misc/Banners.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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 {Banner} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Banner";
|
||||||
|
import {QBrandingMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QBrandingMetaData";
|
||||||
|
import parse from "html-react-parser";
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// One may render a banner using the functions in this file as: //
|
||||||
|
// //
|
||||||
|
// const banner = getBanner(branding, "QFMD_SIDE_NAV_UNDER_LOGO"); //
|
||||||
|
// return (<Box className={getBannerClassName(banner)} sx={{padding: "1rem", ...getBannerStyles(banner)}}> //
|
||||||
|
// {makeBannerContent(banner)} //
|
||||||
|
// </Box>); //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
export function getBanner(branding: QBrandingMetaData, slot: string): Banner | null
|
||||||
|
{
|
||||||
|
if (branding?.banners?.has(slot))
|
||||||
|
{
|
||||||
|
return (branding.banners.get(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
export function getBannerStyles(banner: Banner)
|
||||||
|
{
|
||||||
|
let bgColor = "";
|
||||||
|
let color = "";
|
||||||
|
|
||||||
|
if (banner)
|
||||||
|
{
|
||||||
|
if (banner.backgroundColor)
|
||||||
|
{
|
||||||
|
bgColor = banner.backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (banner.textColor)
|
||||||
|
{
|
||||||
|
bgColor = banner.textColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rest = banner?.additionalStyles ?? {};
|
||||||
|
|
||||||
|
return ({
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
color: color,
|
||||||
|
...rest
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
export function getBannerClassName(banner: Banner)
|
||||||
|
{
|
||||||
|
return `banner ${banner?.severity?.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
export function makeBannerContent(banner: Banner): JSX.Element
|
||||||
|
{
|
||||||
|
return <>{banner?.messageHTML ? parse(banner?.messageHTML) : banner?.messageText}</>;
|
||||||
|
}
|
||||||
|
|
@ -21,11 +21,12 @@
|
|||||||
|
|
||||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import {ReactNode, useEffect, useState} from "react";
|
|
||||||
import Footer from "qqq/components/horseshoe/Footer";
|
import Footer from "qqq/components/horseshoe/Footer";
|
||||||
import NavBar from "qqq/components/horseshoe/NavBar";
|
import NavBar from "qqq/components/horseshoe/NavBar";
|
||||||
|
import {getBannerClassName, getBannerStyles, getBanner, makeBannerContent} from "qqq/components/misc/Banners";
|
||||||
import DashboardLayout from "qqq/layouts/DashboardLayout";
|
import DashboardLayout from "qqq/layouts/DashboardLayout";
|
||||||
import Client from "qqq/utils/qqq/Client";
|
import Client from "qqq/utils/qqq/Client";
|
||||||
|
import {ReactNode, useEffect, useState} from "react";
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
@ -80,12 +81,34 @@ function BaseLayout({stickyNavbar, children}: Props): JSX.Element
|
|||||||
return () => window.removeEventListener("resize", handleTabsOrientation);
|
return () => window.removeEventListener("resize", handleTabsOrientation);
|
||||||
}, [tabsOrientation]);
|
}, [tabsOrientation]);
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function banner(): JSX.Element | null
|
||||||
|
{
|
||||||
|
const banner = getBanner(metaData?.branding, "QFMD_TOP_OF_BODY");
|
||||||
|
|
||||||
|
if (!banner)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<Box className={getBannerClassName(banner)} sx={{display: "flex", justifyContent: "center", padding: "0.5rem", margin: "-20px", marginBottom: "20px", ...getBannerStyles(banner)}}>
|
||||||
|
{makeBannerContent(banner)}
|
||||||
|
</Box>);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<>
|
||||||
<NavBar />
|
<DashboardLayout>
|
||||||
<Box>{children}</Box>
|
{banner()}
|
||||||
<Footer company={{href: metaData?.branding?.companyUrl, name: metaData?.branding?.companyName}} />
|
<NavBar />
|
||||||
</DashboardLayout>
|
<Box>{children}</Box>
|
||||||
|
<Footer company={{href: metaData?.branding?.companyUrl, name: metaData?.branding?.companyName}} />
|
||||||
|
</DashboardLayout>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -748,35 +748,54 @@ input[type="search"]::-webkit-search-results-decoration
|
|||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpContentAlert.success
|
.helpContentAlert.info,
|
||||||
|
.banner.info
|
||||||
|
{
|
||||||
|
background-color: rgb(234, 242, 255);
|
||||||
|
color: rgb(20, 51, 102);
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpContentAlert.info .MuiAlert-icon .material-icons-round,
|
||||||
|
.banner.info .MuiAlert-icon .material-icons-round
|
||||||
|
{
|
||||||
|
color: #0062FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpContentAlert.success,
|
||||||
|
.banner.success
|
||||||
{
|
{
|
||||||
background-color: rgb(240, 248, 241);
|
background-color: rgb(240, 248, 241);
|
||||||
color: rgb(44, 76, 46);
|
color: rgb(44, 76, 46);
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpContentAlert.success .MuiAlert-icon .material-icons-round
|
.helpContentAlert.success .MuiAlert-icon .material-icons-round,
|
||||||
|
.banner.success .MuiAlert-icon .material-icons-round
|
||||||
{
|
{
|
||||||
color: #4CAF50;
|
color: #4CAF50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpContentAlert.warning
|
.helpContentAlert.warning,
|
||||||
|
.banner.warning
|
||||||
{
|
{
|
||||||
background-color: rgb(254, 245, 234);
|
background-color: rgb(254, 245, 234);
|
||||||
color: rgb(100, 65, 20);
|
color: rgb(100, 65, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpContentAlert.warning .MuiAlert-icon .material-icons-round
|
.helpContentAlert.warning .MuiAlert-icon .material-icons-round,
|
||||||
|
.banner.warning .MuiAlert-icon .material-icons-round
|
||||||
{
|
{
|
||||||
color: #fb8c00;
|
color: #fb8c00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpContentAlert.error
|
.helpContentAlert.error,
|
||||||
|
.banner.error
|
||||||
{
|
{
|
||||||
background-color: rgb(254, 239, 238);
|
background-color: rgb(254, 239, 238);
|
||||||
color: rgb(98, 41, 37);
|
color: rgb(98, 41, 37);
|
||||||
}
|
}
|
||||||
|
|
||||||
.helpContentAlert.error .MuiAlert-icon .material-icons-round
|
.helpContentAlert.error .MuiAlert-icon .material-icons-round,
|
||||||
|
.banner.error .MuiAlert-icon .material-icons-round
|
||||||
{
|
{
|
||||||
color: #F44335;
|
color: #F44335;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user