Unverified Commit 9b2e1850 authored by Ruslan Konviser's avatar Ruslan Konviser Committed by GitHub
Browse files

Merge pull request #1260 from ever-co/feat/#806-mobileshop-search

Feat/#806 mobileshop search
parents 94efe627 49e92e62
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
<div class="app-loading-container">
	<ion-spinner></ion-spinner>
	<ion-spinner [color]="color"></ion-spinner>
</div>
+2 −1
Original line number Diff line number Diff line
import { Component } from '@angular/core';
import { Component, Input } from '@angular/core';

@Component({
	selector: 'e-cu-loading',
@@ -6,6 +6,7 @@ import { Component } from '@angular/core';
	templateUrl: 'loading.component.html',
})
export class LoadingComponent {
	@Input() color: string;
	text: string;

	constructor() {
+94 −81
Original line number Diff line number Diff line
<div class="brand-dark search-products">
<div class="brand-light search-products">
	<ion-searchbar
		[(ngModel)]="searchInput"
		mode="ios"
		color="light"
		placeholder="Product or a shop name"
		[placeholder]="'SEARCH_VIEW.SEARCH_PLACEHOLDER' | translate"
		animated
		debounce="500"
		(ionChange)="loadFullData()"
		(ionChange)="onSearchChange()"
	></ion-searchbar>

	<ion-list class="brand-dark">
		<div class="products-container">
	<e-cu-loading color="light" *ngIf="isLoading"></e-cu-loading>
	<ion-list class="brand-light" *ngIf="!isLoading">
		<h5
			*ngIf="
				searchResultProducts.length === 0 &&
@@ -18,57 +18,68 @@
			"
			class="not-found-text p-3"
		>
				Empty List
			{{ 'SEARCH_VIEW.EMPTY_LIST' | translate }}
		</h5>

			<ion-item
				color="medium"
				*ngFor="let merchant of searchResultMerchants"
				lines="none"
				class="m-2"
			>
				<ion-avatar slot="start">
		<ion-card *ngFor="let merchant of searchResultMerchants">
			<ion-row class="merchant-container">
				<ion-col class="col-2 logo-container">
					<ion-avatar>
						<img [src]="merchant.logo" />
					</ion-avatar>
				<ion-label>{{ merchant.name }} </ion-label>
			</ion-item>
				</ion-col>
				<ion-col class="col-8">
					<span class="merchant-name">{{ merchant.name }}</span>
				</ion-col>
				<ion-col class="col-2 is-open">
					<span [ngClass]="merchant.isActive ? 'open' : 'close'">
						{{
							merchant.isActive
								? ('SEARCH_VIEW.OPEN' | translate)
								: ('SEARCH_VIEW.CLOSED' | translate)
						}}
					</span>
				</ion-col>
			</ion-row>
		</ion-card>

		<div class="center" *ngIf="searchResultMerchants.length">
			<ion-button
					color="tertiary"
				[disabled]="merchantsShowMoreButton"
				color="secondary"
				size="small"
					(click)="loadMoreMerchants()"
					>View More</ion-button
				(click)="loadMerchants(false)"
			>
				{{ 'SEARCH_VIEW.VIEW_MORE' | translate }}
			</ion-button>
		</div>

			<ion-card *ngFor="let productInfo of searchResultProducts">
				<ion-row
					class="product-container"
		<ion-card
			*ngFor="let productInfo of searchResultProducts"
			(click)="goToDetailsPage(productInfo)"
			class="product-container"
		>
			<ion-row>
				<ion-col class="col-3 image-container">
					<img [src]="getProductImage(productInfo)" />
				</ion-col>
					<ion-col class="col-9" class="col-9 info-container">
				<ion-col class="col-9 info-container">
					<ion-row>
							<ion-col>
						<ion-col class="col-10">
							<span class="product-title">
								{{
									localeTranslate(
											productInfo.warehouseProduct
												.product['title']
										productInfo.warehouseProduct.product[
											'title'
										]
									)
								}}
							</span>
						</ion-col>
							<ion-col class="col-3 price">
								<ion-badge color="primary"
									>{{
										productInfo.warehouseProduct.price
									}}
									$</ion-badge
								>
						<ion-col class="col-2 price">
							<ion-badge color="primary">
								{{ productInfo.warehouseProduct.price }}$
							</ion-badge>
						</ion-col>
					</ion-row>
					<ion-row>
@@ -76,8 +87,9 @@
							{{
								shortenDescription(
									localeTranslate(
											productInfo.warehouseProduct
												.product['description']
										productInfo.warehouseProduct.product[
											'description'
										]
									)
								)
							}}
@@ -89,12 +101,13 @@

		<div class="center" *ngIf="searchResultProducts.length">
			<ion-button
					color="tertiary"
				[disabled]="productsShowMoreButton"
				color="secondary"
				size="small"
					(click)="loadMoreProducts()"
					>View More</ion-button
				(click)="loadProducts(false)"
			>
			</div>
				{{ 'SEARCH_VIEW.VIEW_MORE' | translate }}
			</ion-button>
		</div>
	</ion-list>
</div>
+58 −82
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ import { ILocaleMember } from '@modules/server.common/interfaces/ILocale';

import { environment } from 'environments/environment';
import { GeoLocationProductsService } from 'app/services/geo-location/geo-location-products';
import { WarehouseProductsService } from 'app/services/merchants/warehouse-products';
import { ProductsService } from '../../../services/products.service';

@Component({
	selector: 'search-products',
@@ -26,13 +26,17 @@ import { WarehouseProductsService } from 'app/services/merchants/warehouse-produ
})
export class SearchProductsComponent implements OnInit {
	private static MAX_DESCRIPTION_LENGTH: number = 100;
	private loadingOrdersLimit: number = 6;
	private loadingProductsLimit: number = 10;
	private loadingMerchantsLimit: number = 5;

	isLoading: boolean = true;
	searchInput: string = '';
	searchResultMerchants: Warehouse[] = [];
	searchResultProducts: ProductInfo[] = [];
	getOrdersGeoObj: { loc: ILocation };
	geoObj: { loc: ILocation };

	merchantsShowMoreButton: boolean = false;
	productsShowMoreButton: boolean = false;

	constructor(
		private merchantsService: MerchantsService,
@@ -42,7 +46,7 @@ export class SearchProductsComponent implements OnInit {
		private geoLocationProductsService: GeoLocationProductsService,
		private router: Router,
		private readonly translateProductLocales: ProductLocalesService,
		private warehouseProductsService: WarehouseProductsService
		private productsService: ProductsService
	) {}
	ngOnInit() {
		this.loadFullData();
@@ -50,78 +54,70 @@ export class SearchProductsComponent implements OnInit {

	async loadFullData() {
		await this.loadGeoLocationProducts();
		this.loadMerchants();
		this.loadProducts();
		this.onSearchChange();
	}

	async loadMerchants() {
		const location = this.getOrdersGeoObj.loc;
		const merchants = await this.merchantsService.getMerchantsBuyName(
			this.searchInput,
			{ loc: location }
		);
		this.searchResultMerchants = merchants.slice(0, 5);
	async onSearchChange() {
		this.isLoading = true;
		await this.loadMerchants(true);
		await this.loadProducts(true);
		this.isLoading = false;
	}
	async loadMoreMerchants() {
		const location = this.getOrdersGeoObj.loc;

	async loadMerchants(isNewList) {
		const location = this.geoObj.loc;
		const merchants = await this.merchantsService.getMerchantsBuyName(
			this.searchInput,
			{ loc: location }
		);
		merchants

		isNewList
			? (this.searchResultMerchants = merchants.slice(
					0,
					this.loadingMerchantsLimit
			  ))
			: merchants
					.slice(
						this.searchResultMerchants.length,
				this.searchResultMerchants.length + this.loadingMerchantsLimit
						this.searchResultMerchants.length +
							this.loadingMerchantsLimit
					)
					.map((merch) => this.searchResultMerchants.push(merch));
		this.merchantsShowMoreButton =
			merchants.length === this.searchResultMerchants.length;
	}

	async loadProducts() {
		const isDeliveryRequired =
			this.store.deliveryType === DeliveryType.Delivery;
		const isTakeaway = this.store.deliveryType === DeliveryType.Takeaway;
	async loadProducts(isNewList) {
		const options = {
			isDeliveryRequired:
				this.store.deliveryType === DeliveryType.Delivery,
			isTakeaway: this.store.deliveryType === DeliveryType.Takeaway,
		};

		await this.geoLocationProductsService
			.geoLocationProductsByPaging(
				this.getOrdersGeoObj,
				this.geoObj,
				{
					limit: this.loadingOrdersLimit,
					skip: 0,
				},
				{
					isDeliveryRequired,
					isTakeaway,
					limit: this.loadingProductsLimit,
					skip: isNewList ? 0 : this.searchResultProducts.length,
				},
				options,
				this.searchInput
			)
			.pipe(first())
			.subscribe((products) => {
				this.searchResultProducts = products;
				// products.map((pr) => this.searchResultProducts.push(pr));
				isNewList
					? (this.searchResultProducts = products)
					: products.map((pr) => this.searchResultProducts.push(pr));
			});
	}
	async loadMoreProducts() {
		const isDeliveryRequired =
			this.store.deliveryType === DeliveryType.Delivery;
		const isTakeaway = this.store.deliveryType === DeliveryType.Takeaway;

		await this.geoLocationProductsService
			.geoLocationProductsByPaging(
				this.getOrdersGeoObj,
				{
					limit: this.loadingOrdersLimit,
					skip: this.searchResultProducts.length,
				},
				{
					isDeliveryRequired,
					isTakeaway,
				},
		const orderCount = await this.geoLocationProductsService.getCountOfGeoLocationProducts(
			this.geoObj,
			options,
			this.searchInput
			)
			.pipe(first())
			.subscribe((products) => {
				products.map((pr) => this.searchResultProducts.push(pr));
			});
		);
		this.productsShowMoreButton =
			orderCount === this.searchResultProducts.length;
	}

	private async loadGeoLocationProducts() {
@@ -146,7 +142,7 @@ export class SearchProductsComponent implements OnInit {
		}

		if (geoLocationForProducts) {
			this.getOrdersGeoObj = {
			this.geoObj = {
				loc: {
					type: 'Point',
					coordinates: geoLocationForProducts.loc.coordinates,
@@ -154,6 +150,7 @@ export class SearchProductsComponent implements OnInit {
			};
		}
	}

	localeTranslate(member: ILocaleMember[]): string {
		return this.translateProductLocales.getTranslate(member);
	}
@@ -166,36 +163,15 @@ export class SearchProductsComponent implements OnInit {
					SearchProductsComponent.MAX_DESCRIPTION_LENGTH - 3
			  ) + '...';
	}

	getProductImage(product) {
		return this.localeTranslate(product.warehouseProduct.product['images']);
	}

	async goToDetailsPage(product: ProductInfo) {
		const prod = await this.warehouseProductsService.getWarehouseProduct(
			product.warehouseId,
			product.warehouseProduct.id
		await this.productsService.goToDetailsPage(
			product,
			this.searchResultProducts
		);

		if (prod) {
			this.router.navigate(
				[
					`/products/product-details/${product.warehouseProduct.product['id']}`,
				],
				{
					queryParams: {
						backUrl: '/products',
						warehouseId: product.warehouseId,
					},
				}
			);
		} else {
			const loadedProduct = this.searchResultProducts.find(
				(p) => p.warehouseProduct.id === product.warehouseProduct.id
			);

			if (loadedProduct) {
				loadedProduct['soldOut'] = true;
			}
		}
	}
}
+2 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ import { TranslateModule } from '@ngx-translate/core';

import { WarehouseLogoModule } from '../../../components/warehouse-logo/warehouse-logo.module';
import { ProductsModule } from '../../../components/products/products.module';
import { LoadingModule } from '../../../components/loading/loading.module';

@NgModule({
	imports: [
@@ -20,6 +21,7 @@ import { ProductsModule } from '../../../components/products/products.module';
		WarehouseLogoModule,
		ProductsModule,
		TranslateModule.forChild(),
		LoadingModule,
	],
	declarations: [SearchProductsComponent],
	exports: [SearchProductsComponent],
Loading