Skip to content

Commit

Permalink
Merge pull request #243 from swisstopo/feature/workgroups
Browse files Browse the repository at this point in the history
Feature: Workgroups
  • Loading branch information
daniel-va authored Jul 25, 2024
2 parents ce37989 + 84ebfee commit 88c7a82
Show file tree
Hide file tree
Showing 210 changed files with 5,498 additions and 2,652 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
/node_modules
/tmp
/.idea
/development/volumes
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
- Direktauswahl von Assets ohne Suche
- Testing
- Regeneriere Elasticsearch Index via Admin Panel
- Einteilung von Assets in Arbeitsgruppen

### Changed

- UI Refactoring: Neuanordnung der Container
- UI Refactoring: Suchergebnisse als Tabelle
- Update Dependencies
- Bearbeitungsrechte werden auf Basis der Arbeitsgruppen vergeben anstatt global

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion apps/client-asset-sg/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"assets": ["apps/client-asset-sg/src/favicon.ico", "apps/client-asset-sg/src/assets"],
"styles": ["apps/client-asset-sg/src/styles.scss"],
"scripts": [],
"allowedCommonJsDependencies": ["tsafe", "validator", "xml-utils", "pbf", "rbush", "earcut"]
"allowedCommonJsDependencies": ["tsafe", "validator", "xml-utils", "pbf", "rbush", "earcut", "@prisma/client"]
},
"configurations": {
"production": {
Expand Down
25 changes: 6 additions & 19 deletions apps/client-asset-sg/src/app/app-guards.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
import { inject } from '@angular/core';
import { CanActivateFn } from '@angular/router';
import { fromAppShared } from '@asset-sg/client-shared';
import { ORD } from '@asset-sg/core';
import { User, isAdmin, isEditor } from '@asset-sg/shared';
import { isNotNull } from '@asset-sg/core';
import { User } from '@asset-sg/shared/v2';
import { Store } from '@ngrx/store';
import * as E from 'fp-ts/Either';
import { pipe } from 'fp-ts/function';
import { map } from 'rxjs';
import { filter, map } from 'rxjs';

import { AppState } from './state/app-state';

export const roleGuard = (rolePredicate: (u: User) => boolean) => {
export const roleGuard = (testUser: (u: User) => boolean) => {
const store = inject(Store<AppState>);
return store.select(fromAppShared.selectRDUserProfile).pipe(
ORD.filterIsCompleteEither,
map((user) =>
E.isRight(
pipe(
user,
E.filterOrElseW(rolePredicate, () => undefined)
)
)
)
);
return store.select(fromAppShared.selectUser).pipe(filter(isNotNull), map(testUser));
};

export const adminGuard: CanActivateFn = () => roleGuard(isAdmin);
export const editorGuard: CanActivateFn = () => roleGuard(isEditor);
export const adminGuard: CanActivateFn = () => roleGuard((user) => user.isAdmin);
40 changes: 23 additions & 17 deletions apps/client-asset-sg/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
<ng-container *ngIf="errorService.onMessage | async as error; else splashScreen">
<ng-container *ngIf="errorService.onMessage | async as error; else content">
<asset-sg-app-bar />
<div class="error">
<span>{{ error }}</span>
<button asset-sg-warn (click)="authService.logOut()">Logout</button>
</div>
</ng-container>

<ng-template #splashScreen>
<ng-container *ngIf="(authService.state$ | async) !== AuthState.Success; else content">
<ng-template #content>
<ng-container *ngIf="authService.state$ | async as authState">
@if (authState === AuthState.Success || authState === AuthState.ForbiddenResource) {
<asset-sg-app-bar>
<ng-template [cdkPortalOutlet]="(appPortalService.appBarPortalContent$ | push) ?? ''"></ng-template>
</asset-sg-app-bar>
<asset-sg-menu-bar />
<div class="router-outlet">
@if (authState === AuthState.Success) {
<router-outlet />
} @else {
<div class="forbidden">
<h1 translate>resourceForbidden</h1>
<p>403 - Forbidden</p>
</div>
}
</div>
<div class="drawer-portal">
<ng-template [cdkPortalOutlet]="(appPortalService.drawerPortalContent$ | push) ?? ''"></ng-template>
</div>
} @else {
<app-splash-screen />
}
</ng-container>
</ng-template>

<ng-template #content>
<asset-sg-app-bar>
<ng-template [cdkPortalOutlet]="appPortalService.appBarPortalContent$ | push"></ng-template>
</asset-sg-app-bar>
<asset-sg-menu-bar />
<div class="router-outlet">
<router-outlet></router-outlet>
</div>
<div class="drawer-portal">
<ng-template [cdkPortalOutlet]="appPortalService.drawerPortalContent$ | push"></ng-template>
</div>
</ng-template>

<div class="alerts">
<ul app-alert-list></ul>
</div>
22 changes: 22 additions & 0 deletions apps/client-asset-sg/src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ asset-sg-menu-bar {
.router-outlet {
grid-area: router-outlet;
display: grid;

router-outlet {
display: none;
}
Expand Down Expand Up @@ -71,3 +72,24 @@ asset-sg-menu-bar {
/// angular-cdk-overlay is at 1000, so we go to 1500.
z-index: 1500;
}

.forbidden {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;

h1 {
font-size: 4rem;
margin: 0;
color: mat.get-color-from-palette($asset-sg-warn, 300);
}

p {
font-size: 1.5rem;
margin: 0;
padding: 1rem 0 0 0;
color: mat.get-color-from-palette($asset-sg-warn, 200);
}
}
1 change: 1 addition & 0 deletions apps/client-asset-sg/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class AppComponent {
await this.authService.signIn();
this.store.dispatch(appSharedStateActions.loadUserProfile());
this.store.dispatch(appSharedStateActions.loadReferenceData());
this.store.dispatch(appSharedStateActions.loadWorkgroups());
});

const wndw = this._wndw;
Expand Down
15 changes: 9 additions & 6 deletions apps/client-asset-sg/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@ import { DialogModule } from '@angular/cdk/dialog';
import { CommonModule, NgOptimizedImage, registerLocaleData } from '@angular/common';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import locale_deCH from '@angular/common/locales/de-CH';
import { NgModule, inject } from '@angular/core';
import { inject, NgModule } from '@angular/core';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { AuthInterceptor, AuthModule, ErrorService } from '@asset-sg/auth';
import {
AdminOnlyDirective,
AlertModule,
AnchorComponent,
ButtonComponent,
CanCreateDirective,
CURRENT_LANG,
TranslateTsLoader,
currentLangFactory,
icons,
TranslateTsLoader,
} from '@asset-sg/client-shared';
import { storeLogger } from '@asset-sg/core';
import { SvgIconComponent, provideSvgIcons } from '@ngneat/svg-icon';
import { provideSvgIcons, SvgIconComponent } from '@ngneat/svg-icon';
import { EffectsModule } from '@ngrx/effects';
import { FullRouterStateSerializer, StoreRouterConnectingModule, routerReducer } from '@ngrx/router-store';
import { FullRouterStateSerializer, routerReducer, StoreRouterConnectingModule } from '@ngrx/router-store';
import { StoreModule } from '@ngrx/store';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
import { ForModule } from '@rx-angular/template/for';
Expand All @@ -31,7 +33,7 @@ import { PushModule } from '@rx-angular/template/push';

import { environment } from '../environments/environment';

import { adminGuard, editorGuard } from './app-guards';
import { adminGuard } from './app-guards';
import { assetsPageMatcher } from './app-matchers';
import { AppComponent } from './app.component';
import { AppBarComponent, MenuBarComponent, NotFoundComponent, RedirectToLangComponent } from './components';
Expand Down Expand Up @@ -73,7 +75,6 @@ registerLocaleData(locale_deCH, 'de-CH');
{
path: ':lang/asset-admin',
loadChildren: () => import('@asset-sg/asset-editor').then((m) => m.AssetEditorModule),
canActivate: [editorGuard],
},
{
matcher: assetsPageMatcher,
Expand Down Expand Up @@ -116,6 +117,8 @@ registerLocaleData(locale_deCH, 'de-CH');
AlertModule,
NgOptimizedImage,
MatProgressSpinnerModule,
AdminOnlyDirective,
CanCreateDirective,
],
providers: [
provideSvgIcons(icons),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<svg-icon key="favourite" />
<span translate>menuBar.favourites</span>
</a>
<ng-container *ngIf="isEditor$ | push">
<ng-container *canCreate="AssetEditPolicy">
<button
asset-sg-reset
class="menu-bar-item active"
Expand All @@ -47,11 +47,11 @@
</ng-template>
</ng-container>
<a
*ngIf="isAdmin$ | push"
*adminOnly
[routerLink]="[_translateService.currentLang, 'admin']"
asset-sg-reset
class="menu-bar-item"
[ngClass]="{ active: (isAdminActive$ | async) }"
[class]="{ active: (isAdminActive$ | async) }"
>
<svg-icon key="user-management" />
<span translate>menuBar.userManagement</span>
Expand All @@ -71,12 +71,4 @@
<svg-icon key="help" />
<span translate>menuBar.help</span>
</a>
<!-- <a routerLink="/test" asset-sg-reset disabled class="menu-bar-item">
<svg-icon key="settings"></svg-icon>
<span translate>menuBar.settings</span>
</a> -->
<!-- <button asset-sg-reset class="menu-bar-item" disabled>
<svg-icon key="close-nav"></svg-icon>
<span translate>menuBar.signOut</span>
</button> -->
</div>
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, Component, HostBinding, inject } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { appSharedStateActions, fromAppShared } from '@asset-sg/client-shared';
import { isAdmin, isEditor } from '@asset-sg/shared';
import * as RD from '@devexperts/remote-data-ts';
import { appSharedStateActions } from '@asset-sg/client-shared';
import { AssetEditPolicy } from '@asset-sg/shared/v2';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
Expand All @@ -24,10 +23,6 @@ export class MenuBarComponent {
public _translateService = inject(TranslateService);
private _store = inject(Store<AppState>);

private userProfile$ = inject(Store<AppState>).select(fromAppShared.selectRDUserProfile);
public isAdmin$ = this.userProfile$.pipe(map((user) => RD.isSuccess(user) && isAdmin(user.value)));
public isEditor$ = this.userProfile$.pipe(map((user) => RD.isSuccess(user) && isEditor(user.value)));

public isAssetsActive$ = this.createIsRouteActive$((url) => Boolean(url.match(/^\/\w\w$/)));
public isEditActive$ = this.isSegmentActive('asset-admin');
public isFavouritesActive$ = this.isSegmentActive('favourites');
Expand Down Expand Up @@ -56,4 +51,6 @@ export class MenuBarComponent {
public openAssetDrawer() {
this._store.dispatch(appSharedStateActions.toggleSearchFilter());
}

protected readonly AssetEditPolicy = AssetEditPolicy;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
{{ "login" | translate }}
</button>
</ng-container>
<ng-container *ngSwitchCase="AuthState.Forbidden">
<ng-container *ngSwitchCase="AuthState.AccessForbidden">
<div class="alert">
<p>
{{ "accessForbidden" | translate }}
Expand Down
38 changes: 38 additions & 0 deletions apps/client-asset-sg/src/app/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const deAppTranslations = {
logoSwissGeol: 'Logo Swissgeol Assets',
welcomeTo: 'Willkommen bei',
accessForbidden: 'Sie haben keinen Zugriff auf diese Applikation.',
resourceForbidden: 'Sie haben keinen Zugriff auf diese Ressource.',
ok: 'OK',
submit: 'Absenden',
cancel: 'Abbrechen',
Expand All @@ -16,6 +17,9 @@ export const deAppTranslations = {
delete: 'Löschen',
close: 'Schliessen',
datePlaceholder: 'JJJJ-MM-TT',
workgroup: {
title: 'Arbeitsgruppe',
},
menuBar: {
assets: 'Assets',
admin: 'Verwaltung',
Expand Down Expand Up @@ -77,6 +81,7 @@ export const deAppTranslations = {
languageItem: {
None: 'keine',
},
workgroup: 'Arbeitsgruppe',
resetSearch: 'Suche zurücksetzen',
file: 'Datei',
openFileInNewTab: '{{fileName}} in neuem Tab öffnen',
Expand Down Expand Up @@ -232,4 +237,37 @@ export const deAppTranslations = {
' Damit wird sichergestellt, dass die Suche alle vorhandenen Assets miteinbezieht.',
adminInstructionsSyncElasticAssetsStart: 'Synchronisation starten',
},
admin: {
users: 'Benutzer',
workgroups: 'Arbeitsgruppen',
name: 'Name',
role: 'Rolle',
actions: 'Aktionen',
email: 'E-Mail',
back: 'Zurück',
languages: {
de: 'Deutsch',
en: 'Englisch',
fr: 'Französisch',
it: 'Italienisch',
rm: 'Rätoromanisch',
},
userPage: {
admin: 'Admin',
lang: 'Sprache',
addWorkgroups: 'Arbeitsgruppen hinzufügen',
more: 'weitere',
userAddError: 'Füge mindestens einen Benutzer hinzu',
},
workgroupPage: {
name: 'Name',
isActive: 'Aktiv',
activate: 'Aktivieren',
deactivate: 'Deaktivieren',
create: 'Erstellen',
isDisabled: 'Deaktiviert',
chooseUsersText: 'Füge Benutzer hinzu, um sie zu verwalten',
addUsers: 'Benutzer hinzufügen',
},
},
};
Loading

0 comments on commit 88c7a82

Please sign in to comment.