diff --git a/app/assets/css/app.css b/app/assets/css/app.css index 4ae31fb5e..553173254 100644 --- a/app/assets/css/app.css +++ b/app/assets/css/app.css @@ -44,11 +44,6 @@ body, white-space: normal !important; } -.logo { - display: inline; - max-width: 155px; - max-height: 55px; -} .legend .title { padding: 0 0.3em; margin: 0.5em; diff --git a/app/assets/ico/logomark.svg b/app/assets/ico/logomark.svg new file mode 100644 index 000000000..b7679d482 --- /dev/null +++ b/app/assets/ico/logomark.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/portainer/services/api/status.service.ts b/app/portainer/services/api/status.service.ts index 6c838b3fe..8e3190d6b 100644 --- a/app/portainer/services/api/status.service.ts +++ b/app/portainer/services/api/status.service.ts @@ -65,6 +65,10 @@ export async function getVersionStatus() { } } +export function useVersionStatus() { + return useQuery(['version'], () => getVersionStatus()); +} + function buildUrl(action?: string) { let url = '/status'; diff --git a/app/react/components/buttons/Button.css b/app/react/components/buttons/Button.css index 49edbbf3c..aaaf34a79 100644 --- a/app/react/components/buttons/Button.css +++ b/app/react/components/buttons/Button.css @@ -2,7 +2,7 @@ padding: 0; margin: 0; background-color: transparent; - width: 1em; + border: 0; } .btn-none:focus { diff --git a/app/react/sidebar/Footer.tsx b/app/react/sidebar/Footer.tsx deleted file mode 100644 index 4ea94c05c..000000000 --- a/app/react/sidebar/Footer.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { useState } from 'react'; -import { useQuery } from 'react-query'; -import clsx from 'clsx'; -import { Database, Hash, Server, Tag, Tool } from 'react-feather'; -import { DialogOverlay } from '@reach/dialog'; - -import { - getStatus, - getVersionStatus, -} from '@/portainer/services/api/status.service'; - -import { Button } from '@@/buttons'; - -import { UpdateNotification } from './UpdateNotifications'; -import styles from './Footer.module.css'; -import '@reach/dialog/styles.css'; - -export function Footer() { - const [showBuildInfo, setShowBuildInfo] = useState(false); - const statusQuery = useStatus(); - const versionQuery = useVersionStatus(); - - if (!statusQuery.data || !versionQuery.data) { - return null; - } - - const { Edition, Version } = statusQuery.data; - const { ServerVersion, DatabaseVersion, Build } = versionQuery.data; - - function toggleModal() { - setShowBuildInfo(!showBuildInfo); - } - - return ( - <> - -
-
-
- -
Portainer {Edition}
-
-
-
- - - - - - - - - - - -
- - - Server Version: {ServerVersion} - - - - - Database Version: {DatabaseVersion} - -
- - - CI Build Number: {Build.BuildNumber} - - - - - Image Tag: {Build.ImageTag} - -
-
-
- - - Compilation tools: - - -
- - Nodejs v{Build.NodejsVersion} - - - Yarn v{Build.YarnVersion} - - - Webpack v{Build.WebpackVersion} - - - Go v{Build.GoVersion} - -
-
-
-
- -
-
-
-
- -
- {process.env.PORTAINER_EDITION === 'CE' && } -
- © - Portainer {Edition} - - - {Version} - - - {process.env.PORTAINER_EDITION === 'CE' && ( - - Upgrade - - )} -
-
- - ); -} - -function useStatus() { - return useQuery(['status'], () => getStatus()); -} - -function useVersionStatus() { - return useQuery(['version'], () => getVersionStatus()); -} diff --git a/app/react/sidebar/Footer/BuildInfoModal.tsx b/app/react/sidebar/Footer/BuildInfoModal.tsx new file mode 100644 index 000000000..1a550cc92 --- /dev/null +++ b/app/react/sidebar/Footer/BuildInfoModal.tsx @@ -0,0 +1,126 @@ +import { useState } from 'react'; +import { Database, Hash, Server, Tag, Tool } from 'react-feather'; +import { DialogOverlay } from '@reach/dialog'; + +import { + useStatus, + useVersionStatus, +} from '@/portainer/services/api/status.service'; + +import { Button } from '@@/buttons'; + +import styles from './Footer.module.css'; + +export function BuildInfoModalButton() { + const [isBuildInfoVisible, setIsBuildInfoVisible] = useState(false); + const statusQuery = useStatus(); + + if (!statusQuery.data) { + return null; + } + + const { Version } = statusQuery.data; + + return ( + <> + + {isBuildInfoVisible && ( + setIsBuildInfoVisible(false)} /> + )} + + ); +} + +function BuildInfoModal({ closeModal }: { closeModal: () => void }) { + const versionQuery = useVersionStatus(); + const statusQuery = useStatus(); + + if (!statusQuery.data || !versionQuery.data) { + return null; + } + + const { Edition } = statusQuery.data; + const { ServerVersion, DatabaseVersion, Build } = versionQuery.data; + + return ( + +
+
+
+ +
Portainer {Edition}
+
+
+
+ + + + + + + + + + + +
+ + + Server Version: {ServerVersion} + + + + + Database Version: {DatabaseVersion} + +
+ + + CI Build Number: {Build.BuildNumber} + + + + + Image Tag: {Build.ImageTag} + +
+
+
+ + + Compilation tools: + + +
+ + Nodejs v{Build.NodejsVersion} + + + Yarn v{Build.YarnVersion} + + + Webpack v{Build.WebpackVersion} + + Go v{Build.GoVersion} +
+
+
+
+ +
+
+
+
+ ); +} diff --git a/app/react/sidebar/Footer.module.css b/app/react/sidebar/Footer/Footer.module.css similarity index 100% rename from app/react/sidebar/Footer.module.css rename to app/react/sidebar/Footer/Footer.module.css diff --git a/app/react/sidebar/Footer/Footer.tsx b/app/react/sidebar/Footer/Footer.tsx new file mode 100644 index 000000000..dd19ce370 --- /dev/null +++ b/app/react/sidebar/Footer/Footer.tsx @@ -0,0 +1,59 @@ +import { PropsWithChildren } from 'react'; +import clsx from 'clsx'; + +import { isBE } from '@/portainer/feature-flags/feature-flags.service'; + +import { UpdateNotification } from './UpdateNotifications'; +import { BuildInfoModalButton } from './BuildInfoModal'; +import '@reach/dialog/styles.css'; +import styles from './Footer.module.css'; +import Logo from './portainer_logo.svg?c'; + +export function Footer() { + return isBE ? : ; +} + +function CEFooter() { + return ( +
+ + + + + Community Edition + + + + + Upgrade + + +
+ ); +} + +function BEFooter() { + return ( +
+ + © + Portainer Business Edition + + + +
+ ); +} + +function FooterContent({ children }: PropsWithChildren) { + return ( +
+ {children} +
+ ); +} diff --git a/app/react/sidebar/UpdateNotifications.module.css b/app/react/sidebar/Footer/UpdateNotifications.module.css similarity index 100% rename from app/react/sidebar/UpdateNotifications.module.css rename to app/react/sidebar/Footer/UpdateNotifications.module.css diff --git a/app/react/sidebar/UpdateNotifications.tsx b/app/react/sidebar/Footer/UpdateNotifications.tsx similarity index 100% rename from app/react/sidebar/UpdateNotifications.tsx rename to app/react/sidebar/Footer/UpdateNotifications.tsx diff --git a/app/react/sidebar/Footer/index.ts b/app/react/sidebar/Footer/index.ts new file mode 100644 index 000000000..65e2506fa --- /dev/null +++ b/app/react/sidebar/Footer/index.ts @@ -0,0 +1 @@ +export { Footer } from './Footer'; diff --git a/app/react/sidebar/Footer/portainer_logo.svg b/app/react/sidebar/Footer/portainer_logo.svg new file mode 100644 index 000000000..1bf390370 --- /dev/null +++ b/app/react/sidebar/Footer/portainer_logo.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/react/sidebar/Header.module.css b/app/react/sidebar/Header.module.css new file mode 100644 index 000000000..404b9b8d0 --- /dev/null +++ b/app/react/sidebar/Header.module.css @@ -0,0 +1,5 @@ +.logo { + display: inline; + max-height: 55px; + max-width: min(100%, 230px); +} diff --git a/app/react/sidebar/Header.tsx b/app/react/sidebar/Header.tsx index 144e3b19a..2fa0ebf3a 100644 --- a/app/react/sidebar/Header.tsx +++ b/app/react/sidebar/Header.tsx @@ -1,37 +1,74 @@ import { ChevronsLeft, ChevronsRight } from 'react-feather'; +import clsx from 'clsx'; -import defaultLogo from '@/assets/images/logo_small_alt.png'; +import { isBE } from '@/portainer/feature-flags/feature-flags.service'; +import smallLogo from '@/assets/ico/logomark.svg'; import { Link } from '@@/Link'; +import fullLogoBE from './portainer_logo-BE.svg'; +import fullLogoCE from './portainer_logo-CE.svg'; import { useSidebarState } from './useSidebarState'; +import styles from './Header.module.css'; interface Props { logo?: string; } -export function Header({ logo }: Props) { +export function Header({ logo: customLogo }: Props) { const { toggle, isOpen } = useSidebarState(); return ( -
- - {!logo - {isOpen && 'portainer.io'} - +
+
+ + + + {isOpen && customLogo && ( +
+ Powered by + + {isBE ? ( + 'portainer business' + ) : ( + + portainer community + + )} + +
+ )} +
); } + +function getLogo(isOpen: boolean, customLogo?: string) { + if (customLogo) { + return customLogo; + } + + if (!isOpen) { + return smallLogo; + } + + return isBE ? fullLogoBE : fullLogoCE; +} + +function Logo({ + customLogo, + isOpen, +}: { + customLogo?: string; + isOpen: boolean; +}) { + const logo = getLogo(isOpen, customLogo); + + return ( + Logo + ); +} diff --git a/app/react/sidebar/portainer_logo-BE.svg b/app/react/sidebar/portainer_logo-BE.svg new file mode 100644 index 000000000..ed7ab076d --- /dev/null +++ b/app/react/sidebar/portainer_logo-BE.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/react/sidebar/portainer_logo-CE.svg b/app/react/sidebar/portainer_logo-CE.svg new file mode 100644 index 000000000..7b6c83a00 --- /dev/null +++ b/app/react/sidebar/portainer_logo-CE.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +