| @@ -12,6 +12,7 @@ import {DocumentsComponent} from "@app/documents/documents.component"; | |||
| import {ContactsComponent} from "@app/contacts/contacts.component"; | |||
| import {ContactsDetailComponent} from "@app/contacts/contacts-detail/contacts-detail.component"; | |||
| import {TasksComponent} from "@app/tasks/tasks.component"; | |||
| import {SalesComponent} from "@app/sales/sales.component"; | |||
| const accountModule = () => import('./account/account.module').then(x => x.AccountModule); | |||
| const usersModule = () => import('./users/users.module').then(x => x.UsersModule); | |||
| @@ -81,6 +82,14 @@ const routes: Routes = [ | |||
| {path: '', component: DocumentsComponent, data: {dataType: 'document'}}, | |||
| ] | |||
| }, | |||
| { | |||
| path: 'sales', | |||
| component: TwoColumnComponent, | |||
| canActivate: [AuthGuard], | |||
| children: [ | |||
| {path: '', component: SalesComponent, data: {dataType: 'sales'}}, | |||
| ] | |||
| }, | |||
| // otherwise redirect to home | |||
| {path: '**', redirectTo: ''} | |||
| @@ -43,6 +43,8 @@ import {MatFormFieldModule} from "@angular/material/form-field"; | |||
| import {MatInputModule} from "@angular/material/input"; | |||
| import { NewTaskNoteComponent } from './tasks/new-task-note/new-task-note.component'; | |||
| import { DocumentsDetailComponent } from './documents/documents-detail/documents-detail.component'; | |||
| import { SalesComponent } from './sales/sales.component'; | |||
| import { SalesDetailComponent } from './sales/sales-detail/sales-detail.component'; | |||
| export function apiConfigFactory(): Configuration { | |||
| const params: ConfigurationParameters = { | |||
| @@ -106,7 +108,9 @@ export function HttpLoaderFactory(http: HttpClient) { | |||
| NewProductComponent, | |||
| NewCommentComponent, | |||
| NewTaskNoteComponent, | |||
| DocumentsDetailComponent | |||
| DocumentsDetailComponent, | |||
| SalesComponent, | |||
| SalesDetailComponent | |||
| ], | |||
| providers: [ | |||
| {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, | |||
| @@ -43,6 +43,13 @@ | |||
| </div> | |||
| </a> | |||
| </li> | |||
| <li class="nav-item mb-3"> | |||
| <a class="card" routerLink="/sales" routerLinkActive="active"> | |||
| <div class="card-body position-relative bi bi-journals"> | |||
| <h3 class="position-absolute m-0">{{'basic.sales' | translate}}</h3> | |||
| </div> | |||
| </a> | |||
| </li> | |||
| </ul> | |||
| </div> | |||
| <div class="col-10"> | |||
| @@ -1,6 +1,6 @@ | |||
| <div class="spt-container"> | |||
| <div class="d-flex justify-content-between align-items-start"> | |||
| <h2>{{ partnerName }}</h2> | |||
| <h2>{{ headline }}</h2> | |||
| <button class="btn btn-primary" | |||
| (click)="openModalNewPartner()">{{ 'basic.new' | translate }} {{ partnerNameOne }} | |||
| </button> | |||
| @@ -33,7 +33,7 @@ export class PartnersComponent implements OnInit, AfterViewInit { | |||
| protected websiteOrderAsc: OrderFilter; | |||
| protected dataType!: string; | |||
| protected partnerName: string; | |||
| protected headline: string; | |||
| protected partnerNameOne: string; | |||
| protected displayedColumns: string[]; | |||
| @@ -71,7 +71,7 @@ export class PartnersComponent implements OnInit, AfterViewInit { | |||
| this.partnersPageSize = 10; | |||
| this.partnersPageIndex = 0; | |||
| this.partnerName = ""; | |||
| this.headline = ""; | |||
| this.partnerNameOne = ""; | |||
| } | |||
| @@ -79,7 +79,7 @@ export class PartnersComponent implements OnInit, AfterViewInit { | |||
| this.dataType = this.route.snapshot.data['dataType']; | |||
| // this.translateService.use(this.translateService.getDefaultLang()); | |||
| this.translateService.get('basic.' + this.dataType).subscribe((translation: string) => { | |||
| this.partnerName = translation; | |||
| this.headline = translation; | |||
| }); | |||
| this.translateService.get('basic.' + this.dataType + 'One').subscribe((translation: string) => { | |||
| this.partnerNameOne = translation; | |||
| @@ -0,0 +1 @@ | |||
| <p>sales-detail works!</p> | |||
| @@ -0,0 +1,23 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { SalesDetailComponent } from './sales-detail.component'; | |||
| describe('SalesDetailComponent', () => { | |||
| let component: SalesDetailComponent; | |||
| let fixture: ComponentFixture<SalesDetailComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [SalesDetailComponent] | |||
| }) | |||
| .compileComponents(); | |||
| fixture = TestBed.createComponent(SalesDetailComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,10 @@ | |||
| import { Component } from '@angular/core'; | |||
| @Component({ | |||
| selector: 'app-sales-detail', | |||
| templateUrl: './sales-detail.component.html', | |||
| styleUrl: './sales-detail.component.scss' | |||
| }) | |||
| export class SalesDetailComponent { | |||
| } | |||
| @@ -0,0 +1,96 @@ | |||
| <div *ngFor="let saleSummary of saleSummaries"> | |||
| <div>{{ saleSummary.ownerName }} - {{ saleSummary.turnover }} - {{ saleSummary.profit }}</div> | |||
| </div> | |||
| <div class="spt-container"> | |||
| <div class="d-flex justify-content-between align-items-start"> | |||
| <h2>{{ 'basic.sales' | translate }}</h2> | |||
| <button class="btn btn-primary" (click)="openModalNewSale()">{{ 'basic.new-sale' | translate }}</button> | |||
| </div> | |||
| <table mat-table [dataSource]="dataSource" matSort (matSortChange)="onSortChange($event)" | |||
| class="mat-elevation-z8 mb-3"> | |||
| <ng-container matColumnDef="pos"> | |||
| <th mat-header-cell *matHeaderCellDef> | |||
| {{ 'overview.number' | translate }} | |||
| </th> | |||
| <td mat-cell | |||
| *matCellDef="let element">{{ (pageSize * pageIndex) + dataSource.filteredData.indexOf(element) + 1 }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="user"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.sale-user' | translate }}"> | |||
| {{ 'overview.sale-user' | translate }} | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> | |||
| <span role="button" (click)="navigateToSaleDetails(element)"> | |||
| {{ element.ownerName }} | |||
| </span> | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="partner"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header="address" | |||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.sale-partner' | translate }}"> | |||
| {{ 'overview.sale-partner' | translate }} | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> | |||
| {{ element.partnerName }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="product"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.productname' | translate }}"> | |||
| {{ 'overview.productname' | translate }} | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> | |||
| {{ element.productName }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="turnover"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.turnover' | translate }}"> | |||
| {{ 'overview.turnover' | translate }} | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> | |||
| {{ element.turnover }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="profit"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.profit' | translate }}"> | |||
| {{ 'overview.profit' | translate }} | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> | |||
| {{ element.profit }} | |||
| </td> | |||
| </ng-container> | |||
| <ng-container matColumnDef="date"> | |||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.createdAt' | translate }}"> | |||
| {{ 'overview.createdAt' | translate }} | |||
| </th> | |||
| <td mat-cell *matCellDef="let element"> | |||
| {{ element.createdAt }} | |||
| </td> | |||
| </ng-container> | |||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||
| <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> | |||
| </table> | |||
| <mat-paginator class="rounded-1" | |||
| [pageSizeOptions]="[10,25,50]" | |||
| [length]="length" | |||
| (page)="handlePageEvent($event)" | |||
| [pageSize]="pageSize" | |||
| [pageIndex]="pageIndex" | |||
| showFirstLastButtons> | |||
| </mat-paginator> | |||
| </div> | |||
| @@ -0,0 +1,23 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { SalesComponent } from './sales.component'; | |||
| describe('SalesComponent', () => { | |||
| let component: SalesComponent; | |||
| let fixture: ComponentFixture<SalesComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [SalesComponent] | |||
| }) | |||
| .compileComponents(); | |||
| fixture = TestBed.createComponent(SalesComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,154 @@ | |||
| import {ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core'; | |||
| import { | |||
| PartnerJsonld, ProductJsonld, | |||
| SaleJsonld, | |||
| SaleService, | |||
| SaleSummaryJsonld, | |||
| SaleSummaryService | |||
| } from "@app/core/api/v1"; | |||
| import {Subscription} from "rxjs"; | |||
| import {MatPaginator, MatPaginatorIntl, PageEvent} from "@angular/material/paginator"; | |||
| import {MatSort, Sort} from "@angular/material/sort"; | |||
| import {TranslateService} from "@ngx-translate/core"; | |||
| import {NewPartnerComponent} from "@app/partners/new-partner/new-partner.component"; | |||
| import {ModalStatus} from "@app/_helpers/modal.states"; | |||
| import {NgbModal, NgbModalOptions} from "@ng-bootstrap/ng-bootstrap"; | |||
| import {MatTableDataSource} from "@angular/material/table"; | |||
| import {OrderFilter} from "@app/_models/orderFilter"; | |||
| import {ApiConverter} from "@app/_helpers/api.converter"; | |||
| import {Router} from "@angular/router"; | |||
| @Component({ | |||
| selector: 'app-sales', | |||
| templateUrl: './sales.component.html', | |||
| styleUrl: './sales.component.scss' | |||
| }) | |||
| export class SalesComponent implements OnInit { | |||
| @ViewChild(MatSort) sort; | |||
| @ViewChild(MatPaginator) paginator: MatPaginator; | |||
| protected displayedColumns: string[]; | |||
| protected salesSub: Subscription; | |||
| protected sales: Array<SaleJsonld>; | |||
| protected salesSummarySub: Subscription; | |||
| protected saleSummaries: Array<SaleSummaryJsonld>; | |||
| protected dataSource; | |||
| protected length: number; | |||
| protected pageEvent: PageEvent; | |||
| protected pageSize: number; | |||
| protected pageIndex: number; | |||
| protected modalOptions: NgbModalOptions = { | |||
| centered: true | |||
| }; | |||
| constructor( | |||
| private saleService: SaleService, | |||
| private saleSummaryService: SaleSummaryService, | |||
| private translateService: TranslateService, | |||
| private modalService: NgbModal, | |||
| private router: Router, | |||
| ) { | |||
| this.sort = new MatSort(); | |||
| this.displayedColumns = ['pos', 'user', 'partner', 'product', 'turnover', 'profit', 'date']; | |||
| this.salesSub = new Subscription(); | |||
| this.sales = []; | |||
| this.salesSummarySub = new Subscription(); | |||
| this.saleSummaries = []; | |||
| this.dataSource = new MatTableDataSource<SaleJsonld>(this.sales); | |||
| this.paginator = new MatPaginator(new MatPaginatorIntl(), ChangeDetectorRef.prototype); | |||
| this.length = 0; | |||
| this.pageEvent = new PageEvent(); | |||
| this.pageSize = 10; | |||
| this.pageIndex = 0; | |||
| } | |||
| ngOnInit() { | |||
| this.getSalesData(); | |||
| this.getSalesSummaryData(); | |||
| } | |||
| getSalesData() { | |||
| this.salesSub = this.saleService.salesGetCollection( | |||
| this.pageIndex + 1, | |||
| this.pageSize, | |||
| ).subscribe( | |||
| data => { | |||
| this.sales = data["hydra:member"]; | |||
| this.dataSource = new MatTableDataSource<SaleJsonld>(this.sales); | |||
| this.length = Number(data["hydra:totalItems"]); | |||
| this.paginator.length = this.length; | |||
| console.log(this.sales); | |||
| } | |||
| ) | |||
| } | |||
| getSalesSummaryData() { | |||
| this.salesSummarySub = this.saleSummaryService.saleSummariesGetCollection( | |||
| 1, | |||
| 50 | |||
| ).subscribe( | |||
| data => { | |||
| this.saleSummaries = data["hydra:member"]; | |||
| console.log(this.saleSummaries); | |||
| } | |||
| ) | |||
| } | |||
| onSortChange(sortState: Sort) { | |||
| // Reset page index to first page | |||
| this.pageIndex = 0; | |||
| let order: OrderFilter; | |||
| if (sortState.direction === "") { | |||
| order = OrderFilter.Undefined; | |||
| } else { | |||
| order = sortState.direction; | |||
| } | |||
| // this.nameOrderAsc = OrderFilter.Undefined; | |||
| // this.cityOrderAsc = OrderFilter.Undefined; | |||
| // this.websiteOrderAsc = OrderFilter.Undefined; | |||
| // switch (sortState.active) { | |||
| // case "name": | |||
| // this.nameOrderAsc = order; | |||
| // break; | |||
| // case "address": | |||
| // this.cityOrderAsc = order; | |||
| // break; | |||
| // case "website": | |||
| // this.websiteOrderAsc = order; | |||
| // break; | |||
| // } | |||
| this.getSalesData(); | |||
| } | |||
| handlePageEvent(e: PageEvent) { | |||
| this.pageEvent = e; | |||
| this.length = e.length; | |||
| this.pageIndex = e.pageIndex.valueOf(); | |||
| this.pageSize = e.pageSize.valueOf(); | |||
| this.getSalesData(); | |||
| } | |||
| navigateToSaleDetails(element: any) { | |||
| const product: ProductJsonld = element as ProductJsonld; | |||
| this.router.navigate(['/products', ApiConverter.extractId(product.id)]); | |||
| } | |||
| openModalNewSale() { | |||
| const modalRefContact = this.modalService.open(NewPartnerComponent, this.modalOptions); | |||
| let sale: SaleJsonld = {} as SaleJsonld; | |||
| modalRefContact.componentInstance.partner = sale; | |||
| modalRefContact.componentInstance.submit.subscribe((modalStatus: ModalStatus) => { | |||
| if (modalStatus === ModalStatus.Submitted) { | |||
| modalRefContact.dismiss(); | |||
| this.getSalesData(); | |||
| } | |||
| }); | |||
| } | |||
| } | |||
| @@ -18,6 +18,7 @@ | |||
| "serviceOne": "Lieferant", | |||
| "products": "Produkte", | |||
| "documents": "Dokumente", | |||
| "sales": "Verkäufe", | |||
| "tasks": "Aufgaben", | |||
| "contacts": "Kontakte", | |||
| "posts": "Notizen", | |||
| @@ -30,6 +31,7 @@ | |||
| "new-post": "Neue Notiz", | |||
| "new-comment": "Neuer Kommentar", | |||
| "new-task-note": "Neue Anmerkung", | |||
| "new-sale": "Neuer Verkauf", | |||
| "edit-before": "", | |||
| "edit-after": "bearbeiten", | |||
| "edit-product": "Produkt bearbeiten", | |||
| @@ -58,7 +60,7 @@ | |||
| "address": "Adresse", | |||
| "website": "Website", | |||
| "image": "Bild", | |||
| "productname": "Produktname", | |||
| "productname": "Produkt", | |||
| "storage": "Lagerbestand", | |||
| "number-long": "Nummer", | |||
| "document": "Dokument", | |||
| @@ -68,7 +70,12 @@ | |||
| "createdBy": "Erstellt von", | |||
| "download": "Download", | |||
| "partner": "Partner", | |||
| "product": "Produkt" | |||
| "product": "Produkt", | |||
| "sale-user": "Verkäufer", | |||
| "sale-partner": "Kunde", | |||
| "turnover": "Umsatz", | |||
| "profit": "Gewinn", | |||
| "createdAt": "erstellt am" | |||
| }, | |||
| "form": | |||
| { | |||