| @@ -22,6 +22,7 @@ | |||
| "@ngx-translate/core": "^15.0.0", | |||
| "@ngx-translate/http-loader": "^8.0.0", | |||
| "@popperjs/core": "^2.11.8", | |||
| "@types/node": "^20.11.5", | |||
| "bootstrap": "^5.3.2", | |||
| "bootstrap-icons": "^1.11.2", | |||
| "rxjs": "~7.8.0", | |||
| @@ -4635,10 +4636,9 @@ | |||
| "dev": true | |||
| }, | |||
| "node_modules/@types/node": { | |||
| "version": "20.10.5", | |||
| "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", | |||
| "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", | |||
| "dev": true, | |||
| "version": "20.11.5", | |||
| "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", | |||
| "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", | |||
| "dependencies": { | |||
| "undici-types": "~5.26.4" | |||
| } | |||
| @@ -13408,8 +13408,7 @@ | |||
| "node_modules/undici-types": { | |||
| "version": "5.26.5", | |||
| "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", | |||
| "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", | |||
| "dev": true | |||
| "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" | |||
| }, | |||
| "node_modules/unicode-canonical-property-names-ecmascript": { | |||
| "version": "2.0.0", | |||
| @@ -25,6 +25,7 @@ | |||
| "@ngx-translate/core": "^15.0.0", | |||
| "@ngx-translate/http-loader": "^8.0.0", | |||
| "@popperjs/core": "^2.11.8", | |||
| "@types/node": "^20.11.5", | |||
| "bootstrap": "^5.3.2", | |||
| "bootstrap-icons": "^1.11.2", | |||
| "rxjs": "~7.8.0", | |||
| @@ -1,23 +1,56 @@ | |||
| import { NgModule } from '@angular/core'; | |||
| import { Routes, RouterModule } from '@angular/router'; | |||
| import {NgModule} from '@angular/core'; | |||
| import {Routes, RouterModule} from '@angular/router'; | |||
| import { HomeComponent } from './home'; | |||
| import { AuthGuard } from './_helpers'; | |||
| import {HomeComponent} from './home'; | |||
| import {AuthGuard} from './_helpers'; | |||
| import {PartnersComponent} from "@app/partners/partners.component"; | |||
| import {TwoColumnComponent} from "@app/layout/two-column/two-column.component"; | |||
| import {PartnersDetailComponent} from "@app/partners/partners-detail/partners-detail.component"; | |||
| const accountModule = () => import('./account/account.module').then(x => x.AccountModule); | |||
| const usersModule = () => import('./users/users.module').then(x => x.UsersModule); | |||
| const routes: Routes = [ | |||
| { path: '', component: HomeComponent, canActivate: [AuthGuard] }, | |||
| { path: 'users', loadChildren: usersModule, canActivate: [AuthGuard] }, | |||
| { path: 'account', loadChildren: accountModule }, | |||
| {path: '', component: HomeComponent, canActivate: [AuthGuard]}, | |||
| {path: 'users', loadChildren: usersModule, canActivate: [AuthGuard]}, | |||
| {path: 'account', loadChildren: accountModule}, | |||
| { | |||
| path: 'customers', | |||
| component: TwoColumnComponent, | |||
| canActivate: [AuthGuard], | |||
| children: [ | |||
| {path: '', component: PartnersComponent, data: {dataType: 'customer'}}, | |||
| {path: 'detail', component: PartnersDetailComponent, data: {dataType: 'customer-detail'}}, | |||
| ] | |||
| }, | |||
| { | |||
| path: 'suppliers', | |||
| component: TwoColumnComponent, | |||
| canActivate: [AuthGuard], | |||
| children: [ | |||
| {path: '', component: PartnersComponent, data: {dataType: 'supplier'}}, | |||
| ] | |||
| }, | |||
| { | |||
| path: 'service', | |||
| component: TwoColumnComponent, | |||
| canActivate: [AuthGuard], | |||
| children: [ | |||
| {path: '', component: PartnersComponent, data: {dataType: 'service'}}, | |||
| ] | |||
| }, | |||
| // { path: 'customers', component: PartnersComponent, data: { dataType: 'customer' } }, | |||
| // { path: 'suppliers', component: PartnersComponent, data: { dataType: 'supplier' } }, | |||
| // { path: 'service', component: PartnersComponent, data: { dataType: 'service' } }, | |||
| // otherwise redirect to home | |||
| { path: '**', redirectTo: '' } | |||
| {path: '**', redirectTo: ''} | |||
| ]; | |||
| @NgModule({ | |||
| imports: [RouterModule.forRoot(routes)], | |||
| exports: [RouterModule] | |||
| }) | |||
| export class AppRoutingModule { } | |||
| export class AppRoutingModule { | |||
| } | |||
| @@ -1,11 +1,11 @@ | |||
| import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; | |||
| import { provideServerRendering } from '@angular/platform-server'; | |||
| import { appConfig } from './app.config'; | |||
| import {mergeApplicationConfig, ApplicationConfig} from '@angular/core'; | |||
| import {provideServerRendering} from '@angular/platform-server'; | |||
| import {appConfig} from './app.config'; | |||
| const serverConfig: ApplicationConfig = { | |||
| providers: [ | |||
| provideServerRendering() | |||
| ] | |||
| providers: [ | |||
| provideServerRendering() | |||
| ] | |||
| }; | |||
| export const config = mergeApplicationConfig(appConfig, serverConfig); | |||
| @@ -18,6 +18,10 @@ import {MatCardModule} from "@angular/material/card"; | |||
| import {TranslateLoader, TranslateModule} from "@ngx-translate/core"; | |||
| import {TranslateHttpLoader} from "@ngx-translate/http-loader"; | |||
| import {NgOptimizedImage} from "@angular/common"; | |||
| import {PartnersComponent} from './partners/partners.component'; | |||
| import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; | |||
| import {TwoColumnComponent} from './layout/two-column/two-column.component'; | |||
| import {PartnersDetailComponent} from './partners/partners-detail/partners-detail.component'; | |||
| export function apiConfigFactory(): Configuration { | |||
| const params: ConfigurationParameters = { | |||
| @@ -36,6 +40,7 @@ export function HttpLoaderFactory(http: HttpClient) { | |||
| imports: [ | |||
| ApiModule.forRoot(apiConfigFactory), | |||
| BrowserModule, | |||
| BrowserAnimationsModule, | |||
| TranslateModule.forRoot({ | |||
| defaultLanguage: 'de', | |||
| loader: { | |||
| @@ -49,12 +54,15 @@ export function HttpLoaderFactory(http: HttpClient) { | |||
| NgbModule, | |||
| AppRoutingModule, | |||
| MatCardModule, | |||
| NgOptimizedImage | |||
| NgOptimizedImage, | |||
| PartnersComponent | |||
| ], | |||
| declarations: [ | |||
| AppComponent, | |||
| AlertComponent, | |||
| HomeComponent | |||
| HomeComponent, | |||
| TwoColumnComponent, | |||
| PartnersDetailComponent | |||
| ], | |||
| providers: [ | |||
| {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, | |||
| @@ -9,21 +9,21 @@ | |||
| <div class="container"> | |||
| <div class="row pt-4 pb-4 d-flex"> | |||
| <div class="col d-flex"> | |||
| <a class="card" routerLink="/users" routerLinkActive="active"> | |||
| <a class="card" routerLink="/customers" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-emoji-heart-eyes"> | |||
| <h3 class="position-absolute m-0">Kunden</h3> | |||
| </div> | |||
| </a> | |||
| </div> | |||
| <div class="col d-flex"> | |||
| <a class="card" routerLink="/users" routerLinkActive="active"> | |||
| <a class="card" routerLink="/suppliers" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-emoji-kiss"> | |||
| <h3 class="position-absolute m-0">Dienstleister</h3> | |||
| </div> | |||
| </a> | |||
| </div> | |||
| <div class="col d-flex"> | |||
| <a class="card" routerLink="/users" routerLinkActive="active"> | |||
| <a class="card" routerLink="/service" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-emoji-smile"> | |||
| <h3 class="position-absolute m-0">Lieferanten</h3> | |||
| </div> | |||
| @@ -0,0 +1,51 @@ | |||
| <div class="row ps-2"> | |||
| <div class="col-2 pt-2" style="background: rgba(255,0,0,.7);"> | |||
| <ul class="nav flex-column"> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/customers" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-emoji-heart-eyes"> | |||
| <h3 class="position-absolute m-0">Kunden</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/suppliers" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-emoji-kiss"> | |||
| <h3 class="position-absolute m-0">Dienstleister</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/service" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-emoji-smile"> | |||
| <h3 class="position-absolute m-0">Lieferanten</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/users" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-droplet-fill"> | |||
| <h3 class="position-absolute m-0">Produkte</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/users" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-list-check"> | |||
| <h3 class="position-absolute m-0">Aufgaben</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/users" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-journals"> | |||
| <h3 class="position-absolute m-0">Dokumente</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| </ul> | |||
| </div> | |||
| <div class="col-10"> | |||
| <router-outlet></router-outlet> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,31 @@ | |||
| .card { | |||
| width: 100%; | |||
| text-decoration: none; | |||
| } | |||
| .card-body { | |||
| min-height: 50px; | |||
| text-transform: uppercase; | |||
| text-align: right; | |||
| &.bi { | |||
| &:before { | |||
| display: none; | |||
| position: absolute; | |||
| left: 50%; | |||
| top: 50%; | |||
| transform: translate(-50%,-50%); | |||
| font-size: 50px; | |||
| } | |||
| } | |||
| .active & { | |||
| min-height: 100px; | |||
| &.bi { | |||
| &:before { | |||
| display: block; | |||
| } | |||
| } | |||
| } | |||
| h3 { | |||
| right: 10px; | |||
| bottom: 10px; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { TwoColumnComponent } from './two-column.component'; | |||
| describe('TwoColumnComponent', () => { | |||
| let component: TwoColumnComponent; | |||
| let fixture: ComponentFixture<TwoColumnComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [TwoColumnComponent] | |||
| }) | |||
| .compileComponents(); | |||
| fixture = TestBed.createComponent(TwoColumnComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,10 @@ | |||
| import { Component } from '@angular/core'; | |||
| @Component({ | |||
| selector: 'app-two-column', | |||
| templateUrl: './two-column.component.html', | |||
| styleUrl: './two-column.component.scss' | |||
| }) | |||
| export class TwoColumnComponent { | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| <p>partners-detail works!</p> | |||
| @@ -0,0 +1,23 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { PartnersDetailComponent } from './partners-detail.component'; | |||
| describe('PartnersDetailComponent', () => { | |||
| let component: PartnersDetailComponent; | |||
| let fixture: ComponentFixture<PartnersDetailComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [PartnersDetailComponent] | |||
| }) | |||
| .compileComponents(); | |||
| fixture = TestBed.createComponent(PartnersDetailComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,10 @@ | |||
| import { Component } from '@angular/core'; | |||
| @Component({ | |||
| selector: 'app-partners-detail', | |||
| templateUrl: './partners-detail.component.html', | |||
| styleUrl: './partners-detail.component.scss' | |||
| }) | |||
| export class PartnersDetailComponent { | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| <button (click)="goBack()">Zurück</button> | |||
| <table mat-table [dataSource]="dataSource" matSort (matSortChange)="announceSortChange($event)" | |||
| class="mat-elevation-z8"> | |||
| <ng-container matColumnDef="position"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription="Nach Nummer sortieren"> | |||
| Nr. | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> {{element.position}} </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="partner"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription="Nach Partner sortieren"> | |||
| Partner | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> {{element.partner}} </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="address"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header="street" sortActionDescription="Nach Adresse sortieren"> | |||
| Adresse | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> {{element.street}} {{element.street_no}}<br />{{element.zip}} {{element.city}} </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="website"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription="Nach Website sortieren"> | |||
| Website | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> {{element.website}} </td> | |||
| </ng-container> | |||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||
| <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> | |||
| </table> | |||
| @@ -0,0 +1,7 @@ | |||
| table { | |||
| width: 100%; | |||
| } | |||
| th.mat-sort-header-sorted { | |||
| color: black; | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { PartnersComponent } from './partners.component'; | |||
| describe('PartnersComponent', () => { | |||
| let component: PartnersComponent; | |||
| let fixture: ComponentFixture<PartnersComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [PartnersComponent] | |||
| }) | |||
| .compileComponents(); | |||
| fixture = TestBed.createComponent(PartnersComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,67 @@ | |||
| import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core'; | |||
| import {MatSort, Sort, MatSortModule} from "@angular/material/sort"; | |||
| import {LiveAnnouncer} from "@angular/cdk/a11y"; | |||
| import {MatTableDataSource, MatTableModule} from "@angular/material/table"; | |||
| import {Location} from "@angular/common"; | |||
| import {ActivatedRoute} from "@angular/router"; | |||
| export interface PeriodicElement { | |||
| position: number; | |||
| partner: string; | |||
| street: string; | |||
| street_no: string; | |||
| zip: string; | |||
| city: string; | |||
| website: string; | |||
| } | |||
| const ELEMENT_DATA: PeriodicElement[] = [ | |||
| {position: 1, partner: 'spawntree GmbH', street: 'Bauernvogtskoppel', street_no: '6c', zip: '21465', city: 'Wentorf', website: 'http://spawntree.de'}, | |||
| {position: 2, partner: 'Peter Pan AG', street: 'Heinrich-Löhns-Weg', street_no: '123b', zip: '22134', city: 'Hamburg', website: ''}, | |||
| {position: 3, partner: 'Weit Weg GmbH', street: 'Hans-Werner-Olm Straße', street_no: '1', zip: '87254', city: 'München', website: 'http://weitweg.de'}, | |||
| ]; | |||
| @Component({ | |||
| selector: 'app-partners', | |||
| templateUrl: './partners.component.html', | |||
| styleUrl: './partners.component.scss', | |||
| standalone: true, | |||
| imports: [MatTableModule, MatSortModule], | |||
| }) | |||
| export class PartnersComponent implements OnInit, AfterViewInit { | |||
| @ViewChild(MatSort) sort; | |||
| dataType!: string; | |||
| displayedColumns: string[] = ['position', 'partner', 'address', 'website']; | |||
| dataSource = new MatTableDataSource(ELEMENT_DATA); | |||
| constructor(private _liveAnnouncer: LiveAnnouncer, private _location: Location, private route: ActivatedRoute) { | |||
| this.sort = new MatSort(); | |||
| } | |||
| ngOnInit() { | |||
| this.dataType = this.route.snapshot.data['dataType']; | |||
| console.log('Data Type:', this.dataType); | |||
| } | |||
| ngAfterViewInit() { | |||
| this.dataSource.sort = this.sort; | |||
| } | |||
| goBack() { | |||
| this._location.back(); | |||
| } | |||
| /** Announce the change in sort state for assistive technology. */ | |||
| announceSortChange(sortState: Sort) { | |||
| // This example uses English messages. If your application supports | |||
| // multiple language, you would internationalize these strings. | |||
| // Furthermore, you can customize the message to add additional | |||
| // details about the values being sorted. | |||
| if (sortState.direction) { | |||
| this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`); | |||
| } else { | |||
| this._liveAnnouncer.announce('Sorting cleared'); | |||
| } | |||
| } | |||
| } | |||