Skip to content

Commit cc4c56b

Browse files
emersionlouisgreineraiAdrian
authored
feat: introduce standalone mode (SchweizerischeBundesbahnen#162)
* feat: introduce standalone mode Co-authored-by: adrian_egli <adrian.egli@gmail.com> Co-authored-by: Simon Ser <contact@emersion.fr> * feat: register as a custom element Co-authored-by: Louis Greiner <greiner.louis@gmail.com> * fix: easy return * small fix - the use of ng-container embeded in the sbb-icon-sidebare doesn't work correctly. This fix resolves the issue. --------- Co-authored-by: Louis Greiner <greiner.louis@gmail.com> Co-authored-by: adrian_egli <adrian.egli@gmail.com>
1 parent cd4fbaf commit cc4c56b

19 files changed

+156
-45
lines changed

angular.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@
107107
},
108108
"es5": {
109109
"tsConfig": "tsconfig.app.es5.json"
110+
},
111+
"standalone": {
112+
"fileReplacements": [
113+
{
114+
"replace": "src/environments/environment.ts",
115+
"with": "src/environments/environment.standalone.ts"
116+
}
117+
]
110118
}
111119
},
112120
"defaultConfiguration": ""
@@ -125,6 +133,9 @@
125133
},
126134
"es5": {
127135
"buildTarget": "netzgrafik-frontend:build:es5"
136+
},
137+
"standalone": {
138+
"buildTarget": "netzgrafik-frontend:build:standalone"
128139
}
129140
}
130141
},

package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
"ng": "ng",
44
"start": "ng serve",
55
"start:local": "ng serve --configuration=local",
6+
"start:standalone": "ng serve --configuration=standalone",
67
"e2e:browserstack": "ng e2e -c browserstack",
78
"e2e:puppeteer": "ng e2e -c puppeteer",
89
"build": "ng run netzgrafik-frontend:ngsscbuild",
10+
"build:standalone": "ng build --configuration=standalone",
911
"test": "ng test -c ci",
1012
"lint": "ng lint",
1113
"e2e": "ng e2e",
@@ -24,6 +26,7 @@
2426
"@angular/common": "^17.0.5",
2527
"@angular/compiler": "^17.0.5",
2628
"@angular/core": "^17.0.5",
29+
"@angular/elements": "^17.1.0",
2730
"@angular/forms": "^17.0.5",
2831
"@angular/localize": "^17.0.5",
2932
"@angular/platform-browser": "^17.0.5",

src/app/app.component.html

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,17 @@
3030
</button>
3131
</sbb-menu>
3232
</sbb-header-lean>
33-
<sbb-navigation-bar class="noprint"></sbb-navigation-bar>
33+
<ng-container *ngIf="!disableBackend">
34+
<sbb-navigation-bar class="noprint"></sbb-navigation-bar>
35+
</ng-container>
3436
<router-outlet *ngIf="authenticated | async; else loading"></router-outlet>
3537
<ng-template #loading>
36-
<sbb-loading-indicator mode="big"></sbb-loading-indicator>
37-
<div class="login-message">Sie werden angemeldet...</div>
38+
<ng-container *ngIf="!disableBackend">
39+
<sbb-loading-indicator mode="big"></sbb-loading-indicator>
40+
<div class="login-message">Sie werden angemeldet...</div>
41+
</ng-container>
42+
<ng-container *ngIf="disableBackend">
43+
<sbb-netzgrafik-editor></sbb-netzgrafik-editor>
44+
</ng-container>
3845
</ng-template>
46+

