перевел некоторые элементы с primeNG на AngularMaterial, так как в первое случае были баги с айфоном
добавил отображение описания в карточке товара,
подтянул имя при оформлении заказа,
добавил список терминалов при оформлении заказа, по умолчанию показывается активный терминал в disabled=true
This commit is contained in:
gofnnp 2023-02-27 22:49:33 +04:00
parent ed2e73e598
commit 10ad10d922
26 changed files with 646 additions and 321 deletions

View File

@ -34,7 +34,7 @@
"src/firebase-messaging-sw.js"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/mdb-angular-ui-kit/assets/scss/mdb.scss",
"node_modules/primeng/resources/themes/saga-blue/theme.css",
"node_modules/primeicons/primeicons.css",
@ -114,7 +114,7 @@
"src/manifest.webmanifest"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []

View File

@ -1,23 +1,20 @@
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { PrimeNGConfig } from 'primeng/api';
import * as ConfigActions from './state/config/config.actions'
import * as ConfigActions from './state/config/config.actions';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
title = 'Sakura';
constructor(private primengConfig: PrimeNGConfig,
private store: Store) {}
constructor(private primengConfig: PrimeNGConfig, private store: Store) {}
ngOnInit() {
this.primengConfig.ripple = true;
this.primengConfig.ripple = false;
this.store.dispatch(ConfigActions.getConfig());
}
}

View File

@ -60,6 +60,15 @@ import {MatTabsModule} from '@angular/material/tabs';
import { ModifierComponent } from './components/modifier/modifier.component';
import { OptionComponent } from './components/option/option.component';
import { ChangeQuantityOptionDirective } from './directives/change-quantity-option.directive';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule, MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
import { TerminalListComponent } from './components/terminal-list/terminal-list.component';
import {MatButtonModule} from '@angular/material/button';
import {MatListModule} from '@angular/material/list';
import {MatSnackBarModule} from '@angular/material/snack-bar';
import { SnackBarComponent } from './components/snack-bar/snack-bar.component';
const routes: Routes = [
{ path: '', redirectTo: 'products', pathMatch: 'full' },
@ -95,7 +104,9 @@ const routes: Routes = [
MenuComponent,
ModifierComponent,
OptionComponent,
ChangeQuantityOptionDirective
ChangeQuantityOptionDirective,
TerminalListComponent,
SnackBarComponent
],
imports: [
BrowserModule,
@ -137,9 +148,16 @@ const routes: Routes = [
InputTextModule,
SidebarModule,
RippleModule,
MatTabsModule
MatTabsModule,
MatSelectModule,
MatFormFieldModule,
MatInputModule,
MatBottomSheetModule,
MatButtonModule,
MatListModule,
MatSnackBarModule
],
providers: [DialogService, MessageService, MessagingService ],
providers: [DialogService, MessageService, MessagingService, {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}} ],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,3 +1,5 @@
@import "src/sass/mixins";
:host {
.main-menu-container {
// position: fixed;
@ -41,7 +43,7 @@
}
}
&:hover {
@include hover-supported() {
color: #252525;
a {

View File

@ -8,7 +8,7 @@
}}"
alt="{{ product.name }}"
/>
<p class="product-modal__description">Lorem ipsum dolor sit amet consectetur adipisicing elit. Quasi quas libero consequatur dolorum itaque dolor deserunt aut facilis debitis!</p>
<p class="product-modal__description">{{product.description}}</p>
</div>
<div

View File

@ -0,0 +1,16 @@
<div class="snack-bar">
<span>{{ data.text }}</span>
<div class="buttons">
<button mat-stroked-button matSnackBarAction (click)="buttonClick('no')">
Нет
</button>
<button
mat-stroked-button
matSnackBarAction
color="error"
(click)="buttonClick('yes')"
>
Да
</button>
</div>
</div>

View File

@ -0,0 +1,18 @@
:host {
.snack-bar {
display: flex;
align-items: center;
justify-content: space-between;
span {
color: #323232;
}
.buttons {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: nowrap;
flex-direction: row;
gap: 8px;
}
}
}

View File

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

View File

@ -0,0 +1,23 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar';
@Component({
selector: 'app-snack-bar',
templateUrl: './snack-bar.component.html',
styleUrls: ['./snack-bar.component.scss']
})
export class SnackBarComponent implements OnInit {
constructor(@Inject(MAT_SNACK_BAR_DATA) public data: {text: string}, private _matSnackBarRef: MatSnackBarRef<SnackBarComponent>) { }
ngOnInit(): void {
}
buttonClick(result: 'yes' | 'no') {
if (result === 'yes') {
this._matSnackBarRef.dismissWithAction()
} else {
this._matSnackBarRef.dismiss()
}
}
}

View File

@ -0,0 +1,13 @@
<mat-nav-list>
<div
mat-list-item
*ngFor="let item of data.list"
[ngClass]="{
'list-item': true,
active: item.id === data.active.id
}"
(click)="selectItem(item)"
>
<span matListItemTitle>{{ item.label }}</span>
</div>
</mat-nav-list>

View File

@ -0,0 +1,21 @@
@import 'src/sass/mixins';
:host {
.list-item {
padding: 12px;
font-size: 14px;
border-radius: 6px;
transition: all .4s ease;
text-align: center;
font-weight: 600;
cursor: pointer;
&.active {
background-color: var(--orange-main);
color: #fff;
}
@include hover-supported() {
background-color: #eee;
color: #000;
}
}
}

View File

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

View File

@ -0,0 +1,25 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MatBottomSheetRef, MAT_BOTTOM_SHEET_DATA } from '@angular/material/bottom-sheet';
export interface IListData {
list: Array<any>;
active: any;
}
@Component({
selector: 'app-terminal-list',
templateUrl: './terminal-list.component.html',
styleUrls: ['./terminal-list.component.scss']
})
export class TerminalListComponent implements OnInit {
constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: IListData, private _bottomSheetRef: MatBottomSheetRef<TerminalListComponent>) { }
ngOnInit(): void {
}
selectItem(item: any) {
this._bottomSheetRef.dismiss(item)
}
}

