mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
4 Commits
hotfix/453
...
feature/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d303b1444b | ||
|
|
150e7965ee | ||
|
|
3fcf3d9396 | ||
|
|
52278b8baf |
@@ -1,14 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateProductGuard implements CanActivate {
|
||||
get isTablet() {
|
||||
return this._environment.isTablet();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _applicationService: ApplicationService,
|
||||
private readonly _checkoutService: DomainCheckoutService,
|
||||
private readonly _environment: EnvironmentService,
|
||||
private readonly _router: Router
|
||||
) {}
|
||||
|
||||
@@ -57,7 +63,17 @@ export class CanActivateProductGuard implements CanActivate {
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(newProcessId)]));
|
||||
if (this.isTablet) {
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(newProcessId)]));
|
||||
} else {
|
||||
// TODO: Sollte auch von getUrlFromSnapshot kommen
|
||||
await this._router.navigate([
|
||||
'/kunde',
|
||||
String(newProcessId),
|
||||
'product',
|
||||
{ outlets: { main: null, left: 'search', right: 'filter' } },
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Bei offener Warenausgabe und Klick auf Footer Artikelsuche
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="shell-footer-wrapper">
|
||||
<shell-footer *ngIf="section$ | async; let section">
|
||||
<ng-container *ngIf="section === 'customer'">
|
||||
<a [routerLink]="[customerBasePath$ | async, 'product']" routerLinkActive="active">
|
||||
<a [routerLink]="productRoutePath$ | async" routerLinkActive="active">
|
||||
<ui-icon icon="catalog" size="30px"></ui-icon>
|
||||
Artikelsuche
|
||||
</a>
|
||||
|
||||
@@ -6,17 +6,16 @@
|
||||
@apply fixed right-0 left-0 overflow-auto;
|
||||
top: 8.375rem;
|
||||
bottom: 5rem;
|
||||
|
||||
main {
|
||||
@apply w-full max-w-content mx-auto px-4 self-stretch;
|
||||
}
|
||||
}
|
||||
main {
|
||||
@apply w-full mx-auto max-w-desktop px-px-15 desktop:px-6 self-stretch;
|
||||
}
|
||||
|
||||
.shell-header-wrapper {
|
||||
@apply fixed top-0 left-0 right-0 bg-white;
|
||||
|
||||
shell-header {
|
||||
@apply w-full max-w-content mx-auto;
|
||||
@apply w-full max-w-desktop px-px-15 desktop:px-6 mx-auto;
|
||||
}
|
||||
|
||||
button.notifications-btn {
|
||||
@@ -36,7 +35,7 @@
|
||||
top: 5.125rem;
|
||||
|
||||
shell-process {
|
||||
@apply w-full max-w-content mx-auto;
|
||||
@apply w-full max-w-desktop px-px-15 desktop:px-6 mx-auto;
|
||||
height: 52px;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +49,7 @@ shell-process {
|
||||
@apply fixed bottom-0 left-0 right-0 bg-white z-fixed shadow-card;
|
||||
|
||||
shell-footer {
|
||||
@apply w-full max-w-content mx-auto;
|
||||
@apply w-full max-w-desktop px-px-15 desktop:px-6 mx-auto;
|
||||
|
||||
.active {
|
||||
@apply font-bold;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { ShellProcessTabComponent } from '@shell/process';
|
||||
import { Config } from '@core/config';
|
||||
import { WrongDestinationModalService } from 'apps/page/package-inspection/src/lib/components/wrong-destination-modal/wrong-destination-modal.service';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shell',
|
||||
@@ -52,6 +53,16 @@ export class ShellComponent {
|
||||
})
|
||||
);
|
||||
|
||||
productRoutePath$ = this.customerBasePath$.pipe(
|
||||
map((basePath) => {
|
||||
if (this.isTablet) {
|
||||
return [basePath, 'product'];
|
||||
} else {
|
||||
return [basePath, 'product', { outlets: { main: null, left: 'search', right: 'filter' } }];
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
get section$() {
|
||||
return this._appService.getSection$().pipe(shareReplay());
|
||||
}
|
||||
@@ -90,6 +101,10 @@ export class ShellComponent {
|
||||
return this._availabilityService.getDefaultBranch();
|
||||
}
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.isTablet();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _appService: ApplicationService,
|
||||
private readonly _config: Config,
|
||||
@@ -100,7 +115,8 @@ export class ShellComponent {
|
||||
private readonly _authService: AuthService,
|
||||
private readonly _availabilityService: DomainAvailabilityService,
|
||||
private readonly _zone: NgZone,
|
||||
private readonly _wrongDestinationModalService: WrongDestinationModalService
|
||||
private readonly _wrongDestinationModalService: WrongDestinationModalService,
|
||||
private readonly _environment: EnvironmentService
|
||||
) {}
|
||||
|
||||
async setSection(section: 'customer' | 'branch') {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<div class="page-price-update-item__item-details">
|
||||
<div class="page-price-update-item__item-contributors flex flex-row">
|
||||
{{ environment.isTablet() ? (item?.product?.contributors | substr: 42) : item?.product?.contributors }}
|
||||
{{ environment.isTablet() ? (item?.product?.contributors | substr: 38) : item?.product?.contributors }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -43,7 +43,7 @@
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
{{ item?.product?.formatDetail }}
|
||||
{{ environment.isTablet() ? (item?.product?.formatDetail | substr: 25) : item?.product?.formatDetail }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<button class="filter" [class.active]="hasFilter$ | async" (click)="filterActive$.next(true); shellFilterOverlay.open()">
|
||||
<button *ngIf="isTablet" class="filter" [class.active]="hasFilter$ | async" (click)="filterActive$.next(true); shellFilterOverlay.open()">
|
||||
<ui-icon size="20px" icon="filter_alit"></ui-icon>
|
||||
<span class="label">Filter</span>
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { UiFilterAutocompleteProvider } from '@ui/filter';
|
||||
import { isEqual } from 'lodash';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
@@ -15,7 +16,6 @@ import { ArticleSearchMainAutocompleteProvider } from './providers';
|
||||
styleUrls: ['article-search.component.scss'],
|
||||
providers: [
|
||||
FocusSearchboxEvent,
|
||||
ArticleSearchService,
|
||||
{
|
||||
provide: UiFilterAutocompleteProvider,
|
||||
useClass: ArticleSearchMainAutocompleteProvider,
|
||||
@@ -39,11 +39,17 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
filterActive$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.isTablet();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router,
|
||||
private _articleSearch: ArticleSearchService,
|
||||
private _activatedRoute: ActivatedRoute
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _environment: EnvironmentService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -65,9 +71,19 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
|
||||
this._router.navigate(['/kunde', processId, 'product', 'details', item.id]);
|
||||
} else {
|
||||
const params = state.filter.getQueryParams();
|
||||
this._router.navigate(['/kunde', processId, 'product', 'search', 'results'], {
|
||||
queryParams: params,
|
||||
});
|
||||
if (this.isTablet) {
|
||||
this._router.navigate(['/kunde', processId, 'product', 'search', 'results'], {
|
||||
queryParams: params,
|
||||
});
|
||||
} else {
|
||||
const item = state.items.find((f) => f);
|
||||
this._router.navigate(
|
||||
['/kunde', processId, 'product', { outlets: { main: null, left: 'results', right: ['details', item.id] } }],
|
||||
{
|
||||
queryParams: params,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,11 +7,12 @@ import { SearchResultsModule } from './search-results/search-results.module';
|
||||
import { SearchMainModule } from './search-main/search-main.module';
|
||||
import { SearchFilterModule } from './search-filter/search-filter.module';
|
||||
import { ShellFilterOverlayModule } from '@shell/filter-overlay';
|
||||
import { ArticleSearchService } from './article-search.store';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule, UiIconModule, SearchResultsModule, SearchMainModule, SearchFilterModule, ShellFilterOverlayModule],
|
||||
exports: [ArticleSearchComponent],
|
||||
declarations: [ArticleSearchComponent],
|
||||
providers: [],
|
||||
providers: [ArticleSearchService],
|
||||
})
|
||||
export class ArticleSearchModule {}
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
<ng-container *ngIf="filter$ | async; let filter">
|
||||
<div class="catalog-search-filter-content">
|
||||
<button class="btn-close" type="button" (click)="close.emit()">
|
||||
<button *ngIf="isTablet; else desktop" class="btn-close" type="button" (click)="close.emit()">
|
||||
<ui-icon icon="close" size="20px"></ui-icon>
|
||||
</button>
|
||||
|
||||
<ng-template #desktop>
|
||||
<a
|
||||
class="btn-close"
|
||||
type="button"
|
||||
[routerLink]="[
|
||||
'/kunde',
|
||||
application.activatedProcessId,
|
||||
'product',
|
||||
{ outlets: { main: null, left: 'results', right: ['details', item?.id] } }
|
||||
]"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-icon icon="close" size="20px"></ui-icon>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<div class="catalog-search-filter-content-main">
|
||||
<h1 class="text-3xl font-bold text-center py-4">Filter</h1>
|
||||
<ui-filter
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
}
|
||||
|
||||
.cta-wrapper {
|
||||
@apply fixed bottom-8 whitespace-nowrap;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
@apply text-center whitespace-nowrap;
|
||||
}
|
||||
|
||||
.cta-reset-filter,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { UiFilter, UiFilterComponent } from '@ui/filter';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
@@ -23,7 +25,19 @@ export class ArticleSearchFilterComponent implements OnInit {
|
||||
@ViewChild(UiFilterComponent, { static: false })
|
||||
uiFilterComponent: UiFilterComponent;
|
||||
|
||||
constructor(private articleSearch: ArticleSearchService) {}
|
||||
get isTablet() {
|
||||
return this._environment.isTablet();
|
||||
}
|
||||
|
||||
get item() {
|
||||
return this.articleSearch.items.find((_) => true);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private articleSearch: ArticleSearchService,
|
||||
private _environment: EnvironmentService,
|
||||
public application: ApplicationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.fetching$ = this.articleSearch.fetching$;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiFilterNextModule } from '@ui/filter';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
@@ -7,7 +8,7 @@ import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { ArticleSearchFilterComponent } from './search-filter.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiFilterNextModule, UiIconModule, UiSpinnerModule],
|
||||
imports: [CommonModule, RouterModule, UiFilterNextModule, UiIconModule, UiSpinnerModule],
|
||||
exports: [ArticleSearchFilterComponent],
|
||||
declarations: [ArticleSearchFilterComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:host {
|
||||
@apply flex flex-row rounded-card bg-white mb-2 p-4;
|
||||
@apply flex flex-row rounded-card bg-white mb-2 p-4 desktop:w-[496px];
|
||||
height: 187px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,81 +1,112 @@
|
||||
<a class="product-list-result-content" [routerLink]="['/kunde', applicationService.activatedProcessId, 'product', 'details', item?.id]">
|
||||
<div class="item-thumbnail">
|
||||
<img loading="lazy" *ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl" [src]="thumbnailUrl" [alt]="item?.product?.name" />
|
||||
</div>
|
||||
|
||||
<div class="item-contributors">
|
||||
<a
|
||||
*ngFor="let contributor of contributors; let last = last"
|
||||
[routerLink]="['/kunde', applicationService.activatedProcessId, 'product', 'search', 'results']"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
(click)="$event?.stopPropagation()"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item-title"
|
||||
[class.xl]="item?.product?.name?.length >= 35"
|
||||
[class.lg]="item?.product?.name?.length >= 40"
|
||||
[class.md]="item?.product?.name?.length >= 50"
|
||||
[class.sm]="item?.product?.name?.length >= 60"
|
||||
[class.xs]="item?.product?.name?.length >= 100"
|
||||
>
|
||||
{{ item?.product?.name }}
|
||||
</div>
|
||||
|
||||
<div class="item-price">
|
||||
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR':'code' }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="selectable" class="item-data-selector">
|
||||
<ui-select-bullet [ngModel]="selected" (ngModelChange)="setSelected($event)"></ui-select-bullet>
|
||||
</div>
|
||||
|
||||
<div class="item-stock z-dropdown" [uiOverlayTrigger]="tooltip" [overlayTriggerDisabled]="!(stockTooltipText$ | async)">
|
||||
<ng-container *ngIf="isOrderBranch$ | async">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<ui-icon icon="home" size="1em"></ui-icon>
|
||||
<span
|
||||
*ngIf="inStock$ | async; let stock"
|
||||
[class.skeleton]="stock.inStock === undefined"
|
||||
class="min-w-[1rem] text-right inline-block"
|
||||
>{{ stock?.inStock }}</span
|
||||
>
|
||||
<span>x</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!(isOrderBranch$ | async)">
|
||||
<div class="flex flex-row items-center justify-between z-dropdown">
|
||||
<ui-icon class="block" icon="home" size="1em"></ui-icon>
|
||||
<span class="min-w-[1rem] text-center inline-block">-</span>
|
||||
<span>x</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
<!-- <div class="item-stock"><ui-icon icon="home" size="1em"></ui-icon> {{ item?.stockInfos | stockInfos }} x</div> -->
|
||||
|
||||
<div class="item-ssc" [class.xs]="item?.catalogAvailability?.sscText?.length >= 60">
|
||||
{{ item?.catalogAvailability?.ssc }} - {{ item?.catalogAvailability?.sscText }}
|
||||
</div>
|
||||
|
||||
<div class="item-format" *ngIf="item?.product?.format && item?.product?.formatDetail">
|
||||
<a
|
||||
class="page-search-result-item__item-card p-5 desktop:p-px-10 h-[212px] desktop:h-[181px] bg-white rounded-card"
|
||||
[routerLink]="detailsLink"
|
||||
[queryParamsHandling]="isTablet ? 'preserve' : ''"
|
||||
>
|
||||
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[50px] h-[79px]">
|
||||
<img
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
class="page-search-result-item__item-image w-[50px] h-[79px]"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
*ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl"
|
||||
[src]="thumbnailUrl"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
{{ item?.product?.formatDetail }}
|
||||
</div>
|
||||
|
||||
<div class="item-misc">
|
||||
{{ item?.product?.manufacturer | substr: 18 }} | {{ item?.product?.ean }} <br />
|
||||
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
|
||||
{{ publicationDate }}
|
||||
<div class="grid-container-test">
|
||||
<div
|
||||
class="page-search-result-item__item-contributors desktop:text-sm font-bold text-[#0556B4] text-ellipsis overflow-hidden max-w-[24rem] whitespace-nowrap"
|
||||
>
|
||||
<a
|
||||
*ngFor="let contributor of contributors; let last = last"
|
||||
[routerLink]="['/kunde', applicationService.activatedProcessId, 'product', 'search', 'results']"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
(click)="$event?.stopPropagation()"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-search-result-item__item-title font-bold text-2xl"
|
||||
[class.text-xl]="item?.product?.name?.length >= 35"
|
||||
[class.text-lg]="item?.product?.name?.length >= 40"
|
||||
[class.text-md]="item?.product?.name?.length >= 50"
|
||||
[class.text-sm]="item?.product?.name?.length >= 60 || !isTablet"
|
||||
[class.text-xs]="item?.product?.name?.length >= 100 || (!isTablet && item?.product?.name?.length >= 70)"
|
||||
>
|
||||
{{ item?.product?.name }}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-format desktop:text-sm">
|
||||
<div *ngIf="item?.product?.format && item?.product?.formatDetail" class="font-bold flex flex-row">
|
||||
<img
|
||||
class="mr-3"
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
{{ item?.product?.formatDetail | substr: 25 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-manufacturer desktop:text-sm">
|
||||
{{ item?.product?.manufacturer | substr: 18 }} | {{ item?.product?.ean }}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-misc desktop:text-sm">
|
||||
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
|
||||
{{ publicationDate }}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-price desktop:text-sm font-bold justify-self-end">
|
||||
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR':'code' }}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-select-bullet justify-self-end">
|
||||
<input
|
||||
*ngIf="selectable"
|
||||
(click)="$event.stopPropagation()"
|
||||
[ngModel]="selected$ | async"
|
||||
(ngModelChange)="setSelected()"
|
||||
class="isa-select-bullet"
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-search-result-item__item-stock desktop:text-sm font-bold z-dropdown justify-self-end"
|
||||
[uiOverlayTrigger]="tooltip"
|
||||
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
|
||||
>
|
||||
<ng-container *ngIf="isOrderBranch$ | async">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<ui-icon icon="home" size="1em"></ui-icon>
|
||||
<span
|
||||
*ngIf="inStock$ | async; let stock"
|
||||
[class.skeleton]="stock.inStock === undefined"
|
||||
class="min-w-[1rem] text-right inline-block"
|
||||
>{{ stock?.inStock }}</span
|
||||
>
|
||||
<span>x</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!(isOrderBranch$ | async)">
|
||||
<div class="flex flex-row items-center justify-between z-dropdown">
|
||||
<ui-icon class="block" icon="home" size="1em"></ui-icon>
|
||||
<span class="min-w-[1rem] text-center inline-block">-</span>
|
||||
<span>x</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
|
||||
<div class="page-search-result-item__item-ssc desktop:text-sm" [class.xs]="item?.catalogAvailability?.sscText?.length >= 60">
|
||||
<strong>{{ item?.catalogAvailability?.ssc }}</strong> -
|
||||
{{ !isTablet ? item?.catalogAvailability?.sscText : (item?.catalogAvailability?.sscText | substr: 18) }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -1,113 +1,61 @@
|
||||
.product-list-result-content {
|
||||
@apply text-black no-underline grid;
|
||||
grid-template-columns: 102px 50% auto;
|
||||
grid-template-rows: auto;
|
||||
:host {
|
||||
@apply flex flex-col w-full h-[212px] desktop:h-[181px];
|
||||
}
|
||||
|
||||
.page-search-result-item__item-card {
|
||||
@apply grid grid-flow-col;
|
||||
grid-template-columns: 63px auto;
|
||||
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||
}
|
||||
|
||||
.grid-container-test {
|
||||
@apply grid grid-flow-row gap-[6px];
|
||||
grid-template-areas:
|
||||
'item-thumbnail item-contributors item-contributors'
|
||||
'item-thumbnail item-title item-price'
|
||||
'item-thumbnail item-title item-data-selector'
|
||||
'item-thumbnail item-format item-stock'
|
||||
'item-thumbnail item-misc item-ssc';
|
||||
'contributors contributors contributors'
|
||||
'title title price'
|
||||
'title title price'
|
||||
'title title select'
|
||||
'format format select'
|
||||
'manufacturer manufacturer stock'
|
||||
'misc ssc ssc';
|
||||
}
|
||||
|
||||
.item-thumbnail {
|
||||
grid-area: item-thumbnail;
|
||||
width: 70px;
|
||||
@apply mr-8;
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
@apply rounded-card shadow-cta;
|
||||
}
|
||||
.page-search-result-item__item-contributors {
|
||||
grid-area: contributors;
|
||||
}
|
||||
|
||||
.item-contributors {
|
||||
grid-area: item-contributors;
|
||||
height: 22px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 600px;
|
||||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
@apply text-active-customer font-bold no-underline;
|
||||
}
|
||||
.page-search-result-item__item-price {
|
||||
grid-area: price;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
grid-area: item-title;
|
||||
@apply font-bold text-2xl;
|
||||
height: 64px;
|
||||
max-height: 64px;
|
||||
.page-search-result-item__item-title {
|
||||
grid-area: title;
|
||||
}
|
||||
|
||||
.item-title.xl {
|
||||
@apply font-bold text-xl;
|
||||
.page-search-result-item__item-format {
|
||||
grid-area: format;
|
||||
}
|
||||
|
||||
.item-title.lg {
|
||||
@apply font-bold text-lg;
|
||||
.page-search-result-item__item-manufacturer {
|
||||
grid-area: manufacturer;
|
||||
}
|
||||
|
||||
.item-title.md {
|
||||
@apply font-bold text-base;
|
||||
.page-search-result-item__item-misc {
|
||||
grid-area: misc;
|
||||
}
|
||||
|
||||
.item-title.sm {
|
||||
@apply font-bold text-sm;
|
||||
.page-search-result-item__item-select-bullet {
|
||||
grid-area: select;
|
||||
}
|
||||
|
||||
.item-title.xs {
|
||||
@apply font-bold text-xs;
|
||||
.page-search-result-item__item-stock {
|
||||
grid-area: stock;
|
||||
}
|
||||
|
||||
.item-price {
|
||||
grid-area: item-price;
|
||||
@apply font-bold text-xl text-right;
|
||||
.page-search-result-item__item-ssc {
|
||||
@apply justify-self-end;
|
||||
grid-area: ssc;
|
||||
}
|
||||
|
||||
.item-format {
|
||||
grid-area: item-format;
|
||||
@apply flex flex-row items-center font-bold text-lg whitespace-nowrap;
|
||||
|
||||
img {
|
||||
@apply mr-2;
|
||||
}
|
||||
}
|
||||
|
||||
.item-stock {
|
||||
grid-area: item-stock;
|
||||
@apply flex flex-row justify-end items-baseline font-bold text-lg;
|
||||
|
||||
ui-icon {
|
||||
@apply text-active-customer mr-2;
|
||||
}
|
||||
}
|
||||
|
||||
.item-misc {
|
||||
grid-area: item-misc;
|
||||
}
|
||||
|
||||
.item-ssc {
|
||||
grid-area: item-ssc;
|
||||
@apply font-bold text-right;
|
||||
}
|
||||
|
||||
.item-ssc.xs {
|
||||
@apply font-bold text-xs;
|
||||
}
|
||||
|
||||
.item-data-selector {
|
||||
@apply w-full flex justify-end;
|
||||
grid-area: item-data-selector;
|
||||
}
|
||||
|
||||
ui-select-bullet {
|
||||
@apply p-4 -m-4 z-dropdown;
|
||||
}
|
||||
|
||||
@media (min-width: 1025px) {
|
||||
.item-contributors {
|
||||
max-width: 780px;
|
||||
}
|
||||
.page-search-result-item__item-image {
|
||||
box-shadow: 0px 6px 18px rgba(0, 0, 0, 0.197935);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { DomainAvailabilityService, DomainInStockService } from '@domain/availability';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { DateAdapter } from '@ui/common';
|
||||
import { isEqual } from 'lodash';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { debounceTime, switchMap, map, tap, shareReplay } from 'rxjs/operators';
|
||||
import { debounceTime, switchMap, map, shareReplay } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from '../article-search.store';
|
||||
|
||||
export interface SearchResultItemComponentState {
|
||||
@@ -36,16 +38,7 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
|
||||
readonly item$ = this.select((s) => s.item);
|
||||
|
||||
@Input()
|
||||
get selected() {
|
||||
return this.get((s) => s.selected);
|
||||
}
|
||||
set selected(selected: boolean) {
|
||||
if (this.selected !== selected) {
|
||||
this.patchState({ selected });
|
||||
}
|
||||
}
|
||||
readonly selected$ = this.select((s) => s.selected);
|
||||
selected$ = this._articleSearchService.selectedItemIds$.pipe(map((selectedItemIds) => selectedItemIds.includes(this.item?.id)));
|
||||
|
||||
@Input()
|
||||
get selectable() {
|
||||
@@ -77,6 +70,23 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
return '';
|
||||
}
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.isTablet();
|
||||
}
|
||||
|
||||
get detailsLink() {
|
||||
if (this.isTablet) {
|
||||
return ['/kunde', this.applicationService.activatedProcessId, 'product', 'details', this.item?.id];
|
||||
} else {
|
||||
return [
|
||||
'/kunde',
|
||||
this.applicationService.activatedProcessId,
|
||||
'product',
|
||||
{ outlets: { main: null, left: 'results', right: ['details', this.item?.id] } },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
defaultBranch$ = this._availability.getDefaultBranch();
|
||||
|
||||
selectedBranchId$ = this.applicationService.activatedProcessId$.pipe(
|
||||
@@ -121,7 +131,8 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
private _articleSearchService: ArticleSearchService,
|
||||
public applicationService: ApplicationService,
|
||||
private _stockService: DomainInStockService,
|
||||
private _availability: DomainAvailabilityService
|
||||
private _availability: DomainAvailabilityService,
|
||||
private _environment: EnvironmentService
|
||||
) {
|
||||
super({
|
||||
selected: false,
|
||||
@@ -129,7 +140,26 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
});
|
||||
}
|
||||
|
||||
setSelected(selected: boolean) {
|
||||
this._articleSearchService.setSelected({ selected, itemId: this.item?.id });
|
||||
setSelected() {
|
||||
const isSelected = this._articleSearchService.selectedItemIds.includes(this.item?.id);
|
||||
this._articleSearchService.setSelected({ selected: !isSelected, itemId: this.item?.id });
|
||||
// this._articleSearchService.setSelected({ selected, itemId: this.item?.id });
|
||||
|
||||
// setSelected({ selected, itemUid }: { selected: boolean; itemUid: string }) {
|
||||
// if (selected) {
|
||||
// this.patchState({
|
||||
// selectedItemUids: [...this.selectedItemUids, itemUid],
|
||||
// });
|
||||
// } else if (!selected) {
|
||||
// this.patchState({
|
||||
// selectedItemUids: this.selectedItemUids.filter((id) => id !== itemUid),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// setSelected() {
|
||||
// const isSelected = this._store.selectedItemUids.includes(this.item?.uId);
|
||||
// this._store.setSelected({ selected: !isSelected, itemUid: this.item?.uId });
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,27 +1,43 @@
|
||||
<div class="filter-wrapper">
|
||||
<div class="hits" *ngIf="hits$ | async; let hits">{{ hits }} Titel</div>
|
||||
<div class="page-search-results__header bg-background-liste flex flex-col items-end pb-4">
|
||||
<a
|
||||
[class.active]="hasFilter$ | async"
|
||||
class="page-search-results__filter h-14 rounded-card font-bold px-5 mb-4 text-lg bg-cadet-blue flex flex-row flex-nowrap items-center justify-center"
|
||||
[routerLink]="['/kunde', application.activatedProcessId, 'product', { outlets: { main: null, left: 'results', right: 'filter' } }]"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
|
||||
Filter
|
||||
</a>
|
||||
|
||||
<div *ngIf="hits$ | async; let hits" class="page-search-results__items-count inline-flex flex-row items-center pr-5 text-sm">
|
||||
{{ hits ??
|
||||
0 }}
|
||||
Titel
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-search-results__order-by h-[53px] flex flex-row items-center justify-center bg-white rounded-t-card">
|
||||
<ui-order-by-filter [orderBy]="(filter$ | async)?.orderBy" (selectedOrderByChange)="search(); updateBreadcrumbs()"> </ui-order-by-filter>
|
||||
</div>
|
||||
|
||||
<cdk-virtual-scroll-viewport
|
||||
#scrollContainer
|
||||
class="product-list scroll-bar scroll-bar-margin"
|
||||
class="product-list"
|
||||
[itemSize]="187"
|
||||
minBufferPx="1200"
|
||||
maxBufferPx="1200"
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<div class="product-list-result" *cdkVirtualFor="let item of results$ | async; trackBy: trackByItemId">
|
||||
<search-result-item
|
||||
[selected]="item | searchResultSelected: searchService.selectedItemIds"
|
||||
[selectable]="isSelectable(item)"
|
||||
[item]="item"
|
||||
></search-result-item>
|
||||
</div>
|
||||
<search-result-item
|
||||
class="mb-px-10"
|
||||
*cdkVirtualFor="let item of results$ | async; trackBy: trackByItemId"
|
||||
[selectable]="isSelectable(item)"
|
||||
[item]="item"
|
||||
></search-result-item>
|
||||
<page-search-result-item-loading *ngIf="fetching$ | async"></page-search-result-item-loading>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
||||
<div class="actions">
|
||||
<div class="actions z-fixed">
|
||||
<button
|
||||
[disabled]="loading$ | async"
|
||||
*ngIf="(selectedItemIds$ | async)?.length > 0"
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
:host {
|
||||
@apply box-border grid;
|
||||
max-height: calc(100vh - 364px);
|
||||
height: 100vh;
|
||||
grid-template-rows: auto 1fr;
|
||||
@apply box-border grid h-[100vh] max-h-[calc(100vh-364px)] desktop:max-h-[calc(100vh-300px)];
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
|
||||
.product-list {
|
||||
@apply m-0 p-0 mt-2;
|
||||
@apply m-0 p-0 mt-px-2;
|
||||
}
|
||||
|
||||
.product-list-result {
|
||||
@apply list-none bg-white rounded-card p-4 mb-2;
|
||||
height: 187px;
|
||||
max-height: 187px;
|
||||
.filter {
|
||||
@apply justify-self-end font-sans flex self-end items-center font-bold bg-wild-blue-yonder border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center z-sticky;
|
||||
width: 106px;
|
||||
min-width: 106px;
|
||||
|
||||
.label {
|
||||
@apply ml-px-5;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-active-customer text-white ml-px-5;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-wrapper {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ViewChild, ViewC
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { AddToShoppingCartDTO } from '@swagger/checkout';
|
||||
@@ -11,7 +12,7 @@ import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { CacheService } from 'apps/core/cache/src/public-api';
|
||||
import { isEqual } from 'lodash';
|
||||
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
|
||||
import { debounceTime, first, map, switchMap } from 'rxjs/operators';
|
||||
import { debounceTime, filter, first, map, switchMap, withLatestFrom } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from '../article-search.store';
|
||||
import { AddedToCartModalComponent } from './added-to-cart-modal/added-to-cart-modal.component';
|
||||
import { SearchResultItemComponent } from './search-result-item.component';
|
||||
@@ -47,14 +48,29 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy {
|
||||
|
||||
trackByItemId: TrackByFunction<ItemDTO> = (index, item) => item.id;
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.isTablet();
|
||||
}
|
||||
|
||||
initialFilter$ = this.filter$.pipe(
|
||||
filter((filter) => !!filter),
|
||||
first()
|
||||
);
|
||||
|
||||
hasFilter$ = this.filter$.pipe(
|
||||
withLatestFrom(this.initialFilter$),
|
||||
map(([filter, initialFilter]) => !isEqual(filter?.getQueryParams(), initialFilter?.getQueryParams()))
|
||||
);
|
||||
|
||||
constructor(
|
||||
public searchService: ArticleSearchService,
|
||||
private route: ActivatedRoute,
|
||||
private application: ApplicationService,
|
||||
public application: ApplicationService,
|
||||
private breadcrumb: BreadcrumbService,
|
||||
private cache: CacheService,
|
||||
private _uiModal: UiModalService,
|
||||
private _checkoutService: DomainCheckoutService
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _environment: EnvironmentService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -2,10 +2,55 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { ArticleDetailsComponent } from './article-details/article-details.component';
|
||||
import { ArticleSearchComponent } from './article-search/article-search.component';
|
||||
import { ArticleSearchFilterComponent } from './article-search/search-filter/search-filter.component';
|
||||
import { ArticleSearchMainComponent } from './article-search/search-main/search-main.component';
|
||||
import { ArticleSearchResultsComponent } from './article-search/search-results/search-results.component';
|
||||
import { PageCatalogComponent } from './page-catalog.component';
|
||||
|
||||
const auxiliaryRoutes = [
|
||||
{
|
||||
path: 'search',
|
||||
component: ArticleSearchComponent,
|
||||
outlet: 'left',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: ArticleSearchMainComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'filter',
|
||||
component: ArticleSearchFilterComponent,
|
||||
outlet: 'right',
|
||||
},
|
||||
{
|
||||
path: 'results',
|
||||
component: ArticleSearchResultsComponent,
|
||||
outlet: 'right',
|
||||
},
|
||||
{
|
||||
path: 'results',
|
||||
component: ArticleSearchResultsComponent,
|
||||
outlet: 'left',
|
||||
},
|
||||
{
|
||||
path: 'results',
|
||||
component: ArticleSearchResultsComponent,
|
||||
outlet: 'main',
|
||||
},
|
||||
{
|
||||
path: 'details/ean/:ean',
|
||||
component: ArticleDetailsComponent,
|
||||
outlet: 'right',
|
||||
},
|
||||
{
|
||||
path: 'details/:id',
|
||||
component: ArticleDetailsComponent,
|
||||
outlet: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
@@ -33,6 +78,7 @@ const routes: Routes = [
|
||||
path: 'details/:id',
|
||||
component: ArticleDetailsComponent,
|
||||
},
|
||||
...auxiliaryRoutes,
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
|
||||
@@ -2,4 +2,21 @@
|
||||
<shared-branch-selector [branchType]="1" [value]="selectedBranch$ | async" (valueChange)="patchProcessData($event)">
|
||||
</shared-branch-selector>
|
||||
</shared-breadcrumb>
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<ng-container *ngIf="isTablet; else desktop">
|
||||
<router-outlet></router-outlet>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #desktop>
|
||||
<router-outlet name="main"></router-outlet>
|
||||
<div class="grid desktop:grid-cols-[10.5rem_31rem_45.5rem]">
|
||||
<div class="bg-white hidden desktop:block mr-6"></div>
|
||||
<div class="mr-6 hidden desktop:block">
|
||||
<router-outlet name="left"></router-outlet>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<router-outlet name="right"></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -27,6 +27,10 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
_onDestroy$ = new Subject<boolean>();
|
||||
|
||||
get isTablet() {
|
||||
return this._environmentService.isTablet();
|
||||
}
|
||||
|
||||
constructor(
|
||||
public application: ApplicationService,
|
||||
private _uiModal: UiModalService,
|
||||
@@ -41,7 +45,7 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
if (this._environmentService.isTablet()) {
|
||||
if (this.isTablet) {
|
||||
fromEvent(this.branchSelectorRef.nativeElement, 'focusin')
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
.subscribe((_) => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./apps/**/*.{html,ts}'],
|
||||
darkMode: 'media', // or 'media' or 'class'
|
||||
darkMode: 'media', // or 'media' or 'class',
|
||||
theme: {
|
||||
screens: {
|
||||
tablet: '640px', // 744 x 1133 iPad Optimale Auflösung
|
||||
tablet: '744px', // 744 x 1133 iPad Optimale Auflösung
|
||||
desktop: '1024px', // 1440 x 1024 Desktop Optimale Auflösung
|
||||
},
|
||||
zIndex: {
|
||||
@@ -35,6 +35,7 @@ module.exports = {
|
||||
},
|
||||
maxWidth: {
|
||||
content: '1024px',
|
||||
desktop: '1440px',
|
||||
// content: '1280px',
|
||||
},
|
||||
spacing: {
|
||||
|
||||
Reference in New Issue
Block a user