Compare commits

...

4 Commits

Author SHA1 Message Date
Nino Righi
d303b1444b Zoom Update and Article Result List Styling Update 2023-04-03 17:54:08 +02:00
Nino Righi
150e7965ee Zwischencommit 2023-03-29 12:57:05 +02:00
Nino Righi
3fcf3d9396 Responsive Design Splitscreen Article Search Results Update 2023-03-28 15:08:31 +02:00
Nino Righi
52278b8baf Initial Responsive Design Implementation based on Article Search 2023-03-24 17:19:59 +01:00
23 changed files with 425 additions and 233 deletions

View File

@@ -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

View File

@@ -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>

View File

@@ -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;

View File

@@ -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') {

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,
}
);
}
}
}
});

View File

@@ -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 {}

View File

@@ -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

View File

@@ -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,

View File

@@ -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$;

View File

@@ -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: [],

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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 });
// }
}

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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',

View File

@@ -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>

View File

@@ -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((_) => {

View File

@@ -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: {