View File

@ -1,26 +1,69 @@
<div *ngIf="mainFormGroup && !loading; else loadingEl" class="woocommerce-shipping-fields__field-wrapper">
<form *ngIf="!showAuthoriztion; else authEl" (ngSubmit)="submit()" [formGroup]="mainFormGroup" action="false" autocomplete="on">
<h2 class="order_form__title">Оформление заказа</h2>
<p *ngIf="hasError" class="request-error-message">
Произошла ошибка. Попробуйте позже.
<div
*ngIf="mainFormGroup && !loading; else loadingEl"
class="woocommerce-shipping-fields__field-wrapper"
>
<form
*ngIf="!showAuthoriztion; else authEl"
(ngSubmit)="submit()"
[formGroup]="mainFormGroup"
action="false"
autocomplete="on"
>
<h2 class="order_form__title">Оформление заказа</h2>
<p *ngIf="hasError" class="request-error-message">
Произошла ошибка. Попробуйте позже.
</p>
<div class="order_form" formGroupName="userDataForm">
<p сlass="form-row form-row-wide">
<input
formControlName="first_name"
id="first_name"
pInputText
placeholder="Ваше имя"
type="text"
/>
</p>
<div *ngIf="deliverData.deliveryType?.name === 'Доставка'">
<p сlass="form-row form-row-last">
<input
formControlName="flat"
id="flat"
pInputText
placeholder="Квартира"
type="number"
min="1"
/>
</p>
<div class="order_form" formGroupName="userDataForm">
<p сlass="form-row form-row-wide">
<input formControlName="first_name" id="first_name" pInputText placeholder="Ваше имя" type="text">
</p>
<div *ngIf="deliverData.deliveryType?.name === 'Доставка'">
<p сlass="form-row form-row-last">
<input formControlName="flat" id="flat" pInputText placeholder="Квартира" type="number" min="1">
</p>
<p сlass="form-row form-row-wide">
<input formControlName="street" id="street" pInputText placeholder="Улица" type="text">
</p>
<p сlass="form-row form-row-first">
<input formControlName="house" id="house" pInputText placeholder="Номер дома" type="text">
</p>
</div>
<label class="terminal-list-label">Пункты самовывоза</label>
<div *ngIf="deliverData.deliveryType?.name === 'Самовывоз'" class="terminal-list-container">
<p сlass="form-row form-row-wide">
<input
formControlName="street"
id="street"
pInputText
placeholder="Улица"
type="text"
/>
</p>
<p сlass="form-row form-row-first">
<input
formControlName="house"
id="house"
pInputText
placeholder="Номер дома"
type="text"
/>
</p>
</div>
<p сlass="form-row form-row-wide">
<label class="terminal-list-label">Пункты самовывоза</label>
<p-dropdown
*ngIf="deliverData.deliveryType?.name === 'Самовывоз'"
[options]="terminalList"
formControlName="selectedTerminal"
placeholder="Пункты выдачи"
optionLabel="label"
></p-dropdown>
</p>
<!-- <div *ngIf="deliverData.deliveryType?.name === 'Самовывоз'" class="terminal-list-container">
<div *ngFor="let terminal of terminalList" [ngClass]="{
'terminal-container': true,
'selected': terminal.label === selectedTerminal.label
@ -29,52 +72,88 @@
<span class="terminal-container__name">{{terminal.label}}</span>
<span class="terminal-container__address">{{terminal.address}}</span>
</div>
</div>
</div>
<div formGroupName="deliveryDataForm">
<p сlass="form-row form-row-wide">
<p-dropdown [options]="deliveryTypes" formControlName="deliveryType" placeholder="Доставка"
optionLabel="name" (onChange)="changeDeliveryType($event)"></p-dropdown>
</p>
<p сlass="form-row form-row-wide">
<label id="deliveryDate">Время выдачи</label>
<p-calendar formControlName="deliveryDate" [showTime]="true" [showSeconds]="false"
[touchUI]="true" inputId="time" placeholder="Время выдачи" dateFormat="dd.mm.yy"></p-calendar>
</p>
<p сlass="form-row form-row-wide">
<p-dropdown [options]="paymentMethods" formControlName="paymentMethod" optionLabel="label" placeholder="Тип оплаты">
</p-dropdown>
<!-- *Оплата бонусами -->
</p>
<!-- <p сlass="form-row form-row-last">
</div> -->
</div>
<div formGroupName="deliveryDataForm">
<p сlass="form-row form-row-wide">
<label class="terminal-list-label">Способ получения</label>
<p-dropdown
[options]="deliveryTypes"
formControlName="deliveryType"
placeholder="Доставка"
optionLabel="name"
(onChange)="changeDeliveryType($event)"
></p-dropdown>
</p>
<p сlass="form-row form-row-wide">
<label id="deliveryDate">Время выдачи</label>
<p-calendar
formControlName="deliveryDate"
[showTime]="true"
[showSeconds]="false"
[touchUI]="true"
inputId="time"
placeholder="Время выдачи"
dateFormat="dd.mm.yy"
></p-calendar>
</p>
<p сlass="form-row form-row-wide">
<p-dropdown
[options]="paymentMethods"
formControlName="paymentMethod"
optionLabel="label"
placeholder="Тип оплаты"
>
</p-dropdown>
<!-- *Оплата бонусами -->
</p>
<!-- <p сlass="form-row form-row-last">
<input [maxLength]="255" id="promo-code" pInputText placeholder="Промокод" type="text">
</p> -->
<p сlass="form-row form-row-wide">
<textarea [maxLength]="255" cols="30" formControlName="comment" pInputTextarea placeholder="Комментарий"
rows="1"></textarea>
</p>
</div>
<div class="bootom-info">
<div class="subtotal">
<span class="products-count">Товаров: {{order.products.length}}</span>
<span class="woocommerce-Price-amount amount"><bdi><span
class="woocommerce-Price-currencySymbol">{{order.products[0].currency_symbol}}</span>{{order.price}}</bdi></span>
</div>
<button
class="elementor-button elementor-button--checkout elementor-size-md">
<span class="elementor-button-text">Оформить заказ</span>
</button>
</div>
<p *ngIf="showMyMessage" style="color: red; font-size: 20px">Такой адрес не найден! Введите правильный адрес</p>
</form>
<p сlass="form-row form-row-wide">
<textarea
[maxLength]="255"
cols="30"
formControlName="comment"
pInputTextarea
placeholder="Комментарий"
rows="1"
></textarea>
</p>
</div>
<div class="bootom-info">
<div class="subtotal">
<span class="products-count">Товаров: {{ order.products.length }}</span>
<span class="woocommerce-Price-amount amount"
><bdi
><span class="woocommerce-Price-currencySymbol">{{
order.products[0].currency_symbol
}}</span
>{{ order.price }}</bdi
></span
>
</div>
<button
class="elementor-button elementor-button--checkout elementor-size-md"
>
<span class="elementor-button-text">Оформить заказ</span>
</button>
</div>
<p *ngIf="showMyMessage" style="color: red; font-size: 20px">
Такой адрес не найден! Введите правильный адрес
</p>
</form>
</div>
<ng-template #loadingEl>
<div class="angular-spinner-container" style="width: fit-content; height: 100%; margin: 16px auto;">
<p-progressSpinner styleClass="angular-spinner"></p-progressSpinner>
</div>
<div
class="angular-spinner-container"
style="width: fit-content; height: 100%; margin: 16px auto"
>
<p-progressSpinner styleClass="angular-spinner"></p-progressSpinner>
</div>
</ng-template>
<ng-template #authEl>
<app-auth (phoneConfirmed)="phoneConfirmed()"></app-auth>
</ng-template>
<app-auth (phoneConfirmed)="phoneConfirmed()"></app-auth>
</ng-template>

