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 Nov 19, 2024
1 parent 3fbb0a1 commit e072fd8
Show file tree
Hide file tree
Showing 22 changed files with 368 additions and 12 deletions.
28 changes: 26 additions & 2 deletions backend/capellacollab/navbar/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import fastapi
from sqlalchemy import orm

from capellacollab.core import database
from capellacollab.core import (
CLUSTER_DEVELOPMENT_MODE,
LOCAL_DEVELOPMENT_MODE,
database,
)
from capellacollab.settings.configuration import core as config_core
from capellacollab.settings.configuration import models as config_models
from capellacollab.settings.configuration.models import BadgeVariant

router = fastapi.APIRouter()

Expand All @@ -18,6 +23,25 @@
def get_navbar(db: orm.Session = fastapi.Depends(database.get_db)):
cfg = config_core.get_global_configuration(db)

return config_models.NavbarConfiguration.model_validate(
navbar_config = config_models.NavbarConfiguration.model_validate(
cfg.navbar.model_dump()
)
if navbar_config.badge.show:
if navbar_config.badge.text == "auto":
if CLUSTER_DEVELOPMENT_MODE:
navbar_config.badge.text = "Cluster Development"

Check warning on line 32 in backend/capellacollab/navbar/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/navbar/routes.py#L32

Added line #L32 was not covered by tests
elif LOCAL_DEVELOPMENT_MODE:
navbar_config.badge.text = "Local Development"

Check warning on line 34 in backend/capellacollab/navbar/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/navbar/routes.py#L34

Added line #L34 was not covered by tests
else:
navbar_config.badge.text = cfg.metadata.model_dump().get(

Check warning on line 36 in backend/capellacollab/navbar/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/navbar/routes.py#L36

Added line #L36 was not covered by tests
"environment", "Unknown Environment"
)

if navbar_config.badge.variant == BadgeVariant.AUTO:
words = ["dev", "development", "unknown", "staging"]

Check warning on line 41 in backend/capellacollab/navbar/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/navbar/routes.py#L41

Added line #L41 was not covered by tests
if any(word in navbar_config.badge.text.lower() for word in words):
navbar_config.badge.variant = BadgeVariant.WARNING

Check warning on line 43 in backend/capellacollab/navbar/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/navbar/routes.py#L43

Added line #L43 was not covered by tests
else:
navbar_config.badge.variant = BadgeVariant.SUCCESS

Check warning on line 45 in backend/capellacollab/navbar/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/capellacollab/navbar/routes.py#L45

Added line #L45 was not covered by tests

return navbar_config
31 changes: 30 additions & 1 deletion backend/capellacollab/settings/configuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,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 @@ -68,6 +68,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: list[BuiltInNavbarLink | CustomNavbarLink] = (
pydantic.Field(
Expand Down Expand Up @@ -104,6 +125,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
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 @@ -23,6 +23,7 @@ const config: StorybookConfig = {
docs: {
autodocs: 'tag',
},
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.
9 changes: 4 additions & 5 deletions frontend/src/app/general/header/header.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@

<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>
<app-logo class="ml-5"></app-logo>

@if (navBarService.navbarItems$ | async) {
<div class="flex basis-1/3 justify-center gap-2">
<div class="flex grow 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 +51,7 @@
</div>
}

<div class="!mr-5 hidden basis-1/3 items-center justify-end gap-2 xl:flex">
<div class="!mr-5 hidden items-center justify-end gap-2 xl:flex">
<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 @@ -28,6 +29,7 @@ import { BreadcrumbsComponent } from '../breadcrumbs/breadcrumbs.component';
MatMenuTrigger,
BreadcrumbsComponent,
AsyncPipe,
LogoComponent,
],
})
export class HeaderComponent {
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/app/general/header/header.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
mockOwnUserWrapperServiceProvider,
mockUser,
} from 'src/storybook/user';
import { BadgeOutput } from '../../openapi';
import { NavBarItem, NavBarService } from '../nav-bar/nav-bar.service';
import { HeaderComponent } from './header.component';

Expand Down Expand Up @@ -52,6 +53,12 @@ class MockNavbarService implements Partial<NavBarService> {
href: '#',
},
]);
readonly logoUrl$ = of<string | undefined>(undefined);
readonly badge$ = of<BadgeOutput | undefined>({
variant: 'warning',
text: 'Storybook',
show: true,
});
}

export const NormalUser: Story = {
Expand Down
26 changes: 26 additions & 0 deletions frontend/src/app/general/logo/logo.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
~ SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

<div class="flex select-none items-center gap-2">
@if (navBarService.logoUrl$ | async) {
<img
[src]="navBarService.logoUrl$ | async"
aria-hidden="true"
class="max-h-8 max-w-24"
/>
}
<div class="text-2xl text-primary">Capella</div>
@if ((navBarService.badge$ | async)?.show) {
<div
class="rounded-md px-1 py-0.5 text-xs font-semibold uppercase tracking-wide text-white"
[class.bg-hazard]="(navBarService.badge$ | async)?.variant === 'warning'"
[class.bg-green-600]="
(navBarService.badge$ | async)?.variant === 'success'
"
>
{{ (navBarService.badge$ | async)?.text }}
</div>
}
</div>
17 changes: 17 additions & 0 deletions frontend/src/app/general/logo/logo.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { AsyncPipe } from '@angular/common';
import { Component } from '@angular/core';
import { NavBarService } from '../nav-bar/nav-bar.service';

@Component({
selector: 'app-logo',
standalone: true,
templateUrl: './logo.component.html',
imports: [AsyncPipe],
})
export class LogoComponent {
constructor(public navBarService: NavBarService) {}
}
Loading

0 comments on commit e072fd8

Please sign in to comment.