[HIMA-16] - Integrated scanning component into article search

This commit is contained in:
Peter Skrlj
2019-02-09 22:39:33 +01:00
parent 8e48e87c1b
commit ecc0c925f6
25 changed files with 311 additions and 236 deletions

View File

@@ -1,16 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './modules/dashboard/dashboard.component';
import { ArticleSearchComponent } from './components/article-search/article-search.component';
import {
CustomerSearchComponent,
SearchCustomerResultComponent,
EditCustomerCardComponent
} from './modules/customer-search';
import { SearchResultsComponent } from './components/search-results/search-results.component';
import { ProductDetailsComponent } from './components/product-details/product-details.component';
import { BarcodeSearchComponent } from './modules/barcode-search/barcode-search.component';
import { CartReviewComponent } from './modules/cart/components/cart-review/cart-review.component';
import { ArticleSearchComponent } from './modules/article-search/article-search.component';
import { SearchResultsComponent } from './components/search-results/search-results.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },

View File

@@ -10,16 +10,13 @@ import {
BasicAuthorizationInterceptor,
BasicAuthorizationOptions
} from './core/interceptors';
import { SearchResultsComponent } from './components/search-results/search-results.component';
import {
CatServiceModule,
CAT_SERVICE_ENDPOINT,
CatSearchService,
CAT_AV_SERVICE_ENDPOINT
} from 'cat-service';
import { ProductCardComponent } from './components/product-card/product-card.component';
import { ConfigService } from './core/services/config.service';
import { ProductDetailsComponent } from './components/product-details/product-details.component';
import {
FeedServiceModule,
FEED_SERVICE_ENDPOINT,
@@ -34,10 +31,8 @@ import { ProcessState } from './core/store/state/process.state';
import { environment } from 'src/environments/environment';
import { BreadcrumbsState } from './core/store/state/breadcrumbs.state';
import { FilterState } from './core/store/state/filter.state';
import { CheckoutComponent } from './components/checkout/checkout.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SharedModule } from './shared/shared.module';
const states = [FeedState, ProcessState, BreadcrumbsState, FilterState];
export function _configInitializer(conf: ConfigService) {
@@ -64,13 +59,7 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
}
@NgModule({
declarations: [
AppComponent,
SearchResultsComponent,
ProductCardComponent,
ProductDetailsComponent,
CheckoutComponent
],
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,

View File

@@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ArticleSearchComponent } from './article-search.component';
describe('ArticleSearchComponent', () => {
let component: ArticleSearchComponent;
let fixture: ComponentFixture<ArticleSearchComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ArticleSearchComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ArticleSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,45 +0,0 @@
@import "../../../assets/scss/variables";
.filter-name {
font-size: 16px;
font-weight: 600;
line-height: 21px;
}
.icon {
width: 17px;
margin-left: 5px;
}
.filter-item {
position: relative;
}
.filter-menu {
position: absolute;
z-index: 5;
background-color: white;
-moz-box-shadow: 0 0 20px 1px $hima-content-shadow-color;
-webkit-box-shadow: 0 0 20px 1px $hima-content-shadow-color;
box-shadow: 0 0 20px 1px $hima-content-shadow-color;
border-radius: 4px;
width: 210px;
padding: 0px;
margin-top: 10px;
margin-left: -90px;
}
.item-container {
display: grid;
grid-template-columns: auto;
}
.item {
padding-top: 15px;
padding-left: 20px;
padding-right: 20px;
}
.active {
background-color: $hima-content-color;
}

View File

@@ -1,80 +0,0 @@
@import "../../../assets/scss/variables";
.filter-container {
display: grid;
grid-template-columns: auto auto auto auto;
//grid-gap: 5vh;
margin-top: 15px;
width: 80%;
margin-left: 15%;
line-height: 3;
}
@media screen and (max-width: 1247px) {
.filter-container {
width: 79%;
margin-left: 14%;
}
}
@media screen and (max-width: 989px) {
.filter-container {
width: 76%;
}
}
@media screen and (max-width: 791px) {
.filter-container {
margin-left: 13%;
}
}
@media screen and (max-width: 759px) {
.filter-container {
margin-left: 12%;
}
}
@media screen and (max-width: 744px) {
.filter-container {
grid-template-columns: auto auto auto;
}
}
@media screen and (max-width: 530px) {
.filter-container {
grid-template-columns: auto auto;
}
}
.more-filters {
font-size: 16px;
font-weight: bold;
color: #5a728a;
margin-top: 1px;
}
.more-filters-container {
padding-top: 3px;
}
.more-icon {
width: 14px;
margin-left: 6px;
}
.less-filters {
font-size: 16px;
font-weight: bold;
color: #5a728a;
margin-top: 1px;
margin-left: 6px;
}
.less-filters-container {
padding-top: 3px;
}
.less-icon {
width: 14px;
}

View File

@@ -3,14 +3,17 @@ import { Search } from 'src/app/core/models/search.model';
import { Process } from 'src/app/core/models/process.model';
import { Product } from 'src/app/core/models/product.model';
import { Router } from '@angular/router';
import { ProductMapping } from '../../core/mappings/product.mapping';
import { map, filter } from 'rxjs/operators';
import { Select, Store } from '@ngxs/store';
import { ItemDTO } from 'dist/cat-service/lib/dtos';
import { Observable } from 'rxjs';
import { GetProducts } from 'src/app/core/store/actions/product.actions';
import { ProcessState } from 'src/app/core/store/state/process.state';
import { AddSearch, AllowProductLoad } from 'src/app/core/store/actions/process.actions';
import {
AddSearch,
AllowProductLoad
} from 'src/app/core/store/actions/process.actions';
import { ProductMapping } from 'src/app/core/mappings/product.mapping';
@Component({
selector: 'app-search-results',
@@ -18,7 +21,6 @@ import { AddSearch, AllowProductLoad } from 'src/app/core/store/actions/process.
styleUrls: ['./search-results.component.scss']
})
export class SearchResultsComponent implements OnInit {
currentSearch: Search;
products: Product[];
@Select(ProcessState.getProducts) products$: Observable<ItemDTO[]>;
@@ -28,7 +30,7 @@ export class SearchResultsComponent implements OnInit {
private store: Store,
private router: Router,
private productMapping: ProductMapping
) { }
) {}
ngOnInit() {
this.loadCurrentSearch();
@@ -41,29 +43,30 @@ export class SearchResultsComponent implements OnInit {
}
loadCurrentSearch() {
this.store.select(state => state.processes).subscribe(
(data: any) => {
const process = data.processes
.find(t => t.selected === true);
this.store
.select(state => state.processes)
.subscribe((data: any) => {
const process = data.processes.find(t => t.selected === true);
if (process) {
this.currentSearch = process.search;
}
}
);
});
}
onScroll() {
this.store.dispatch(new AllowProductLoad());
this.skip = this.skip + 5;
const search = { ...this.currentSearch, firstLoad: false, skip: this.skip};
const search = { ...this.currentSearch, firstLoad: false, skip: this.skip };
this.store.dispatch(new GetProducts(search));
this.store.dispatch(new AddSearch(search));
}
loadProducts() {
this.products$.pipe(
filter(f => Array.isArray(f)),
map(items => items.map(item => this.productMapping.fromItemDTO(item)))
).subscribe(data => this.products = data);
this.products$
.pipe(
filter(f => Array.isArray(f)),
map(items => items.map(item => this.productMapping.fromItemDTO(item)))
)
.subscribe(data => (this.products = data));
}
}

View File

@@ -0,0 +1,18 @@
<div class="spacer"></div>
<div [@staggerAnimation]="active">
<app-card
*ngFor="let item of navigation"
(click)="switch(item)"
class="header"
>
<span class="header-title">{{ titles[item] }}</span>
</app-card>
</div>
<div class="content" [ngSwitch]="active">
<div *ngSwitchCase="'search'" [@fadeIn]="active">
<app-text-search></app-text-search>
</div>
<div *ngSwitchCase="'scan'" [@fadeIn]="active">
<app-barcode-search></app-barcode-search>
</div>
</div>

View File

@@ -0,0 +1,34 @@
.header-title {
font-size: 20px;
font-weight: bold;
color: #5a728a;
opacity: 0.8;
text-align: left;
line-height: 21px;
}
.spacer {
height: 20px;
}
.header {
padding-left: 20px;
padding-bottom: 28px;
padding-top: 28px;
margin-top: -10px;
}
.content {
min-height: 400px;
position: relative;
margin-top: -5px;
}
.content > * {
position: absolute;
width: 100%;
height: 100%;
}
* {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-moz-tap-highlight-color: rgba(0, 0, 0, 0);
}

View File

@@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import { fadeInAnimation } from '../customer-search/components/customer-search/fade-in.animation';
import { staggerAnimation } from './stagger.animation';
@Component({
selector: 'app-article-search',
templateUrl: './article-search.component.html',
styleUrls: ['./article-search.component.scss'],
animations: [fadeInAnimation, staggerAnimation]
})
export class ArticleSearchComponent implements OnInit {
titles = {
search: 'Artikelsuche',
scan: 'Artikel scannen'
};
navigation = ['scan'];
active = 'search';
ngOnInit() {}
switch(newItem: string) {
const index = this.navigation.indexOf(newItem);
this.navigation[index] = this.active;
this.active = newItem;
}
}

View File

@@ -0,0 +1,22 @@
import { BarcodeSearchModule } from './../barcode-search/barcode-search.module';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SharedModule } from 'src/app/shared/shared.module';
import { TextSearchComponent } from './components/text-search/text-search.component';
import { ArticleSearchComponent } from './article-search.component';
import { FilterComponent } from './components/filter/filter.component';
import { FilterItemComponent } from './components/filter-item/filter-item.component';
@NgModule({
declarations: [
TextSearchComponent,
ArticleSearchComponent,
FilterComponent,
FilterItemComponent
],
exports: [ArticleSearchComponent, FilterComponent],
imports: [CommonModule, FormsModule, SharedModule, BarcodeSearchModule]
})
export class ArticleSearchModule {}

View File

@@ -0,0 +1,45 @@
@import '../../../../../assets/scss/variables';
.filter-name {
font-size: 16px;
font-weight: 600;
line-height: 21px;
}
.icon {
width: 17px;
margin-left: 5px;
}
.filter-item {
position: relative;
}
.filter-menu {
position: absolute;
z-index: 5;
background-color: white;
-moz-box-shadow: 0 0 20px 1px $hima-content-shadow-color;
-webkit-box-shadow: 0 0 20px 1px $hima-content-shadow-color;
box-shadow: 0 0 20px 1px $hima-content-shadow-color;
border-radius: 4px;
width: 210px;
padding: 0px;
margin-top: 10px;
margin-left: -90px;
}
.item-container {
display: grid;
grid-template-columns: auto;
}
.item {
padding-top: 15px;
padding-left: 20px;
padding-right: 20px;
}
.active {
background-color: $hima-content-color;
}

View File

@@ -0,0 +1,80 @@
@import '../../../../../assets/scss/variables';
.filter-container {
display: grid;
grid-template-columns: auto auto auto auto;
//grid-gap: 5vh;
margin-top: 15px;
width: 80%;
margin-left: 15%;
line-height: 3;
}
@media screen and (max-width: 1247px) {
.filter-container {
width: 79%;
margin-left: 14%;
}
}
@media screen and (max-width: 989px) {
.filter-container {
width: 76%;
}
}
@media screen and (max-width: 791px) {
.filter-container {
margin-left: 13%;
}
}
@media screen and (max-width: 759px) {
.filter-container {
margin-left: 12%;
}
}
@media screen and (max-width: 744px) {
.filter-container {
grid-template-columns: auto auto auto;
}
}
@media screen and (max-width: 530px) {
.filter-container {
grid-template-columns: auto auto;
}
}
.more-filters {
font-size: 16px;
font-weight: bold;
color: #5a728a;
margin-top: 1px;
}
.more-filters-container {
padding-top: 3px;
}
.more-icon {
width: 14px;
margin-left: 6px;
}
.less-filters {
font-size: 16px;
font-weight: bold;
color: #5a728a;
margin-top: 1px;
margin-left: 6px;
}
.less-filters-container {
padding-top: 3px;
}
.less-icon {
width: 14px;
}

View File

@@ -1,11 +1,5 @@
<div class="article-search-container">
<div class="article-section article-scan">
<div><span class="article-scan-header">Artikel scannen</span></div>
</div>
<div class="article-section article-search">
<div class="align-center">
<span class="article-search-title">Artikelsuche</span>
</div>
<div class="align-center">
<span class="article-search-description"
>Welchen Artikel suchen Sie?</span

View File

@@ -1,21 +1,8 @@
@import '../../../assets/scss/variables';
@import '../../../../../assets/scss/variables';
.article-search-container {
background-color: white;
margin-left: 5px;
margin-right: 5px;
margin-top: 5px;
// width: 97%;
-moz-box-shadow: 0 0 3px $hima-content-shadow-color;
-webkit-box-shadow: 0 0 3px $hima-content-shadow-color;
box-shadow: 0 0 3px $hima-content-shadow-color;
border-radius: 4px;
}
@media screen and (max-width: 485px) {
.article-search-container {
width: 95%;
}
background-image: linear-gradient(-180deg, #ffffff 0%, #ffffff 100%);
box-shadow: 0px -2px 24px 0px #dce2e9;
}
.article-section {

View File

@@ -10,18 +10,23 @@ import { Process } from 'src/app/core/models/process.model';
import { getRandomPic } from 'src/app/core/utils/process.util';
import { Breadcrumb } from 'src/app/core/models/breadcrumb.model';
import { ProcessState } from 'src/app/core/store/state/process.state';
import { AddProcess, ChangeCurrentRoute, AddSearch} from 'src/app/core/store/actions/process.actions';
import {
AddProcess,
ChangeCurrentRoute,
AddSearch
} from 'src/app/core/store/actions/process.actions';
import { AllowProductLoad } from 'src/app/core/store/actions/process.actions';
import { LoadRecentProducts } from 'src/app/core/store/actions/product.actions';
@Component({
selector: 'app-article-search',
templateUrl: './article-search.component.html',
styleUrls: ['./article-search.component.scss']
selector: 'app-text-search',
templateUrl: './text-search.component.html',
styleUrls: ['./text-search.component.scss']
})
export class ArticleSearchComponent implements OnInit {
@Select(ProcessState.getRecentProducts) recentArticles$: Observable<RecentArticleSearch[]>;
export class TextSearchComponent implements OnInit {
@Select(ProcessState.getRecentProducts) recentArticles$: Observable<
RecentArticleSearch[]
>;
recentArticles: RecentArticleSearch[];
products$: Observable<Product[]>;
products: Product[];
@@ -32,13 +37,12 @@ export class ArticleSearchComponent implements OnInit {
@Input()
searchParams = '';
constructor(
private store: Store,
private router: Router
) { }
constructor(private store: Store, private router: Router) {}
search() {
if (!this.searchParams) { return; }
if (!this.searchParams) {
return;
}
this.loadSelectedFilters();
const search = <Search>{
query: this.searchParams,
@@ -88,14 +92,14 @@ export class ArticleSearchComponent implements OnInit {
loadRecentArticles() {
this.store.dispatch(new LoadRecentProducts());
this.recentArticles$.subscribe(
(data: RecentArticleSearch[]) => this.recentArticles = data
(data: RecentArticleSearch[]) => (this.recentArticles = data)
);
}
loadSearchResults() {
this.store.select('products').subscribe(
(data: any) => this.searchProductsHandler(data)
);
this.store
.select('products')
.subscribe((data: any) => this.searchProductsHandler(data));
}
loadSelectedFilters() {
@@ -106,9 +110,7 @@ export class ArticleSearchComponent implements OnInit {
}
loadProcesses() {
this.processes$.subscribe(
(data: Process[]) => this.processes = data
);
this.processes$.subscribe((data: Process[]) => (this.processes = data));
}
createProcess() {
@@ -117,10 +119,12 @@ export class ArticleSearchComponent implements OnInit {
name: '# 1',
selected: true,
icon: getRandomPic(),
breadcrumbs: <Breadcrumb[]>[{
name: 'Artikelsuche',
path: '/article-search'
}],
breadcrumbs: <Breadcrumb[]>[
{
name: 'Artikelsuche',
path: '/article-search'
}
],
currentRoute: 'article-search'
};

View File

@@ -0,0 +1,20 @@
import {
trigger,
transition,
stagger,
animate,
style,
query
} from '@angular/animations';
export const staggerAnimation = trigger('staggerAnimation', [
transition(':enter, * => 0, * => -1', []),
transition('* => *', [
// each time the binding value changes
query(':leave', [
style({ height: '0', marginTop: '-56px', visibility: 'hidden' })
]),
query('*', [stagger(20, [animate('0.1s', style({ opacity: 0 }))])]),
query('*', [stagger(-10, [animate('0.1s', style({ opacity: 1 }))])])
])
]);

View File

@@ -1,7 +1,6 @@
import { CardComponent } from './../shared/components/card/card.component';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { AppRoutingModule } from 'src/app/app-routing.module';
import { ArticleSearchComponent } from 'src/app/components/article-search/article-search.component';
import { HeaderComponent } from 'src/app/components/header/header.component';
import { ProcessHeaderComponent } from 'src/app/components/process-header/process-header.component';
import { ProcessTabComponent } from 'src/app/components/process-tab/process-tab.component';
@@ -11,8 +10,6 @@ import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BreadcrumbsComponent } from '../components/breadcrumbs/breadcrumbs.component';
import { FilterItemComponent } from '../components/filter-item/filter-item.component';
import { FilterComponent } from '../components/filter/filter.component';
import { MenuComponent } from '../components/menu/menu.component';
import { BarcodeSearchModule } from './barcode-search/barcode-search.module';
import { DashboardModule } from './dashboard/dashboard.module';
@@ -20,6 +17,11 @@ import { NewsletterSignupModule } from './newsletter-signup/newsletter-signup.mo
import { ContentPageComponent } from '../pages/content/content.component';
import { CustomerSearchModule } from './customer-search/customer-search.module';
import { CartModule } from './cart/cart-modul';
import { ArticleSearchModule } from './article-search/article-search.module';
import { SearchResultsComponent } from '../components/search-results/search-results.component';
import { ProductCardComponent } from '../components/product-card/product-card.component';
import { ProductDetailsComponent } from '../components/product-details/product-details.component';
import { CheckoutComponent } from '../components/checkout/checkout.component';
import { SharedModule } from '../shared/shared.module';
@NgModule({
@@ -28,18 +30,21 @@ import { SharedModule } from '../shared/shared.module';
ProcessHeaderComponent,
ProcessTabComponent,
ContentPageComponent,
ArticleSearchComponent,
MenuComponent,
BreadcrumbsComponent,
FilterComponent,
FilterItemComponent,
SearchResultsComponent,
ProductCardComponent,
ProductDetailsComponent,
CheckoutComponent
],
imports: [
CommonModule,
AppRoutingModule,
FormsModule,
SharedModule,
NewsletterSignupModule,
InfiniteScrollModule,
ArticleSearchModule,
BarcodeSearchModule,
DashboardModule,
CustomerSearchModule,
@@ -50,11 +55,8 @@ import { SharedModule } from '../shared/shared.module';
ProcessHeaderComponent,
ProcessTabComponent,
ContentPageComponent,
ArticleSearchComponent,
MenuComponent,
BreadcrumbsComponent,
FilterComponent,
FilterItemComponent,
InfiniteScrollModule
]
})

View File

@@ -32,6 +32,7 @@
font-size: 20px;
font-weight: bold;
color: #5a728a;
opacity: 0.8;
text-align: left;
line-height: 21px;
}
@@ -40,7 +41,7 @@
height: 20px;
}
.header {
padding-left: 10px;
padding-left: 20px;
padding-bottom: 28px;
padding-top: 28px;
margin-top: -10px;

View File

@@ -1,5 +1,5 @@
<div class="content-body">
<app-breadcrumbs *ngIf="router.url !== '/dashboard'"></app-breadcrumbs>
<router-outlet></router-outlet>
<div style="height: 65px;"></div>
</div>
<div style="height: 90px;"></div>
</div>