View File

@ -1,6 +1,6 @@
:host {
.woocommerce-shipping-fields__field-wrapper {
margin: 8px auto 0 auto;
margin: 8px auto 100px auto;
max-width: 400px;
}

View File

@ -16,6 +16,7 @@ import { Order } from 'src/app/models/order';
import { Store } from '@ngrx/store';
import * as fromConfig from '../../state/config/config.reducer'
import { lastValueFrom } from 'rxjs';
import { GetTerminalsService } from 'src/app/services/get-terminals.service';
@ -43,6 +44,7 @@ export class UserDataOrderComponent implements OnInit, OnDestroy {
private intervalTimeDelivery!: any
public userData: UserData = {
name: '',
first_name: null,
last_name: null,
street: null,
@ -50,6 +52,7 @@ export class UserDataOrderComponent implements OnInit, OnDestroy {
flat: null,
city: this.cities[0],
phone: null,
selectedTerminal: null
};
public deliverData: DeliveryData = {
deliveryDate: null,
@ -75,14 +78,14 @@ export class UserDataOrderComponent implements OnInit, OnDestroy {
private wpJsonService: WpJsonService,
private http: HttpClient,
private cookiesService: CookiesService,
private store: Store
private store: Store,
private _getTerminals: GetTerminalsService
) { }
async ngOnInit() {
this.checkAuthorization(true)
this._createMainForm();
this.getTerminalList();
this.selectedTerminal = JSON.parse(this.cookiesService.getItem('selectedTerminal') || '')
this.checkoutConfig$.subscribe({
next: (value: any) => {
this.checkoutConfig = value
@ -99,21 +102,18 @@ export class UserDataOrderComponent implements OnInit, OnDestroy {
}
getTerminalList() {
this.http.get('./assets/terminal_list1.json').subscribe({
next: (value) => {
this.terminalList = value
},
error: (err) => {
console.error(err);
}
})
// this.wpJsonService.getTerminalList().subscribe({
async getTerminalList() {
// this.http.get('./assets/terminal_list1.json').subscribe({
// next: (value) => {
// this.terminalList = value
// },
// error: (err) => {
// console.error(err);
// }
// })
const _getTerminals = await this._getTerminals.getTerminalList()
this.terminalList = _getTerminals.list
this.selectedTerminal = _getTerminals.active
}
checkAuthorization(showAuthoriztion: boolean, forced = false) {
@ -235,8 +235,9 @@ export class UserDataOrderComponent implements OnInit, OnDestroy {
// await this.autoCompleteService.setCity(this.userData.city);
const isSelfDelivery = this.deliverData.deliveryType?.name === "Самовывоз"
return this.fb.group({
selectedTerminal: [{ value: this.selectedTerminal, disabled: true }, []],
phone: [this.userData.phone],
first_name: [this.userData.first_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
first_name: [this.userData.name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
// last_name: [this.userData.last_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
street: [{ value: this.userData.street, disabled: isSelfDelivery }, isSelfDelivery ?? [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
house: [{ value: this.userData.house, disabled: isSelfDelivery }, isSelfDelivery ?? [Validators.required, Validators.maxLength(10), Validators.pattern('^\\d+[-|\\d]+\\d+$|^\\d*$')]],
@ -250,7 +251,7 @@ export class UserDataOrderComponent implements OnInit, OnDestroy {
this.deliverData.deliveryType = this.deliveryTypes[0];
return this.fb.group({
deliveryDate: [{ value: this.deliverData.deliveryDate, disabled: this.checkoutConfig.timeDelivery.changeTime.disabled }, []],
deliveryType: [{ value: this.deliverData.deliveryType, disabled: this.checkoutConfig.delivery.disabled }, [Validators.required]],
deliveryType: [{ value: this.deliverData.deliveryType, disabled: this.checkoutConfig.delivery.disabled || this.deliveryTypes.length < 2 }, [Validators.required]],
paymentMethod: [{ value: this.deliverData.paymentMethod, disabled: this.checkoutConfig.payments.disabled }, [Validators.required]],
persons: [this.deliverData.persons, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
comment: [this.deliverData.comment, [Validators.maxLength(255),]]

View File

@ -251,4 +251,11 @@ export interface UserData {
flat: string | null;
city: string;
phone: string | null;
selectedTerminal: ITerminal | null;
name: string
}
export interface ITerminal {
label: string;
id: string;
}

View File

@ -62,7 +62,7 @@ export class Order {
city: ''
},
amount: this.price,
terminal_id: this.terminal_id
terminal_id: this.userData?.selectedTerminal?.id || this.terminal_id
},
}
}

View File

@ -1,14 +1,25 @@
import { Component, EventEmitter, HostListener, OnInit, Output } from '@angular/core';
import {
Component,
EventEmitter,
HostListener,
OnInit,
Output,
} from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MessageService } from 'primeng/api';
import { SnackBarComponent } from 'src/app/components/snack-bar/snack-bar.component';
import { Order } from 'src/app/models/order';
import { OrderProduct } from 'src/app/models/order-product';
import { CartService, ProductAmountAction } from 'src/app/services/cart.service';
import {
CartService,
ProductAmountAction,
} from 'src/app/services/cart.service';
import { OrderService } from 'src/app/services/order.service';
@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.scss']
styleUrls: ['./cart.component.scss'],
})
export class CartComponent implements OnInit {
@Output() showAuthoriztion = new EventEmitter<boolean>();
@ -24,25 +35,26 @@ export class CartComponent implements OnInit {
private orderService: OrderService,
private cartService: CartService,
private messageService: MessageService,
) { }
private _snackBar: MatSnackBar
) {}
ngOnInit(): void {
this.width = window.innerWidth;
this.changeDullScreenMode()
this.loadCart()
this.changeDullScreenMode();
this.loadCart();
}
// Изменение размера окна
@HostListener('window:resize', ['$event'])
onResize(event: any) {
this.width = event.target.innerWidth;
this.changeDullScreenMode()
this.width = event.target.innerWidth;
this.changeDullScreenMode();
}
toggleSideBar(): void{
toggleSideBar(): void {
this.visibleSidebar = !this.visibleSidebar;
this.orderConfirmed = false;
this.loadCart()
this.loadCart();
}
hide() {
@ -51,25 +63,25 @@ export class CartComponent implements OnInit {
changeDullScreenMode() {
if (this.width < 650) {
this.isFullScreen = true
this.isFullScreen = true;
} else {
this.isFullScreen = false
this.isFullScreen = false;
}
}
async loadCart(): Promise<void> {
this.loading = true;
this.order = await this.orderService.getOrder(true);
if (this.order) this.price = this.order.price
this.loading = false;
if (this.order) this.price = this.order.price;
this.loading = false;
}
removeFromCart(event: Event, guid: string): void{
removeFromCart(event: Event, guid: string): void {
event.preventDefault();
this.orderService.removeFromCart(guid);
}
confirmOrder(event: Event): void{
confirmOrder(event: Event): void {
event.preventDefault();
this.showAuthoriztion.emit(true);
this.orderConfirmed = true;
@ -78,23 +90,42 @@ export class CartComponent implements OnInit {
setAmount(product: OrderProduct, method: 'plus' | 'minus') {
if (method === 'plus') {
this.cartService.changeAmountProduct(product.guid, ProductAmountAction.increment)
product.amount++
this.cartService.changeAmountProduct(
product.guid,
ProductAmountAction.increment
);
product.amount++;
this.price = this.price + Number(product.price);
} else if (method === 'minus' && product.amount > 1) {
this.cartService.changeAmountProduct(product.guid, ProductAmountAction.decrement)
product.amount--
this.cartService.changeAmountProduct(
product.guid,
ProductAmountAction.decrement
);
product.amount--;
this.price = this.price - Number(product.price);
}
}
orderSubmitted(orderid: number) {
this.visibleSidebar = false
this.messageService.add({ severity: 'success', summary: `Заказ оформлен! Номер заказа: ${orderid}` });
this.visibleSidebar = false;
this._snackBar.open(`Заказ оформлен! Номер заказа: ${orderid}`, 'Ок')
}
confirmClearCart() {
this.messageService.add({ key: 'c', sticky: true, severity: 'warn', summary: 'Вы уверены, что хотите очистить корзину?' });
const snackBar = this._snackBar.openFromComponent(SnackBarComponent, {
duration: 4000,
data: {
text: 'Очистить корзину?',
},
});
snackBar.afterDismissed().subscribe(({ dismissedByAction }) => {
if (dismissedByAction) {
this.cartService.clearCart();
this.loadCart();
this.visibleSidebar = false;
}
});
// this.messageService.add({ key: 'c', sticky: true, severity: 'warn', summary: 'Вы уверены, что хотите очистить корзину?' });
}
onReject() {
@ -102,10 +133,9 @@ export class CartComponent implements OnInit {
}
onConfirm() {
this.cartService.clearCart()
this.loadCart()
this.visibleSidebar = false
this.cartService.clearCart();
this.loadCart();
this.visibleSidebar = false;
this.messageService.clear('c');
}
}

View File

@ -1,9 +1,6 @@
<p-toast position="top-center"></p-toast>
<div *ngIf="!loading; else loadingEl" class="products-container">
<div class="products-container__filters-container">
<!-- <label>Категория</label>
<p-treeSelect *ngIf="groups" [(ngModel)]="selectedGroup" [options]="groups"
(onNodeSelect)="changeGroup()" (onNodeUnselect)="onGroupUnselect($event)" placeholder="Группа"></p-treeSelect> -->
<div class="products-container__categories-container">
<div
*ngFor="let group of groups; let first = first"
@ -19,20 +16,10 @@
</span>
</div>
</div>
<p-treeSelect
*ngIf="terminalList"
[(ngModel)]="selectedTerminal"
[options]="terminalList"
(onNodeSelect)="changeTerminal()"
(onNodeUnselect)="onTerminalUnselect($event)"
placeholder="Пункт самовывоза"
[disabled]="!!cartService.cartCount"
></p-treeSelect>
<!-- <label>Пункт выдачи</label> -->
<button mat-stroked-button [disabled]="!!cartService.cartCount" (click)="showTerminals()">
{{ selectedTerminal.label }}
</button>
</div>
<!-- <p-paginator [style]="{'background': 'transparent', padding: '8px 0'}" [alwaysShow]="false" [first]="currentPage" [rows]="10"
[totalRecords]="filterByGroup().length" (onPageChange)="onPageChange($event)" [pageLinkSize]="3"
[showPageLinks]="true"></p-paginator> -->
<div
*ngIf="selectedGroup && selectedGroup.label !== 'Все'"
class="products-container__category-name-container"
@ -72,7 +59,7 @@
<p>{{ product.name }}</p>
</div>
<p class="products-container__product-composition">
{{product.description}}
{{ product.description }}
</p>
<div class="products-container__item-footer">
<span>{{ product.price }}₽</span>
@ -104,8 +91,7 @@
<p>{{ product.name }}</p>
</div>
<p class="products-container__product-composition">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio
consequuntur voluptates est.
{{ product.description }}
</p>
<div class="products-container__item-footer">
<span>{{ product.price }}₽</span>
@ -114,45 +100,6 @@
</div>
</div>
</ng-container>
<!-- <p-paginator [style]="{'background': 'transparent', padding: '8px 0'}" [alwaysShow]="false" [first]="currentPage" [rows]="10"
[totalRecords]="filterByGroup().length" (onPageChange)="onPageChange($event)" [pageLinkSize]="3"
[showPageLinks]="true"></p-paginator> -->
<p-toast
position="bottom-center"
key="c"
(onClose)="onReject()"
[baseZIndex]="5000"
>
<ng-template let-message pTemplate="message">
<div class="flex flex-column" style="flex: 1">
<div class="text-center">
<i class="pi pi-exclamation-triangle" style="font-size: 3rem"></i>
<h4>{{ message.summary }}</h4>
<p style="font-weight: 600">{{ message.detail }}</p>
</div>
<div class="grid p-fluid">
<div class="col-6">
<button
type="button"
pButton
(click)="onConfirm()"
label="Да"
class="p-button-success"
></button>
</div>
<div class="col-6">
<button
type="button"
pButton
(click)="onReject()"
label="Нет"
class="p-button-secondary"
></button>
</div>
</div>
</div>
</ng-template>
</p-toast>
</div>
<ng-template #loadingEl>
<div

View File

@ -1,3 +1,5 @@
@import 'src/sass/mixins';
:host {
.products-container {
width: 100%;
@ -53,7 +55,7 @@
font-size: 14px;
align-items: center;
overflow-x: auto;
width: 100%;
width: auto;
margin-right: 16px;
gap: 8px;
@ -93,7 +95,7 @@
border: none;
}
&:hover {
@include hover-supported() {
color: #fff;
background: var(--orange-main);
border: none;
@ -135,7 +137,7 @@
color: #fff;
transition: all 0.3s;
&:hover {
@include hover-supported() {
box-shadow: 0 0 10px 5px rgb(211 211 211);
}
}
@ -163,7 +165,7 @@
transition: all 0.3s;
}
&:hover {
@include hover-supported() {
scale: 1.01;
border: none;
@ -244,3 +246,11 @@
}
}
}
.mat-input-element {
cursor: pointer;
}
.mat-stroked-button {
border-radius: 1.125rem;
}

View File

@ -1,5 +1,5 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Group, Modifier, ModifiersGroup, Product } from "../../interface/data";
import { Group, Modifier, ModifiersGroup, Product } from '../../interface/data';
import { v4 as uuidv4 } from 'uuid';
import { DialogService } from 'primeng/dynamicdialog';
import { ProductModalComponent } from 'src/app/components/product-modal/product-modal.component';
@ -9,12 +9,16 @@ import { lastValueFrom } from 'rxjs';
import { CartService } from 'src/app/services/cart.service';
import { CookiesService } from 'src/app/services/cookies.service';
import { ActivatedRoute, Router } from '@angular/router';
import { MatSelectChange } from '@angular/material/select';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { TerminalListComponent } from 'src/app/components/terminal-list/terminal-list.component';
import { GetTerminalsService } from 'src/app/services/get-terminals.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.scss'],
providers: [MessageService]
providers: [MessageService],
})
export class ProductsComponent implements OnInit {
public products!: Product[];
@ -25,95 +29,60 @@ export class ProductsComponent implements OnInit {
public terminalList!: any;
public selectedTerminal!: any;
public loading: boolean = false;
public currentPage: number = 0
public currentPage: number = 0;
constructor(
public dialogService: DialogService,
private wpJsonService: WpJsonService,
private messageService: MessageService,
public cartService: CartService,
private cookiesService: CookiesService,
private router: Router,
private route: ActivatedRoute,
) { }
private _bottomSheet: MatBottomSheet,
private _getTerminals: GetTerminalsService
) {}
async ngOnInit() {
this.loading = true;
await this.getTerminalList()
this.getData()
this.messageService.add({
severity: 'info',
summary: 'В одном заказе могут быть товары только из выбранного пункта выдачи',
life: 5000
});
setTimeout(() => {
this.confirmTerminalList()
}, 0)
await this.getTerminalList();
this.getData();
// this.messageService.add({
// severity: 'info',
// summary: 'В одном заказе могут быть товары только из выбранного пункта выдачи',
// life: 5000
// });
this.loading = false;
}
async getTerminalList() {
const terminalList = (await lastValueFrom(
this.wpJsonService.getTerminalList()
))
this.terminalList = this.toTreeJson(this.keyValue(terminalList), terminalList)
const terminalFromCookie = JSON.parse(this.cookiesService.getItem('selectedTerminal') || 'null')
const conditionDelete = this.terminalList.find((terminal: any) => JSON.stringify(terminal) === JSON.stringify(terminalFromCookie))
if (!conditionDelete) {
this.cookiesService.deleteCookie('selectedTerminal')
}
this.selectedTerminal = JSON.parse(this.cookiesService.getItem('selectedTerminal') || 'null') || this.terminalList[0]
this.cartService.changeTerminal(this.selectedTerminal)
}
confirmTerminalList() {
if (this.cartService.cartCount) return
this.messageService.clear();
this.messageService.add({ key: 'c', sticky: true, severity: 'warn', summary: 'Вам подходит пункт выдачи?', detail: this.selectedTerminal.label });
}
onConfirm() {
this.messageService.add({
severity: 'info',
summary: 'В одном заказе могут быть товары только из выбранного пункта выдачи',
life: 5000
});
this.messageService.clear('c');
}
onReject() {
this.messageService.clear('c');
this.messageService.add({
severity: 'info',
summary: 'Выберите пункт выдачи. В одном заказе могут быть товары только из выбранного пункта.',
life: 6000
});
const _getTerminals = await this._getTerminals.getTerminalList()
this.terminalList = _getTerminals.list
this.selectedTerminal = _getTerminals.active
}
getData() {
this.wpJsonService.getAllData(`${this.selectedTerminal.label}${this.selectedTerminal.id}`).subscribe({
next: (value) => {
this.products = value.products
this.groups = value.groups
this.groups.unshift(
{
this.wpJsonService
.getAllData(`${this.selectedTerminal.label}${this.selectedTerminal.id}`)
.subscribe({
next: (value) => {
this.products = value.products;
this.groups = value.groups;
this.groups.unshift({
id: uuidv4(),
label: 'Все'
}
)
this.selectedGroup = this.groups[0]
this.modifiersGroups = value.modifiers_groups
this.modifiers = value.modifiers
label: 'Все',
});
this.selectedGroup = this.groups[0];
this.modifiersGroups = value.modifiers_groups;
this.modifiers = value.modifiers;
this.route.queryParams.subscribe((params) => {
if (params['group']) {
this.selectedGroup = this.groups.find((group) => group.label === params['group']) || this.groups[0]
}
})
}
})
this.route.queryParams.subscribe((params) => {
if (params['group']) {
this.selectedGroup =
this.groups.find((group) => group.label === params['group']) ||
this.groups[0];
}
});
},
});
}
onPageChange(event: any) {
@ -121,24 +90,37 @@ export class ProductsComponent implements OnInit {
}
filterByGroup(group?: Group) {
if (!this.selectedGroup) return []
if (!this.selectedGroup) return [];
if (this.selectedGroup.label === 'Все') {
if (group) return JSON.parse(JSON.stringify(this.products.filter((product) => product.groupId === group.id))).slice(0, 4)
return this.products
if (group)
return JSON.parse(
JSON.stringify(
this.products.filter((product) => product.groupId === group.id)
)
).slice(0, 4);
return this.products;
}
return JSON.parse(JSON.stringify(this.products.filter((product) => product.groupId === this.selectedGroup.id)))
return JSON.parse(
JSON.stringify(
this.products.filter(
(product) => product.groupId === this.selectedGroup.id
)
)
);
}
cropList(list: Array<any>, quantity: number) {
return list.slice(this.currentPage, this.currentPage + quantity)
return list.slice(this.currentPage, this.currentPage + quantity);
}
addToCart(event: MouseEvent, product: Product) {
if (event) {
event.preventDefault()
event.preventDefault();
}
const productModalWidth = product.modifiers_group.length ? '94vw' : '50vw'
const productModalMaxWidth = product.modifiers_group.length ? '1400px' : '500px'
const productModalWidth = product.modifiers_group.length ? '94vw' : '50vw';
const productModalMaxWidth = product.modifiers_group.length
? '1400px'
: '500px';
const ref = this.dialogService.open(ProductModalComponent, {
header: product.name,
width: 'fit-content',
@ -147,7 +129,7 @@ export class ProductsComponent implements OnInit {
'min-width': '300px',
'max-height': '90vh',
'border-radius': '1.125rem',
width: productModalWidth
width: productModalWidth,
},
contentStyle: {
'max-height': '90vh',
@ -158,67 +140,46 @@ export class ProductsComponent implements OnInit {
data: {
product: product,
modifiersGroups: this.modifiersGroups,
modifiers: this.modifiers
modifiers: this.modifiers,
},
baseZIndex: 10000,
autoZIndex: true,
dismissableMask: true,
closeOnEscape: true,
});
}
changeTerminal() {
setTimeout(() => {
this.products.length = 0;
this.loading = true;
this.getData()
this.cartService.changeTerminal(this.selectedTerminal);
this.loading = false;
this.router.navigate([]);
this.currentPage = 0
}, 0);
showTerminals() {
const bottomSheet = this._bottomSheet.open(TerminalListComponent, {
data: {
list: this.terminalList,
active: this.selectedTerminal,
},
ariaLabel: 'Список точек',
});
bottomSheet.afterDismissed().subscribe((selectedTerminal) => {
if (!selectedTerminal) return;
setTimeout(() => {
this.products.length = 0;
this.loading = true;
this.selectedTerminal = selectedTerminal;
this.getData();
this.cartService.changeTerminal(this.selectedTerminal);
this.loading = false;
this.router.navigate([]);
this.currentPage = 0;
}, 0);
});
}
changeGroup(group: Group) {
this.selectedGroup = group
this.selectedGroup = group;
this.router.navigate([], {
queryParams: {
group: group.label,
},
queryParamsHandling: 'merge',
});
this.currentPage = 0
this.currentPage = 0;
}
onGroupUnselect(event: any) {
setTimeout(() => {
this.selectedGroup = event.node
}, 0);
}
onTerminalUnselect(event: any) {
setTimeout(() => {
this.selectedTerminal = event.node
this.cartService.changeTerminal(this.selectedTerminal)
}, 0);
}
keyValue(obj: Object) {
return Object.keys(obj)
}
toTreeJson(array: Array<string>, terminalList: any): Array<any> {
let treeJson: Object[] = []
for (const key of array) {
treeJson.push(
{
label: key,
id: terminalList[key]
}
)
}
return treeJson
}
}

View File

@ -0,0 +1,76 @@
import { Injectable } from '@angular/core';
import { lastValueFrom } from 'rxjs';
import { ITerminal } from '../interface/data';
import { CartService } from './cart.service';
import { CookiesService } from './cookies.service';
import { WpJsonService } from './wp-json.service';
export interface IGetTerminalList {
list: ITerminal[];
active: ITerminal;
}
@Injectable({
providedIn: 'root'
})
export class GetTerminalsService {
public terminalList!: ITerminal[];
public selectedTerminal!: ITerminal;
constructor(
private wpJsonService: WpJsonService,
public cartService: CartService,
private cookiesService: CookiesService,
) { }
async getTerminalList(): Promise<IGetTerminalList> {
const terminalList = await lastValueFrom(
this.wpJsonService.getTerminalList()
);
this.terminalList = this.toTreeJson(
this.keyValue(terminalList),
terminalList
);
const terminalFromCookie = JSON.parse(
this.cookiesService.getItem('selectedTerminal') || 'null'
);
const conditionDelete = this.terminalList.find(
(terminal: any) =>
JSON.stringify(terminal) === JSON.stringify(terminalFromCookie)
);
if (!conditionDelete) {
this.cookiesService.deleteCookie('selectedTerminal');
}
const selectedTerminal = terminalFromCookie
? this.terminalList.find(
(value: any) => value.id === terminalFromCookie.id
)
: null;
if (!selectedTerminal) {
this.cartService.clearCart();
}
this.selectedTerminal = selectedTerminal || this.terminalList[0];
this.cartService.changeTerminal(this.selectedTerminal);
return {
list: this.terminalList,
active: this.selectedTerminal
}
}
toTreeJson(array: Array<string>, terminalList: any): Array<ITerminal> {
let treeJson: ITerminal[] = [];
for (const key of array) {
treeJson.push({
label: key,
id: terminalList[key],
});
}
return treeJson;
}
keyValue(obj: Object) {
return Object.keys(obj);
}
}

View File

@ -42,14 +42,20 @@ export class OrderService {
const cart = this.cartService.getCart();
if (cart.products.length) {
const token = this.cookiesService.getItem('token') || '';
const products = await this.getProducts(cart);
const additionalInfo = this.jsonRpcService.rpc(
{
method: 'getAdditionalInfo',
params: [],
},
RpcService.authService,
true
// const additionalInfo = this.jsonRpcService.rpc(
// {
// method: 'getAdditionalInfo',
// params: [],
// },
// RpcService.authService,
// true
// );
const additionalInfo = this.wpJsonService.getCustomerInfo(
environment.systemId,
token,
environment.icardProxy
);
const tokenData = this.jsonRpcService.rpc(
@ -64,14 +70,15 @@ export class OrderService {
const info = await lastValueFrom(
forkJoin([additionalInfo, tokenData, products])
);
const token = this.cookiesService.getItem('token');
const customer_info = info[0]?.customer_info
const terminal =
JSON.parse(
this.cookiesService.getItem('selectedTerminal') || 'null'
) || this.cartService.selectedTerminal$;
this.order = new Order({
products: products,
userData: info[0]?.data,
userData: customer_info,
phone: info[1].data?.mobile_number,
token: token,
terminal_id: terminal.id,
@ -95,6 +102,10 @@ export class OrderService {
const productSub = allData.products.find(
(product: any) => product.id === cart.products[i].id
);
if (!productSub) {
this.cartService.removeFromCart(cart.products[i].guid);
break;
}
const product = Object.assign(cloneDeep(cart.products[i]), {
category_id: 0,
price: productSub.price,

View File

@ -0,0 +1,7 @@
@mixin hover-supported {
@media not all and (pointer: coarse) {
&:hover {
@content;
}
}
}

View File

@ -3,6 +3,7 @@
@import '~@fortawesome/fontawesome-free/scss/solid.scss';
@import '~@fortawesome/fontawesome-free/scss/regular.scss';
@import '~@fortawesome/fontawesome-free/scss/brands.scss';
@import 'src/sass/mixins';
// @import '~mdb-angular-ui-kit/assets/scss/mdb.scss';
@ -24,6 +25,17 @@ table{border-collapse:collapse;border-spacing:0}
font-family: 'Raleway', sans-serif;
}
div.cdk-overlay-container {
z-index: 11200;
}
.mat-snack-bar-container {
transform: scale(1);
opacity: 1;
background: #fff;
color: #323232;
}
.p-inputtext {
width: 100%;
border: 1px solid #B8DEFF;
@ -52,9 +64,11 @@ table{border-collapse:collapse;border-spacing:0}
border-color: #f9b004;
}
.p-selectbutton .p-button.p-highlight:hover {
background: #f9b004;
border-color: #f9b004;
.p-selectbutton .p-button.p-highlight {
@include hover-supported() {
background: #f9b004;
border-color: #f9b004;
}
}
mark {
@ -254,3 +268,6 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; background:
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }