Merged in feature/HIMA-59-create-animation-filter-dropdown (pull request #50)

Feature/HIMA-59 create animation filter dropdown
This commit is contained in:
Peter Skrlj
2019-02-13 13:12:12 +00:00
committed by Eraldo Hasanaj
15 changed files with 247 additions and 122 deletions

View File

@@ -14,6 +14,7 @@ import { Breadcrumb } from '../../core/models/breadcrumb.model';
import { ProcessDeleteDialogComponent } from 'src/app/modules/process/process-delete-dialog/process-delete-dialog.component';
import { ProcessState } from 'src/app/core/store/state/process.state';
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
@Component({
selector: 'app-process-tab',
@@ -25,29 +26,26 @@ export class ProcessTabComponent implements OnInit {
@Input() processes: Array<Process>;
@Select(ProcessState.getProcesses) procecesses$: Observable<Process[]>;
@Select(ProcessState.getProcessCount) processCount: Observable<number>;
@ViewChild('deleteporcessdialog') processDeleteDialogComponent: ProcessDeleteDialogComponent;
@ViewChild('deleteporcessdialog')
processDeleteDialogComponent: ProcessDeleteDialogComponent;
cartCount = 0;
constructor(private store: Store, private router: Router) {}
deleteProcess(process: Process) {
this.store.dispatch(new DeleteProcess(process));
this.processCount.subscribe(
(count: number) => {
this.processCount.subscribe((count: number) => {
if (count < 1) {
this.router.navigate(['/dashboard']);
} else {
this.procecesses$.subscribe(
(data: Process[]) => {
this.procecesses$.subscribe((data: Process[]) => {
const newSelectedProcess = data[count - 1];
if (newSelectedProcess) {
this.router.navigate([newSelectedProcess.currentRoute]);
}
});
}
);
}
}
);
});
}
openDeleteConfirmationDialog() {

View File

@@ -1,5 +1,5 @@
<div class="result-container">
<app-filter></app-filter>
<app-filter (filtersChanged)="updateSearch()"></app-filter>
<div *ngIf="!ds || (ds.loading && !ds.results)">
<div [@stagger]="'yes'">
<div *ngFor="let dummy of dummies" [style.marginTop.px]="10">

View File

@@ -5,6 +5,7 @@ app-product-card-loading {
}
app-filter {
display: block;
margin-bottom: 10px;
}
.result-container {
@@ -19,5 +20,5 @@ app-filter {
.product-item {
height: 180px;
overflow: hidden;
padding-top: 10px;
padding-bottom: 10px;
}

View File

@@ -10,8 +10,7 @@ import {
filter,
take,
debounceTime,
distinctUntilChanged,
switchMap
distinctUntilKeyChanged
} from 'rxjs/operators';
import { Select, Store } from '@ngxs/store';
import { ItemDTO } from 'dist/cat-service/lib/dtos';
@@ -49,7 +48,6 @@ export class SearchResultsComponent implements OnInit {
constructor(
private store: Store,
private router: Router,
private productMapping: ProductMapping,
private productService: ProductService
) {}
@@ -65,14 +63,10 @@ export class SearchResultsComponent implements OnInit {
this.store,
[]
);
}
this.store
.select(FilterState.getFiltersJSON)
.pipe(
distinctUntilChanged(),
switchMap(f => this.store.selectOnce(FilterState.getFilters))
)
.subscribe(fil => {
updateSearch() {
this.store.selectOnce(FilterState.getFilters).subscribe(fil => {
if (!!fil) {
this.ds = new SearchDataSource(
this.productService,
@@ -93,23 +87,18 @@ export class SearchResultsComponent implements OnInit {
this.currentSearch = process.search;
}
});
}
onScroll() {
this.store.dispatch(new AllowProductLoad());
this.skip = this.skip + 5;
const search = { ...this.currentSearch, firstLoad: false, skip: this.skip };
this.store.dispatch(new GetProducts(search));
this.store.dispatch(new AddSearch(search));
}
loadProducts() {
this.products$
/*
this.store
.select(ProcessState.getCurrentProcess)
.pipe(
filter(f => Array.isArray(f)),
map(items => items.map(item => this.productMapping.fromItemDTO(item)))
filter(p => !!p),
distinctUntilKeyChanged('id')
)
.subscribe(data => (this.products = data));
.subscribe(process => {
this.currentSearch = process.search;
this.updateSearch();
});
*/
}
}
@@ -153,7 +142,9 @@ export class SearchDataSource extends DataSource<Product | undefined> {
this.dssub.add(
this.dataStreamDTO
.pipe(debounceTime(1000))
.subscribe(i => this.store.dispatch(new SetProducts([...i], this.search)))
.subscribe(i =>
this.store.dispatch(new SetProducts([...i], this.search))
)
);
this.fetchPage(0);
@@ -211,6 +202,12 @@ export class SearchDataSource extends DataSource<Product | undefined> {
);
this.dataStream.next(this.cachedData);
this.dataStreamDTO.next(this.cachedItemsDTO);
if (page === 0) {
// dispatch immediately on first page load
this.store.dispatch(
new SetProducts([...this.cachedItemsDTO], this.search)
);
}
});
}
}

View File

@@ -13,7 +13,7 @@ export const staggerAnimation = trigger('stagger', [
':enter',
[
style({ opacity: 0 }),
stagger(100, [animate(300, style({ opacity: 1 }))])
stagger(50, [animate(300, style({ opacity: 1 }))])
],
{ optional: true }
)

View File

@@ -66,9 +66,18 @@ export class FilterState {
load(ctx: StateContext<FilterStateModel>) {
const state = ctx.getState();
this.filterService.getFilters().subscribe((filters: Filter[]) => {
const mock = ['Warengruppe'];
const missingfilters = mock.map((f, i) => ({
expanded: false,
id: 'mock' + i,
items: [],
max: 1,
name: f
}));
ctx.patchState({
...state,
filters: [...filters]
filters: [...filters, ...missingfilters]
});
});
}
@@ -77,9 +86,24 @@ export class FilterState {
loadFullFilters(ctx: StateContext<FilterStateModel>) {
const state = ctx.getState();
this.filterService.getFullFilter().subscribe((filters: Filter[]) => {
const mock = [
'Warengruppe',
'Lesealter',
'Sprache',
'Bestand',
'Kategorien'
];
const missingfilters = mock.map((f, i) => ({
expanded: false,
id: 'mock' + i,
items: [],
max: 1,
name: f
}));
ctx.patchState({
...state,
filters: [...filters]
filters: [...filters, ...missingfilters]
});
});
}

View File

@@ -76,7 +76,7 @@ export class ProcessState {
if (currenProcces[0]) {
return currenProcces[0];
} else {
return [];
return null;
}
}
@@ -384,10 +384,14 @@ export class ProcessState {
if (!state.processes) {
return;
}
const breadcrumb = <Breadcrumb>{ name: payload.query, path: '/search-results#start' };
const breadcrumb = <Breadcrumb>{
name: payload.query,
path: '/search-results#start'
};
const currentProcess = this.updateBreadcrumbForCurrentProcess(
getCurrentProcess(state.processes),
breadcrumb);
breadcrumb
);
if (
currentProcess.search === payload &&
currentProcess.itemsDTO &&
@@ -425,10 +429,12 @@ export class ProcessState {
}
}
@Action(SetProducts, { cancelUncompleted: true })
setProducts(ctx: StateContext<ProcessStateModel>, { payload, search }: SetProducts) {
setProducts(
ctx: StateContext<ProcessStateModel>,
{ payload, search }: SetProducts
) {
const state = ctx.getState();
const currentProcess = getCurrentProcess(state.processes);
ctx.patchState({
...state,
processes: this.changeProducResultsForCurrentProcess(
@@ -516,9 +522,16 @@ export class ProcessState {
search: string
): Process[] {
const newProcessState = processes.map(process => {
const breadcrumb = <Breadcrumb>{ name: search, path: '/search-results#start' };
const breadcrumb = <Breadcrumb>{
name: search + ` (Ergrebnisse ${items.length})`,
path: '/search-results#start'
};
if (process.selected === true) {
return { ...this.updateBreadcrumbForCurrentProcess(process, breadcrumb), itemsDTO: items, loading: false };
return {
...this.updateBreadcrumbForCurrentProcess(process, breadcrumb),
itemsDTO: items,
loading: false
};
}
return { ...this.updateBreadcrumbForCurrentProcess(process, breadcrumb) };
});
@@ -584,7 +597,9 @@ export class ProcessState {
...state,
processes: state.processes.map(process => {
if (process.selected === true) {
return { ...this.updateBreadcrumbForCurrentProcess(process, payload) };
return {
...this.updateBreadcrumbForCurrentProcess(process, payload)
};
}
return { ...process };
})
@@ -601,7 +616,9 @@ export class ProcessState {
...state,
processes: state.processes.map(process => {
if (process.selected === true) {
return { ...this.updateBreadcrumbForCurrentProcess(process, payload) };
return {
...this.updateBreadcrumbForCurrentProcess(process, payload)
};
}
return { ...process };
})
@@ -609,11 +626,16 @@ export class ProcessState {
}
@Action(actions.PopBreadcrumbsAfterCurrent)
popBreadcrumbs(ctx: StateContext<ProcessStateModel>, { payload }: actions.PopBreadcrumbsAfterCurrent) {
popBreadcrumbs(
ctx: StateContext<ProcessStateModel>,
{ payload }: actions.PopBreadcrumbsAfterCurrent
) {
const state = ctx.getState();
const process = state.processes.find(t => t.selected === true);
const breadcrumbs = [...process.breadcrumbs];
const indexOfCurrentBreadcrumb = breadcrumbs.findIndex(b => b.name === payload.name);
const indexOfCurrentBreadcrumb = breadcrumbs.findIndex(
b => b.name === payload.name
);
console.log(indexOfCurrentBreadcrumb);
for (let x = breadcrumbs.length - 1; x >= 0; x--) {
if (indexOfCurrentBreadcrumb < x) {
@@ -631,7 +653,10 @@ export class ProcessState {
});
}
updateBreadcrumbForCurrentProcess(process: Process, payload: Breadcrumb): Process {
updateBreadcrumbForCurrentProcess(
process: Process,
payload: Breadcrumb
): Process {
const breadcrumbExist = process.breadcrumbs.filter(
(breadcrumb: Breadcrumb) => breadcrumb.name === payload.name
);
@@ -640,13 +665,16 @@ export class ProcessState {
}
const updatedBreadcrumbs = process.breadcrumbs.map(
(breadcrumb: Breadcrumb) => {
if (breadcrumb.path === payload.path || breadcrumb.path.substring(0, 16) === payload.path.substring(0, 16)) {
return { name: payload, path: payload.path };
if (
breadcrumb.path === payload.path ||
breadcrumb.path.substring(0, 16) === payload.path.substring(0, 16)
) {
return { name: payload.name, path: payload.path };
}
return breadcrumb;
}
);
if (!updatedBreadcrumbs.find(b => b.name === payload)) {
if (!updatedBreadcrumbs.find(b => b.name === payload.name)) {
return <Process>{
...process,
breadcrumbs: [

View File

@@ -21,4 +21,9 @@
{{ item.name }}
</div>
</div>
<div class="button">
<a class="active" (click)="confirmed.emit(true) && toggleMenu(filter.id)"
>Filtern</a
>
</div>
</div>

View File

@@ -56,3 +56,32 @@
.active {
background-color: $hima-content-color;
}
.button {
display: flex;
align-items: center;
justify-content: center;
margin-top: 25px;
margin-bottom: 25px;
a {
font-size: 18px;
font-weight: bold;
color: $hima-button-color;
padding: 8px 0px;
text-decoration: none;
&.active {
color: #fff;
background-color: $hima-button-color;
border-radius: 40px;
outline: none;
text-align: center;
width: 115px;
}
&:hover {
cursor: pointer;
}
}
}

View File

@@ -1,5 +1,5 @@
import { FilterState } from 'src/app/core/store/state/filter.state';
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Filter } from 'src/app/core/models/filter.model';
import {
SelectFilterById,
@@ -10,6 +10,7 @@ import { FilterItem } from 'src/app/core/models/filter-item.model';
import { Store } from '@ngxs/store';
import { map, take } from 'rxjs/operators';
import { fadeInAnimation } from './fadeIn.animation';
import { OuterSubscriber } from 'rxjs/internal/OuterSubscriber';
@Component({
selector: 'app-filter-item',
@@ -23,6 +24,8 @@ export class FilterItemComponent implements OnInit {
filter: Filter;
@Output() confirmed = new EventEmitter();
constructor(private store: Store) {}
toggleMenu(id: string) {

View File

@@ -2,6 +2,7 @@
<app-filter-item
*ngFor="let index of (filterIndexArray$ | async)"
[index]="index"
(confirmed)="filtersChanged.emit(true)"
></app-filter-item>
<div (click)="toggleMore()" class="more-filters-container">
<span class="more-filters">{{ showMore ? 'Weniger' : 'Mehr' }} Filter</span>

View File

@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Filter } from 'src/app/core/models/filter.model';
import {
LoadFilters,
@@ -19,6 +19,7 @@ export class FilterComponent implements OnInit {
filterIndexArray$: Observable<number[]>;
showMore: boolean;
@Output() filtersChanged = new EventEmitter();
constructor(private store: Store) {}
ngOnInit() {

View File

@@ -5,7 +5,10 @@ import { Store, Select } from '@ngxs/store';
import { Observable } from 'rxjs';
import { ProcessState } from '../../../../core/store/state/process.state';
import { User } from '../../../../core/models/user.model';
import { ChangeCurrentRoute, AddBreadcrumb } from '../../../../core/store/actions/process.actions';
import {
ChangeCurrentRoute,
AddBreadcrumb
} from '../../../../core/store/actions/process.actions';
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
import { CatImageService } from 'cat-service';
import { Cart } from '../../../../core/models/cart.model';
@@ -54,9 +57,17 @@ export class CartReviewComponent implements OnInit {
if (user) {
this.processUserData(user);
} else {
this.setInputData('invoice_addrees', 'Karl Schneier I Kazmairstraße 34, 80339 München', this.invoiceForm);
this.setInputData(
'invoice_addrees',
'Karl Schneier I Kazmairstraße 34, 80339 München',
this.invoiceForm
);
this.setInputData('notification_type', 'Email', this.invoiceForm);
this.setInputData('delivery_addrees', 'Karl Schneier I Kazmairstraße 34, 80339 München', this.deliveryForm);
this.setInputData(
'delivery_addrees',
'Karl Schneier I Kazmairstraße 34, 80339 München',
this.deliveryForm
);
}
});
@@ -74,31 +85,47 @@ export class CartReviewComponent implements OnInit {
totalSum += bookInCart.book.av[0].price.value.value * bookInCart.quantity;
totalQuantity += bookInCart.quantity;
const currency = bookInCart.book.av[0].price.value.currency;
const formatedPrice = (+Math.round(bookInCart.book.av[0].price.value.value * 100) / 100).toFixed(2).toLocaleString().replace('.', ',');
await this.catImageService.getImageUrl(bookInCart.book.pr.ean).subscribe((url: string) => {
this.books.push(
{
const formatedPrice = (
+Math.round(bookInCart.book.av[0].price.value.value * 100) / 100
)
.toFixed(2)
.toLocaleString()
.replace('.', ',');
await this.catImageService
.getImageUrl(bookInCart.book.pr.ean)
.subscribe((url: string) => {
this.books.push({
book: bookInCart.book,
quantity: bookInCart.quantity,
currency: currency,
price: formatedPrice,
imgUrl: url
}
);
});
})
});
});
this.displayItemsNumber = totalQuantity;
this.currentPoints = totalQuantity * points;
this.booksTotalSum = totalSum;
this.booksTotalSumString = (+Math.round(totalSum * 100) / 100).toFixed(2).toLocaleString().replace('.', ',');
this.booksTotalSumString = (+Math.round(totalSum * 100) / 100)
.toFixed(2)
.toLocaleString()
.replace('.', ',');
}
private processUserData(user: User) {
this.user = user;
const invoiceAddress = `${user.invoice_address.street} ${user.invoice_address.zip} ${user.invoice_address.city}`;
const deliveryAddress = `${user.delivery_addres.street} ${user.delivery_addres.zip} ${user.delivery_addres.city}`;
this.setInputData('invoice_addrees', invoiceAddress ? invoiceAddress : deliveryAddress, this.invoiceForm);
const invoiceAddress = `${user.invoice_address.street} ${
user.invoice_address.zip
} ${user.invoice_address.city}`;
const deliveryAddress = `${user.delivery_addres.street} ${
user.delivery_addres.zip
} ${user.delivery_addres.city}`;
this.setInputData(
'invoice_addrees',
invoiceAddress ? invoiceAddress : deliveryAddress,
this.invoiceForm
);
this.setInputData('notification_type', 'Email', this.invoiceForm);
this.setInputData('delivery_addrees', deliveryAddress, this.deliveryForm);
}
@@ -108,7 +135,7 @@ export class CartReviewComponent implements OnInit {
const newBread: Breadcrumb = {
name: 'Kundendetails',
path: 'customer-edit/' + this.user.id
}
};
this.store.dispatch(new AddBreadcrumb(newBread));
const currentRoute = 'customer-edit/' + this.user.id;
@@ -142,7 +169,10 @@ export class CartReviewComponent implements OnInit {
this.displayItemsNumber = totalQuantity;
this.booksTotalSum = totalSum;
this.booksTotalSumString = (+Math.round(totalSum * 100) / 100).toFixed(2).toLocaleString().replace('.', ',');
this.booksTotalSumString = (+Math.round(totalSum * 100) / 100)
.toFixed(2)
.toLocaleString()
.replace('.', ',');
this.currentPoints = totalQuantity * points;
}
@@ -165,7 +195,6 @@ export class CartReviewComponent implements OnInit {
icon.classList.add('open-icon');
ele.style.display = 'none';
}
}
}
@@ -182,8 +211,8 @@ export class CartReviewComponent implements OnInit {
if (!ele.style.display || ele.style.display === 'none') {
// open
var rect = icon.getBoundingClientRect();
ele.style.top = (rect.top + 33) + 'px';
ele.style.left = (rect.left - 30) + 'px';
ele.style.top = rect.top + 33 + 'px';
ele.style.left = rect.left - 30 + 'px';
ele.style.display = 'flex';
icon.classList.remove('open-icon');
icon.classList.add('close-icon');
@@ -203,7 +232,7 @@ export class CartReviewComponent implements OnInit {
const newBread: Breadcrumb = {
name: 'Rechnungsadresse',
path: 'customer-edit/' + this.user.id + '/billing'
}
};
this.store.dispatch(new AddBreadcrumb(newBread));
const currentRoute = 'customer-edit/' + this.user.id + '/billing';
@@ -217,20 +246,24 @@ export class CartReviewComponent implements OnInit {
form.get(field).disable();
}
get f() { return this.invoiceForm.controls; }
get f() {
return this.invoiceForm.controls;
}
get deliveryF() { return this.deliveryForm.controls; }
get deliveryF() {
return this.deliveryForm.controls;
}
private buildInvoiceForm(fb: FormBuilder) {
return fb.group({
invoice_addrees: ['', Validators.required],
notification_type: ['', Validators.required],
notification_type: ['', Validators.required]
});
}
private buildDeliveryForm(fb: FormBuilder) {
return fb.group({
delivery_addrees: ['', Validators.required],
delivery_addrees: ['', Validators.required]
});
}
}

View File

@@ -1,7 +1,14 @@
<div *ngFor="let card of feed$ | async">
<div *ngFor="let card of (feed$ | async)">
<app-book-card *ngIf="card.type === 'products'" [card]="card"></app-book-card>
<app-event-card *ngIf="card.type === 'events'" [card]="card"></app-event-card>
<app-news-card *ngIf="card.type === 'info'" [card]="card"></app-news-card>
<app-recommandation-card *ngIf="card.type === 'REC'" [card]="card"></app-recommandation-card>
<app-recommandation-card
*ngIf="card.type === 'REC'"
[card]="card"
></app-recommandation-card>
</div>
<app-loading [loading]="loading" text="Inhalte werden geladen"></app-loading>
<app-loading
*ngIf="loading"
loading="true"
text="Inhalte werden geladen"
></app-loading>

View File

@@ -2,9 +2,7 @@
display: flex;
position: relative;
width: 100%;
height: 100%;
flex-direction: column;
min-height: 40px;
align-items: center;
justify-content: center;
}