diff --git a/.vscode/launch.json b/.vscode/launch.json index 87637c268..e88e12cb9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft/bin/Debug/netcoreapp2.2/Phonebook.Source.PeopleSoft.dll", + "program": "${workspaceFolder}/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft/bin/Debug/netcoreapp3.1/Phonebook.Source.PeopleSoft.dll", "args": [], "cwd": "${workspaceFolder}/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft", "stopAtEntry": false, diff --git a/Phonebook.Frontend/package.json b/Phonebook.Frontend/package.json index d56244984..43ea43fc3 100644 --- a/Phonebook.Frontend/package.json +++ b/Phonebook.Frontend/package.json @@ -5,8 +5,8 @@ "scripts": { "ng": "ng", "start": "npm run start:en", - "start:en": "concurrently -n mock-backend,assets,angular \"docker run --rm -p 8080:80 --name phonebook-source-mock -e ASPNETCORE_ENVIRONMENT=Development tsystemsmms/phonebook-build:source-peoplesoft\" \"docker run --rm -p 8081:80 --name phonebook-assets tsystemsmms/phonebook-build:assets\" \"ng serve --ssl --proxy-config proxy.conf.json --configuration en\"", - "start:de": "concurrently -n mock-backend,assets,angular \"docker run --rm -p 8080:80 --name phonebook-source-mock -e ASPNETCORE_ENVIRONMENT=Development tsystemsmms/phonebook-build:source-peoplesoft\" \"docker run --rm -p 8081:80 --name phonebook-assets tsystemsmms/phonebook-build:assets\" \"ng serve --ssl --proxy-config proxy.conf.json --configuration de\"", + "start:en": "concurrently -n mock-backend,assets,angular \"docker run --rm -p 8080:80 --name phonebook-source-mock -e ASPNETCORE_ENVIRONMENT=Development tsystemsmms/phonebook-source-peoplesoft\" \"docker run --rm -p 8081:80 --name phonebook-assets tsystemsmms/phonebook-build:assets\" \"ng serve --ssl --proxy-config proxy.conf.json --configuration en\"", + "start:de": "concurrently -n mock-backend,assets,angular \"docker run --rm -p 8080:80 --name phonebook-source-mock -e ASPNETCORE_ENVIRONMENT=Development tsystemsmms/phonebook-source-peoplesoft\" \"docker run --rm -p 8081:80 --name phonebook-assets tsystemsmms/phonebook-build:assets\" \"ng serve --ssl --proxy-config proxy.conf.json --configuration de\"", "start:debug": "ng serve --ssl --proxy-config=proxy.conf.json --configuration debug", "start-compose": "npm run compose-build && docker-compose run --rm --service-ports angular npm run start:en", "start-compose:debug": "npm run compose-build && docker-compose run --rm --service-ports angular npm run start:debug", diff --git a/Phonebook.Frontend/proxy.conf.json b/Phonebook.Frontend/proxy.conf.json index d41e91f88..e634dd26b 100644 --- a/Phonebook.Frontend/proxy.conf.json +++ b/Phonebook.Frontend/proxy.conf.json @@ -6,7 +6,7 @@ "logLevel": "debug" }, "/api/*": { - "target": "http://localhost:8080", + "target": "https://localhost:5001", "secure": false, "changeOrigin": true, "logLevel": "debug" diff --git a/Phonebook.Frontend/src/app/modules/organigram/helpers.ts b/Phonebook.Frontend/src/app/modules/organigram/helpers.ts new file mode 100644 index 000000000..cad2a04b4 --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/helpers.ts @@ -0,0 +1,14 @@ +import { ParamMap } from '@angular/router'; + +export class OrganigramHelpers { + public static getParamsAsArray(params: ParamMap, paramNames: string[]): string[] { + const paramArray: string[] = []; + paramNames.forEach((paramName) => { + const string = params.get(paramName); + if (string != null) { + paramArray.push(string); + } + }); + return paramArray; + } +} diff --git a/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.html b/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.html new file mode 100644 index 000000000..666403a3c --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.html @@ -0,0 +1,79 @@ + + +

+ link + {{ node?.id + ': ' + node?.name }} +

+
+ +
+ {node?.supervisors.length, plural, =0 { No Supervisor } =1 { Supervisor: } other { + Supervisors: }} + + {{ person.Firstname + ' ' + person.Surname }} + +
+
+ {node?.assistents.length, plural, =0 { No Assistant } =1 { Assistant: } other { Assistants: + }} + + {{ person.Firstname + ' ' + person.Surname }} + +
+
+ +
+
+

{node?.employees.length, plural, =0 { No Employees } =1 { Employee: } other { Employees: + }}

+
  • + + {{ person.Firstname + ' ' + person.Surname }} + +
  • +
    +
    +

    {node?.learners.length, plural, =0 { No Learners } =1 { Learner: } other { Learners: + }}

    +
  • + + {{ person.Firstname + ' ' + person.Surname }} + +
  • +
    +
    +
    +

    Sub-Organization Unit:

    +
    + + +

    {{ child.id + ': ' + child.name }}

    +
    + +
    +
    +
    +
    +
    diff --git a/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.scss b/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.scss new file mode 100644 index 000000000..74061c1ea --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.scss @@ -0,0 +1,40 @@ +mat-card-title { + display: flex; + + .link { + opacity: 0; + transition: opacity 0.2s ease-in-out; + } + + &:hover .link { + opacity: 1; + } +} + +button { + margin-left: auto; +} + +mat-card-subtitle, +mat-card-content { + margin-left: 24px; +} + +.persons { + display: flex; + flex-flow: row; + margin-bottom: 20px; + > div { + flex: 1; + } +} + +.node { + margin-left: 40px; +} + +.child-card { + width: 250px; + height: 70px; + padding: 16px 30px; +} diff --git a/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.ts b/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.ts new file mode 100644 index 000000000..e0aa4888a --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/node1/node1.component.ts @@ -0,0 +1,101 @@ +import { Component, OnInit } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { ActivatedRoute, Router } from '@angular/router'; + +import { RuntimeEnvironmentInterface } from 'src/environments/EnvironmentInterfaces'; +import { runtimeEnvironment } from 'src/environments/runtime-environment'; +import { OrganigramHelpers } from 'src/app/modules/organigram/helpers'; +import { OrganigramService, UnitTreeNode } from 'src/app/services/api/organigram.service'; +import { Navigate, RouterState } from '@ngxs/router-plugin'; +import { WindowRef } from 'src/app/services/windowRef.service'; +import { Store } from '@ngxs/store'; + +@Component({ + selector: 'app-node1', + templateUrl: './node1.component.html', + styleUrls: ['./node1.component.scss'], + host: { class: 'pb-height-expand' }, +}) +export class Node1Component implements OnInit { + public node: UnitTreeNode; + public runtimeEnvironment: RuntimeEnvironmentInterface = runtimeEnvironment; + + constructor( + private route: ActivatedRoute, + private organigramService: OrganigramService, + private router: Router, + private windowRef: WindowRef, + private snackBar: MatSnackBar, + private store: Store + ) {} + + public ngOnInit() { + this.route.paramMap.subscribe((params) => { + this.organigramService + .getNodeByPath( + OrganigramHelpers.getParamsAsArray(params, ['node1Id', 'node2Id', 'node3Id']) + ) + .subscribe((node) => { + if (node == null) { + this.snackBar.open( + $localize`:NodeComponent|Error Message if Node does not exist.@@NodeComponentErrorNoNode:Organisation does not exist.`, + '', + { duration: 5000 } + ); + this.router.navigate(['/organigram']); + } else { + this.node = node; + } + }); + }); + } + + public getLink() { + return this.windowRef.getCurrentUrl(); + } + public generatePath(withNode: boolean): string[] { + // Get the Path till the depth of the node + let path = this.getCurrentRouteAsArray().slice(0, this.node.depth + 1); + // + if (withNode) { + path = [...path, this.node.id]; + } + return path; + } + + public copiedToast(success: boolean) { + if (success) { + this.store.dispatch(new Navigate(this.generatePath(true))); + this.snackBar.open( + $localize`:NodeComponent|First part of the message displayed when copying a link to the node@@NodeComponentCopiedFirstPart:Link to` + + ' "' + + this.node.name + + '" ' + + $localize`:NodeComponent|Second part of the message displayed when copying a link to the node@@NodeComponentCopiedSecondPart:copied to clipboard!`, + '', + { duration: 2000 } + ); + } else { + this.snackBar.open( + $localize`:GeneralErrorMessageCopy|Message displayed when copying something went wrong@@GeneralErrorMessageCopy:Couldn't copy to the clipboard, something went wrong. Try again.`, + '', + { duration: 2000 } + ); + } + } + + public navigateToChildNode(nodePath: UnitTreeNode) { + let tree = this.getCurrentRouteAsArray().slice(0, nodePath.depth + 1); + tree = [...tree, nodePath.id]; + this.store.dispatch(new Navigate(tree)); + } + public getCurrentRouteAsArray(): string[] { + const navState = this.store.selectSnapshot(RouterState.state); + return [ + navState!.root.firstChild!.url[0].path, + ...navState!.root.firstChild!.firstChild!.firstChild!.url.map((obj) => { + return obj.path; + }), + ]; + } +} diff --git a/Phonebook.Frontend/src/app/modules/organigram/organigram-routing.module.ts b/Phonebook.Frontend/src/app/modules/organigram/organigram-routing.module.ts index d4ee17414..3b66a4af6 100644 --- a/Phonebook.Frontend/src/app/modules/organigram/organigram-routing.module.ts +++ b/Phonebook.Frontend/src/app/modules/organigram/organigram-routing.module.ts @@ -1,15 +1,21 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { OrganigramComponent } from 'src/app/modules/organigram/pages/organigram/organigram.component'; +import { OrganigramOverviewComponent } from 'src/app/modules/organigram/overview/organigram-overview.component'; +import { Node1Component } from 'src/app/modules/organigram/node1/node1.component'; const routes: Routes = [ - { path: '', component: OrganigramComponent, pathMatch: 'full' }, - { path: ':first', component: OrganigramComponent }, - { path: ':first/:second', component: OrganigramComponent }, - { path: ':first/:second/:third', component: OrganigramComponent }, - { path: ':first/:second/:third/:fourth', component: OrganigramComponent }, - { path: ':first/:second/:third/:fourth/:fifth', component: OrganigramComponent }, - { path: ':first/:second/:third/:fourth/:fifth/:sixth', component: OrganigramComponent }, + { + path: '', + component: OrganigramComponent, + children: [ + { path: '', component: OrganigramOverviewComponent, pathMatch: 'full' }, + { path: ':node1Id', component: Node1Component }, + { path: ':node1Id/:node2Id', component: Node1Component }, + { path: ':node1Id/:node2Id/:node3Id', component: Node1Component }, + ], + }, + { path: '**', redirectTo: '' }, ]; @NgModule({ diff --git a/Phonebook.Frontend/src/app/modules/organigram/organigram.module.ts b/Phonebook.Frontend/src/app/modules/organigram/organigram.module.ts index 64d836539..eef647348 100644 --- a/Phonebook.Frontend/src/app/modules/organigram/organigram.module.ts +++ b/Phonebook.Frontend/src/app/modules/organigram/organigram.module.ts @@ -9,6 +9,10 @@ import { ClipboardModule } from '@angular/cdk/clipboard'; import { OrganigramNodeComponent } from 'src/app/modules/organigram/components/organigram-node/organigram-node.component'; import { OrganigramRoutingModule } from 'src/app/modules/organigram/organigram-routing.module'; import { OrganigramComponent } from 'src/app/modules/organigram/pages/organigram/organigram.component'; +import { MaterialModule } from 'src/app/shared/material.module'; +import { OrganigramOverviewComponent } from 'src/app/modules/organigram/overview/organigram-overview.component'; +import { Node1Component } from 'src/app/modules/organigram/node1/node1.component'; +import { AddressModule } from 'src/app/shared/components/address/address.module'; @NgModule({ imports: [ @@ -20,8 +24,15 @@ import { OrganigramComponent } from 'src/app/modules/organigram/pages/organigram ClipboardModule, MatButtonModule, MatTooltipModule, + MaterialModule, + AddressModule, + ], + declarations: [ + OrganigramComponent, + OrganigramNodeComponent, + OrganigramOverviewComponent, + Node1Component, ], - declarations: [OrganigramComponent, OrganigramNodeComponent], exports: [], }) export class OrganigramModule {} diff --git a/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.html b/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.html new file mode 100644 index 000000000..1d946895c --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.html @@ -0,0 +1,16 @@ +
    +
    + + +

    {{ node.id + ': ' + node.name }}

    +
    + +
    +
    +
    diff --git a/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.scss b/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.scss new file mode 100644 index 000000000..38781bd4b --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.scss @@ -0,0 +1,5 @@ +.overview-card { + width: 250px; + height: 70px; + padding: 16px 30px; +} diff --git a/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.ts b/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.ts new file mode 100644 index 000000000..090bac303 --- /dev/null +++ b/Phonebook.Frontend/src/app/modules/organigram/overview/organigram-overview.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { OrganigramService, UnitTreeNode } from 'src/app/services/api/organigram.service'; +import { OrganigramHelpers } from 'src/app/modules/organigram/helpers'; +import { Navigate, RouterState } from '@ngxs/router-plugin'; +import { Store } from '@ngxs/store'; + +@Component({ + selector: 'app-organigram-overview', + templateUrl: './organigram-overview.component.html', + styleUrls: ['./organigram-overview.component.scss'], +}) +export class OrganigramOverviewComponent implements OnInit { + public nodes: UnitTreeNode[] = []; + + constructor( + private organigramService: OrganigramService, + private router: Router, + private store: Store + ) {} + + public ngOnInit() { + this.organigramService.getOrganigramTree().subscribe((organigram) => { + this.nodes = organigram; + }); + } + + public navigateToFirstNode(nodePath: UnitTreeNode) { + let tree = this.getCurrentRouteAsArray().slice(0, nodePath.depth + 1); + tree = [...tree, nodePath.id]; + this.store.dispatch(new Navigate(tree)); + } + public getCurrentRouteAsArray(): string[] { + const navState = this.store.selectSnapshot(RouterState.state); + return [ + navState!.root.firstChild!.url[0].path, + ...navState!.root.firstChild!.firstChild!.firstChild!.url.map((obj) => { + return obj.path; + }), + ]; + } +} diff --git a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.html b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.html index 6cf62d873..8cb1352a3 100644 --- a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.html +++ b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.html @@ -1,5 +1,80 @@ -
    - - - -
    + + + + + + business + + Where am I? + + + + + +
  • {{ node.id + ': ' + node.name }}
  • +
  • +
    {{ node.id + ': ' + node.name }}
    +
  • + +
    + +
    +
    + +
    +
    +
    diff --git a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.scss b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.scss index d636253d4..2f586cffe 100644 --- a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.scss +++ b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.scss @@ -1,10 +1,28 @@ -.node { - margin: 20px 40px; +.tree-drawer { + width: 400px; + height: 100%; } -#pb-organigram { - width: 100%; - display: flex; +.toggle-drawer { height: 100%; - flex-direction: column; + display: flex; + position: absolute; + z-index: 1; + + button { + align-self: center; + } +} + +.btn-whereAmI { + margin-left: auto; +} + +.location-tree { + ul, + li { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; + } } diff --git a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.spec.ts b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.spec.ts index bb441a93e..d6b4c11be 100644 --- a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.spec.ts +++ b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.spec.ts @@ -5,6 +5,8 @@ import { RouterTestingModule } from '@angular/router/testing'; import { Observable, of } from 'rxjs'; import { OrganigramService, UnitTreeNode } from 'src/app/services/api/organigram.service'; import { OrganigramComponent } from './organigram.component'; +import { HttpClient } from '@angular/common/http'; +import { PersonService } from 'src/app/services/api/person.service'; describe('OrganigramComponent', () => { let component: OrganigramComponent; @@ -16,6 +18,8 @@ describe('OrganigramComponent', () => { providers: [ { provide: OrganigramService, useClass: MockOrganigramService }, { provide: Location, useValue: null }, + { provide: HttpClient, useValue: null }, + { provide: PersonService, useValue: null }, ], imports: [RouterTestingModule], schemas: [NO_ERRORS_SCHEMA], diff --git a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.ts b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.ts index 453a8a0ca..c4a47344b 100644 --- a/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.ts +++ b/Phonebook.Frontend/src/app/modules/organigram/pages/organigram/organigram.component.ts @@ -1,20 +1,121 @@ import { Component, OnInit } from '@angular/core'; -import { OrganigramService, UnitTreeNode } from 'src/app/services/api/organigram.service'; +import { untilComponentDestroyed } from 'ng2-rx-componentdestroyed'; +import { of, Observable } from 'rxjs'; +import { BreakpointObserver } from '@angular/cdk/layout'; +import { + OrganigramService, + UnitTreeNode, + getNodeFromTreeSync, + OrgUnit, +} from 'src/app/services/api/organigram.service'; +import { MatTreeNestedDataSource } from '@angular/material/tree'; +import { NestedTreeControl } from '@angular/cdk/tree'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; +import { OrganigramHelpers } from 'src/app/modules/organigram/helpers'; +import { filter, switchMap } from 'rxjs/operators'; +import { CurrentUserService } from 'src/app/services/api/current-user.service'; +import { Person } from 'src/app/shared/models'; +import { Navigate, RouterState } from '@ngxs/router-plugin'; +import { Store } from '@ngxs/store'; +import { isDefined } from '@angular/compiler/src/util'; @Component({ selector: 'app-organigram', templateUrl: './organigram.component.html', styleUrls: ['./organigram.component.scss'], - host: { class: 'pb-expand' }, + host: { class: 'pb-fill-parent pb-flex-column' }, }) export class OrganigramComponent implements OnInit { - public nodes: UnitTreeNode[] = []; + public params: string[] = []; + public currentUser: Person | null = null; + public drawerOpenByDefault: boolean = false; + public dataSource: MatTreeNestedDataSource; + public treeControl: NestedTreeControl; + public whereAmI: string[] = ['/organigram']; + public nodeName: string[] = []; + public currentNode: UnitTreeNode; + constructor( + private organigramService: OrganigramService, + private route: ActivatedRoute, + private router: Router, + private breakpointObserver: BreakpointObserver, + private currentUserService: CurrentUserService, + private store: Store + ) { + this.treeControl = new NestedTreeControl(this._getChildren); + this.dataSource = new MatTreeNestedDataSource(); + this.organigramService.getOrganigramTree().subscribe((organigram) => { + this.dataSource.data = organigram; + }); + } + public hasChild = (_: number, nodeData: UnitTreeNode) => nodeData.children.length > 0; - constructor(private organigramService: OrganigramService) {} + private _getChildren = (node: UnitTreeNode) => node.children; public ngOnInit() { - this.organigramService.getOrganigram().subscribe((organigram) => { - this.nodes = organigram; + this.currentUserService + .getCurrentUser() + .pipe(untilComponentDestroyed(this)) + .subscribe( + (user) => { + if (user != null) { + this.currentUser = user; + this.whereAmI = this.whereAmI.concat(this.currentUser.Business.ShortOrgUnit); + } + }, + (error) => { + this.currentUser = null; + } + ); + if (this.route.firstChild == null) { + return; + } + this.route.firstChild.paramMap.subscribe((paramMap) => { + this.params = OrganigramHelpers.getParamsAsArray(paramMap, ['node1Id', 'node2Id', 'node3Id']); + this.updateTreeExtendedState(); }); + this.router.events + .pipe( + untilComponentDestroyed(this), + filter((event) => event instanceof NavigationEnd && event.url.includes('organigram')), + switchMap(() => (this.route.firstChild ? this.route.firstChild.paramMap : of(null))) + ) + .subscribe((params) => { + if (params != null) { + this.params = OrganigramHelpers.getParamsAsArray(params, [ + 'node1Id', + 'node2Id', + 'node3Id', + ]); + this.updateTreeExtendedState(); + } + }); + this.drawerOpenByDefault = this.breakpointObserver.isMatched('(min-width: 1500px)'); + } + + public updateTreeExtendedState() { + this.treeControl.collapseAll(); + this.params.forEach((x, i) => { + this.currentNode = getNodeFromTreeSync(this.params.slice(0, i + 1), this.dataSource.data); + if (this.currentNode != null) { + this.treeControl.expand(this.currentNode); + this.nodeName.push(this.currentNode.name); + } + }); + } + public ngOnDestroy() {} + public navigateToNodePath(nodePath: UnitTreeNode) { + let tree = this.getCurrentRouteAsArray().slice(0, nodePath.depth + 1); + tree = [...tree, nodePath.id]; + this.store.dispatch(new Navigate(tree)); + } + public getCurrentRouteAsArray(): string[] { + const navState = this.store.selectSnapshot(RouterState.state); + return [ + navState!.root.firstChild!.url[0].path, + ...navState!.root.firstChild!.firstChild!.firstChild!.url.map((obj) => { + return obj.path; + }), + ]; } } diff --git a/Phonebook.Frontend/src/app/services/api/current-user.service.ts b/Phonebook.Frontend/src/app/services/api/current-user.service.ts index 08ac5721e..0917b7407 100644 --- a/Phonebook.Frontend/src/app/services/api/current-user.service.ts +++ b/Phonebook.Frontend/src/app/services/api/current-user.service.ts @@ -42,6 +42,8 @@ export class CurrentUserService { } public getCurrentUserId(): Observable { + // Take out before merging! + return of('0634'); return this.getCurrentUserObject().pipe( map((str) => { // Userstring Layout is "Domain\\user" diff --git a/Phonebook.Frontend/src/app/services/api/organigram.service.ts b/Phonebook.Frontend/src/app/services/api/organigram.service.ts index c81ef15de..c998de568 100644 --- a/Phonebook.Frontend/src/app/services/api/organigram.service.ts +++ b/Phonebook.Frontend/src/app/services/api/organigram.service.ts @@ -1,32 +1,59 @@ import { Injectable } from '@angular/core'; import { Observable, forkJoin, of } from 'rxjs'; -import { map, flatMap, publishReplay, refCount } from 'rxjs/operators'; +import { map, flatMap, publishReplay, refCount, filter } from 'rxjs/operators'; import { Person } from 'src/app/shared/models'; import { PersonService } from './person.service'; import { HttpClient } from '@angular/common/http'; +import { stringify } from 'querystring'; +import { nameToState } from '@ngxs/store/src/internal/internals'; @Injectable() export class OrganigramService { constructor(private http: HttpClient, private personService: PersonService) {} + public orgUnits: Observable; public organigram: Observable; - public getOrganigram(): Observable { + public getOrgUnits(): Observable { + if (this.orgUnits != null) { + return this.orgUnits; + } + this.orgUnits = this.http.get('/api/OrgUnit').pipe( + publishReplay(1), // this tells Rx to cache the latest emitted + refCount() + ); + return this.orgUnits; + } + public getOrganigramTree(): Observable { if (this.organigram != null) { return this.organigram; } - this.organigram = this.http.get('/api/OrgUnit').pipe( + this.organigram = this.getOrgUnits().pipe( flatMap((d) => this.ConvertOrgUnitsToUnitTree(d)), publishReplay(1), // this tells Rx to cache the latest emitted refCount() ); return this.organigram; } + + public getOrgUnitById(id: string): Observable { + return this.getOrgUnits().pipe( + map((orgUnitArray) => { + const orgUnit = orgUnitArray.find((x) => { + return x.ShortName === id; + }); + if (orgUnit === undefined) { + return null; + } + return orgUnit; + }) + ); + } private ConvertOrgUnitsToUnitTree( orgUnits: OrgUnit[], depth: number = 0 ): Observable { return forkJoin( orgUnits.map((o) => { - var TaShortNames = o.OrgUnitToFunctions.filter((f) => f.RoleName == 'TA').map( + let TaShortNames = o.OrgUnitToFunctions.filter((f) => f.RoleName == 'TA').map( (t) => t.Person.ShortName ); return forkJoin( @@ -42,7 +69,7 @@ export class OrganigramService { o.ShortName == null ? of([]) : this.personService.getByOrgUnit(o.ShortName) ).pipe( map(([childs, headofOrgUnit, assistents, members]) => { - var tree = new UnitTreeNode( + const tree = new UnitTreeNode( o.ShortName == null ? '' : o.ShortName, o.Name == null ? '' : o.Name, depth, @@ -58,6 +85,39 @@ export class OrganigramService { }) ); } + public getNodeByPath(pathArray: string[], level: number = 0): Observable { + return this.getOrganigramTree().pipe( + flatMap((tree) => { + return of(getNodeFromTreeSync(pathArray, tree, level)); + }) + ); + } +} + +export function getNodeFromTreeSync( + paramArray: string[], + tree: UnitTreeNode[], + level: number = 0 +): UnitTreeNode | null { + if (paramArray.length === 1) { + return ( + tree.find((node) => { + return node.id === paramArray[0]; + }) || null + ); + } else { + const nextNode = tree.find((node) => { + return node.id === paramArray[0]; + }); + if (nextNode == null || nextNode.children.length === 0) { + return null; + } + return getNodeFromTreeSync( + paramArray.slice(1, paramArray.length), + nextNode.children, + level + 1 + ); + } } export class UnitTreeNode { @@ -90,7 +150,7 @@ export class UnitTreeNode { } } -class OrgUnit { +export class OrgUnit { public Id: number; public Name?: string; diff --git a/Phonebook.Frontend/src/i18n/messages.de.xlf b/Phonebook.Frontend/src/i18n/messages.de.xlf index 57fd64de7..43b1bc26d 100644 --- a/Phonebook.Frontend/src/i18n/messages.de.xlf +++ b/Phonebook.Frontend/src/i18n/messages.de.xlf @@ -1171,6 +1171,52 @@ Link zu deinem Profil: wurde in die Zwischenablage kopiert! Second part of the message displayed when copying a link to the node OrganigramNodeComponent + + {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} + {VAR_PLURAL, plural, =0 {Kein Leiter } =1 {Leiter: } other {Leiter: }} + Supervisor Property + NodeComponent + + {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: + }} + {VAR_PLURAL, plural, =0 {Kein Assistent } =1 {Assistent: } other {Assistenten: + }} + Assistant Property + NodeComponent + + {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: + }} + {VAR_PLURAL, plural, =0 {Keine Mitarbeiter } =1 {Mitarbeiter: } other {Mitarbeiter: + }} + Employee Property + NodeComponent + + {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: + }} + {VAR_PLURAL, plural, =0 {Keine Lernenden } =1 {Lernende: } other {Lernende: + }} + Learners Property + NodeComponent + + Sub-Organization Unit: + Unterorganisationseinheit: + SubOrganization Unit Property + NodeComponent + + Organisation does not exist. + Organisation existiert nicht. + Error Message if Node does not exist. + NodeComponent + + Link to + Link nach + First part of the message displayed when copying a link to the node + NodeComponent + + copied to clipboard! + Wurde in die Zwischenablage kopiert! + Second part of the message displayed when copying a link to the node + NodeComponent Copied to clipboard! @@ -1227,6 +1273,28 @@ Link zu deinem Profil: City does not exist.Diese Stadt existiert nicht. Error Message if City does not exist. CityComponent + + Button expanding or collapsing the tree view on the left. + Knopf zum ein und ausfahren des Menüs auf der linken Seite. + Aria Label for expanding the tree + view + OrganigramComponent + + Expand or Collapse the tree view. + Baumansicht ein oder ausfahren. + Tooltip for expanding the tree + view + OrganigramComponent + + Overview + Übersicht + Root of the Organigram Tree + OrganigramComponent + + Where am I? + Wo bin ich? + Button for going to user's organigram + OrganigramComponent Floor does not exist.Dieser Flur existiert nicht. Error Message if Floor does not exist. diff --git a/Phonebook.Frontend/src/i18n/messages.en.xlf b/Phonebook.Frontend/src/i18n/messages.en.xlf index db3394a93..58c120661 100644 --- a/Phonebook.Frontend/src/i18n/messages.en.xlf +++ b/Phonebook.Frontend/src/i18n/messages.en.xlf @@ -954,6 +954,52 @@ Please contact the HR Department to fix it.This is the Phonebook Link: copied to clipboard! Second part of the message displayed when copying a link to the node OrganigramNodeComponent + + {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} + {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} + Supervisor Property + NodeComponent + + {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: + }} + {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: + }} + Assistant Property + NodeComponent + + {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: + }} + {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: + }} + Employee Property + NodeComponent + + {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: + }} + {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: + }} + Learners Property + NodeComponent + + Sub-Organization Unit: + Sub-Organization Unit: + SubOrganization Unit Property + NodeComponent + + Organisation does not exist. + Organisation does not exist. + Error Message if Node does not exist. + NodeComponent + + Link to + Link to + First part of the message displayed when copying a link to the node + NodeComponent + + copied to clipboard! + copied to clipboard! + Second part of the message displayed when copying a link to the node + NodeComponent Copied to clipboard! @@ -1010,6 +1056,28 @@ Please contact the HR Department to fix it.This is the Phonebook Link: City does not exist.City does not exist. Error Message if City does not exist. CityComponent + + Button expanding or collapsing the tree view on the left. + Button expanding or collapsing the tree view on the left. + Aria Label for expanding the tree + view + OrganigramComponent + + Expand or Collapse the tree view. + Expand or Collapse the tree view. + Tooltip for expanding the tree + view + OrganigramComponent + + Overview + Overview + Root of the Organigram Tree + OrganigramComponent + + Where am I? + Where am I? + Button for going to user's organigram + OrganigramComponent Floor does not exist.Floor does not exist. Error Message if Floor does not exist. diff --git a/Phonebook.Frontend/src/i18n/messages.xlf b/Phonebook.Frontend/src/i18n/messages.xlf index 78871a898..a7468ba65 100644 --- a/Phonebook.Frontend/src/i18n/messages.xlf +++ b/Phonebook.Frontend/src/i18n/messages.xlf @@ -1,816 +1,977 @@ - + Save the site URL as a favourite now in order to not get any more startup-dialogs. Please notice: You won't get any information about updates or releases with the set url parameter. + Save the site URL as a favourite now in order to not get any more startup-dialogs. Please notice: You won't get any information about updates or releases with the set url parameter. Message for just set no cookie url Display advice to new url Reset + Reset Message for following no cookie url Restore Url Startup-Dialogs are deactivated. Please notice: You won't get any information about updates or releases. + Startup-Dialogs are deactivated. Please notice: You won't get any information about updates or releases. Message to inform user that no dialogs will be shown Warning no dialogs are shown Change Profile Picture + Change Profile Picture + Title ChangeProfilePictureDialogComponent You're changing your profile picture? Nice! But please notice: The picture is used only by the Phonebook Application. To upload a profile picture is your free choice. You can change or delete your picture at any time. (Just click the Button right next to the upload Button.) Your profile picture will deleted if you choose to leave the company. + You're changing your profile picture? Nice! But please notice: The picture is used only by the Phonebook Application. To upload a profile picture is your free choice. You can change or delete your picture at any time. (Just click the Button right next to the upload Button.) Your profile picture will deleted if you choose to leave the company. Content ChangeProfilePictureDialogComponent Cancel + Cancel Change my Picture! + Change my Picture! Button to consent sending bug reports ChangeProfilePictureDialogComponent This opens up a dialog to select a profile picture from you file system. + This opens up a dialog to select a profile picture from you file system. Aria Label for the 'Change Picture' Button ChangeProfilePictureComponent Change Picture + Change Picture Button for changing the Profile Picture ChangeProfilePictureComponent Delete your Profile Picture + Delete your Profile Picture Tooltip for deleting the Profile picture ChangeProfilePictureComponent Button for deleting your Profile Picture. + Button for deleting your Profile Picture. Aria Label for deleting the Profile Picture ChangeProfilePictureComponent Updated your Profile Picture! + Updated your Profile Picture! Success Message if profile picture was updated ChangeProfilePictureComponent The File Type is not supported. + The File Type is not supported. Error Message if wrong File Type is supplied ChangeProfilePictureComponent I want to add more support! + I want to add more support! Button on Error Message if wrong File Type is supplied ChangeProfilePictureComponent Something went wrong. Please try again. + Something went wrong. Please try again. A general Error message, that can be displayed everywhere GeneralErrorMessage Your file is too big! It should be smaller than + Your file is too big! It should be smaller than Error Message if supplied file size is to big (without size and unit) ChangeProfilePictureComponent Your profile picture was deleted! + Your profile picture was deleted! Success Message if user deleted its profile picture ChangeProfilePictureComponent Change sorting for column Id + Change sorting for column Id Label for sorting the column 'Id' TableComponent Change sorting for column Name + Change sorting for column Name Label for sorting the column 'Name' TableComponent Change sorting for column Phone + Change sorting for column Phone Label for sorting the column 'Phone' TableComponent Change sorting for column Mobile + Change sorting for column Mobile Label for sorting the column 'Mobile' TableComponent Change sorting for column Role + Change sorting for column Role Label for sorting the column 'Role' TableComponent Change sorting for column City + Change sorting for column City Label for sorting the column 'City' TableComponent Change sorting for column Organization Unit + Change sorting for column Organization Unit Label for sorting the column 'Organization Unit' TableComponent Change sorting for column Room + Change sorting for column Room Label for sorting the column 'Room' TableComponent Change sorting for column Building + Change sorting for column Building Label for sorting the column 'Building' TableComponent Change sorting for column Profitcenter + Change sorting for column Profitcenter Label for sorting the column 'Profitcenter' once Costcenter TableComponent Change sorting for column status + Change sorting for column status Label for sorting the column 'Status' TableComponent Your search returned no results. + Your search returned no results. Sentence displayed if search did not return any results TableComponent Configure Table Columns + Configure Table Columns Title for Table Column Settings TableSettingsDialog Drag and Drop the Columns in order to arrange them in an order you like. You can hide them as well by dragging them to the right. + Drag and Drop the Columns in order to arrange them in an order you like. You can hide them as well by dragging them to the right. Description for Table Column Settings TableSettingsDialog Note: You can only search for displayed columns. + Note: You can only search for displayed columns. Note for Table Column Settings TableSettingsDialog Displayed Columns: + Displayed Columns: Title of displayed table columns TableSettingsDialog Not displayed Columns: + Not displayed Columns: Title of not displayed table columns TableSettingsDialog Reset + Reset Button for resetting something GeneralResetButton Close + Close Button for closing something GeneralCloseButton Recent People + Recent People Title of the recent people section DashboardComponent Bookmarked People + Bookmarked People Title of the Bookmarked people section DashboardComponent Custom Order + Custom Order The standard (customizable) order of the favorite cards DashboardComponent Alphabetical asc + Alphabetical asc The alphabetical asc order of the favorite cards DashboardComponent Alphabetical desc + Alphabetical desc The alphabetical desc order of the favorite cards DashboardComponent Here you can change the layout of your dashboard. + Here you can change the layout of your dashboard. Layout Change Button Tooltip DashboardComponent Expand or Collapse recently viewed people + Expand or Collapse recently viewed people Tooltip for expanding recently viewed people DashboardComponent Button expanding or collapsing the recently viewed people section + Button expanding or collapsing the recently viewed people section Aria Label for expanding recently viewed people DashboardComponent Undo + Undo Button for undoing something GeneralUndoButton Looks like you haven't searched for any colleagues yet. Search for somebody to see how it works! The most recent ones will be displayed here. + Looks like you haven't searched for any colleagues yet. Search for somebody to see how it works! The most recent ones will be displayed here. Message displayed if no people have been visited recently DashboardComponent You haven't bookmarked anybody yet, look for the button bookmark_border on a workmates page. + You haven't bookmarked anybody yet, look for the button bookmark_border on a workmates page. Message displayed if no people are bookmarked DashboardComponent Medium Cards + Medium Cards View Mode - MediumCards NavigationComponent Small Cards + Small Cards View Mode - SmallCards NavigationComponent Settings + Settings Title of the settings page SettingsComponent Change the theme or language of this application. + Change the theme or language of this application. Title description of the settings page SettingsComponent General + General Subtitle General of the settings page SettingsComponent Language + Language Language SubTitle SettingsComponent Color Theme + Color Theme Color Theme SubTitle SettingsComponent Here you can change the color theme of the app. + Here you can change the color theme of the app. Color Change Button Tooltip navigationBar Blue Light Theme + Blue Light Theme Color Theme Option - Blue Light SettingsComponent Unicorn Theme + Unicorn Theme Color Theme Option - Unicorn SettingsComponent Blue Dark Theme + Blue Dark Theme Color Theme Option - Blue Dark SettingsComponent Magenta Light Theme + Magenta Light Theme Color Theme Option - Magenta Light SettingsComponent Magenta Dark Theme + Magenta Dark Theme Color Theme Option - Magenta Dark SettingsComponent User with Id + User with Id First part of the message displayed when a user is not found UserDetailPageResolver not found. + not found. Second part of the message displayed when a user is not found UserDetailPageResolver German + German GeneralLanguageGerman GeneralLanguageGerman English + English GeneralLanguageEnglish GeneralLanguageEnglish We've fixed some Bugs and added some new Features for you, with ❤ + We've fixed some Bugs and added some new Features for you, with ❤ Snack Bar display for a feature update ReleaseInfoService Fixed what? + Fixed what? Snack Bar display Action Button for a feature update ReleaseInfoService Add this as a filter + Add this as a filter Tooltip for adding a filter to the search AddFilterComponent Refines the Search by adding this as a Filter. + Refines the Search by adding this as a Filter. Aria Label for adding a filter to the search AddFilterComponent Room + Room Floor + Floor Label for Person.Floor data Datapoint No Location provided. + No Location provided. Message displayed if the location of the user could not be determined User-detailComponent Call for Contributors! + Call for Contributors! Subtitle Contributes Page-informationComponent The Phonebook is an Open Source Project and lives from his active contributors. There are many different topics where you can work on. You can help with solving an Issue, work out an User-Guide or document new Bugs. You can get in touch with us in our official Repository or you take a look at our Documentation. To be a part of this community it is not required to have programming skills.Thanks to our Contributors! + The Phonebook is an Open Source Project and lives from his active contributors. There are many different topics where you can work on. You can help with solving an Issue, work out an User-Guide or document new Bugs. You can get in touch with us in our official Repository or you take a look at our Documentation. To be a part of this community it is not required to have programming skills.Thanks to our Contributors! Text for Contribute Page-informationComponent Contributors could not be loaded. + Contributors could not be loaded. Contributors error Message ContributorsErrorMessage Dashboard + Dashboard Link to Dashboard NavigationBar Organigram + Organigram Link to Organigram NavigationBar Rooms + Rooms Link to Rooms NavigationBar My profile + My profile Link to the users profile NavigationComponent Got Feedback? Than that`s the right place to click. + Got Feedback? Than that`s the right place to click. Info Button Tooltip navigationBar Information + Information Link to Information page NavigationComponent Release Notes + Release Notes Release Notes Menu Item NavigationComponent Found a Bug? Than that`s the right place to click. + Found a Bug? Than that`s the right place to click. Bug Button Tooltip navigationBar Submit Feedback + Submit Feedback Link to Bug page NavigationComponent Phonebook + Phonebook Application Name NavigationBar If you upload a picture it will be easier for your colleagues to find and contact you! + If you upload a picture it will be easier for your colleagues to find and contact you! Tooltip for MissingUserImage NavigationComponent Button which greets and asks for picture uploading. + Button which greets and asks for picture uploading. Aria label for MissingUserImage NavigationComponent Hey + Hey Greeting NavigationComponent you haven't uploaded a picture yet. Want to upload one? + you haven't uploaded a picture yet. Want to upload one? Message NavigationComponent Results + Results Result Count TableComponent Happy April Fools' Day + Happy April Fools' Day Greetings Message on first April NavigationBar Have a nice day + Have a nice day Greetings Message NavigationBar The green bar indicates that you are online. + The green bar indicates that you are online. Tooltip for bar indicating the user is online Online-barComponent The red bar indicates that you are offline. + The red bar indicates that you are offline. Tooltip for bar indicating the user is offline Online-barComponent Unfortunately there is no plan for this floor. Do you know who can make one or want to help us fix this? code Contribute + Unfortunately there is no plan for this floor. Do you know who can make one or want to help us fix this? code Contribute Message displayed if there is no room plan RoomPlanComponent Search for names, shorthands, phone numbers and more... + Search for names, shorthands, phone numbers and more... Placholder for search SearchComponent Found Incorrect Data? Report them! + Found Incorrect Data? Report them! Tooltip Incorrect Data User-detailComponent Bookmark this person + Bookmark this person Tooltip for saving a person as favourite User-detailComponent Share + Share Tooltip for opening the share menu User-detailComponent Download Contact + Download Contact Share Menu: Donwload Contact Option User-detailComponent Link + Link Share Menu: Link Option User-detailComponent Share by mail + Share by mail Share Menu: Mail Option User-detailComponent Contact Details + Contact Details Subtitle for Contact Details User-detailComponent Rocket Chat + Rocket Chat Share Menu: Rocket Chat Option User-detailComponent Email + Email Phone + Phone Mobile + Mobile Fax + Fax Label for Person.Fax data Datapoint Further Information + Further Information Subtitle for Further Information User-detailComponent Direct Supervisor + Direct Supervisor Label for Person.Supervisor (direct supervisor) Datapoint Team Assistants + Team Assistants Label for Person.TeamAssistant Datapoint Profitcenter + Profitcenter Status + Status Label for Person.Status data Datapoint Organization Unit + Organization Unit Show in Organigram + Show in Organigram Button to show the persons position in the organigram User-detailComponent Location + Location Combined location of the person (city, building, floor, room) User-detailComponent Show Room + Show Room Button to show the persons room User-detailComponent Roomplan + Roomplan Tab Headline for Roomplan UserDetailComponent Skills + Skills Tab Headline for Skills UserDetailComponent This is where a coworkers skills would be shown. But this is still work in Progress, you can make it happen faster! code Contribute + This is where a coworkers skills would be shown. But this is still work in Progress, you can make it happen faster! code Contribute Text for the upcoming skill tag feature User-detailComponent Picture + Picture Title of Table Column "Picture" TableComponent Id + Id Title of Table Column "Id" TableComponent Name + Name Title of Table Column "Name" TableComponent Email + Email Title of Table Column "Email" TableComponent Role + Role Title of Table Column "Role" TableComponent City + City Title of Table Column "City" TableComponent Building + Building Title of Table Column "Building" TableComponent We need your help! + We need your help! Title of Bug Report Consent Dialog Bug-report-consentComponent Sending automatic Bug reports + Sending automatic Bug reports Sub title of bug report consent dialog Bug-report-consentComponent If an error occurs while the application is running, an anonymous error report is automatically generated. In exceptional cases - since this is a preview - the following personal data can still be found in one of the reports: Username (short and full name) Bookmarks and Last seen people The Last 20 Actions performed in the app The data from the error report is used exclusively for the purpose of analysis and correction of the error by our core development team. The data will not be passed on to third parties. TL;DR With your permission, you agree that bug reports that may contain your personal data can be sent to our core development team and be used for error analysis and resolution. + If an error occurs while the application is running, an anonymous error report is automatically generated. In exceptional cases - since this is a preview - the following personal data can still be found in one of the reports: Username (short and full name) Bookmarks and Last seen people The Last 20 Actions performed in the app The data from the error report is used exclusively for the purpose of analysis and correction of the error by our core development team. The data will not be passed on to third parties. TL;DR With your permission, you agree that bug reports that may contain your personal data can be sent to our core development team and be used for error analysis and resolution. Text of Bug Report consent dialog Bug-report-consentComponent Do not send Bug Reports + Do not send Bug Reports Button to decline sending bug reports Bug-report-consentComponent Consent + Consent Button to consent sending bug reports Bug-report-consentComponent Welcome to the new Phonebook! + Welcome to the new Phonebook! Welcome Headline Display-notificationDialog The new Phonebook not only offers you a new design, but also numerous features that make it better than its predecessor. + The new Phonebook not only offers you a new design, but also numerous features that make it better than its predecessor. Welcome Introduction Display-notificationDialog You can set a profile picture by yourself + You can set a profile picture by yourself Profile Image Feature Headline Display-notificationDialog Simply click on the person icon in the upper right corner and then on "My Profile". + Simply click on the person icon in the upper right corner and then on "My Profile". Profile Image Feature Description Display-notificationDialog Colleagues can be saved as a favorite + Colleagues can be saved as a favorite Bookmark Feature Headline Display-notificationDialog Just click on the bookmark_border icon at one of your colleagues. + Just click on the bookmark_border icon at one of your colleagues. Bookmark Feature Description Display-notificationDialog Table columns can be sorted and customized + Table columns can be sorted and customized Table Settings Headlin Display-notificationDialog The settings settings can be found at the search page in the upper right corner. + The settings settings can be found at the search page in the upper right corner. Table Settings Description Display-notificationDialog People can be shared via links + People can be shared via links Share Headline Display-notificationDialog The different ways to share a profile can be found by clicking on the share icon. + The different ways to share a profile can be found by clicking on the share icon. Share description Display-notificationDialog The Phonebook is available in German and English + The Phonebook is available in German and English Language Headline Display-notificationDialog Simply click on the person icon in the top right corner and go to the settings. + Simply click on the person icon in the top right corner and go to the settings. Language description Display-notificationDialog You can view a roomplan for each employee + You can view a roomplan for each employee Room Plan Headline Display-notificationDialog Just go to the profile of a colleague and scroll down. + Just go to the profile of a colleague and scroll down. Room Plan description Display-notificationDialog This site uses cookies to ensure the basic functionality of the app. + This site uses cookies to ensure the basic functionality of the app. Cookie notice Display-notificationDialog You deactivated cookies and keep getting those dialogs? + You deactivated cookies and keep getting those dialogs? Annoyed by cookies notice Display-notificationDialog Deactivate startup-dialogs! + Deactivate startup-dialogs! Deactivate Startup Dialogs Display-notificationDialog This Website may not function properly in Internet Explorer + This Website may not function properly in Internet Explorer + Title IeWarningComponent The support for older versions of Internet Explorer ended in 2016. It is not actively developed anymore and as such lags modern features. Making a feature work in Internet Explorer takes up as much time as building the feature itself for all other browser. As we only have limited time we strive to develop features of the future instead of supporting the past. Please change to a more modern browser: Chrome, Firefox, Edge, Safari. + The support for older versions of Internet Explorer ended in 2016. It is not actively developed anymore and as such lags modern features. Making a feature work in Internet Explorer takes up as much time as building the feature itself for all other browser. As we only have limited time we strive to develop features of the future instead of supporting the past. Please change to a more modern browser: Chrome, Firefox, Edge, Safari. Content IeWarningComponent I still want to use Internet Explorer! + I still want to use Internet Explorer! Button IeWarningComponent Incorrect User Information + Incorrect User Information Headline User-InformationDialog Got it! + Got it! Button User-InformationDialog Dear , unfortunately, you can't change your user information via the Phonebook interface. If your data is incorrect, please contact your Human Resources department. + Dear , unfortunately, you can't change your user information via the Phonebook interface. If your data is incorrect, please contact your Human Resources department. Profile User-InformationDialog Is there something wrong with the profile? Please let your colleague know that the data is incorrect and that it can be changed through your Human Resources department. + Is there something wrong with the profile? Please let your colleague know that the data is incorrect and that it can be changed through your Human Resources department. Profile User-InformationDialog Send your colleague a direct message + Send your colleague a direct message Button User-InformationDialog Notify + Notify Profile User-InformationDialog There is an Issue with your Phonebook-Profile + There is an Issue with your Phonebook-Profile Send a mail to your mate if there is something wrong on the profile MailToMateSubject Hi + Hi Send a mail to your mate if there is something wrong on the profile MailToMateGreeting @@ -818,81 +979,98 @@ , while browsing your profile I noticed that something is not right: Please contact the HR Department to fix it.This is the Phonebook Link: + , +while browsing your profile I noticed that something is not right: +Please contact the HR Department to fix it.This is the Phonebook Link: Send a mail to your mate if there is something wrong on the profile MailToMateBody Copy to clipboard + Copy to clipboard Copy to Clipboard button GeneralCopyButton Send an Email + Send an Email Send Mail button GeneralMailButton Call the Number + Call the Number call Number button GeneralCallButton Copied to clipboard! + Copied to clipboard! Message displayed when copying a link is successfully GeneralSuccessMessageCopy Couldn't copy to the clipboard, something went wrong. Try again. + Couldn't copy to the clipboard, something went wrong. Try again. Message displayed if something was not copied succesfully ErrorMessageCopy Leave Feedback, report a Bug or suggest a new Idea + Leave Feedback, report a Bug or suggest a new Idea Title of the Feedback Drawer FeedbackDrawerSheetComponent Suggest an idea or a new feature + Suggest an idea or a new feature Button for suggesting a new feature on Github FeedbackDrawerSheetComponent Report a bug + Report a bug Button for reporting a bug on Github FeedbackDrawerSheetComponent Contact your private organization via email + Contact your private organization via email Button for contacting the private Organization via email FeedbackDrawerSheetComponent Contact your private organization + Contact your private organization Button for contacting the private Organization FeedbackDrawerSheetComponent {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} + {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} Supervisor Property OrganigramNodeComponent {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: }} + {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: }} Assistant Property OrganigramNodeComponent Button expanding or collapsing this node. + Button expanding or collapsing this node. Aria Label for expand Button on Organigram Node OrganigramNodeComponent Expand or Collapse this node. + Expand or Collapse this node. Tooltip for expanding a Organigram Node OrganigramNodeComponent @@ -900,237 +1078,362 @@ Please contact the HR Department to fix it.This is the Phonebook Link: {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: }} + {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: + }} Employee Property OrganigramNodeComponent {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: }} + {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: + }} Learners Property OrganigramNodeComponent Link to + Link to First part of the message displayed when copying a link to the node OrganigramNodeComponent copied to clipboard! + copied to clipboard! Second part of the message displayed when copying a link to the node OrganigramNodeComponent + + {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} + {VAR_PLURAL, plural, =0 {No Supervisor } =1 {Supervisor: } other {Supervisors: }} + Supervisor Property + NodeComponent + + + {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: + }} + {VAR_PLURAL, plural, =0 {No Assistant } =1 {Assistant: } other {Assistants: + }} + Assistant Property + NodeComponent + + + {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: + }} + {VAR_PLURAL, plural, =0 {No Employees } =1 {Employee: } other {Employees: + }} + Employee Property + NodeComponent + + + {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: + }} + {VAR_PLURAL, plural, =0 {No Learners } =1 {Learner: } other {Learners: + }} + Learners Property + NodeComponent + + + Sub-Organization Unit: + Sub-Organization Unit: + SubOrganization Unit Property + NodeComponent + + + Organisation does not exist. + Organisation does not exist. + Error Message if Node does not exist. + NodeComponent + + + Link to + Link to + First part of the message displayed when copying a link to the node + NodeComponent + + + copied to clipboard! + copied to clipboard! + Second part of the message displayed when copying a link to the node + NodeComponent + + + Button expanding or collapsing the tree view on the left. + Button expanding or collapsing the tree view on the left. + Aria Label for expanding the tree + view + OrganigramComponent + + + Expand or Collapse the tree view. + Expand or Collapse the tree view. + Tooltip for expanding the tree + view + OrganigramComponent + + + Overview + Overview + Root of the Organigram Tree + OrganigramComponent + + + Where am I? + Where am I? + Button for going to user's organigram + OrganigramComponent + Address + Address Label for address Datapoint Contact + Contact Label for contact information Datapoint Floors + Floors Floor plural Datapoint Show on Google-Maps + Show on Google-Maps Button label for showing the location on google maps General Contact Person + Contact Person Label for Location.ContactPerson Datapoint Building does not exist. + Building does not exist. Error Message if Building does not exist. BuildingComponent Buildings + Buildings Subheader for all building locations in the city CityComponent City does not exist. + City does not exist. Error Message if City does not exist. CityComponent Rooms + Rooms SubHeader for all Rooms in the floor FloorComponent Floor does not exist. + Floor does not exist. Error Message if Floor does not exist. FloorComponent Button expanding or collapsing the tree view on the left. + Button expanding or collapsing the tree view on the left. Aria Label for expanding the tree view RoomTreeComponent Expand or Collapse the tree view. + Expand or Collapse the tree view. Tooltip for expanding the tree view RoomTreeComponent Overview + Overview Root of the Navigation Tree Room-treeComponent Booking Tool + Booking Tool Button for changing to the booking tool RoomTreeComponent Share this room. + Share this room. Tooltip for the Room sharing button RoomDetailComponent Button for sharing this room. + Button for sharing this room. Aria Label for sharing the room RoomDetailComponent Link + Link Option for getting a Link to the Room RoomDetailComponent Share by mail + Share by mail Option for sharing the room by Mail RoomDetailComponent Information + Information Title for the main information of the room RoomDetailComponent Location + Location Subtitle for the location information of the room RoomDetailComponent In this Room + In this Room Title for the subtitle about the people in this room RoomDetailComponent Phone Number + Phone Number Subtitle for the communication information of the room RoomDetailComponent Nobody works in this room. + Nobody works in this room. Message displayed if no people are in the room DashboardComponent Room does not exist. + Room does not exist. Error Message if Room does not exist. RoomComponent Information about the room: + Information about the room: subject of the email message that is preset when clicking "Share by mail". The room is applied after this sentence. RoomDetailComponent Here is the Link: + Here is the Link: the body of the email that is preset when clicking "Share by mail". The link to the room is applied after this sentence. RoomDetailComponent Phonebook + Phonebook Title of the page information PageInformationComponent Contact & Feedback + Contact & Feedback Subtitle for Contact and Feedback Page-informationComponent Contact us + Contact us Feedback Button Page-informationComponent User Guide + User Guide Subtitle for User Guide Page-informationComponent Are you new to the Phonebook? Do you have any questions? Then visit the Phonebook's User Guide. It explains all its features and possibilities. + Are you new to the Phonebook? Do you have any questions? Then visit the Phonebook's User Guide. It explains all its features and possibilities. User Guide Section Page-informationComponent Open User Guide + Open User Guide User Guide Button Page-informationComponent Cookie Notice + Cookie Notice Subtitle for Cookie Page-informationComponent This site uses cookies, to enhance your experience. Cookies are only used for necessary tasks, e.g. saving the language you want to use. + This site uses cookies, to enhance your experience. Cookies are only used for necessary tasks, e.g. saving the language you want to use. CookiesSection Page-informationComponent Licenses + Licenses Subtitle for Licenses Page-informationComponent This Project is licensed under the MIT License. Other licenses from modules and packets may apply. + This Project is licensed under the MIT License. Other licenses from modules and packets may apply. Text for Licenses SubTitle Page-informationComponent Icon of the Phonebook made by Gregor Cresnar from www.flaticon.com is licensed by CC 3.0 BY + Icon of the Phonebook made by Gregor Cresnar from www.flaticon.com is licensed by CC 3.0 BY Attributions SubTitle Page-informationComponent This is a Preview application. We are doing our best not to break anything, but we can't guarantee. Thats why all Features are still subject to change without further notice and your data may get lost. + This is a Preview application. We are doing our best not to break anything, but we can't guarantee. Thats why all Features are still subject to change without further notice and your data may get lost. Disclaimer beneath title Page-informationComponent This is an Installation of the Open Source Phonebook. You can contact your own support at regarding this installation. You've found a bug or have an idea for a new feature? + This is an Installation of the Open Source Phonebook. You can contact your own support at regarding this installation. You've found a bug or have an idea for a new feature? Feedback Section Page-informationComponent diff --git a/Phonebook.Source.PeopleSoft/.vscode/launch.json b/Phonebook.Source.PeopleSoft/.vscode/launch.json index f985a9a00..3a1c84140 100644 --- a/Phonebook.Source.PeopleSoft/.vscode/launch.json +++ b/Phonebook.Source.PeopleSoft/.vscode/launch.json @@ -9,13 +9,13 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Phonebook.Source.PeopleSoft/bin/Debug/netcoreapp2.2/Phonebook.Source.PeopleSoft.dll", + "program": "${workspaceFolder}/src/Phonebook.Source.PeopleSoft/bin/Debug/netcoreapp3.1/Phonebook.Source.PeopleSoft.dll", "args": [], "cwd": "${workspaceFolder}/src/Phonebook.Source.PeopleSoft", "stopAtEntry": false, "internalConsoleOptions": "openOnSessionStart", "launchBrowser": { - "enabled": true, + "enabled": false, "args": "${auto-detect-url}/api/peoples", "windows": { "command": "cmd.exe", diff --git a/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft.Models/Context/SeedingContext.cs b/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft.Models/Context/SeedingContext.cs index 8ef97bb88..bf561ac33 100644 --- a/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft.Models/Context/SeedingContext.cs +++ b/Phonebook.Source.PeopleSoft/src/Phonebook.Source.PeopleSoft.Models/Context/SeedingContext.cs @@ -1,6 +1,7 @@ using Bogus; using Microsoft.EntityFrameworkCore; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -149,7 +150,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #endregion Room #region OrgUnit - var maxOrgUnits = 500; + var maxOrgUnits = 200; var OrgUnitFaker = new Faker() .StrictMode(false) .Rules((f, b) => @@ -160,28 +161,49 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) b.ParentId = null; b.CostCenter = new Bogus.Randomizer().Replace("####"); }); - var OrgUnitList = Enumerable.Range(1, maxOrgUnits) + + var OrgUnitList = Enumerable.Range(1, 20) .Select(_ => OrgUnitFaker.Generate()) .ToList(); + var ChildOrgUnitFaker = new Faker() + .StrictMode(false) + .Rules((f, b) => + { + b.Id = f.IndexVariable++ + 1 + OrgUnitList.Count; + b.Name = f.Commerce.Department(); + b.ShortName = new Bogus.Randomizer().Replace("**"); + b.ParentId = f.PickRandom(OrgUnitList).Id; + b.CostCenter = new Bogus.Randomizer().Replace("####"); + }); + + var ChildOrgUnitList = Enumerable.Range(21, 100) + .Select(_ => ChildOrgUnitFaker.Generate()) + .ToList(); + + var ChildTwoOrgUnitFaker = new Faker() + .StrictMode(false) + .Rules((f, b) => + { + b.Id = f.IndexVariable++ + 1 + ChildOrgUnitList.Count + OrgUnitList.Count; + b.Name = f.Commerce.Department(); + b.ShortName = new Bogus.Randomizer().Replace("**"); + b.ParentId = f.PickRandom(ChildOrgUnitList).Id; + b.CostCenter = new Bogus.Randomizer().Replace("####"); + }); + + var ChildTwoOrgUnitList = Enumerable.Range(101, maxOrgUnits) + .Select(_ => ChildTwoOrgUnitFaker.Generate()) + .ToList(); + var boolRandomizer = new Bogus.Randomizer(); var randomPicker = new Faker(); - foreach (var OrgUnit in OrgUnitList) - { - if (boolRandomizer.Bool()) - { - var maybeParent = randomPicker.PickRandom(OrgUnitList); - while ( - maybeParent.Id == OrgUnit.Id || - maybeParent.ParentId == OrgUnit.Id) - { - maybeParent = randomPicker.PickRandom(OrgUnitList); - } + ChildOrgUnitList.AddRange(ChildTwoOrgUnitList); + OrgUnitList.AddRange(ChildOrgUnitList); + // ToArray() is important. Otherwise, it doesn't work. + // https://github.com/dotnet/efcore/issues/12003 + modelBuilder.Entity().HasData(OrgUnitList.ToArray()); - OrgUnit.ParentId = maybeParent.ParentId; - } - modelBuilder.Entity().HasData(OrgUnit); - } #endregion OrgUnit #region Status @@ -214,7 +236,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) b.MobilPhone = f.Phone.PhoneNumber(); b.Phone = f.Phone.PhoneNumber(); b.Title = f.PickRandom(new[] { "Dr.", string.Empty, string.Empty, "Prof." }); - b.StatusId = f.Random.Number(1,4); + b.StatusId = f.Random.Number(1, 4); b.FunctionId = f.Random.Number(1, 4); b.OrgUnitId = f.PickRandom(OrgUnitList).Id; b.RoomId = f.PickRandom(RoomList).Id; @@ -227,7 +249,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) if (boolRandomizer.Bool()) { var orgUnit = new Faker().PickRandom(OrgUnitList); - if(orgUnit.Id != Person.OrgUnitId) + if (orgUnit.Id != Person.OrgUnitId) { orgUnit.HeadOfOrgUnitId = Person.Id; } @@ -242,12 +264,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) var faker = new Faker(); foreach (var person in PersonList) { - + modelBuilder.Entity().HasData( - new OrgUnitToFunction { - FunctionId = faker.Random.Number(1,4), - OrgUnitId = faker.PickRandom(OrgUnitList).Id, - PersonId = person.Id, + new OrgUnitToFunction + { + FunctionId = faker.Random.Number(1, 4), + OrgUnitId = faker.PickRandom(OrgUnitList).Id, + PersonId = person.Id, RoleName = faker.Random.Replace("###").ToUpper() }); }