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) => {
if (count < 1) {
this.router.navigate(['/dashboard']);
} else {
this.procecesses$.subscribe(
(data: Process[]) => {
const newSelectedProcess = data[count - 1];
if (newSelectedProcess) {
this.router.navigate([newSelectedProcess.currentRoute]);
}
}
);
}
this.processCount.subscribe((count: number) => {
if (count < 1) {
this.router.navigate(['/dashboard']);
} else {
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,23 +63,19 @@ export class SearchResultsComponent implements OnInit {
this.store,
[]
);
}
this.store
.select(FilterState.getFiltersJSON)
.pipe(
distinctUntilChanged(),
switchMap(f => this.store.selectOnce(FilterState.getFilters))
)
.subscribe(fil => {
if (!!fil) {
this.ds = new SearchDataSource(
this.productService,
this.currentSearch.query,
this.store,
fil
);
}
});
updateSearch() {
this.store.selectOnce(FilterState.getFilters).subscribe(fil => {
if (!!fil) {
this.ds = new SearchDataSource(
this.productService,
this.currentSearch.query,
this.store,
fil
);
}
});
}
loadCurrentSearch() {
@@ -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);
@@ -203,14 +194,20 @@ export class SearchDataSource extends DataSource<Product | undefined> {
page * this.pageSize,
this.pageSize,
...data.result.map((item, i) => {
if (i === 3) {
return procuctsMock[3];
}
return this.productMapping.fromItemDTO(item);
if (i === 3) {
return procuctsMock[3];
}
return this.productMapping.fromItemDTO(item);
})
);
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,13 +522,20 @@ 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) };
});
return newProcessState;
return newProcessState;
}
extendProducResultsForCurrentProcess(
@@ -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 breadcrumbs = [...process.breadcrumbs];
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: [
@@ -658,7 +686,7 @@ export class ProcessState {
]
};
}
return <Process>{
return <Process>{
...process,
breadcrumbs: [...updatedBreadcrumbs]
};

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';
@@ -31,7 +34,7 @@ export class CartReviewComponent implements OnInit {
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
possibleItems = [1, 2, 3, 4];
books: BookData[] = [];
currentNumberOfItems = 1;
displayItemsNumber = 0;
currentPrice = '';
@@ -45,7 +48,7 @@ export class CartReviewComponent implements OnInit {
private fb: FormBuilder,
private router: Router,
private catImageService: CatImageService
) { }
) {}
ngOnInit() {
this.invoiceForm = this.buildInvoiceForm(this.fb);
@@ -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
);
}
});
@@ -70,35 +81,51 @@ export class CartReviewComponent implements OnInit {
private processCartData(cart: Cart[]) {
let totalSum = 0;
let totalQuantity = 0;
cart.forEach(async(bookInCart: Cart) => {
cart.forEach(async (bookInCart: Cart) => {
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,8 +135,8 @@ 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;
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
@@ -117,7 +144,7 @@ export class CartReviewComponent implements OnInit {
}
}
setNumberOfItems (numberOfItems: number, id: number, book: BookData) {
setNumberOfItems(numberOfItems: number, id: number, book: BookData) {
const updatedBooks = this.books.map((bookToUpdate: BookData) => {
if (bookToUpdate.book.id === book.book.id) {
bookToUpdate.quantity = numberOfItems;
@@ -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;
}
@@ -154,7 +184,7 @@ export class CartReviewComponent implements OnInit {
let text = document.getElementById('drop_txt_' + i);
let icon = document.getElementById('drop_img_' + i);
if(text && text.classList.contains('dropdown-selected-text-active')) {
if (text && text.classList.contains('dropdown-selected-text-active')) {
text.classList.remove('dropdown-selected-text-active');
icon.classList.remove('open-icon');
icon.classList.add('close-icon');
@@ -165,15 +195,14 @@ export class CartReviewComponent implements OnInit {
icon.classList.add('open-icon');
ele.style.display = 'none';
}
}
}
const ele = document.getElementById('drop_' + id);
const text = document.getElementById('drop_txt_' + id);
const icon = document.getElementById('drop_img_' + id);
if(text.classList.contains('dropdown-selected-text-active')) {
if (text.classList.contains('dropdown-selected-text-active')) {
text.classList.remove('dropdown-selected-text-active');
} else {
text.classList.add('dropdown-selected-text-active');
@@ -182,11 +211,11 @@ 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');
icon.classList.add('close-icon');
} else {
icon.classList.remove('close-icon');
icon.classList.add('open-icon');
@@ -199,12 +228,12 @@ export class CartReviewComponent implements OnInit {
}
redirectToBilling() {
if (this.user) {
if (this.user) {
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';
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
@@ -212,25 +241,29 @@ export class CartReviewComponent implements OnInit {
}
}
private setInputData (field: string, val: string, form: FormGroup) {
private setInputData(field: string, val: string, form: FormGroup) {
form.get(field).patchValue(val);
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;
}