Skip to content

Commit

Permalink
feat: Add the ability to set a company logo and show environment badge
Browse files Browse the repository at this point in the history
Add the ability to set a logo that will be displayed next to the Capella text. Add a badge that can
show the current environment with different UI depending on if the environment is productive. The
badge is enabled during development by default.
  • Loading branch information
zusorio committed Dec 9, 2024
1 parent eb01c8d commit 99caba4
Show file tree
Hide file tree
Showing 24 changed files with 473 additions and 12 deletions.
31 changes: 30 additions & 1 deletion backend/capellacollab/configuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from sqlalchemy import orm

from capellacollab import core
from capellacollab.core import database
from capellacollab.core import DEVELOPMENT_MODE, database
from capellacollab.core import pydantic as core_pydantic
from capellacollab.users import models as users_models

Expand Down Expand Up @@ -69,6 +69,27 @@ class CustomNavbarLink(NavbarLink):
)


class BadgeVariant(str, enum.Enum):
AUTO = "auto"
WARNING = "warning"
SUCCESS = "success"


class Badge(core_pydantic.BaseModelStrict):
show: bool = pydantic.Field(
default=True,
description="Show a badge with the current environment.",
)
variant: BadgeVariant = pydantic.Field(
default=BadgeVariant.AUTO,
description="Color of the badge.",
)
text: str | t.Literal["auto"] = pydantic.Field(
default="auto",
description="Text to display in the badge. Use 'auto' to display the environment name.",
)


class NavbarConfiguration(core_pydantic.BaseModelStrict):
external_links: collections_abc.Sequence[
BuiltInNavbarLink | CustomNavbarLink
Expand Down Expand Up @@ -105,6 +126,14 @@ class NavbarConfiguration(core_pydantic.BaseModelStrict):
),
description="Links to display in the navigation bar.",
)
logo_url: str | None = pydantic.Field(
default=None,
description="URL to a logo to display in the navigation bar.",
)
badge: Badge = pydantic.Field(
default=Badge(show=DEVELOPMENT_MODE),
description="Badge to display in the navigation bar.",
)


class FeedbackIntervalConfiguration(core_pydantic.BaseModelStrict):
Expand Down
2 changes: 1 addition & 1 deletion backend/capellacollab/configuration/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_unified_config(
metadata=util.get_metadata(cfg),
feedback=util.get_feedback(cfg),
beta=cfg.beta,
navbar=cfg.navbar,
navbar=util.get_navbar(cfg),
)


Expand Down
27 changes: 27 additions & 0 deletions backend/capellacollab/configuration/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import capellacollab
from capellacollab import core
from capellacollab.configuration.app import config

