This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git

commit 09ea30ee09b10db001105bdcfe7f901a4592690a
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Fri Feb 27 16:43:56 2026 -0500

    Front-end App for 4.18.0
---
 karavan-app/src/main/webui/src/karavan/app/App.tsx |  20 +-
 .../src/main/webui/src/karavan/app/MainHook.tsx    |  31 +-
 .../main/webui/src/karavan/app/ReadinessPanel.tsx  |  29 +-
 .../main/webui/src/karavan/app/login/LoginPage.css | 358 ++++++++++++++++++++-
 .../main/webui/src/karavan/app/login/LoginPage.tsx | 215 ++++++++++---
 .../webui/src/karavan/app/login/UserPopupOidc.tsx  |   1 -
 .../src/karavan/app/navigation/MainRoutes.tsx      |  57 +++-
 .../src/karavan/app/navigation/NavigationMenu.tsx  |  50 ++-
 .../karavan/app/navigation/NotAuthorizedPage.tsx   |  16 +
 .../src/karavan/app/navigation/PageNavigation.css  | 178 ++++++----
 .../src/karavan/app/navigation/PageNavigation.tsx  | 137 ++++----
 .../webui/src/karavan/app/navigation/Routes.ts     |  27 +-
 .../webui/src/karavan/app/theme/DarkModeToggle.tsx |  16 +
 .../webui/src/karavan/app/theme/ThemeContext.tsx   |  45 ++-
 14 files changed, 940 insertions(+), 240 deletions(-)

diff --git a/karavan-app/src/main/webui/src/karavan/app/App.tsx 
b/karavan-app/src/main/webui/src/karavan/app/App.tsx
index e7f7536e..a75ee5d1 100644
--- a/karavan-app/src/main/webui/src/karavan/app/App.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/App.tsx
@@ -1,12 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import React, {useContext, useEffect, useRef} from "react";
 import './App.css';
 import {mainHook} from "./MainHook";
-import {Notification} from "@features/integration/designer/utils/Notification";
+import {Notification} from "@features/project/designer/utils/Notification";
 import {NotificationApi} from "@api/NotificationApi";
 import {AuthContext} from "@api/auth/AuthProvider";
 import {AuthApi, getCurrentUser} from "@api/auth/AuthApi";
 import {PLATFORM_DEVELOPER} from "@models/AccessModels";
-import {PageNavigation} from "@app/navigation/PageNavigation";
+import PageNavigation from "@app/navigation/PageNavigation";
 import {MainRoutes} from "@app/navigation/MainRoutes";
 import {ReadinessPanel} from "@app/ReadinessPanel";
 import {useReadinessStore} from "@stores/ReadinessStore";
diff --git a/karavan-app/src/main/webui/src/karavan/app/MainHook.tsx 
b/karavan-app/src/main/webui/src/karavan/app/MainHook.tsx
index b27c052a..09f53db3 100644
--- a/karavan-app/src/main/webui/src/karavan/app/MainHook.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/MainHook.tsx
@@ -1,8 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import {KaravanApi} from "@api/KaravanApi";
 import {ComponentApi} from "@karavan-core/api/ComponentApi";
-import {AppConfig, ContainerStatus, Project} from "@models/ProjectModels";
-import {useAppConfigStore, useProjectsStore, useStatusesStore} from 
"@stores/ProjectStore";
-import {InfrastructureAPI} from 
"@features/integration/designer/utils/InfrastructureAPI";
+import {AppConfig, Project} from "@models/ProjectModels";
+import {useAppConfigStore, useProjectsStore} from "@stores/ProjectStore";
+import {InfrastructureAPI} from 
"@features/project/designer/utils/InfrastructureAPI";
 import {shallow} from "zustand/shallow";
 import {ProjectService} from "@services/ProjectService";
 import {SpiBeanApi} from "@karavan-core/api/SpiBeanApi";
@@ -11,21 +27,20 @@ import {useContext} from "react";
 import {AuthContext} from "@api/auth/AuthProvider";
 import {useReadinessStore} from "@stores/ReadinessStore";
 import {AuthApi} from "@api/auth/AuthApi";
+import {useContainerStatusesStore} from "@stores/ContainerStatusesStore";
 
 export function mainHook () {
 
     const {readiness} = useReadinessStore();
     const [setConfig, setDockerInfo] = useAppConfigStore((s) => [s.setConfig, 
s.setDockerInfo], shallow)
     const [setProjects] = useProjectsStore((s) => [s.setProjects], shallow)
-    const [setContainers] = useStatusesStore((state) => [state.setContainers], 
shallow);
+    const {fetchContainers} = useContainerStatusesStore();
     const [selectedEnv, selectEnvironment] = useAppConfigStore((state) => 
[state.selectedEnv, state.selectEnvironment], shallow)
     const { user } = useContext(AuthContext);
 
     const getStatuses = () =>  {
         if (user) {
-            KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) 
=> {
-                setContainers(statuses);
-            });
+            fetchContainers();
         }
     }
 
@@ -50,8 +65,6 @@ export function mainHook () {
             updateBeans();
             updateAllConfigurations();
             ProjectService.loadCamelAndCustomKamelets();
-            ProjectService.reloadBlockedTemplates();
-            // updateSupportedComponents(); // not implemented yet
         }
     }
 
diff --git a/karavan-app/src/main/webui/src/karavan/app/ReadinessPanel.tsx 
b/karavan-app/src/main/webui/src/karavan/app/ReadinessPanel.tsx
index b777151b..9f75b8d0 100644
--- a/karavan-app/src/main/webui/src/karavan/app/ReadinessPanel.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/ReadinessPanel.tsx
@@ -1,27 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import React from "react";
 import './App.css';
 import {useReadinessStore} from "@stores/ReadinessStore";
 import {useDataPolling} from "@shared/polling/useDataPolling";
 import {Bullseye, Content, ContentVariants, Flex, FlexItem, ProgressStep, 
ProgressStepper, Spinner, Tooltip, TooltipPosition} from 
"@patternfly/react-core";
 import {mainHook} from "@app/MainHook";
