diff --git a/cypress/e2e/app-cat.cy.ts b/cypress/e2e/app-cat.cy.ts
new file mode 100644
index 00000000..e8cc0c51
--- /dev/null
+++ b/cypress/e2e/app-cat.cy.ts
@@ -0,0 +1,15 @@
+describe('Test AppCat embed', () => {
+ beforeEach(() => {
+ cy.setupAuth();
+ });
+ beforeEach(() => {
+ // needed for initial getUser request
+ cy.setPermission({ verb: 'list', resource: 'zones', group: 'rbac.appuio.io' });
+ cy.disableCookieBanner();
+ });
+
+ it('shows the application catalog', () => {
+ cy.visit('/app-cat');
+ cy.get('iframe').children().should('not.be.empty');
+ });
+});
diff --git a/src/app/app-cat/app-cat.component.html b/src/app/app-cat/app-cat.component.html
new file mode 100644
index 00000000..a74093f5
--- /dev/null
+++ b/src/app/app-cat/app-cat.component.html
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/src/app/app-cat/app-cat.component.scss b/src/app/app-cat/app-cat.component.scss
new file mode 100644
index 00000000..0cff2bae
--- /dev/null
+++ b/src/app/app-cat/app-cat.component.scss
@@ -0,0 +1,49 @@
+:host {
+ height: calc(100% + 2rem);
+ width: calc(100% + 4rem);
+ margin: -2rem;
+}
+
+.header {
+ padding-left: 0.75rem;
+ padding-right: 0.75rem;
+}
+
+/* Using the break points from services.appcat.ch (+280px for the sidebar on desktop)
+** to set the width so the header with margin:auto is aligned with the iframe content
+*/
+@media (min-width: 576px) {
+ .header {
+ width: 540px;
+ }
+}
+
+@media (min-width: 768px) {
+ .header {
+ width: 720px;
+ }
+}
+
+@media (min-width: 1048px) {
+ .header {
+ width: 720px;
+ }
+}
+
+@media (min-width: 1272px) {
+ .header {
+ width: 960px;
+ }
+}
+
+@media (min-width: 1480px) {
+ .header {
+ width: 1140px;
+ }
+}
+
+@media (min-width: 1680px) {
+ .header {
+ width: 1312px;
+ }
+}
diff --git a/src/app/app-cat/app-cat.component.ts b/src/app/app-cat/app-cat.component.ts
new file mode 100644
index 00000000..d37bcb0b
--- /dev/null
+++ b/src/app/app-cat/app-cat.component.ts
@@ -0,0 +1,12 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+@Component({
+ selector: 'app-app-cat',
+ standalone: true,
+ imports: [CommonModule],
+ templateUrl: './app-cat.component.html',
+ styleUrls: ['./app-cat.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppCatComponent {}
diff --git a/src/app/app-cat/app-cat.routing.ts b/src/app/app-cat/app-cat.routing.ts
new file mode 100644
index 00000000..9949cbe9
--- /dev/null
+++ b/src/app/app-cat/app-cat.routing.ts
@@ -0,0 +1,4 @@
+import { Routes } from '@angular/router';
+import { AppCatComponent } from './app-cat.component';
+
+export const routes: Routes = [{ path: '', component: AppCatComponent }];
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 33d6abe3..a24650bc 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -2,7 +2,15 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@
import { Store } from '@ngrx/store';
import { selectOrganizationSelectionEnabled } from './store/app.selectors';
import { Verb } from './store/app.reducer';
-import { faComment, faDatabase, faDollarSign, faGift, faSitemap, faUserGroup } from '@fortawesome/free-solid-svg-icons';
+import {
+ faBookOpen,
+ faComment,
+ faDatabase,
+ faDollarSign,
+ faGift,
+ faSitemap,
+ faUserGroup,
+} from '@fortawesome/free-solid-svg-icons';
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
import * as Sentry from '@sentry/browser';
import { AppConfigService } from './app-config.service';
@@ -181,6 +189,11 @@ export class AppComponent implements OnInit {
routerLink: ['invitations'],
});
}
+ this.menuItems.push({
+ label: $localize`VSHN AppCat`,
+ icon: faBookOpen,
+ routerLink: ['app-cat'],
+ });
// needed to render the menu if other rendering tasks are running in the background,
// e.g. polling invitations
this.changeDetectorRef.markForCheck();
diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts
index 675d0c61..73f3fe88 100644
--- a/src/app/app.routing.ts
+++ b/src/app/app.routing.ts
@@ -54,4 +54,9 @@ export const appRoutes: Routes = [
loadChildren: () => import('./productboard/productboard.routing').then((m) => m.routes),
title: $localize`Feedback`,
},
+ {
+ path: 'app-cat',
+ loadChildren: () => import('./app-cat/app-cat.routing').then((m) => m.routes),
+ title: $localize`AppCat`,
+ },
];