Loading portal-gui/package.json +3 −1 Original line number Diff line number Diff line Loading @@ -12,13 +12,15 @@ "next": "16.0.5", "react": "19.2.0", "react-dom": "19.2.0", "rsuite": "^6.0.0" "rsuite": "^6.0.0", "sass": "^1.94.2" }, "devDependencies": { "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "babel-plugin-react-compiler": "1.0.0", "baseline-browser-mapping": "^2.8.32", "eslint": "^9", "eslint-config-next": "16.0.5", "typescript": "^5" Loading portal-gui/src/app/components/TopBar/TopBar.tsx +37 −66 Original line number Diff line number Diff line "use client"; import React, { useState } from "react"; import styles from "./topbar.module.scss"; import {Button, Toggle} from "rsuite" import { Button, Nav, Toggle } from "rsuite"; import Link from "next/link"; import { useRouter } from "next/navigation"; import buttons from "../../app/buttons.module.scss"; import { logoIcon } from "@/app/utils/icons"; import buttons from "../../styles/buttons.module.scss"; import { logoIcon, userIcon } from "@/app/utils/icons"; import { navLinks } from "@/app/utils/constants"; import { formatName } from "@/app/utils/helpers"; const TopBar = () => { const [menuOpen, setMenuOpen] = useState(false); const route = useRouter(); const user = { name: "Chnarakis Panagiotis", role: "Developer" }; // Example user object // const user = null; // No user logged in return ( <div className={styles.container}> <header className={styles.header}> <Link href={"/"} className={styles.logo}> {logoIcon} Your Digital Assistant {logoIcon} </Link> <div className={styles.menuBox}> {/* {user && ( <Whisper placement="bottomEnd" trigger="click" speaker={ <Popover arrow={false} className={styles.popover}> <ul className={styles.notificationBox}> {notifications.length === 0 ? ( <p className={styles.noNotifications}> No notifications yet </p> ) : ( <> {notifications .sort((a, b) => { const dateA = new Date(a.published); const dateB = new Date(b.published); return dateB.getTime() - dateA.getTime(); }) .slice(0, 3) .map((notification) => ( <NotificationItem key={notification.id} notification={notification} /> <Nav defaultActiveKey="Api Cataloque" className={styles.navLinks}> {navLinks.map((link) => ( <Nav.Item key={link.id} eventKey={link.name} href={link.path}> {link.name} </Nav.Item> ))} {notifications.length > 3 && ( <Link href="/notifications" className={styles.viewAll} </Nav> {/* <Button className={buttons.primary} onClick={() => route.push("/login")} > {" "} + {notifications.length - 3} more...{" "} <strong>View all</strong> </Link> )} </> )} </ul> </Popover> } Login </Button> */} {user && ( <Nav className={styles.userBtn}> <Nav.Menu icon={userIcon} title={formatName(user?.name)} placement="bottomEnd" > {unreadNotifications > 0 ? ( <Badge content={unreadNotifications} maxCount={99}> <Button>{BellIcon}</Button> </Badge> ) : ( <Button>{BellIcon}</Button> <Nav.Item href="/profile" as="a"> Profile </Nav.Item> <Nav.Item href="/settings" as="a"> Settings </Nav.Item> <Nav.Item>Log Out</Nav.Item> </Nav.Menu> </Nav> )} </Whisper> )} */} <div className={`${styles.userSection} ${menuOpen ? styles.open : ""}`} > </div> </div> </header> </div> ); Loading portal-gui/src/app/components/TopBar/topbar.module.scss +152 −0 Original line number Diff line number Diff line .container { top: 0; height: 100px; align-items: center; padding: 1.5rem 6rem; width: 100%; background: var(--background-color); z-index: 3; color: var(--blue-text); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); button { height: 100%; } .logo { display: flex; flex-direction: column; align-items: center; font-size: clamp(0.4rem, 1.2vw, 0.56rem); color: var(--text-color); gap: 0.4rem; svg { transform: scale(1.05); } } header { display: flex; flex-direction: row; justify-content: space-between; align-items: center; height: max-content; height: 100%; } .navLinks { :global(.rs-nav-item) { position: relative; padding: 0 1.5rem 0 1rem; &:hover { color: var(--orange-color) !important; background: none !important; } &::after { content: ""; position: absolute; background: var(--main-gradient); height: 8px; width: 8px; right: 0; top: 50%; transform: translateY(-50%); border-radius: 50%; pointer-events: none; } } :global(.rs-nav-item:last-child)::after { content: none; } :global(.rs-nav-item[aria-selected="true"]:hover) { color: var(--blue-text) !important; cursor: default; } } .mobileMenuIcon { display: none; cursor: pointer; svg { width: 32px; height: 32px; } } .userBtn { :global(.rs-dropdown) { background: var(--background-color) !important; box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.1); &:hover { background: var(--rs-navs-bg-hover) !important; } .rs-dropdown-toggle .rs-nav-item { width: max-content; height: max-content; padding: 0.5rem 0.7rem !important; } // a:hover{ // background: none !important; // } // padding: 0.5rem 0.7rem; border-radius: 10px; } } //------------------------- Responsive -------------------------// @media screen and (max-width: 769px) { padding: 1rem 2rem; .mobileMenuIcon { display: block; } .navLinks { display: none; } } } @media screen and (max-width:425px) { .logo { gap: 0.3rem; svg { transform: scale(0.95) !important; } } } No newline at end of file portal-gui/src/app/layout.tsx +16 −11 Original line number Diff line number Diff line import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./styles/globals.css"; import TopBar from "./components/TopBar/TopBar"; import "rsuite/dist/rsuite.min.css"; import { Manrope, Montserrat } from "next/font/google"; // const geistSans = Geist({ // variable: "--font-geist-sans", // subsets: ["latin"], // }); const manrope = Manrope({ subsets: ["latin"], variable: "--font-manrope", }); // const geistMono = Geist_Mono({ // variable: "--font-geist-mono", // subsets: ["latin"], // }); const montserrat = Montserrat({ subsets: ["latin"], variable: "--font-montserrat", }); export const metadata: Metadata = { title: "Create Next App", Loading @@ -23,9 +25,12 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( <html lang="en"> <html lang="en" className={`${manrope.variable} ${montserrat.variable}`}> <body> <div className="main"> <TopBar /> {children} </div> </body> </html> ); Loading portal-gui/src/app/page.tsx +34 −59 Original line number Diff line number Diff line "use client" import Image from "next/image"; import styles from "./styles/page.module.css"; import styles from "./styles/page.module.scss"; import { Button } from "rsuite"; import buttons from "./styles/buttons.module.scss"; export default function Home() { return ( <div className={styles.page}> <main className={styles.main}> <Image className={styles.logo} src="/next.svg" alt="Next.js logo" width={100} height={20} priority /> <div className={styles.intro}> <h1>To get started, edit the page.tsx file.</h1> <p> Looking for a starting point or more instructions? Head over to{" "} <a href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > Templates </a>{" "} or the{" "} <a href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > Learning </a>{" "} center. <main className={styles.container}> <div className={styles.bgImage}></div> <div className={styles.mainContent}> <div className={styles.illustration}> {/* <Lottie maxLength={400} style={{ maxHeight: 600 }} animationData={HomeAnimation} loop={true} /> */} </div> <p className={styles.description}> ODEON demonstrates and integrates innovative solutions to create an inclusive ecosystem of stakeholders characterized by the integration of a mesh of Data, Intelligence, Service and Market flows in the energy system. ODEON enables the resilient operation of the energy system considering the increased RES integration, and the effective orchestration of the flexibility from assets residing at the edges of the system. </p> <div className={styles.btnBox}> {/* {!user && ( <Button className={buttons.primary} href="/login"> Start </Button> )} */} <Button className={buttons.secondary} href="https://odeonproject.eu/"> Read more </Button> </div> <div className={styles.ctas}> <a className={styles.primary} href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > <Image className={styles.logo} src="/vercel.svg" alt="Vercel logomark" width={16} height={16} /> Deploy Now </a> <a className={styles.secondary} href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > Documentation </a> </div> </main> </div> ); } Loading
portal-gui/package.json +3 −1 Original line number Diff line number Diff line Loading @@ -12,13 +12,15 @@ "next": "16.0.5", "react": "19.2.0", "react-dom": "19.2.0", "rsuite": "^6.0.0" "rsuite": "^6.0.0", "sass": "^1.94.2" }, "devDependencies": { "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "babel-plugin-react-compiler": "1.0.0", "baseline-browser-mapping": "^2.8.32", "eslint": "^9", "eslint-config-next": "16.0.5", "typescript": "^5" Loading
portal-gui/src/app/components/TopBar/TopBar.tsx +37 −66 Original line number Diff line number Diff line "use client"; import React, { useState } from "react"; import styles from "./topbar.module.scss"; import {Button, Toggle} from "rsuite" import { Button, Nav, Toggle } from "rsuite"; import Link from "next/link"; import { useRouter } from "next/navigation"; import buttons from "../../app/buttons.module.scss"; import { logoIcon } from "@/app/utils/icons"; import buttons from "../../styles/buttons.module.scss"; import { logoIcon, userIcon } from "@/app/utils/icons"; import { navLinks } from "@/app/utils/constants"; import { formatName } from "@/app/utils/helpers"; const TopBar = () => { const [menuOpen, setMenuOpen] = useState(false); const route = useRouter(); const user = { name: "Chnarakis Panagiotis", role: "Developer" }; // Example user object // const user = null; // No user logged in return ( <div className={styles.container}> <header className={styles.header}> <Link href={"/"} className={styles.logo}> {logoIcon} Your Digital Assistant {logoIcon} </Link> <div className={styles.menuBox}> {/* {user && ( <Whisper placement="bottomEnd" trigger="click" speaker={ <Popover arrow={false} className={styles.popover}> <ul className={styles.notificationBox}> {notifications.length === 0 ? ( <p className={styles.noNotifications}> No notifications yet </p> ) : ( <> {notifications .sort((a, b) => { const dateA = new Date(a.published); const dateB = new Date(b.published); return dateB.getTime() - dateA.getTime(); }) .slice(0, 3) .map((notification) => ( <NotificationItem key={notification.id} notification={notification} /> <Nav defaultActiveKey="Api Cataloque" className={styles.navLinks}> {navLinks.map((link) => ( <Nav.Item key={link.id} eventKey={link.name} href={link.path}> {link.name} </Nav.Item> ))} {notifications.length > 3 && ( <Link href="/notifications" className={styles.viewAll} </Nav> {/* <Button className={buttons.primary} onClick={() => route.push("/login")} > {" "} + {notifications.length - 3} more...{" "} <strong>View all</strong> </Link> )} </> )} </ul> </Popover> } Login </Button> */} {user && ( <Nav className={styles.userBtn}> <Nav.Menu icon={userIcon} title={formatName(user?.name)} placement="bottomEnd" > {unreadNotifications > 0 ? ( <Badge content={unreadNotifications} maxCount={99}> <Button>{BellIcon}</Button> </Badge> ) : ( <Button>{BellIcon}</Button> <Nav.Item href="/profile" as="a"> Profile </Nav.Item> <Nav.Item href="/settings" as="a"> Settings </Nav.Item> <Nav.Item>Log Out</Nav.Item> </Nav.Menu> </Nav> )} </Whisper> )} */} <div className={`${styles.userSection} ${menuOpen ? styles.open : ""}`} > </div> </div> </header> </div> ); Loading
portal-gui/src/app/components/TopBar/topbar.module.scss +152 −0 Original line number Diff line number Diff line .container { top: 0; height: 100px; align-items: center; padding: 1.5rem 6rem; width: 100%; background: var(--background-color); z-index: 3; color: var(--blue-text); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); button { height: 100%; } .logo { display: flex; flex-direction: column; align-items: center; font-size: clamp(0.4rem, 1.2vw, 0.56rem); color: var(--text-color); gap: 0.4rem; svg { transform: scale(1.05); } } header { display: flex; flex-direction: row; justify-content: space-between; align-items: center; height: max-content; height: 100%; } .navLinks { :global(.rs-nav-item) { position: relative; padding: 0 1.5rem 0 1rem; &:hover { color: var(--orange-color) !important; background: none !important; } &::after { content: ""; position: absolute; background: var(--main-gradient); height: 8px; width: 8px; right: 0; top: 50%; transform: translateY(-50%); border-radius: 50%; pointer-events: none; } } :global(.rs-nav-item:last-child)::after { content: none; } :global(.rs-nav-item[aria-selected="true"]:hover) { color: var(--blue-text) !important; cursor: default; } } .mobileMenuIcon { display: none; cursor: pointer; svg { width: 32px; height: 32px; } } .userBtn { :global(.rs-dropdown) { background: var(--background-color) !important; box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.1); &:hover { background: var(--rs-navs-bg-hover) !important; } .rs-dropdown-toggle .rs-nav-item { width: max-content; height: max-content; padding: 0.5rem 0.7rem !important; } // a:hover{ // background: none !important; // } // padding: 0.5rem 0.7rem; border-radius: 10px; } } //------------------------- Responsive -------------------------// @media screen and (max-width: 769px) { padding: 1rem 2rem; .mobileMenuIcon { display: block; } .navLinks { display: none; } } } @media screen and (max-width:425px) { .logo { gap: 0.3rem; svg { transform: scale(0.95) !important; } } } No newline at end of file
portal-gui/src/app/layout.tsx +16 −11 Original line number Diff line number Diff line import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./styles/globals.css"; import TopBar from "./components/TopBar/TopBar"; import "rsuite/dist/rsuite.min.css"; import { Manrope, Montserrat } from "next/font/google"; // const geistSans = Geist({ // variable: "--font-geist-sans", // subsets: ["latin"], // }); const manrope = Manrope({ subsets: ["latin"], variable: "--font-manrope", }); // const geistMono = Geist_Mono({ // variable: "--font-geist-mono", // subsets: ["latin"], // }); const montserrat = Montserrat({ subsets: ["latin"], variable: "--font-montserrat", }); export const metadata: Metadata = { title: "Create Next App", Loading @@ -23,9 +25,12 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( <html lang="en"> <html lang="en" className={`${manrope.variable} ${montserrat.variable}`}> <body> <div className="main"> <TopBar /> {children} </div> </body> </html> ); Loading
portal-gui/src/app/page.tsx +34 −59 Original line number Diff line number Diff line "use client" import Image from "next/image"; import styles from "./styles/page.module.css"; import styles from "./styles/page.module.scss"; import { Button } from "rsuite"; import buttons from "./styles/buttons.module.scss"; export default function Home() { return ( <div className={styles.page}> <main className={styles.main}> <Image className={styles.logo} src="/next.svg" alt="Next.js logo" width={100} height={20} priority /> <div className={styles.intro}> <h1>To get started, edit the page.tsx file.</h1> <p> Looking for a starting point or more instructions? Head over to{" "} <a href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > Templates </a>{" "} or the{" "} <a href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > Learning </a>{" "} center. <main className={styles.container}> <div className={styles.bgImage}></div> <div className={styles.mainContent}> <div className={styles.illustration}> {/* <Lottie maxLength={400} style={{ maxHeight: 600 }} animationData={HomeAnimation} loop={true} /> */} </div> <p className={styles.description}> ODEON demonstrates and integrates innovative solutions to create an inclusive ecosystem of stakeholders characterized by the integration of a mesh of Data, Intelligence, Service and Market flows in the energy system. ODEON enables the resilient operation of the energy system considering the increased RES integration, and the effective orchestration of the flexibility from assets residing at the edges of the system. </p> <div className={styles.btnBox}> {/* {!user && ( <Button className={buttons.primary} href="/login"> Start </Button> )} */} <Button className={buttons.secondary} href="https://odeonproject.eu/"> Read more </Button> </div> <div className={styles.ctas}> <a className={styles.primary} href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > <Image className={styles.logo} src="/vercel.svg" alt="Vercel logomark" width={16} height={16} /> Deploy Now </a> <a className={styles.secondary} href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app" target="_blank" rel="noopener noreferrer" > Documentation </a> </div> </main> </div> ); }