from . import models
Expand Down Expand Up @@ -33,3 +34,29 @@ def get_feedback(
feedback.interval.enabled = False

return feedback


def get_navbar(
global_config: models.GlobalConfiguration,
) -> models.NavbarConfiguration:
navbar_config = global_config.navbar

if navbar_config.badge.show:
if navbar_config.badge.text == "auto":
if core.CLUSTER_DEVELOPMENT_MODE:
navbar_config.badge.text = "Cluster Development"
elif core.LOCAL_DEVELOPMENT_MODE:
navbar_config.badge.text = "Local Development"
else:
navbar_config.badge.text = (
global_config.metadata.environment or "Unknown Environment"
)

if navbar_config.badge.variant == models.BadgeVariant.AUTO:
words = ["dev", "development", "unknown", "staging"]
if any(word in navbar_config.badge.text.lower() for word in words):
navbar_config.badge.variant = models.BadgeVariant.WARNING
else:
navbar_config.badge.variant = models.BadgeVariant.SUCCESS

return navbar_config
104 changes: 104 additions & 0 deletions backend/tests/test_navbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import pytest
from fastapi import testclient

from capellacollab import core


@pytest.fixture(name="cluster_development_mode")
def fixture_cluster_development_mode(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(core, "CLUSTER_DEVELOPMENT_MODE", True)
monkeypatch.setattr(core, "DEVELOPMENT_MODE", True)


@pytest.fixture(name="local_development_mode")
def fixture_local_development_mode(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(core, "LOCAL_DEVELOPMENT_MODE", True)
monkeypatch.setattr(core, "DEVELOPMENT_MODE", True)


@pytest.mark.usefixtures("admin", "cluster_development_mode")
def test_cluster_dev_mode(
client: testclient.TestClient,
):
client.put(
"/api/v1/configurations/global",
json={
"navbar": {
"badge": {"text": "auto", "variant": "auto", "show": True}
}
},
)
response = client.get("/api/v1/configurations/unified")
assert response.status_code == 200
assert response.json()["navbar"]["badge"]["text"] == "Cluster Development"
assert response.json()["navbar"]["badge"]["variant"] == "warning"


@pytest.mark.usefixtures(
"admin",
"local_development_mode",
)
def test_local_dev_mode(
client: testclient.TestClient,
):
client.put(
"/api/v1/configurations/global",
json={
"navbar": {
"badge": {"text": "auto", "variant": "auto", "show": True}
}
},
)
response = client.get("/api/v1/configurations/unified")
assert response.status_code == 200
assert response.json()["navbar"]["badge"]["text"] == "Local Development"
assert response.json()["navbar"]["badge"]["variant"] == "warning"


@pytest.mark.usefixtures("admin")
def test_fallback_env_mode(client: testclient.TestClient):
response = client.put(
"/api/v1/configurations/global",
json={
"metadata": {
"environment": "Fallback Environment",
},
"navbar": {
"badge": {"text": "auto", "variant": "auto", "show": True}
},
},
)

assert response.status_code == 200

response = client.get("/api/v1/configurations/unified")
assert response.status_code == 200
assert response.json()["navbar"]["badge"]["text"] == "Fallback Environment"
assert response.json()["navbar"]["badge"]["variant"] == "success"


@pytest.mark.usefixtures("admin")
def test_unknown_env_mode(
client: testclient.TestClient,
):
response = client.put(
"/api/v1/configurations/global",
json={
"metadata": {
"environment": "",
},
"navbar": {
"badge": {"text": "auto", "variant": "auto", "show": True}
},
},
)

assert response.status_code == 200

response = client.get("/api/v1/configurations/unified")
assert response.status_code == 200
assert response.json()["navbar"]["badge"]["text"] == "Unknown Environment"
assert response.json()["navbar"]["badge"]["variant"] == "warning"
22 changes: 18 additions & 4 deletions docs/docs/admin/configure-for-your-org.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ metadata:
environment: '-'
```
## Navigation Bar
## Logo and Navigation Bar
You can edit the links in the navigation bar. This can be useful if you want to
link to external resources or if you are not using the default monitoring
setup.
You can edit the logo, badge, and links in the navigation bar. This can be
useful to brand the Collaboration Manager for your organization, remind users
which environment they are in, or link to external resources.
```yaml
navbar:
Expand All @@ -45,6 +45,11 @@ navbar:
- name: Documentation
service: documentation
role: user
logo_url: null
badge:
show: true
variant: auto
text: auto
```
In addition to the default service links, you can add your own by using `href`
Expand All @@ -63,6 +68,15 @@ hide the link from users without the appropriate role, it is not a security
feature, and you should make sure that the linked service enforces the
necessary access controls.

To show the logo in the navigation bar, set the `logo_url` field to the URL of
the image you want to use.

The badge can be used to show the environment the user is in. The `variant`
field can be set to `auto` (it will be determined by the environment),
`success`, or `warning`. The `text` field will use the environment name if set
to `auto`, or you can specify a custom text. If you don't want to show the
badge, set `show` to `false`.

## Feedback

!!! info "Configure SMTP server for feedback"
Expand Down
1 change: 1 addition & 0 deletions frontend/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const config: StorybookConfig = {
name: '@storybook/angular',
options: {},
},
staticDirs: [{ from: './test-assets', to: '/test-assets' }],
core: {
disableTelemetry: true,
enableCrashReports: false,
Expand Down
9 changes: 9 additions & 0 deletions frontend/.storybook/test-assets/narrow_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions frontend/.storybook/test-assets/wide_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions frontend/src/app/general/header/header.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
</div>

<div class="hidden xl:block">
<div class="flex min-h-[65px] items-center justify-between gap-2">
<div class="ml-5 basis-1/3 select-none text-2xl text-primary">
Capella Collaboration Manager
</div>
<div
class="grid min-h-[65px] grid-cols-[1fr,auto,minmax(0,1fr)] items-center gap-2 px-5"
>
<app-logo></app-logo>
@if (navBarService.navbarItems$ | async) {
<div class="flex basis-1/3 justify-center gap-2">
<div class="flex justify-center gap-2">
@for (item of navBarService.navbarItems$ | async; track item.name) {
@if (userService.validateUserRole(item.requiredRole)) {
@if (item.href) {
Expand Down Expand Up @@ -52,7 +52,7 @@
</div>
}

<div class="!mr-5 hidden basis-1/3 items-center justify-end gap-2 xl:flex">
<div class="flex items-center justify-end gap-2">
<mat-menu #profileMenu="matMenu" class="flex items-center">
<a
class="px-[15px] text-left"
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/app/general/header/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NavBarService } from 'src/app/general/nav-bar/nav-bar.service';
import { AuthenticationWrapperService } from '../../services/auth/auth.service';
import { OwnUserWrapperService } from '../../services/user/user.service';
import { BreadcrumbsComponent } from '../breadcrumbs/breadcrumbs.component';
import { LogoComponent } from '../logo/logo.component';

@Component({
selector: 'app-header',
Expand All @@ -27,6 +28,7 @@ import { BreadcrumbsComponent } from '../breadcrumbs/breadcrumbs.component';
MatMenuTrigger,
BreadcrumbsComponent,
AsyncPipe,
LogoComponent,
],
})
export class HeaderComponent {
Expand Down
Loading

0 comments on commit 99caba4

Please sign in to comment.