src/app/app.component.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,37 @@ import {ProjectDto} from "./api/generated";
1111
styleUrls: ["./app.component.scss"],
1212
})
1313
export class AppComponent {
14+
readonly disableBackend = environment.disableBackend;
15+
1416
version = packageJson.version;
1517
environmentLabel = environment.label;
1618
authenticated: Promise<unknown>;
1719

1820
projectInMenu: Observable<ProjectDto | null>;
1921

2022
get userName() {
23+
if (this.disableBackend) {
24+
return undefined;
25+
}
2126
return this.authService.claims?.name;
2227
}
2328

2429
get email() {
30+
if (this.disableBackend) {
31+
return undefined;
32+
}
2533
return this.authService.claims?.email;
2634
}
2735

2836
constructor(private authService: AuthService) {
29-
this.authenticated = authService.initialized;
37+
if (!this.disableBackend) {
38+
this.authenticated = authService.initialized;
39+
}
3040
}
3141

3242
logout() {
33-
this.authService.logOut();
43+
if (!this.disableBackend) {
44+
this.authService.logOut();
45+
}
3446
}
3547
}

src/app/app.module.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {NgModule} from "@angular/core";
1+
import {NgModule, Injector, ApplicationRef, DoBootstrap} from "@angular/core";
22
import {NgxEditorModule} from "ngx-editor";
33
import {BrowserModule} from "@angular/platform-browser";
4+
import {createCustomElement} from "@angular/elements";
45
import {HTTP_INTERCEPTORS, HttpClientModule} from "@angular/common/http";
56
import {OAuthModule} from "angular-oauth2-oidc";
67
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
@@ -203,7 +204,7 @@ import {ActionMenuComponent} from "./view/action-menu/action-menu/action-menu.co
203204
// and you send a request to these, the access token is appended.
204205
// Documentation:
205206
// https://manfredsteyer.github.io/angular-oauth2-oidc/docs/additional-documentation/working-with-httpinterceptors.html
206-
allowedUrls: [environment.backendUrl],
207+
allowedUrls: environment.backendUrl ? [environment.backendUrl] : [],
207208
sendAccessToken: true,
208209
},
209210
}),
@@ -230,10 +231,20 @@ import {ActionMenuComponent} from "./view/action-menu/action-menu/action-menu.co
230231
SbbBreadcrumbModule,
231232
SbbAutocompleteModule,
232233
],
233-
bootstrap: [AppComponent],
234+
bootstrap: environment.customElement ? [] : [AppComponent],
234235
providers: [
235-
{provide: BASE_PATH, useValue: environment.backendUrl},
236+
... environment.backendUrl ? [{provide: BASE_PATH, useValue: environment.backendUrl}] : [],
236237
{provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true},
237238
],
238239
})
239-
export class AppModule {}
240+
241+
export class AppModule implements DoBootstrap {
242+
constructor(private injector: Injector) {}
243+
244+
ngDoBootstrap() {
245+
if (environment.customElement) {
246+
const element = createCustomElement(AppComponent, {injector: this.injector});
247+
customElements.define("sbb-root", element);
248+
}
249+
}
250+
}

src/app/netzgrafik-application/netzgrafik-application.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
<sbb-icon-sidebar-container>
2-
<sbb-icon-sidebar [(expanded)]="expanded">
1+
<sbb-icon-sidebar-container [attr.style]="getSidebarContainerStyle()">
2+
<sbb-icon-sidebar [(expanded)]="expanded" >
33
<!--<a sbbIconSidebarItem label="Varianten" (click)="onVariantenClicked()" class="sbb-active">-->
4-
<a
4+
<a *ngIf="!disableBackend"
55
sbbIconSidebarItem
66
label="Varianten"
77
[class]="getVariantsActivatedTag()"

src/app/netzgrafik-application/netzgrafik-application.component.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
sbb-icon-sidebar-container {
2-
top: 85px;
3-
}
4-
51
::ng-deep a.SideBarMainIcon {
62
background: whitesmoke;
73
}

src/app/netzgrafik-application/netzgrafik-application.component.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {DomSanitizer} from "@angular/platform-browser";
1212
import {EditorMode} from "../view/editor-menu/editor-mode";
1313
import {UndoService} from "../services/data/undo.service";
1414
import {EditorView} from "../view/editor-main-view/data-views/editor.view";
15+
import {NetzgrafikDefault} from "../sample-netzgrafik/netzgrafik.default";
16+
import {environment} from "../../environments/environment";
1517

