diff --git a/app/_pattern_navigation.scss b/app/_pattern_navigation.scss new file mode 100644 index 00000000..bed549a5 --- /dev/null +++ b/app/_pattern_navigation.scss @@ -0,0 +1,115 @@ +@mixin lxdui-icon-sidebar-collapse() { + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' width='16px' height='16px' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 0 7.743 L 6.742 14.485 L 7.899 13.329 L 2.311 7.743 L 7.899 2.157 L 6.742 1 L 0 7.743 Z M 7.899 7.743 L 14.642 14.485 L 15.797 13.329 L 10.21 7.743 L 15.797 2.157 L 14.642 1 L 7.899 7.743 Z' fill='%23FFF' style=''/%3E%3C/svg%3E"); +} + +@mixin navbar-collapse-icon { + .p-icon--sidebar-toggle { + @extend %icon; + @include lxdui-icon-sidebar-collapse; + } +} + +@include navbar-collapse-icon; + +.logo { + .logo-tag { + background-color: #e95420; + height: 2.2rem; + position: absolute; + top: 0; + width: 1.313rem; + } + + .logo-image { + bottom: 0.125rem; + height: 1rem; + left: 50%; + position: absolute; + transform: translate(-50%, 0); + width: 1rem; + } + + .logo-text { + color: #fff; + display: inline-block; + font-size: 1.3rem; + font-weight: 300; + line-height: 1rem; + margin-top: 0.5rem; + padding-left: 1.5rem; + } +} + +@media screen and (min-width: $breakpoint-small) { + .l-navigation.is-collapsed .logo-text { + color: $colors--dark-theme--background-default; + } +} + +@media screen and (max-width: $breakpoint-small) { + .l-navigation { + transition-duration: 0s !important; + } +} + +@media screen and (min-width: $breakpoint-x-small) and (max-width: $breakpoint-small - 1px) { + .l-navigation__drawer { + width: 17rem; + } +} + +.l-navigation.is-collapsed:focus-within { + transform: translateX(-100%); +} + +@media screen and (min-width: $breakpoint-small) { + .l-navigation.is-collapsed, + .l-navigation.is-collapsed:focus-within, + .l-navigation.is-collapsed:hover { + transform: translateX(0); + width: 4rem; + } +} + +.l-navigation .p-panel__content { + padding-bottom: 0; +} + +.l-navigation .p-panel__header { + z-index: 1001; +} + +.sidenav-top-ul::after { + display: none; +} + +.sidenav-bottom-ul { + bottom: $spv--x-large; + position: absolute; + width: 15rem; +} + +.sidenav-toggle-wrapper { + background: $colors--dark-theme--background-default; + bottom: 0; + padding: $spv--small $sph--x-large $spv--large $sph--large; + position: absolute; + text-align: right; + width: 100%; + + .sidenav-toggle { + background-color: #444 !important; + } +} + +.l-navigation.is-collapsed .sidenav-toggle-wrapper { + text-align: left; + + .sidenav-toggle { + rotate: 180deg; + } +} + +.p-side-navigation__item--title { + white-space: nowrap; +} diff --git a/app/globals.scss b/app/globals.scss index 41e3b6fb..e369569f 100644 --- a/app/globals.scss +++ b/app/globals.scss @@ -9,3 +9,13 @@ body { @import "node_modules/vanilla-framework"; @include vanilla; + +@include vf-p-icon-close; +@include vf-p-icon-connected; +@include vf-p-icon-profile; + +@import "pattern_navigation"; + +.l-main .p-panel__header { + padding: 0; +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index bc5298f3..b1291776 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,17 +1,17 @@ "use client"; import "./globals.scss"; import { Inter } from "next/font/google"; -import React, { useState, useEffect, Suspense } from "react"; -import { usePathname } from 'next/navigation' +import React, { useState, useEffect } from "react"; import { checkBackendAvailable } from "@/utils/checkBackendAvailable"; import { - Notification, - Theme, List, - Navigation, + Notification, Row, } from "@canonical/react-components"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import Navigation from "@/components/Navigation"; +import PageContent from "@/components/PageContent"; +import Loader from "@/components/Loader"; const inter = Inter({ subsets: ["latin"] }); const queryClient = new QueryClient(); @@ -24,7 +24,6 @@ export default function RootLayout({ const [backendAvailable, setBackendAvailable] = useState( null, ); - const pathname = usePathname() useEffect(() => { const fetchData = async () => { @@ -39,75 +38,47 @@ export default function RootLayout({ SD Core + -
- - {backendAvailable === false && ( - - {"Backend not available"} - - )} - {backendAvailable === true && ( - - {children} - - )} -
- +
+ +
+
+ {backendAvailable === null && ( + + )} + {backendAvailable === false && ( + + + {"Backend not available"} + + + )} + {backendAvailable === true && ( + + {children} + + )} +
+
+ +

+ © 2023 Canonical Ltd. Ubuntu and{" "} + Canonical are registered trademarks of Canonical + Ltd. +

+ + Legal information + , + ]} + middot + /> +
+
+
diff --git a/app/network-configuration/page.tsx b/app/network-configuration/page.tsx index 412395f8..ad0f7937 100644 --- a/app/network-configuration/page.tsx +++ b/app/network-configuration/page.tsx @@ -1,8 +1,6 @@ "use client"; import React, { useState } from "react"; import { - Row, - Col, Button, Card, ConfirmationButton, @@ -15,6 +13,8 @@ import { NetworkSliceTable } from "@/components/NetworkSliceTable"; import Loader from "@/components/Loader"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "@/utils/queryKeys"; +import PageHeader from "@/components/PageHeader"; +import PageContent from "@/components/PageContent"; const NetworkConfiguration = () => { const queryClient = useQueryClient(); @@ -38,21 +38,18 @@ const NetworkConfiguration = () => { } return ( -
- - -

Network Slices ({networkSlices.length})

- {networkSlices.length === 0 && } - {networkSlices.length > 0 && ( - <> -
- -
+ <> + {networkSlices.length > 0 && ( + + + + )} + + {networkSlices.length === 0 && } + {networkSlices.length > 0 && ( + <> {networkSlices.map((slice) => (

{slice.SliceName}

@@ -82,14 +79,13 @@ const NetworkConfiguration = () => { ))} )} - -
+ {isModalVisible && ( )} -
+ ); }; export default NetworkConfiguration; diff --git a/app/subscribers/page.tsx b/app/subscribers/page.tsx index ceca1e8d..bef2c70f 100644 --- a/app/subscribers/page.tsx +++ b/app/subscribers/page.tsx @@ -4,8 +4,6 @@ import React, { useState } from "react"; import { Button, MainTable, - Row, - Col, ConfirmationButton, } from "@canonical/react-components"; import CreateSubscriberModal from "@/components/CreateSubscriberModal"; @@ -15,6 +13,8 @@ import { deleteSubscriber } from "@/utils/deleteSubscriber"; import Loader from "@/components/Loader"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "@/utils/queryKeys"; +import PageHeader from "@/components/PageHeader"; +import PageContent from "@/components/PageContent"; export type Subscriber = { plmnID: string; @@ -81,17 +81,16 @@ const Subscribers = () => { } return ( - - -

Subscribers ({subscribers.length})

-
- - -
+ <> + + + + + { ]} rows={tableContent} /> - + {isModalVisible && } -
+ ); }; export default Subscribers; diff --git a/components/Logo.tsx b/components/Logo.tsx new file mode 100644 index 00000000..33fcd09d --- /dev/null +++ b/components/Logo.tsx @@ -0,0 +1,18 @@ +import React, { FC } from "react"; + +const Logo: FC = () => { + return ( + +
+ circle of friends +
+ 5G NMS +
+ ); +}; + +export default Logo; diff --git a/components/Navigation.tsx b/components/Navigation.tsx new file mode 100644 index 00000000..d673fa88 --- /dev/null +++ b/components/Navigation.tsx @@ -0,0 +1,149 @@ +import React, { FC, MouseEvent, useEffect, useState } from "react"; +import { Button, Icon } from "@canonical/react-components"; +import classnames from "classnames"; +import Logo from "./Logo"; +import { usePathname } from "next/navigation"; + +const Navigation: FC = () => { + const pathname = usePathname() + const [isCollapsed, setCollapsed] = useState(false); + + const softToggleMenu = () => { + if (window.innerWidth < 620) { + setCollapsed((prev) => !prev); + } + }; + + const hardToggleMenu = (e: MouseEvent) => { + setCollapsed((prev) => !prev); + e.stopPropagation(); + }; + + return ( + <> +
+
+
+ +
+ +
+
+
+
+ + + ); +}; + +export default Navigation; diff --git a/components/PageContent.tsx b/components/PageContent.tsx new file mode 100644 index 00000000..ba56458c --- /dev/null +++ b/components/PageContent.tsx @@ -0,0 +1,20 @@ +"use client"; +import React, { FC, ReactNode } from "react"; +import { Col, Row, } from "@canonical/react-components"; + +interface Props { + children: ReactNode; +} + +const PageContent: FC = ({ children }) => { + return ( + + +
+ {children} +
+ +
+ ); +}; +export default PageContent; diff --git a/components/PageHeader.tsx b/components/PageHeader.tsx new file mode 100644 index 00000000..0cf7a6f4 --- /dev/null +++ b/components/PageHeader.tsx @@ -0,0 +1,25 @@ +"use client"; +import React, { FC, ReactNode } from "react"; +import { Col, Row, } from "@canonical/react-components"; + +interface Props { + title: string; + children: ReactNode; +} + +const PageHeader: FC = ({ title, children }) => { + + return ( + + +
+

{title}

+
+ {children} +
+
+ +
+ ); +}; +export default PageHeader;