-import {KaravanIcon} from "@features/integration/designer/icons/KaravanIcons";
+import {KaravanIcon} from "@features/project/designer/icons/KaravanIcons";
+import {PlatformLogoBase64} from "@app/navigation/PlatformLogo";
 
 const FAST_INTERVAL = 1000; // 1 second (when not ready)
 const SLOW_INTERVAL = 10000; // 10 seconds (when ready)
 
 export function ReadinessPanel() {
 
-    // 1. Subscribe to the state and action
     const { readiness, fetchReadiness } = useReadinessStore();
-
-    // 2. Determine the dynamic interval based on the current state
     const isReady = readiness && readiness.status === true;
-
     const currentInterval = isReady ? SLOW_INTERVAL : FAST_INTERVAL;
-
-    console.log(`Polling interval set to: ${currentInterval}ms`);
-
-    // 3. Pass the dynamic interval to the hook
     useDataPolling('readiness', fetchReadiness, currentInterval);
     const {showSpinner, showStepper} = mainHook();
 
@@ -31,7 +40,7 @@ export function ReadinessPanel() {
             <Bullseye className="loading-page">
                 <Flex direction={{default: "column"}} 
justifyContent={{default: "justifyContentCenter"}}>
                     <FlexItem style={{textAlign: "center"}}>
-                        {KaravanIcon()}
+                        <img src={PlatformLogoBase64()} className="logo" 
alt='logo'/>
                         <Content>
                             <Content component={ContentVariants.h2}>
                                 Waiting for services
diff --git a/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.css 
b/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.css
index 439a3243..40e29f1a 100644
--- a/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.css
+++ b/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.css
@@ -1,19 +1,355 @@
-.karavan .login-page {
+.karavan-container {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-evenly;
+    align-items: center;
+    height: 100vh;
+    width: 100vw;
+    overflow: hidden;
+}
+
+/* --- LEFT PANEL: Branding --- */
+.karavan-brand-panel {
+    width: 35%;
+    height: 100vh;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    background: var(--pf-t--color--gray--95);
+    gap: 48px;
+}
+
+.brand-content {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
     width: 100%;
-    height: 100%;
+
+    .brand-name {
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        gap: 20px;
+
+        .brand-logo-container {
+            display: flex;
+            flex-direction: row;
+            align-items: center;
+            gap: 12px;
+
+            .logo {
+                height: 40px;
+                width: 40px;
+            }
+            .platform-name-text {
+                font-size: 24px;
+                font-weight: 700;
+                letter-spacing: 1px;
+            }
+        }
+        .gradient-text-blue {
+            background: linear-gradient(to right, #4394e5, #92c5f9);
+
+            -webkit-background-clip: text; /* Safari / Chrome */
+            background-clip: text;
+            color: transparent;
+        }
+        .gradient-text-blue-gold {
+            background: linear-gradient(to right, #fda422, #ec7a08);
+
+            -webkit-background-clip: text; /* Safari / Chrome */
+            background-clip: text;
+            color: transparent;
+        }
+        .gradient-text-gold {
+            background: linear-gradient(to right, #ec7a08, #db5b04);
+
+            -webkit-background-clip: text; /* Safari / Chrome */
+            background-clip: text;
+            color: transparent;
+        }
+
+        .tagline1 {
+            font-weight: 800;
+            font-size: 56px;
+            line-height: 1.1;
+            text-align: center;
+        }
+
+        .tagline2 {
+            font-weight: 400;
+            line-height: 1.5;
+            color: #b9dafc; /* Tblue-200 for subtitle */
+            font-size: 20px;
+            letter-spacing: 0.5px;
+            margin-top: 16px;
+            text-align: center;
+        }
+    }
+}
+
+.solar-content {
     display: flex;
     flex-direction: column;
-    background-color: 
var(--pf-t--global--background--color--secondary--default);
+    justify-content: center;
+    gap: 8px;
+    /* Container that holds both the center logo and the ring */
+    .solar-system {
+        position: relative;
+        width: 350px; /* Width of the whole system */
+        height: 350px;
+        margin: 0 auto;
+        display: flex;
+        flex-direction: column;
+        justify-content: flex-end;
+    }
 }
-.karavan .login-page .logo-panel {
-    background-color: var(--pf-t--color--gray--95);
-    height: 64px;
+
+.orbit-lines-svg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    transform: translate(-50%, -50%);
+    width: 350px; /* Must match SVG viewBox width */
+    height: 350px; /* Must match SVG viewBox height */
+    z-index: 1; /* Lowest level, behind icons */
+    pointer-events: none; /* Let clicks pass through to the logo */
+
+    /* OPTIONAL: Rotate the gradient slowly for extra "flash" */
+    animation: slowSpin 20s linear infinite;
+}
+
+/* Ensure your icons are on top */
+.orbit-ring {
+    z-index: 5;
+}
+
+/* 1. THE STATIC CENTER LOGO */
+.static-sun {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%); /* Perfectly centered */
+    width: 100px;
+    height: 100px;
+    z-index: 10; /* Ensures it sits ON TOP of the ring lines */
+
+    /* Optional: A nice glow behind the main logo */
+    background: radial-gradient(
+            circle,
+            rgba(236, 122, 8, 0.4) 0%,
+            rgba(236, 122, 8, 0) 60%
+    );
+    border-radius: 50%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
 }
 
-.karavan .login-page .login {
-    width: 470px;
+.static-sun a {
+    pointer-events: auto;
+    width: 48px; /* Your actual logo size */
+    height: 48px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    transition: transform 0.3s ease, filter 0.3s ease;
+    &:hover {
+        transform: scale(1.1);
+    }
 }
 
-.karavan .login-page .logo {
-    /*height: 48px;*/
-}
\ No newline at end of file
+.static-sun .logo {
+    width: 100%; /* Adjust to fit inside the glow */
+    height: auto;
+    filter: none;
+}
+
+/* 1. The Ring spins clockwise */
+.orbit-ring {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    animation: slowSpin 60s linear infinite;
+}
+
+/* 2. The Item is placed on the ring (Static Position) */
+.orbit-item {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 32px; /* Adjusted size */
+    height: 32px;
+    margin-top: -16px;
+    margin-left: -16px;
+
+    /* Logic: Rotate to angle -> Move out -> Rotate back to upright */
+    transform:
+            rotate(var(--angle))
+            translate(var(--radius))
+            rotate(calc(var(--angle) * -1));
+}
+
+/* 3. The Counter-Rotator spins Counter-Clockwise
+   This cancels out the ring's rotation so the icon always faces North */
+.counter-rotator {
+    width: 100%;
+    height: 100%;
+    animation: slowSpinReverse 60s linear infinite;
+}
+
+/* 4. The Image handles ONLY the look and hover scale */
+.counter-rotator img,
+.counter-rotator svg {
+    display: block;
+    width: 100%;
+    height: 100%;
+    object-fit: contain;
+
+    /* Look */
+    /*filter: grayscale(100%);*/
+    transition: transform 0.3s ease, filter 0.3s ease;
+}
+
+/* Hover Effect: Scale the image, NOT the rotating wrapper */
+.counter-rotator:hover img,
+.counter-rotator:hover svg {
+    filter: none;
+    transform: scale(1.2);
+}
+
+/* --- Keyframes --- */
+@keyframes slowSpin {
+    from { transform: rotate(0deg); }
+    to { transform: rotate(360deg); }
+}
+
+@keyframes slowSpinReverse {
+    /* Must match the ring's speed but in REVERSE */
+    from { transform: rotate(360deg); }
+    to { transform: rotate(0deg); }
+}
+
+.anchor-line {
+    width: 1px;
+    height: 50px; /* Length of line */
+    background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 10%, 
rgba(236, 122, 8, 1) 50%, rgba(255, 255, 255, 0.1) 100%);
+    margin: 0 auto 8px auto; /* Center it and add space below */
+}
+
+.brand-footer {
+    opacity: 0.6;
+    font-size: 0.85rem;
+    letter-spacing: 1px;
+    text-transform: uppercase;
+    text-align: center;
+    color: var(--pf-t--color--blue--10);
+    padding-bottom: 60px;
+}
+
+/* --- RIGHT PANEL: Minimal Form --- */
+.karavan-form-panel {
+    width: 65%;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    align-items: center;
+}
+
+.form-wrapper {
+    width: 100%;
+    max-width: 400px;
+    padding: 20px;
+
+    .login {
+        &::before {
+            position: absolute;
+            inset: 0;
+            pointer-events: none;
+            content: "";
+            border-color: var(--pf-t--color--blue--60);
+            border-style: var(--pf-v6-c-card--BorderStyle);
+            border-width: var(--pf-v6-c-card--BorderWidth);
+            border-radius: inherit;
+        }
+
+        .login-header {
+            color: var(--pf-t--global--text--color--subtle);
+        }
+
+        .environment-dev {
+            background-color: var(--pf-t--global--text--color--brand--default);
+        }
+        .environment-default {
+            background-color: 
var(--pf-t--global--icon--color--status--warning--default);
+        }
+        .environment-prod {
+            background-color: 
var(--pf-t--global--icon--color--status--danger--default);
+        }
+    }
+
+    .pf-v6-c-form-control, .pf-v6-c-text-input-group {
+        /*background-color: transparent;*/
+    }
+
+    .pf-v6-c-card__header, .pf-v6-c-card__title, .pf-v6-c-card__body {
+        padding-block-end: var(--pf-t--global--spacer--xl);
+    }
+
+    .pf-v6-c-card__footer {
+        padding-block-end: var(--pf-t--global--spacer--md);
+    }
+
+    .platform-version {
+        color: var(--pf-t--global--text--color--subtle);
+        font-size: var(--pf-t--global--font--size--xs);
+    }
+
+    .button {
+        width: 100%;
+    }
+
+    .button-disabled {
+        opacity: 0.5;
+        cursor: not-allowed;
+    }
+}
+
+.powered-by-logo {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-start;
+    align-items: center;
+
+
+    .logo, .icon{
+        width: 32px;
+        height: 32px;
+    }
+    .openapi-logo {
+        height: 36px;
+    }
+    .asyncapi-logo {
+        height: 32px;
+    }
+    .json-schema-logo {
+        height: 28px;
+    }
+    .groovy-logo {
+        height: 32px;
+    }
+    .jib-logo {
+        height: 32px;
+    }
+    .infinispan-logo {
+        height: 30px;
+    }
+    .jkube-logo {
+        height: 32px;
+    }
+    .patternfly-logo {
+        height: 28px;
+    }
+}
diff --git a/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.tsx 
b/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.tsx
index 1d2cefc0..aeac2333 100644
--- a/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/login/LoginPage.tsx
@@ -1,12 +1,12 @@
-import React, {useContext} from 'react';
+import React, {useContext, useEffect} from 'react';
 import {
-    ActionGroup,
     Alert,
-    Bullseye,
     Button,
     Card,
     CardBody,
-    CardTitle,
+    CardFooter,
+    CardHeader,
+    Checkbox,
     Content,
     Form,
     FormAlert,
@@ -19,11 +19,15 @@ import {
 import './LoginPage.css'
 import EyeIcon from '@patternfly/react-icons/dist/esm/icons/eye-icon';
 import EyeSlashIcon from 
'@patternfly/react-icons/dist/esm/icons/eye-slash-icon';
-import DarkModeToggle from "../theme/DarkModeToggle";
-import {MainToolbar} from "@shared/ui/MainToolbar";
 import {AuthContext} from "@api/auth/AuthProvider";
 import {AuthApi} from "@api/auth/AuthApi";
-import {KaravanIcon} from "@features/integration/designer/icons/KaravanIcons";
+import {CamelIcon, KaravanIcon, OpenApiIcon} from 
"@features/project/designer/icons/KaravanIcons";
+import {SvgIcon} from "@shared/icons/SvgIcon";
+import jibLogo from '@shared/icons/jib.png';
+import {PlatformNameForToolbar, PlatformVersion} from 
"@shared/ui/PlatformLogos";
+import PlatformLogo from "@app/navigation/PlatformLogo";
+import OrbitLines from "@app/login/OrbitLines";
+import {useReadinessStore} from "@stores/ReadinessStore";
 
 export const LoginPage: React.FunctionComponent = () => {
 
@@ -33,19 +37,31 @@ export const LoginPage: React.FunctionComponent = () => {
     const [showError, setShowError] = React.useState(false);
     const [error, setError] = React.useState('');
     const {reload} = useContext(AuthContext);
+    const { readiness } = useReadinessStore();
+
+    const [titleSvg, setTitleSvg] = React.useState<string>();
+
+    useEffect(() => {
+    }, []);
 
     function onLoginButtonClick(event: any) {
-        event.preventDefault();
-        AuthApi.login(username, password, (ok, res) => {
-            if (!ok) {
-                setError(res?.response?.data);
-                setShowError(true);
-            } else {
-                setError('');
-                setShowError(false);
-                reload();
-            }
-        })
+        if (!getButtonDisabled()) {
+            event.preventDefault();
+            AuthApi.login(username, password, (ok, res) => {
+                if (!ok) {
+                    setError(res?.response?.data);
+                    setShowError(true);
+                } else {
+                    setError('');
+                    setShowError(false);
+                    reload();
+                }
+            })
+        }
+    }
+
+    function getButtonDisabled(): boolean {
+        return (username?.length < 3 || password?.length < 3);
     }
 
     function onKeyDown(event: React.KeyboardEvent<HTMLFormElement>): void {
@@ -56,24 +72,74 @@ export const LoginPage: React.FunctionComponent = () => {
         }
     }
 
+
+    function getLogos() {
+        return [
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://github.com/apache/camel-karavan"; 
target="_blank">{KaravanIcon("logo")}</a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://www.openapis.org/"; 
target="_blank"><OpenApiIcon className={"logo"}/></a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://www.asyncapi.com/"; 
target="_blank">{SvgIcon({icon: 'asyncapi', className: 'asyncapi-logo'})}</a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://json-schema.org/"; target="_blank">
+                    {SvgIcon({icon: 'json-schema-dark', className: 
'json-schema-logo'})}
+                </a>
+            </div>,
+            // <div className="powered-by-logo counter-rotator">
+            //     <a href="https://groovy-lang.org/"; 
target="_blank">{SvgIcon({icon: 'groovy', className: 'groovy-logo'})}</a>
+            // </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://github.com/GoogleContainerTools/jib"; 
target="_blank">
+                    <img src={jibLogo} alt="Logo" className="jib-logo"/>
+                </a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://camel.apache.org/"; 
target="_blank">{CamelIcon()}</a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://www.patternfly.org/"; target={'_blank'}>
+                    {SvgIcon({icon: 'patternfly', className: 
'patternfly-logo'})}
+                </a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://eclipse.dev/jkube/"; target="_blank">
+                    {SvgIcon({icon: 'jkube', className: 'jkube-logo'})}
+                </a>
+            </div>,
+            <div className="powered-by-logo counter-rotator">
+                <a href="https://www.keycloak.org/"; target="_blank">
+                    {SvgIcon({icon: 'keycloak', className: 'patternfly-logo'})}
+                </a>
+            </div>
+        ];
+    }
+
+    const LOGOS = getLogos();
+
     function getLoginForm() {
         return (
             <Form onKeyDown={onKeyDown}>
-                <FormGroup fieldId="username" label="Username" isRequired>
+                <FormGroup fieldId="username">
                     <TextInput className="text-field"
                                type="text"
                                id="username"
                                name="username"
                                value={username}
+                               placeholder={"Username"}
                                onChange={(_, value) => setUsername(value)}/>
                 </FormGroup>
-                <FormGroup fieldId="password" label="Password" isRequired>
+                <FormGroup fieldId="password">
                     <TextInputGroup>
                         <TextInputGroupMain className="text-field"
                                             type={passwordHidden ? "password" 
: 'text'}
                                             id="password"
                                             name="password"
                                             value={password}
+                                            placeholder={"Password"}
                                             onChange={(_, value) => 
setPassword(value)}
                         />
                         <TextInputGroupUtilities>
@@ -87,14 +153,6 @@ export const LoginPage: React.FunctionComponent = () => {
                         </TextInputGroupUtilities>
                     </TextInputGroup>
                 </FormGroup>
-                <ActionGroup>
-                    <Button variant="primary"
-                            style={{width: '100%'}}
-                            onClick={onLoginButtonClick}
-                    >
-                        Login
-                    </Button>
-                </ActionGroup>
                 {showError && (
                     <FormAlert>
                         <Alert variant="danger" 
title={<div>{error?.toString()}</div>} aria-live="polite" isInline/>
@@ -104,29 +162,88 @@ export const LoginPage: React.FunctionComponent = () => {
         )
     }
 
-    return (
-        <div className='login-page'>
-            <div className="logo-panel">
-                <MainToolbar title={<></>} toolsStart={<></>} tools={
-                    <div id="toolbar-group-types" style={{display: 'flex', 
alignItems: 'center', gap: '8px', height: '65px'}}>
-                        {KaravanIcon()}
-                        <Content component='h1' style={{color: 
'var(--pf-t--color--blue--30)'}}>Apache Camel Karavan</Content>
-                    </div>
-                }/>
+
+    function getRightSide() {
+        const buttonClassName = getButtonDisabled() ? "button button-disabled" 
: "button";
+        return (
+            <div className="karavan-form-panel dark-form">
+                <div className="form-wrapper">
+                    <Card className="login" isLarge>
+                        <CardHeader>
+                            <div style={{display: "flex", flexDirection: 
'row', justifyContent: 'space-between', alignItems: "center"}}>
+                                <Content component='h3' 
className="login-header">Login</Content>
+                                <PlatformVersion 
environment={readiness?.environment}/>
+                            </div>
+                        </CardHeader>
+                        <CardBody>
+                            {getLoginForm()}
+                        </CardBody>
+                        <CardFooter style={{ textAlign: "center" }}>
+                            <Button variant="primary"
+                                    className={buttonClassName}
+                                    onClick={onLoginButtonClick}
+                            >
+                                Access Platform
+                            </Button>
+                        </CardFooter>
+                    </Card>
+                    {/*<DarkModeToggle/>*/}
+                </div>
             </div>
-            <Bullseye>
-                <Card className="login">
-                    <CardTitle>
-                        <Content component="h2">Login</Content>
-                    </CardTitle>
-                    <CardBody>
-                        {getLoginForm()}
-                    </CardBody>
-                </Card>
-            </Bullseye>
-            <div style={{padding: 16}}>
-                <DarkModeToggle/>
+        )
+    }
+
+    function getLeftSide() {
+        return (
+            <div className="karavan-brand-panel">
+                <div className="brand-content">
+                    <div className="brand-name">
+                        <div className="brand-logo-container">
+                            {PlatformLogo("logo")}
+                            <span 
className="platform-name-text">{PlatformNameForToolbar()}</span>
+                        </div>
+                        <div>
+                            <div className="tagline1 
gradient-text-blue">Accelerate</div>
+                            <div className="tagline1 
gradient-text-blue-gold">Integration</div>
+                            <div className="tagline1 
gradient-text-gold">Development</div>
+                        </div>
+                        <Content component='p' className="tagline2">Unified 
Design and Runtime for <br/> APIs • Events • Data Pipelines</Content>
+                    </div>
+                </div>
+                <div className="solar-content">
+                    <div className="solar-system">
+                        <OrbitLines />
+                        <div className="static-sun">
+                            <a href="" target="_blank">
+                                {PlatformLogo("logo")}
+                            </a>
+                        </div>
+                        <div className="orbit-ring">
+                            {LOGOS.map((logo, index) => {
+                                const total = LOGOS.length;
+                                const angle = (360 / total) * index;
+                                // Increased radius slightly to make room for 
the center logo
+                                const radius = 150;
+                                const style = {'--angle': `${angle}deg`, 
'--radius': `${radius}px`,} as React.CSSProperties;
+                                return (
+                                    <div key={index} className="orbit-item" 
style={style}>
+                                        {logo}
+                                    </div>
+                                );
+                            })}
+                        </div>
+                        <div className="anchor-line"></div>
+                        <p className="brand-footer">Powered by</p>
+                    </div>
+                </div>
             </div>
+        )
+    }
+
+    return (
+        <div className="karavan-container">
+            {getLeftSide()}
+            {getRightSide()}
         </div>
     )
 }
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/karavan/app/login/UserPopupOidc.tsx 
b/karavan-app/src/main/webui/src/karavan/app/login/UserPopupOidc.tsx
index 79b8b472..1498d6a9 100644
--- a/karavan-app/src/main/webui/src/karavan/app/login/UserPopupOidc.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/login/UserPopupOidc.tsx
@@ -1,6 +1,5 @@
 import React from 'react';
 import {Badge, Content, ContentVariants, DescriptionList, 
DescriptionListDescription, DescriptionListGroup, DescriptionListTerm, Flex, 
Popover,} from '@patternfly/react-core';
-import '../App.css';
 import UserIcon from "@patternfly/react-icons/dist/esm/icons/user-icon";
 import {shallow} from "zustand/shallow";
 import {useAccessStore} from "@stores/AccessStore";
diff --git 
a/karavan-app/src/main/webui/src/karavan/app/navigation/MainRoutes.tsx 
b/karavan-app/src/main/webui/src/karavan/app/navigation/MainRoutes.tsx
index 5e5822bb..799d4293 100644
--- a/karavan-app/src/main/webui/src/karavan/app/navigation/MainRoutes.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/navigation/MainRoutes.tsx
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import {Navigate, Route, Routes} from 'react-router-dom';
 import React from "react";
 import {NotAuthorizedPage} from "@app/navigation/NotAuthorizedPage";
@@ -5,13 +21,19 @@ import {SystemPage} from "@features/system/SystemPage";
 import {AccessPage} from "@features/access/AccessPage";
 import {DocumentationPage} from "@features/documentation/DocumentationPage";
 import {ROUTES} from "./Routes";
+import {AsyncPage} from "@features/landscape/AsyncPage";
 import {ProtectedRoute} from "@app/navigation/ProtectedRoute";
-import {LoginPage} from "@app/login/LoginPage";
 import {ProjectFunctionHook} from "@app/navigation/ProjectFunctionHook";
-import {ProjectProvider} from "@features/integration/ProjectContext";
-import {DeveloperManager} from 
"@features/integration/developer/DeveloperManager";
-import ProjectPage from "@features/integration/ProjectPage";
-import {IntegrationsPage} from "@features/integrations/IntegrationsPage";
+import {ProjectProvider} from "@features/project/ProjectContext";
+import {DeveloperManager} from "@features/project/developer/DeveloperManager";
+import ProjectPage from "@features/project/ProjectPage";
+import {ProjectsPage} from "@features/projects/ProjectsPage";
+import {SharedSchemasPage} from "@features/schemas/SharedSchemasPage";
+import {DashboardPage} from "@features/dashboard/DashboardPage";
+import {OpenApiPage} from "@features/apis/OpenApiPage";
+import {LoginPage} from "@app/login/LoginPage";
+import {useReadinessStore} from "@stores/ReadinessStore";
+import {SettingsPage} from "@features/settings/SettingsPage";
 
 export function MainRoutes() {
 
@@ -22,32 +44,47 @@ export function MainRoutes() {
                     <LoginPage/>
                 </ProtectedRoute>}
             />
-            <Route path={ROUTES.INTEGRATIONS} element={
+            <Route path={ROUTES.PROJECTS} element={
                 <ProtectedRoute>
                     <ProjectProvider useProjectHook={ProjectFunctionHook}>
-                        <IntegrationsPage key="integrations"/>
+                        <ProjectsPage key="integrations"/>
                     </ProjectProvider>
                 </ProtectedRoute>
             }/>
-            <Route path={ROUTES.INTEGRATION_DETAIL} element={
+            <Route path={ROUTES.PROJECT_DETAIL} element={
                 <ProtectedRoute>
                     <ProjectProvider useProjectHook={ProjectFunctionHook}>
                         <ProjectPage key="project" 
developerManager={<DeveloperManager/>}/>
                     </ProjectProvider>
                 </ProtectedRoute>
             }/>
-            <Route path={ROUTES.INTEGRATION_FILE} element={
+            <Route path={ROUTES.PROJECT_FILE} element={
                 <ProtectedRoute>
                     <ProjectProvider useProjectHook={ProjectFunctionHook}>
                         <ProjectPage key="project" 
developerManager={<DeveloperManager/>}/>
                     </ProjectProvider>
                 </ProtectedRoute>
             }/>
+            <Route path={ROUTES.SETTINGS} element={
+                <ProtectedRoute>
+                    <ProjectProvider useProjectHook={ProjectFunctionHook}>
+                        <SettingsPage/>
+                    </ProjectProvider>
+                </ProtectedRoute>
+            }/>
+            <Route path={ROUTES.SETTINGS_FILE} element={
+                <ProtectedRoute>
+                    <ProjectProvider useProjectHook={ProjectFunctionHook}>
+                        <SettingsPage/>
+                    </ProjectProvider>
+                </ProtectedRoute>
+            }/>
             <Route path={ROUTES.SYSTEM} 
element={<ProtectedRoute><SystemPage/></ProtectedRoute>}/>
             <Route path={ROUTES.DOCUMENTATION} 
element={<ProtectedRoute><DocumentationPage/></ProtectedRoute>}/>
             <Route path={ROUTES.ACL} 
element={<ProtectedRoute><AccessPage/></ProtectedRoute>}/>
             <Route path={ROUTES.FORBIDDEN} element={<NotAuthorizedPage/>}/>
-            <Route path="*" element={<Navigate to={ROUTES.INTEGRATIONS} 
replace/>}/>
+            {/*{readiness?.environment === 'dev' && <Route path="*" 
element={<Navigate to={ROUTES.PROJECTS} replace/>}/>}*/}
+            <Route path="*" element={<Navigate to={ROUTES.DASHBOARD} 
replace/>}/>
         </Routes>
     )
 }
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/karavan/app/navigation/NavigationMenu.tsx 
b/karavan-app/src/main/webui/src/karavan/app/navigation/NavigationMenu.tsx
index 7d47fb8a..6ee1f728 100644
--- a/karavan-app/src/main/webui/src/karavan/app/navigation/NavigationMenu.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/navigation/NavigationMenu.tsx
@@ -1,9 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import {AuthApi, getCurrentUser} from "@api/auth/AuthApi";
 import React from "react";
-import {CamelIcon} from "@features/integration/designer/icons/KaravanIcons";
 import {SvgNavigationIcon} from "@shared/icons/SvgNavigationIcon";
-import {KubernetesIcon} from 
"@features/integration/designer/icons/ComponentIcons";
-import DockerIcon from "@patternfly/react-icons/dist/esm/icons/docker-icon";
+import {KubernetesIcon} from "@features/project/designer/icons/ComponentIcons";
+import {DockerIcon} from "@patternfly/react-icons";
 
 export class MenuItem {
     pageId: string = '';
@@ -17,26 +32,33 @@ export class MenuItem {
     }
 }
 
-export function getNavigationMenu(environment: string, infrastructure: 
string): MenuItem[] {
+export function getNavigationFirstMenu(): MenuItem[] {
+    return [
+        new MenuItem("projects", "Projects", SvgNavigationIcon({icon: 
'apps'})),
+        new MenuItem("settings", "Settings", SvgNavigationIcon({icon: 
'settings'})),
+    ]
+}
+
+
+export function getNavigationSecondMenu(environment: string, infrastructure: 
string): MenuItem[] {
     const iconInfra = infrastructure === 'kubernetes' ? 
KubernetesIcon("infra-icon-k8s") : <DockerIcon className='infra-icon-docker'/>;
 
-    const pages: MenuItem[] = [
-        new MenuItem("integrations", "Integrations", <CamelIcon />),
-    ]
-    // if (environment === 'dev') {
-    //     pages.push(new MenuItem("services", "Services", <ServicesIcon/>))
-    // }
+    const pages: MenuItem[] = []
+
+    if (environment === 'dev') {
+        pages.push(new MenuItem("documentation", "Learn", 
SvgNavigationIcon({icon: 'documentation'})));
+    }
 
     if (getCurrentUser()?.roles?.includes('platform-admin')) {
         pages.push(new MenuItem("system", "System", iconInfra));
     }
 
     if (AuthApi.authType === 'session') {
-        pages.push(new MenuItem("acl", "Access", SvgNavigationIcon({icon: 
'access', width: 24, height: 24})));
-    }
-    if (environment === 'dev') {
-        pages.push(new MenuItem("documentation", "Docs", 
SvgNavigationIcon({icon: 'documentation', width: 24, height: 24})));
+        pages.push(new MenuItem("acl", "Access", SvgNavigationIcon({icon: 
'access'})));
     }
+
+    pages.push(new MenuItem("logout", "Logout", SvgNavigationIcon({icon: 
'logout'})));
+
     return pages;
 }
 
diff --git 
a/karavan-app/src/main/webui/src/karavan/app/navigation/NotAuthorizedPage.tsx 
b/karavan-app/src/main/webui/src/karavan/app/navigation/NotAuthorizedPage.tsx
index 33e01bd9..95db9777 100644
--- 
a/karavan-app/src/main/webui/src/karavan/app/navigation/NotAuthorizedPage.tsx
+++ 
b/karavan-app/src/main/webui/src/karavan/app/navigation/NotAuthorizedPage.tsx
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import React, {useEffect} from "react";
 import {Bullseye, EmptyState, EmptyStateBody, EmptyStateVariant} from 
"@patternfly/react-core";
 import NotAuthorizedIcon from 
"@patternfly/react-icons/dist/esm/icons/user-secret-icon";
diff --git 
a/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.css 
b/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.css
index 73acddf3..a5d0ce44 100644
--- a/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.css
+++ b/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.css
@@ -1,38 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 .karavan .nav-buttons {
     width: fit-content;
     display: flex;
     flex-direction: column;
     height: 100%;
     justify-content: space-between;
-    background-color: var(--pf-t--color--gray--95);
+    background: var(--pf-t--color--gray--95);
 }
-.karavan .nav-buttons .nav-button-part-wrapper {
+
+.karavan .nav-buttons .nav-button-part-wrapper,
+.karavan .nav-buttons .nav-button-part-darkmode {
     display: flex;
     flex-direction: column;
     align-items: center;
-    height: 64px;
     position: relative;
-    .logo {
-        margin-left: 3px;
-        width: 48px;
-        height: 48px;
-    }
 }
 
 .karavan .nav-buttons .environment-wrapper {
     display: flex;
-    flex-direction: row;
+    flex-direction: column;
     justify-content: center;
     align-items: center;
-    margin-bottom: 6px;
-    margin-top: 6px;
+    margin-bottom: 16px;
+    gap: 3px;
+    color: var(--pf-t--color--blue--10);
+    /*margin-top: 6px;*/
+
     .version {
         color: var(--pf-t--color--gray--30);
     }
+
     .environment {
-        border-color: var(--pf-t--color--gray--60);
-        color: var(--pf-t--color--white);
-        background-color: var(--pf-t--color--gray--60);
+        padding: 0 4px 0 4px;
+        border-color: #4bb9ecff;
+        color: var(--pf-t--color--gray--95);
+        background-color: #4bb9ecff;
+        font-weight: normal;
+    }
+
+    .environment-line {
+        display: flex;
+        flex-direction: row;
+        gap: 3px;
+        align-items: center;
+    }
+
+    .login-logo {
+        width: 18px;
+        height: 18px;
+    }
+
+    .icon {
+        width: 16px;
+        height: 16px;
     }
 }
 
@@ -42,33 +78,21 @@
     width: 32px;
     height: 32px;
 }
-.karavan .nav-buttons .pf-v6-c-button {
-    padding: 0;
-    /*width: fit-content;*/
-    height: 64px;
-    /*color: var(--pf-t-global--color--light-100);*/
-}
 
 .karavan .nav-buttons .pf-v6-c-button svg {
-    width: 24px;
-    height: 24px;
-    /*fill: var(--pf-t--color--white);*/
+    width: 16px;
+    height: 16px;
+    fill: #4bb9ec;
 }
 
 /* Adapt navigation for screens less than 800px */
 @media screen and (max-height: 800px) {
-    .karavan .nav-buttons .pf-v6-c-button {
-        height: 50px;
-    }
-    .karavan .nav-buttons .pf-v6-c-button .pf-v6-c-button__icon {
-        /*display: none;*/
+    .karavan .nav-buttons .nav-button {
+        /*padding: 16px 12px 16px 12px;*/
     }
-    .karavan .nav-buttons .pf-v6-c-button .nav-button-badge {
-        top: 2px;
-        right: 2px;
-    }
-    .karavan .nav-buttons .pf-v6-c-button .nav-button-badge .pf-v6-c-badge {
-        font-size: 10px;
+
+    .karavan .nav-buttons .pf-v6-c-button {
+        font-size: 12px;
     }
 }
 
@@ -81,26 +105,38 @@
     background-color: var(--pf-t--color--gray--60);
 }
 
-.karavan .nav-buttons .nav-button-wrapper {
-    .infra-icon-docker {
-        fill: #1074FF;
-    }
-}
 .karavan .nav-buttons .nav-button {
-    border-left-width: 3px;
-    border-left-style: solid;
-    border-left-color: transparent;
     border-top-width: 1px;
     border-top-style: solid;
     border-top-color: var(--pf-t--color--gray--80);
     border-radius: 0;
     display: flex;
-    flex-direction: column;
-    justify-content: center;
+    flex-direction: row;
+    justify-content: flex-start;
     align-items: center;
-    gap: 0;
-    padding: 0 9px 0 6px;
-    color: var(--pf-t--color--white);
+    gap: 4px;
+    padding: 10px 10px 10px 10px;
+    color: var(--pf-t--color--blue--10);
+    &:hover {
+        background: color-mix(in srgb, var(--pf-t--color--blue--60) 50%, 
transparent);
+    }
+
+    .pf-v6-c-button__text {
+
+    }
+    .nav-button-badge {
+        position: absolute;
+        top: 3px;
+        right: 3px;
+        margin: 0;
+        .pf-v6-c-badge {
+            padding: 0 6px;
+            min-width: fit-content;
+            background-color: var(--pf-t--color--orange--40);
+            color: var(--pf-t--color--gray--95);
+            font-weight: normal;
+        }
+    }
 }
 
 .karavan .nav-buttons .nav-button .pf-v6-c-button__icon.pf-m-start {
@@ -114,21 +150,41 @@
 }
 
 .karavan .nav-button-selected .pf-v6-c-button {
-    border-left-color: #ec7a08;
-    background-color: var(--pf-t--color--gray--60);
-}
+    background: rgba(236, 122, 8, 0.25);
+    &:hover {
+        /*background: color-mix(in srgb, var(--pf-t--color--blue--60) 50%, 
transparent);*/
+    }
 
-.karavan .nav-buttons .nav-button-badge {
-    position: absolute;
-    top: 5px;
-    right: 5px;
-    margin: 0;
+    .nav-button-badge {
+        .pf-v6-c-badge {
+            padding: 0 6px;
+            min-width: fit-content;
+            color: var(--pf-t--color--orange--40);
+            background-color: var(--pf-t--color--gray--95);
+            font-weight: 500;
+        }
+    }
+
+    &::before {
+        content: "";
+        position: absolute;
+        inset: 0;
+        border-radius: inherit;
+        border-left: 2px solid rgba(236, 122, 8, 1);
+        pointer-events: none;
+    }
 }
 
-.karavan .nav-buttons .nav-button-badge .pf-v6-c-badge {
-    padding: 0 6px;
-    min-width: fit-content;
-    background-color: var(--pf-t--color--orange--30);
-    color: var(--pf-t--color--gray--95);
-    font-weight: normal;
+.karavan .nav-buttons {
+    .dark-mode-toggle {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        width: 100%;
+        padding: 12px;
+        border-top-width: 1px;
+        border-top-style: solid;
+        border-top-color: var(--pf-t--color--gray--80);
+        border-radius: 0;
+    }
 }
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.tsx 
b/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.tsx
index db6cae62..5a1a59bf 100644
--- a/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/navigation/PageNavigation.tsx
@@ -1,8 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import React, {useContext, useState} from 'react';
 import {Badge, Button,} from '@patternfly/react-core';
 import './PageNavigation.css';
-import LogoutIcon from "@patternfly/react-icons/dist/esm/icons/door-open-icon";
-import {useAppConfigStore, useDevModeStore, useFileStore, useProjectsStore, 
useStatusesStore} from "@stores/ProjectStore";
+import {useAppConfigStore, useDevModeStore, useFileStore} from 
"@stores/ProjectStore";
 import {shallow} from "zustand/shallow";
 import {useLocation, useNavigate} from "react-router-dom";
 import {SsoApi} from "@api/auth/SsoApi";
@@ -10,29 +25,26 @@ import {UserPopupOidc} from "../login/UserPopupOidc";
 import {BUILD_IN_PROJECTS} from "@models/ProjectModels";
 import DarkModeToggle from "@app/theme/DarkModeToggle";
 import {AuthApi} from "@api/auth/AuthApi";
-import {getNavigationMenu} from "@app/navigation/NavigationMenu";
+import {getNavigationFirstMenu, getNavigationSecondMenu, MenuItem} from 
"@app/navigation/NavigationMenu";
 import {AuthContext} from "@api/auth/AuthProvider";
-import {KaravanIcon} from "@features/integration/designer/icons/KaravanIcons";
+import {PlatformLogoBase64} from "@app/navigation/PlatformLogo";
+import {PlatformVersions} from "@shared/ui/PlatformLogos";
 
-
-export function PageNavigation() {
+function PageNavigation() {
 
     const [config] = useAppConfigStore((s) => [s.config], shallow)
-    const menu = getNavigationMenu(config.environment, config.infrastructure);
+    const firstMenu = getNavigationFirstMenu();
+    const secondMenu = getNavigationSecondMenu(config.environment, 
config.infrastructure);
     const [setFile] = useFileStore((state) => [state.setFile], shallow)
     const [setStatus, setPodName] = useDevModeStore((state) => 
[state.setStatus, state.setPodName], shallow)
-    const [projects] = useProjectsStore((s) => [s.projects], shallow)
-    const [deployments, containers] = useStatusesStore((state) => 
[state.deployments, state.containers], shallow)
-    const [pageId, setPageId] = useState<string>(menu?.at(0)?.pageId || 
'integrations');
+    const [pageId, setPageId] = useState<string>();
     const navigate = useNavigate();
     const location = useLocation();
     const {reload} = useContext(AuthContext);
 
-    const projectCount = projects.filter(p => 
!BUILD_IN_PROJECTS.includes(p.projectId))?.length;
-
     React.useEffect(() => {
         var page = location.pathname?.split("/").filter(Boolean)[0];
-        if (page === 'integrations') {
+        if (page === 'projects') {
             var projectId = location.pathname?.split("/").filter(Boolean)[1];
             if (BUILD_IN_PROJECTS.includes(projectId)) {
                 setPageId('settings');
@@ -41,66 +53,79 @@ export function PageNavigation() {
             }
         } else if (page !== undefined) {
             setPageId(page);
+        } else if (config.environment === 'dev') {
+            setPageId('projects');
         } else {
-            setPageId('integrations');
+            setPageId('projects');
         }
     }, [location]);
 
+    function onClick(page: MenuItem) {
+        if (page.pageId === 'logout') {
+            if (AuthApi.authType === 'oidc') {
+                SsoApi.logout(() => {
+                });
+            } else if (AuthApi.authType === 'session') {
+                AuthApi.logout();
+                reload();
+            }
+        } else {
+            setFile('none', undefined);
+            setPodName(undefined);
+            setStatus("none");
+            setPageId(page.pageId);
+            navigate(page.pageId);
+        }
+    }
+
+    function getMenu(menu: MenuItem[]) {
+        return (
+            menu.map((page, index) => {
+                    let className = "nav-button";
+                    const isSelected = pageId === page.pageId;
+                    className = className.concat(isSelected ? " 
nav-button-selected" : "");
+                    return (
+                        <div key={page.pageId} className={isSelected ? 
"nav-button-selected nav-button-wrapper" : "nav-button-wrapper"}>
+                            <Button  id={page.pageId}
+                                     style={{width: '100%'}}
+                                     variant={"link"}
+                                     className={className}
+                                // countOptions={badge}
+                                     onClick={_ => onClick(page)}
+                            >
+                                <div style={{display: 'flex', flexDirection: 
'row', alignItems: 'center', gap: '8px'}}>
+                                    {page.icon}
+                                    {page.name}
+                                </div>
+                            </Button>
+                        </div>
+                    )
+                })
+        )
+    }
 
     return (
         <div className="nav-buttons pf-v6-theme-dark">
             <div className='nav-button-part-wrapper'>
-                {KaravanIcon()}
+                <img src={PlatformLogoBase64()} className="logo" alt='logo'/>
             </div>
             <div style={{alignSelf: 'center'}} className='environment-wrapper'>
+                <Badge isRead 
className='environment'>{config.environment}</Badge>
             </div>
-            {menu.map((page, index) => {
-                let className = "nav-button";
-                className = className.concat(pageId === page.pageId ? " 
nav-button-selected" : "");
-                className = className.concat((index === menu.length - 1) ? " 
nav-button-last" : "");
-                return (
-                    <div key={page.pageId} className={pageId === page.pageId ? 
"nav-button-selected nav-button-wrapper" : "nav-button-wrapper"}>
-                        <Button id={page.pageId}
-                                style={{width: '100%'}}
-                                icon={page.icon}
-                                variant={"link"}
-                                className={className}
-                                onClick={event => {
-                                    setFile('none', undefined);
-                                    setPodName(undefined);
-                                    setStatus("none");
-                                    setPageId(page.pageId);
-                                    navigate(page.pageId);
-                                }}
-                        >
-                            {page.name}
-                        </Button>
-                    </div>
-                )
-            })}
-            <div className='nav-button-part-wrapper' style={{flexGrow: '2'}}/>
+            {getMenu(firstMenu)}
+            <div style={{flex: 2}}/>
+            {getMenu(secondMenu)}
             {AuthApi.authType === 'oidc' &&
                 <div className='nav-button-part-wrapper'>
                     <UserPopupOidc/>
                 </div>
             }
-
-            <div className='nav-button-part-wrapper'>
+            <div className={"dark-mode-toggle"}>
                 <DarkModeToggle/>
             </div>
-            <div className='nav-button-part-wrapper'>
-                <Button icon={<LogoutIcon/>} className={"nav-button"} 
style={{width: '100%'}} variant={"link"}
-                        onClick={event => {
-                            if (AuthApi.authType === 'oidc') {
-                                SsoApi.logout(() => {
-                                });
-                            } else if (AuthApi.authType === 'session') {
-                                AuthApi.logout();
-                                reload();
-                            }
-                        }}
-                >Exit</Button>
-            </div>
+            <PlatformVersions/>
         </div>
     )
-}
\ No newline at end of file
+}
+
+export default PageNavigation
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/karavan/app/navigation/Routes.ts 
b/karavan-app/src/main/webui/src/karavan/app/navigation/Routes.ts
index 807c6ce7..c9daa3e8 100644
--- a/karavan-app/src/main/webui/src/karavan/app/navigation/Routes.ts
+++ b/karavan-app/src/main/webui/src/karavan/app/navigation/Routes.ts
@@ -1,10 +1,27 @@
-// routes.ts
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 export const ROUTES = {
-    INTEGRATIONS: "/integrations",
-    INTEGRATION_DETAIL: "/integrations/:projectId",
-    INTEGRATION_FILE: "/integrations/:projectId/:fileName",
-    SERVICES: "/services",
+    PROJECTS: "/projects",
+    PROJECT_DETAIL: "/projects/:projectId",
+    PROJECT_FILE: "/projects/:projectId/:fileName",
     SYSTEM: "/system",
+    SETTINGS: "/settings",
+    SETTINGS_FILE: "/settings/:projectId/:fileName",
     DOCUMENTATION: "/documentation",
     FORBIDDEN: "/403",
     ACL: "/acl",
diff --git 
a/karavan-app/src/main/webui/src/karavan/app/theme/DarkModeToggle.tsx 
b/karavan-app/src/main/webui/src/karavan/app/theme/DarkModeToggle.tsx
index ea4910fd..1a50f6c6 100644
--- a/karavan-app/src/main/webui/src/karavan/app/theme/DarkModeToggle.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/theme/DarkModeToggle.tsx
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
 import { SunIcon, MoonIcon } from '@patternfly/react-icons';
 import { useTheme } from './ThemeContext';
diff --git a/karavan-app/src/main/webui/src/karavan/app/theme/ThemeContext.tsx 
b/karavan-app/src/main/webui/src/karavan/app/theme/ThemeContext.tsx
index 0eec10e9..90fb6251 100644
--- a/karavan-app/src/main/webui/src/karavan/app/theme/ThemeContext.tsx
+++ b/karavan-app/src/main/webui/src/karavan/app/theme/ThemeContext.tsx
@@ -1,4 +1,20 @@
-import React, { createContext, useContext, useEffect, useState } from 'react';
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React, {createContext, useContext, useEffect, useState} from 'react';
 
 interface ThemeContextType {
     isDark: boolean;
@@ -11,22 +27,27 @@ export const ThemeProvider: React.FC<{ children: 
React.ReactNode }> = ({ childre
     const [isDark, setIsDark] = useState(false);
 
     useEffect(() => {
-        const stored = localStorage.getItem('pf-theme');
-        if (stored === 'dark') {
-            setIsDark(true);
-            document.documentElement.classList.add('pf-v6-theme-dark');
+        const storedTheme = localStorage.getItem('pf-theme');
+
+        let shouldUseDark = false;
+
+        if (storedTheme === 'dark') {
+            shouldUseDark = true;
+        } else if (storedTheme === 'light') {
+            shouldUseDark = false;
+        } else {
+            // No stored preference → use browser preference
+            shouldUseDark = window.matchMedia('(prefers-color-scheme: 
dark)').matches;
         }
+
+        setIsDark(shouldUseDark);
+        document.documentElement.classList.toggle('pf-v6-theme-dark', 
shouldUseDark);
     }, []);
 
     const toggleDarkMode = (checked: boolean) => {
         setIsDark(checked);
-        if (checked) {
-            document.documentElement.classList.add('pf-v6-theme-dark');
-            localStorage.setItem('pf-theme', 'dark');
-        } else {
-            document.documentElement.classList.remove('pf-v6-theme-dark');
-            localStorage.setItem('pf-theme', 'light');
-        }
+        document.documentElement.classList.toggle('pf-v6-theme-dark', checked);
+        localStorage.setItem('pf-theme', checked ? 'dark' : 'light');
     };
 
     return (

Reply via email to