1618
export enum IconSidebarMode {
1719
VARIANTEN = "varianten",
@@ -29,6 +31,8 @@ export class NetzgrafikApplicationComponent {
2931
mode = IconSidebarMode.NONE;
3032
expanded = false;
3133

34+
readonly disableBackend = environment.disableBackend;
35+
3236
private readonly destroyed = new Subject<void>();
3337

3438
constructor(
@@ -48,13 +52,24 @@ export class NetzgrafikApplicationComponent {
4852
.subscribe((params) => {
4953
uiInteractionService.setEditorMode(EditorMode.NetzgrafikEditing);
5054
uiInteractionService.showNetzgrafik();
51-
versionControlService.load(params.getVariantId(), true);
55+
try {
56+
versionControlService.load(params.getVariantId(), true);
57+
} catch (e) {
58+
versionControlService.loadNetzgrafikDTO(NetzgrafikDefault.getDefaultNetzgrafik());
59+
}
5260
uiInteractionService.setViewboxProperties(
5361
EditorView.svgName,
5462
uiInteractionService.getDefaultViewProperties());
5563
});
5664
}
5765

66+
getSidebarContainerStyle(): string {
67+
if (this.disableBackend) {
68+
return "top: 53px;";
69+
}
70+
return "top: 85px;";
71+
}
72+
5873
getVariantIsWritable(): boolean {
5974
if (this.versionControlService.variant === null) {
6075
return true;

src/app/services/auth/auth.service.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,28 @@ export class AuthService {
3030
private router: Router,
3131
location: Location,
3232
) {
33-
this.oauthService.configure(environment.authConfig);
34-
this.oauthService.setupAutomaticSilentRefresh();
35-
// If the user should not be forcefully logged in (e.g. if you have pages, which can be
36-
// accessed anonymously), change loadDiscoveryDocumentAndLogin to
37-
// loadDiscoveryDocumentAndTryLogin and have a login functionality in the
38-
// template of the component injecting the AuthService which calls the login() method.
39-
this.initialized = this.oauthService
40-
.loadDiscoveryDocumentAndLogin({state: location.path()})
41-
// If the user is not logged in, he will be forwarded to the identity provider
42-
// and this promise will not resolve. After being redirected from the identity
43-
// provider, the login promise will return true.
44-
.then((v) => (v ? true : new Promise(() => {})));
45-
// Redirect the user to the url configured with state above or in a separate login call.
46-
this.oauthService.events
47-
.pipe(first((e) => e.type === "token_received"))
48-
.subscribe(() => {
49-
const state = decodeURIComponent(this.oauthService.state || "");
50-
if (state && state !== "/") {
51-
this.router.navigate([state]);
52-
}
53-
});
33+
if (environment.disableBackend) return;
34+
this.oauthService.configure(environment.authConfig);
35+
this.oauthService.setupAutomaticSilentRefresh();
36+
// If the user should not be forcefully logged in (e.g. if you have pages, which can be
37+
// accessed anonymously), change loadDiscoveryDocumentAndLogin to
38+
// loadDiscoveryDocumentAndTryLogin and have a login functionality in the
39+
// template of the component injecting the AuthService which calls the login() method.
40+
this.initialized = this.oauthService
41+
.loadDiscoveryDocumentAndLogin({state: location.path()})
42+
// If the user is not logged in, he will be forwarded to the identity provider
43+
// and this promise will not resolve. After being redirected from the identity
44+
// provider, the login promise will return true.
45+
.then((v) => (v ? true : new Promise(() => {})));
46+
// Redirect the user to the url configured with state above or in a separate login call.
47+
this.oauthService.events
48+
.pipe(first((e) => e.type === "token_received"))
49+
.subscribe(() => {
50+
const state = decodeURIComponent(this.oauthService.state || "");
51+
if (state && state !== "/") {
52+
this.router.navigate([state]);
53+
}
54+
});
5455
}
5556

5657
logOut() {

src/app/services/data/undo.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,8 @@ export class UndoService implements OnDestroy {
128128
}
129129
this.currentVariantId = variantId;
130130
}
131+
132+
public getCurrentVariantId():number{
133+
return this.currentVariantId;
134+
}
131135
}

src/app/services/data/version-control.service.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Injectable, OnDestroy} from "@angular/core";
1+
import {HostListener, Injectable, OnDestroy} from "@angular/core";
22
import {
33
VariantControllerBackendService,
44
VariantDto,
@@ -13,6 +13,7 @@ import {AutoSaveService} from "./auto-save.service";
1313
import {LogService} from "../../logger/log.service";
1414
import {VersionId} from "../../view/variant/variant-view/variant-history/model";
1515
import {UndoService} from "./undo.service";
16+
import {environment} from "../../../environments/environment";
1617

1718
@Injectable({
1819
providedIn: "root",
@@ -34,7 +35,8 @@ export class VersionControlService implements OnDestroy {
3435
private readonly undoService: UndoService,
3536
private readonly logService: LogService,
3637
) {
37-
autoSaveService.autosaveTrigger$
38+
if (!environment.disableBackend) {
39+
autoSaveService.autosaveTrigger$
3840
.pipe(
3941
takeUntil(this.destroyed),
4042
filter(() => this.variant.isWritable),
@@ -43,6 +45,7 @@ export class VersionControlService implements OnDestroy {
4345
logService.debug("auto saving changes");
4446
this.createSnapshot();
4547
});
48+
}
4649
}
4750

4851
get variant(): VariantDto {
@@ -69,6 +72,12 @@ export class VersionControlService implements OnDestroy {
6972
});
7073
}
7174

75+
loadNetzgrafikDTO(netzgrafik: NetzgrafikDto) {
76+
this.dataService.loadNetzgrafikDto(netzgrafik);
77+
this.autoSaveService.reset();
78+
this.undoService.reset(this.undoService.getCurrentVariantId() + 1);
79+
}
80+
7281
reload(loadModel = false): void {
7382
this.load(this.variant.id, loadModel);
7483
}

src/app/view/column-layout/column-layout.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
id="cd-layout-content"
33
[ngClass]="['mode-' + mode]"
44
[class.new-view]="newView"
5+
[attr.style]="getLayoutContentStyle()"
56
>
67
<div
78
id="cd-layout-filter"

src/app/view/column-layout/column-layout.component.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Input,
88
Output,
99
} from "@angular/core";
10+
import {environment} from "../../../environments/environment";
1011

1112
/**
1213
* Different layout modes. Generally the layout contains (up to) 3 columns that are distributed over a 4 columen layout:
@@ -70,6 +71,13 @@ export class ColumnLayoutComponent implements AfterViewInit {
7071
this.newView = false;
7172
}
7273

74+
getLayoutContentStyle(): string {
75+
if (environment.disableBackend) {
76+
return "height: 100%;";
77+
}
78+
return "";
79+
}
80+
7381
hideAside() {
7482
switch (this.mode) {
7583
case LayoutMode.FILTER_ONLY:

src/app/view/editor-menu/editor-menu.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@
235235
class="NoteEditor"
236236
[class.isStreckengrafikEditing]="isStreckengrafikEditing()"
237237
>
238-
<ng-container *ngIf="!isNoteEditing()">
238+
<ng-container *ngIf="!isNoteEditing() && !disableBackend">
239239
<button
240240
mode="icon"
241241
class="ButtonNoteEditor NetzgrafikEditing"
@@ -254,7 +254,7 @@
254254
></sbb-icon>
255255
</button>
256256
</ng-container>
257-
<ng-container *ngIf="isNoteEditing()">
257+
<ng-container *ngIf="isNoteEditing() && !disableBackend">
258258
<button
259259
mode="icon"
260260
class="ButtonNoteEditor NoteEditing"

0 commit comments

Comments
 (0)