parent
b5fd880379
commit
aea275e705
@ -1,4 +1,4 @@
|
||||
import {MainPageCode, OrderStatus, Page, PageCode} from "./interface/data";
|
||||
import {MainPageCode, OrderStatus, Page, PageCode, PaymentMethod} from "./interface/data";
|
||||
|
||||
export const PageList: Page[] = [
|
||||
{
|
||||
@ -85,4 +85,15 @@ export const orderStatuses: OrderStatus = {
|
||||
'OnWay': 'В пути',
|
||||
'Delivered': 'Выполнен',
|
||||
'Closed': 'Выполнен',
|
||||
};
|
||||
};
|
||||
|
||||
export const paymentMethods: PaymentMethod[] = [
|
||||
{
|
||||
type: 'Card',
|
||||
label: 'Безналичный расчет'
|
||||
},
|
||||
{
|
||||
type: 'Cash',
|
||||
label: 'Наличными'
|
||||
}
|
||||
]
|
||||
@ -38,6 +38,10 @@ import { CartComponent } from './pages/cart/cart.component';
|
||||
import {ListboxModule} from 'primeng/listbox';
|
||||
import { ProductModalComponent } from './components/product-modal/product-modal.component';
|
||||
import { CheckboxGroupComponent } from './components/checkbox-group/checkbox-group.component';
|
||||
import { TreeSelectModule } from 'primeng/treeselect';
|
||||
import { UserDataOrderComponent } from './components/user-data-order/user-data-order.component';
|
||||
import {DropdownModule} from "primeng/dropdown";
|
||||
import {SelectButtonModule} from 'primeng/selectbutton';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -58,7 +62,8 @@ import { CheckboxGroupComponent } from './components/checkbox-group/checkbox-gro
|
||||
ProductsComponent,
|
||||
CartComponent,
|
||||
ProductModalComponent,
|
||||
CheckboxGroupComponent
|
||||
CheckboxGroupComponent,
|
||||
UserDataOrderComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -91,7 +96,10 @@ import { CheckboxGroupComponent } from './components/checkbox-group/checkbox-gro
|
||||
debug: true
|
||||
}),
|
||||
ShareIconsModule,
|
||||
ListboxModule
|
||||
ListboxModule,
|
||||
TreeSelectModule,
|
||||
DropdownModule,
|
||||
SelectButtonModule
|
||||
],
|
||||
providers: [DialogService, MessageService, MessagingService ],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
<div *ngIf="mainFormGroup && !loading; else loadingEl" class="woocommerce-shipping-fields__field-wrapper">
|
||||
<form (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>
|
||||
<p *ngIf="deliverData.deliveryType?.title === 'Доставка'" сlass="form-row form-row-wide">
|
||||
<input formControlName="street" id="street" pInputText placeholder="Улица" type="text">
|
||||
</p>
|
||||
<p *ngIf="deliverData.deliveryType?.title === 'Доставка'" сlass="form-row form-row-first">
|
||||
<input formControlName="house" id="house" pInputText placeholder="Номер дома" type="text">
|
||||
</p>
|
||||
<p *ngIf="deliverData.deliveryType?.title === 'Доставка'" сlass="form-row form-row-last">
|
||||
<input formControlName="flat" id="flat" pInputText placeholder="Квартира" type="number" min="1">
|
||||
</p>
|
||||
</div>
|
||||
<div formGroupName="deliveryDataForm">
|
||||
<p сlass="form-row form-row-wide">
|
||||
<p-dropdown [options]="deliveryTypes" formControlName="deliveryType" placeholder="Доставка"
|
||||
optionLabel="title" (onChange)="changeDeliveryType($event)"></p-dropdown>
|
||||
</p>
|
||||
<p сlass="form-row form-row-wide">
|
||||
<p-selectButton [options]="paymentMethods" formControlName="paymentMethod" optionLabel="label">
|
||||
</p-selectButton>
|
||||
</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>
|
||||
<button [disabled]="!mainFormGroup.valid" class="elementor-button elementor-button--checkout elementor-size-md"
|
||||
(click)="submit()">
|
||||
<span class="elementor-button-text">Оформить заказ</span>
|
||||
</button>
|
||||
<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>
|
||||
</ng-template>
|
||||
@ -0,0 +1,67 @@
|
||||
:host {
|
||||
.woocommerce-shipping-fields__field-wrapper {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
|
||||
.order_form__title {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
color: #000000;
|
||||
border: 1px solid #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Segoe UI Symbol;
|
||||
font-size: 1rem;
|
||||
color: #495057;
|
||||
background: #ffffff;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid #ced4da;
|
||||
transition: background-color .15s, border-color .15s, box-shadow .15s;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
color: #000000;
|
||||
border: 1px solid #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Segoe UI Symbol;
|
||||
font-size: 1rem;
|
||||
color: #495057;
|
||||
background: #ffffff;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid #ced4da;
|
||||
transition: background-color .15s, border-color .15s, box-shadow .15s;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
form {
|
||||
&>button {
|
||||
background-color: #09467f;
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
width: calc(100% - 66px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserDataOrderComponent } from './user-data-order.component';
|
||||
|
||||
describe('UserDataOrderComponent', () => {
|
||||
let component: UserDataOrderComponent;
|
||||
let fixture: ComponentFixture<UserDataOrderComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ UserDataOrderComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserDataOrderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,200 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { DeliveryData, DeliveryType, UserData } from 'src/app/interface/data';
|
||||
import { paymentMethods } from "../../app.constants";
|
||||
import { OrderService } from "../../services/order.service";
|
||||
import { AutocompleteService } from "../../services/autocomplete.service";
|
||||
import { StreetValidator } from "../../validators/street.validator";
|
||||
import { CartService } from 'src/app/services/cart.service';
|
||||
import { environment } from "../../../environments/environment";
|
||||
import { MessageService } from "primeng/api";
|
||||
import { WpJsonService } from "../../services/wp-json.service";
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-data-order',
|
||||
templateUrl: './user-data-order.component.html',
|
||||
styleUrls: ['./user-data-order.component.scss']
|
||||
})
|
||||
export class UserDataOrderComponent implements OnInit {
|
||||
|
||||
@Output() orderSubmitted = new EventEmitter<void>();
|
||||
readonly cities = environment.cities;
|
||||
readonly paymentMethods = paymentMethods;
|
||||
public loading = false;
|
||||
public hasError = false;
|
||||
public mainFormGroup!: FormGroup;
|
||||
public deliveryTypes: DeliveryType[] = [];
|
||||
public minDate!: Date;
|
||||
public new_street!: string | null;
|
||||
public street!: string;
|
||||
public new_house!: string | null;
|
||||
public checkAddress: boolean = true;
|
||||
public showMyMessage: boolean = false;
|
||||
|
||||
public userData: UserData = {
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
street: null,
|
||||
house: null,
|
||||
flat: null,
|
||||
city: this.cities[0],
|
||||
phone: null,
|
||||
};
|
||||
public deliverData: DeliveryData = {
|
||||
deliveryDate: null,
|
||||
deliveryType: null,
|
||||
paymentMethod: paymentMethods[0],
|
||||
comment: '',
|
||||
persons: 1,
|
||||
};
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private orderService: OrderService,
|
||||
private autoCompleteService: AutocompleteService,
|
||||
private streetValidator: StreetValidator,
|
||||
private cartService: CartService,
|
||||
private messageService: MessageService,
|
||||
private wpJsonService: WpJsonService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.minDate = new Date();
|
||||
this._createMainForm();
|
||||
}
|
||||
|
||||
changeDeliveryType(event: any) {
|
||||
this.deliverData.deliveryType = event.value;
|
||||
if (this.deliverData.deliveryType?.title) {
|
||||
this.changeValidators(this.deliverData.deliveryType.title)
|
||||
}
|
||||
}
|
||||
|
||||
changeValidators(title: string) {
|
||||
const comment = this.mainFormGroup.controls['deliveryDataForm'].value.comment;
|
||||
const streetValidators = title === 'Доставка' ? [Validators.required, Validators.minLength(2), Validators.maxLength(255),] : []
|
||||
const houseValidators = title === 'Доставка' ? [Validators.required, Validators.maxLength(10),] : []
|
||||
const userDataForm = this.fb.group({
|
||||
phone: [this.userData.phone],
|
||||
first_name: [this.userData.first_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
// last_name: [this.userData.last_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
street: [this.userData.street, streetValidators],
|
||||
house: [this.userData.house, houseValidators],
|
||||
flat: [this.userData.flat, []],
|
||||
// city: [this.userData.city, [Validators.required]],
|
||||
});
|
||||
const deliveryDataForm = this.fb.group({
|
||||
deliveryDate: [this.deliverData.deliveryDate, []],
|
||||
deliveryType: [this.deliverData.deliveryType, [Validators.required]],
|
||||
paymentMethod: [this.deliverData.paymentMethod, [Validators.required]],
|
||||
// persons: [this.deliverData.persons, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
comment: [comment, [Validators.maxLength(255),]],
|
||||
});
|
||||
|
||||
this.mainFormGroup = this.fb.group({
|
||||
userDataForm,
|
||||
deliveryDataForm,
|
||||
});
|
||||
}
|
||||
|
||||
submit(): void {
|
||||
const mainControls = this.mainFormGroup.controls;
|
||||
if (this.mainFormGroup.invalid) {
|
||||
Object.keys(mainControls).forEach(groupName => {
|
||||
const childGroupControls = (mainControls[groupName] as FormGroup).controls;
|
||||
Object.keys(mainControls).forEach(controlName => {
|
||||
childGroupControls[controlName].markAsTouched();
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.submitOrder();
|
||||
}
|
||||
|
||||
submitOrder(): void {
|
||||
this.loading = true;
|
||||
const userData: UserData = this.mainFormGroup.controls['userDataForm'].value;
|
||||
userData.phone = this.userData.phone;
|
||||
this.orderService.setUserData(userData);
|
||||
this.orderService.setDeliveryData(this.mainFormGroup.controls['deliveryDataForm'].value);
|
||||
this.orderService.submit().subscribe({
|
||||
next: (_) => {
|
||||
this.loading = false;
|
||||
this.cartService.clearCart();
|
||||
this.orderSubmitted.next();
|
||||
},
|
||||
error: () => {
|
||||
this.loading = false;
|
||||
this.hasError = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async _createMainForm(): Promise<void> {
|
||||
try {
|
||||
this.loading = true;
|
||||
const userDataForm = await this._createUserDataForm();
|
||||
const deliveryDataForm = await this._createDeliveryDataForm();
|
||||
this.mainFormGroup = this.fb.group({
|
||||
userDataForm,
|
||||
deliveryDataForm,
|
||||
});
|
||||
this.loading = false;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Erroe: ', e);
|
||||
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private async _createUserDataForm(): Promise<FormGroup> {
|
||||
const order = await this.orderService.getOrder(true);
|
||||
this.userData = Object.assign({}, this.userData, order.userData);
|
||||
this.userData.city = this.cities[0];
|
||||
this.userData.phone = order.phone;
|
||||
// await this.autoCompleteService.setCity(this.userData.city);
|
||||
return this.fb.group({
|
||||
phone: [this.userData.phone],
|
||||
first_name: [this.userData.first_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
// last_name: [this.userData.last_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
street: [this.userData.street, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
house: [this.userData.house, [Validators.required, Validators.maxLength(10), Validators.pattern('^\\d+[-|\\d]+\\d+$|^\\d*$')]],
|
||||
flat: [this.userData.flat, []],
|
||||
// city: [this.userData.city, [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
private async _createDeliveryDataForm(): Promise<FormGroup> {
|
||||
this.deliveryTypes = [
|
||||
{
|
||||
"cost": 100,
|
||||
"title": "Доставка",
|
||||
"id": 11,
|
||||
"type": "delivery"
|
||||
},
|
||||
{
|
||||
"cost": 0,
|
||||
"title": "Самовывоз",
|
||||
"id": 16,
|
||||
"type": "self_delivery"
|
||||
}
|
||||
];
|
||||
this.deliverData.deliveryType = this.deliveryTypes[0];
|
||||
return this.fb.group({
|
||||
// deliveryDate: [this.deliverData.deliveryDate, []],
|
||||
deliveryType: [this.deliverData.deliveryType, [Validators.required]],
|
||||
paymentMethod: [this.deliverData.paymentMethod, [Validators.required]],
|
||||
// persons: [this.deliverData.persons, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
comment: [this.deliverData.comment, [Validators.maxLength(255),]],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { CartProduct } from "../models/cart-product";
|
||||
|
||||
|
||||
export enum PageCode {
|
||||
@ -79,6 +80,7 @@ export interface DeliveryType {
|
||||
cost: number;
|
||||
title: string;
|
||||
id: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface AcceptedOrder {
|
||||
@ -111,7 +113,7 @@ export interface Product {
|
||||
description: string;
|
||||
stock_status: string;
|
||||
currency_symbol: string;
|
||||
modifier_data: Modifier[];
|
||||
modifier_data: CartModifier[];
|
||||
short_description: string;
|
||||
guid: string;
|
||||
groupId: string;
|
||||
@ -129,7 +131,7 @@ export interface AllData {
|
||||
|
||||
export interface Group {
|
||||
id: string;
|
||||
name: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface ModifiersGroup {
|
||||
@ -147,6 +149,7 @@ export interface Modifier {
|
||||
id: string,
|
||||
name: string,
|
||||
groupId: string,
|
||||
price?: number,
|
||||
restrictions: {
|
||||
minQuantity: number,
|
||||
maxQuantity: number,
|
||||
@ -155,6 +158,16 @@ export interface Modifier {
|
||||
}
|
||||
}
|
||||
|
||||
export interface CartModifier {
|
||||
id: string;
|
||||
name: string;
|
||||
options: Modifier[];
|
||||
}
|
||||
|
||||
export interface Cart {
|
||||
products: CartProduct[];
|
||||
}
|
||||
|
||||
// export interface Modifier {
|
||||
// id: number;
|
||||
// name: string;
|
||||
@ -170,9 +183,13 @@ export interface Modifier {
|
||||
export interface Option {
|
||||
id: number;
|
||||
name: string;
|
||||
price: string;
|
||||
prechecked: string;
|
||||
active?: boolean;
|
||||
groupId: string;
|
||||
restrictions: {
|
||||
minQuantity: number,
|
||||
maxQuantity: number,
|
||||
freeQuantity: number,
|
||||
byDefault: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface OrderProduct {
|
||||
|
||||
44
angular/src/app/models/cart-product.ts
Normal file
44
angular/src/app/models/cart-product.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {CartModifier, Modifier, ModifiersGroup, Option} from "../interface/data";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export class CartProduct {
|
||||
|
||||
|
||||
constructor(id: string, name: string, modifiers: ModifiersGroup[] = [], options: Modifier[], amount: number = 1) {
|
||||
this.id = id;
|
||||
this.guid = uuidv4();
|
||||
this.amount = amount;
|
||||
this.name = name;
|
||||
this.modifiers = modifiers.map(modifier => ({name: modifier.name, id: modifier.id, options: []}));
|
||||
}
|
||||
|
||||
id: string;
|
||||
guid: string;
|
||||
amount: number;
|
||||
name: string;
|
||||
modifiers: CartModifier[];
|
||||
|
||||
|
||||
increment(): void{
|
||||
this.amount++;
|
||||
}
|
||||
|
||||
decrement(): void{
|
||||
if (this.amount > 0){
|
||||
this.amount--;
|
||||
}
|
||||
}
|
||||
|
||||
addOption(modifier: ModifiersGroup, option: Modifier): void{
|
||||
const productModifier = this.modifiers.find(value => value.id === modifier.id);
|
||||
if (productModifier){
|
||||
const optionIndex = productModifier.options.findIndex(value => value.id === option.id);
|
||||
if(optionIndex === -1){
|
||||
productModifier.options.push(option);
|
||||
}
|
||||
else {
|
||||
productModifier.options.splice(optionIndex, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import {Modifier, Product} from "../interface/data";
|
||||
import {CartModifier, Modifier, Product} from "../interface/data";
|
||||
export class OrderProduct implements Product{
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ export class OrderProduct implements Product{
|
||||
public id: string;
|
||||
public image_gallery: string[];
|
||||
public image: string;
|
||||
public modifier_data: Modifier[];
|
||||
public modifier_data: CartModifier[];
|
||||
public name: string;
|
||||
public price: number;
|
||||
public stock_status: string;
|
||||
@ -39,29 +39,27 @@ export class OrderProduct implements Product{
|
||||
|
||||
|
||||
get finalPrice(): number{
|
||||
// const modifiersPrice = this.modifier_data.reduce<number>((previousValue, currentValue) => {
|
||||
// return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => {
|
||||
// return previousOptionValue + Number(currentOptionValue.price);
|
||||
// }, 0);
|
||||
// }, 0);
|
||||
// return (Number(this.price) + modifiersPrice) * this.amount;
|
||||
return 1
|
||||
new Date()
|
||||
const modifiersPrice = this.modifier_data.reduce<number>((previousValue, currentValue) => {
|
||||
return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => {
|
||||
return previousOptionValue + Number(currentOptionValue.price ? currentOptionValue.price : 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
return (Number(this.price) + modifiersPrice) * this.amount;
|
||||
}
|
||||
|
||||
toJson(){
|
||||
return {
|
||||
id: this.id,
|
||||
amount: this.amount,
|
||||
name: this.name,
|
||||
modifiers: this.modifier_data?.map(modifier => {
|
||||
amount: this.amount * this.price,
|
||||
price: this.price,
|
||||
options: this.modifier_data?.map((modifier) => {
|
||||
return {
|
||||
id: modifier.id,
|
||||
// options: modifier.options,
|
||||
option: modifier.name,
|
||||
variant: modifier.options[0]?.name || null
|
||||
}
|
||||
}),
|
||||
quantity: this.amount,
|
||||
name: this.name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import {DeliveryData, UserData} from "../interface/data";
|
||||
import {OrderProduct} from "./order-product";
|
||||
import * as moment from 'moment';
|
||||
import { CookiesService } from "../services/cookies.service";
|
||||
import { environment } from "src/environments/environment";
|
||||
|
||||
export interface OrderInfo {
|
||||
products: OrderProduct[];
|
||||
@ -36,33 +37,23 @@ export class Order {
|
||||
toJson(): any {
|
||||
const date = moment(this.deliveryData?.deliveryDate ?? Date.now());
|
||||
return {
|
||||
items: this.products.map(product => {
|
||||
return product.toJson();
|
||||
}),
|
||||
user_data: {
|
||||
phone: this.phone,
|
||||
...this.userData
|
||||
},
|
||||
payment_method: this.deliveryData?.paymentMethod.type,
|
||||
delivery_time: date.format('HH:mm'),
|
||||
delivery_date: date.format("YYYY-MM-DD"),
|
||||
delivery_instance_id: this.deliveryData?.deliveryType?.id,
|
||||
formname: "Cart",
|
||||
paymentsystem: this.deliveryData?.paymentMethod.type,
|
||||
phone: this.phone,
|
||||
persons: 1,
|
||||
payments: [
|
||||
{
|
||||
type: this.deliveryData?.paymentMethod.type,
|
||||
summ: this.price,
|
||||
},
|
||||
{
|
||||
type: "crm4retail",
|
||||
summ: 0,
|
||||
payload: {
|
||||
id: "c07a10d8-ba7e-43b0-92aa-ae470060bc7d"
|
||||
}
|
||||
}
|
||||
],
|
||||
comment: this.deliveryData?.comment,
|
||||
token: this.token
|
||||
name: "31",
|
||||
payment: {
|
||||
delivery_price: 100,
|
||||
products: this.products.map(product => {
|
||||
return product.toJson();
|
||||
}),
|
||||
delivery_fio: this.userData?.first_name,
|
||||
subtotal: this.price,
|
||||
delivery_comment: this.deliveryData?.comment,
|
||||
delivery: this.deliveryData?.deliveryType?.type,
|
||||
delivery_address: `${environment.cities[0]}, ул ${this.userData?.street}, ${this.userData?.house}`,
|
||||
amount: this.price + 100
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,12 +7,14 @@
|
||||
<ng-container *ngFor="let page of mainPageList; let index = index; let last = last; let first = first;">
|
||||
<li *ngIf="page.onSideBar" class="main-menu-container__item"
|
||||
[ngClass]="{
|
||||
'cart': page.resName === 'cart',
|
||||
'is-active': page === currentPageMain
|
||||
}"
|
||||
[ngStyle]="{
|
||||
border: last && 0,
|
||||
'border-radius': first ? '6px 0 0 6px' : (last ? '0 6px 6px 0' : 0)
|
||||
}"
|
||||
[attr.data-counter]="page.resName === 'cart' ? cartCount : null"
|
||||
(click)="changeMainPage(page, $event)">
|
||||
<span>
|
||||
{{page.name}}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
.woocommerce {
|
||||
min-height: calc(100vh - 39px);
|
||||
padding: 20px 18px;
|
||||
position: relative;
|
||||
|
||||
&.auth-page {
|
||||
display: flex;
|
||||
@ -13,6 +14,7 @@
|
||||
max-width: 600px;
|
||||
height: 50px;
|
||||
margin: -20px auto 0 auto;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -20,16 +22,36 @@
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
padding: 0 16px;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
border-right: solid #e1e1e1 1px;
|
||||
padding: 8px 0;
|
||||
cursor: pointer;
|
||||
|
||||
&.is-active {
|
||||
background: #0d457e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.cart {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: attr(data-counter);
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 1px;
|
||||
background: #D7120B;
|
||||
border-radius: 50px;
|
||||
min-width: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
font-size: .8rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,6 +140,8 @@
|
||||
|
||||
.version {
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { CartService } from 'src/app/services/cart.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
@ -27,7 +28,8 @@ export class AccountComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private dialogService: DialogService,
|
||||
private jsonRpcService: JsonrpcService,
|
||||
private messageService: MessageService
|
||||
private messageService: MessageService,
|
||||
private cartService: CartService,
|
||||
) { }
|
||||
|
||||
public currentPage!: Page;
|
||||
@ -42,7 +44,8 @@ export class AccountComponent implements OnInit {
|
||||
|
||||
readonly MainPageCode = MainPageCode;
|
||||
readonly mainPageList = PageListMain;
|
||||
public currentPageMain: Page = this.mainPageList[0];
|
||||
public currentPageMain: Page = this.mainPageList[environment.production ? 0 : 1];
|
||||
public cartCount = 0;
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.getToken()) {
|
||||
@ -62,6 +65,13 @@ export class AccountComponent implements OnInit {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
});
|
||||
this.cartCount = this.cartService.cartCount;
|
||||
this.cartService.cartCount$.subscribe({
|
||||
next: (count) => {
|
||||
this.cartCount = count;
|
||||
document.querySelectorAll('.cart')[0].setAttribute("data-counter", this.cartCount.toString())
|
||||
}
|
||||
});
|
||||
}
|
||||
document.body.classList.add(
|
||||
'woocommerce-account',
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&>p {
|
||||
|
||||
@ -1 +1,77 @@
|
||||
<p>cart works!</p>
|
||||
<div class="cart" *ngIf="!loading && order && !orderConfirmed" [ngStyle]="{margin: !order.products.length && 0}">
|
||||
<div class="widget_shopping_cart_content" style="opacity: 1;">
|
||||
<div class="elementor-menu-cart__products woocommerce-mini-cart cart woocommerce-cart-form__contents" [ngStyle]="{margin: !order.products.length && 0}">
|
||||
<div *ngFor="let product of order.products"
|
||||
class="elementor-menu-cart__product woocommerce-cart-form__cart-item cart_item"
|
||||
style="grid-template-columns: 70px auto;">
|
||||
|
||||
<div class="elementor-menu-cart__product-image product-thumbnail">
|
||||
<img *ngIf="product.image" width="70" height="70" src="{{product.image}}"
|
||||
class="attachment-woocommerce_thumbnail size-woocommerce_thumbnail" alt="{{product.name}}" loading="lazy">
|
||||
<img *ngIf="!product.image" width="70" height="70" src="./assets/no-image.png"
|
||||
class="attachment-woocommerce_thumbnail size-woocommerce_thumbnail" alt="{{product.name}}" loading="lazy">
|
||||
</div>
|
||||
<div class="elementor-menu-cart__product-name product-name" data-title="Product">
|
||||
<span>{{product.name}}</span>
|
||||
<dl *ngFor="let modifier of product.modifier_data" class="variation" [ngStyle]="{margin: !modifier.options.length && 0}" >
|
||||
<ng-container *ngFor="let option of modifier.options">
|
||||
<dt style="max-width: 160px;" class="variation-">{{option.name}}:</dt>
|
||||
<dd style="display: flex; align-items: flex-end; margin-bottom: 0;" class="variation-"><p>{{product.currency_symbol}}{{option.price ?? 0}}</p>
|
||||
</dd>
|
||||
</ng-container>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="elementor-menu-cart__product-price product-price" data-title="Price">
|
||||
<span class="quantity">
|
||||
<span class="product-quantity">{{product.amount}} ×</span>
|
||||
<span class="woocommerce-Price-amount amount">
|
||||
<bdi>
|
||||
<span class="woocommerce-Price-currencySymbol">{{product.currency_symbol}}</span>
|
||||
{{product.finalPrice}}
|
||||
</bdi>
|
||||
</span>
|
||||
</span>
|
||||
<div class="product-change-amount">
|
||||
<div class="product-change-amount__symbol" (click)="setAmount(product, 'minus')">
|
||||
-
|
||||
</div>
|
||||
<div class="product-change-amount__symbol" (click)="setAmount(product, 'plus')">
|
||||
+
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="elementor-menu-cart__product-remove product-remove">
|
||||
<a href="#" class="remove_from_cart_button" aria-label="Remove this item"
|
||||
(click)="removeFromCart($event, product.guid)"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="order.products.length != 0" class="elementor-menu-cart__bottom-info">
|
||||
<div class="elementor-menu-cart__subtotal">
|
||||
<strong>К оплате: </strong>
|
||||
<span class="woocommerce-Price-amount amount"><bdi><span
|
||||
class="woocommerce-Price-currencySymbol">{{order.products[0].currency_symbol}}</span>{{order.price}}</bdi></span>
|
||||
</div>
|
||||
<div class="elementor-menu-cart__footer-buttons">
|
||||
<a href="#" class="elementor-button elementor-button--checkout elementor-size-md"
|
||||
(click)="confirmOrder($event)">
|
||||
<span class="elementor-button-text">Оформление заказа</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-user-data-order *ngIf="orderConfirmed" (orderSubmitted)="orderSubmitted()"></app-user-data-order>
|
||||
|
||||
<div #loadingEl *ngIf="loading">
|
||||
<div class="angular-spinner-container" style="width: fit-content; height: 100%; margin: 16px auto;">
|
||||
<p-progressSpinner styleClass="angular-spinner"></p-progressSpinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div #empty *ngIf="!loading && (!order || !order.products.length)">
|
||||
<div class="woocommerce-mini-cart__empty-message jupiterx-icon-shopping-cart-6">Корзина пустая.</div>
|
||||
</div>
|
||||
@ -0,0 +1,196 @@
|
||||
:host {
|
||||
.cart {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
.elementor-menu-cart {
|
||||
&__product {
|
||||
grid-template-columns: 71px auto;
|
||||
grid-template-rows: var(--price-quantity-position--grid-template-rows, auto auto);
|
||||
position: relative;
|
||||
display: grid;
|
||||
padding-bottom: 20px;
|
||||
padding-right: 30px;
|
||||
|
||||
.variation {
|
||||
display: grid;
|
||||
grid-template-columns: max-content auto;
|
||||
margin: 10px 8px;
|
||||
color: var(--product-variations-color, #373a3c);
|
||||
|
||||
dt {
|
||||
grid-column-start: 1;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
grid-column-start: 2;
|
||||
-webkit-margin-start: 5px;
|
||||
margin-inline-start: 5px;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__product-image {
|
||||
grid-row-start: 1;
|
||||
grid-row-end: 3;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&__product-name {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 3;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
&__product-price {
|
||||
font-size: 14px;
|
||||
padding-left: 20px;
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 3;
|
||||
-ms-flex-item-align: var(--price-quantity-position--align-self, end);
|
||||
align-self: var(--price-quantity-position--align-self, end);
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
&__product-remove {
|
||||
color: #818a91;
|
||||
width: var(--remove-item-button-size, 22px);
|
||||
height: var(--remove-item-button-size, 22px);
|
||||
border-radius: var(--remove-item-button-size, 22px);
|
||||
border: 1px solid var(--remove-item-button-color, #d4d4d4);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
bottom: 20px;
|
||||
-webkit-transition: .3s;
|
||||
-o-transition: .3s;
|
||||
transition: .3s;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 50%;
|
||||
top: 50%;
|
||||
left: 25%;
|
||||
margin-top: -1px;
|
||||
background: var(--remove-item-button-color, #d4d4d4);
|
||||
z-index: 1;
|
||||
-webkit-transition: .3s;
|
||||
-o-transition: .3s;
|
||||
transition: .3s;
|
||||
}
|
||||
|
||||
&::before {
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
&>a {
|
||||
display: block;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
&__bottom-info {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
bottom: 0;
|
||||
padding: 18px;
|
||||
background: #fff;
|
||||
border-top: solid #d9d9d9 1px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&__subtotal {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__footer-buttons {
|
||||
a {
|
||||
padding: 12px;
|
||||
display: block;
|
||||
width: fit-content;
|
||||
background: #09467f;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-thumbnail {
|
||||
background: #eee;
|
||||
border-radius: 9px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.product-change-amount {
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
margin-top: 4px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
border: solid #cbcbcb 1px;
|
||||
color: #525252;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&__symbol {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-right: solid #cbcbcb 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-product {
|
||||
&__supplements {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 26px;
|
||||
padding: 8px;
|
||||
background: #f9b004;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-mini-cart__empty-message {
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
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 { OrderService } from 'src/app/services/order.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cart',
|
||||
@ -6,10 +10,52 @@ import { Component, OnInit } from '@angular/core';
|
||||
styleUrls: ['./cart.component.scss']
|
||||
})
|
||||
export class CartComponent implements OnInit {
|
||||
public loading = false;
|
||||
public orderConfirmed = false;
|
||||
public order!: Order;
|
||||
public price!: number;
|
||||
|
||||
constructor() { }
|
||||
constructor(
|
||||
private orderService: OrderService,
|
||||
private cartService: CartService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadCart()
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
removeFromCart(event: Event, guid: string): void{
|
||||
event.preventDefault();
|
||||
this.orderService.removeFromCart(guid);
|
||||
}
|
||||
|
||||
confirmOrder(event: Event): void{
|
||||
event.preventDefault();
|
||||
this.orderConfirmed = true;
|
||||
// this.confirm.emit();
|
||||
}
|
||||
|
||||
setAmount(product: OrderProduct, method: 'plus' | 'minus') {
|
||||
if (method === 'plus') {
|
||||
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.price = this.price - Number(product.price);
|
||||
}
|
||||
}
|
||||
|
||||
orderSubmitted() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
77
angular/src/app/services/autocomplete.service.ts
Normal file
77
angular/src/app/services/autocomplete.service.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||
import {map} from "rxjs/operators";
|
||||
import {lastValueFrom, Observable} from "rxjs";
|
||||
|
||||
enum CompleteType {
|
||||
city,
|
||||
street,
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AutocompleteService {
|
||||
|
||||
private city!: string;
|
||||
private cityId!: string;
|
||||
private query?: string
|
||||
private streets: string[] = [];
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
async setCity(city: string | null): Promise<boolean> {
|
||||
if (city && this.city != city) {
|
||||
this.city = city;
|
||||
let headers = new HttpHeaders();
|
||||
const cityData = await lastValueFrom(
|
||||
this._request(`query=${city}&contentType=city`)
|
||||
.pipe(
|
||||
map(
|
||||
(res: any) => res.result.filter(
|
||||
(city: any) => city.id != 'Free'
|
||||
)[0]
|
||||
),
|
||||
)
|
||||
);
|
||||
this.cityId = cityData.id;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async queryStreet(query: string, city: string | null = null): Promise<string[]> {
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.set('Content-Type', 'application/json');
|
||||
let newCityId = await this.setCity(city);
|
||||
if (!this.cityId) {
|
||||
return [];
|
||||
}
|
||||
if (!this.query || this.query !== query || newCityId) {
|
||||
this.query = query;
|
||||
this.streets = await lastValueFrom(
|
||||
this._request(`query=${query}&offset=0&limit=20&cityId=${this.cityId}&contentType=street`)
|
||||
.pipe(
|
||||
map(
|
||||
(res: any) => res.result
|
||||
.filter(
|
||||
(street: any) => street.id != 'Free'
|
||||
)
|
||||
.map(
|
||||
(street: any) => street.name
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return this.streets;
|
||||
}
|
||||
|
||||
//jsonp запрос для обхода cors кладра
|
||||
_request(params: String): Observable<any> {
|
||||
const src = '//kladr-api.ru/api.php?';
|
||||
return this.http.jsonp(src + params, 'callback');
|
||||
};
|
||||
|
||||
}
|
||||
111
angular/src/app/services/cart.service.ts
Normal file
111
angular/src/app/services/cart.service.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {CookiesService} from "./cookies.service";
|
||||
import {Cart} from "../interface/data";
|
||||
import {isEqual} from 'lodash/fp';
|
||||
import {CartProduct} from "../models/cart-product";
|
||||
import {Subject} from "rxjs";
|
||||
import { update } from 'lodash';
|
||||
import { WpJsonService } from './wp-json.service';
|
||||
|
||||
export enum ProductAmountAction {
|
||||
increment,
|
||||
decrement,
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CartService {
|
||||
|
||||
constructor(
|
||||
private cookieService: CookiesService,
|
||||
private wpJsonService: WpJsonService,
|
||||
) { }
|
||||
|
||||
private cart!: Cart;
|
||||
|
||||
public cartCount$ = new Subject<number>();
|
||||
|
||||
|
||||
getCart(){
|
||||
return this._getCartProducts();
|
||||
}
|
||||
|
||||
|
||||
addToCart(product: CartProduct): void{
|
||||
const cart = this._getCartProducts();
|
||||
|
||||
cart.products = cart.products ?? [];
|
||||
const sameProduct = cart.products.find((value) => value.id === product.id && isEqual(value.modifiers, product.modifiers));
|
||||
if(sameProduct){
|
||||
sameProduct.amount ++;
|
||||
}
|
||||
else {
|
||||
cart.products.push(product);
|
||||
this.cartCount$.next(cart.products.length);
|
||||
}
|
||||
this.cookieService.setCookie('cart', JSON.stringify(cart));
|
||||
}
|
||||
|
||||
removeFromCart(guid: string): void{
|
||||
const cart = this._getCartProducts();
|
||||
if(!cart.products){
|
||||
return;
|
||||
}
|
||||
cart.products = cart.products.filter((value) => value.guid !== guid);
|
||||
this.cookieService.setCookie('cart', JSON.stringify(cart));
|
||||
this.cartCount$.next(cart.products.length);
|
||||
}
|
||||
|
||||
updateProductFromCart(product: CartProduct): void{
|
||||
// const cart = this._getCartProducts();
|
||||
// if(!cart.products){
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
// const updateProduct = cart.products.find((value) => Number(value.id) === product.id)
|
||||
// if (updateProduct) {
|
||||
// updateProduct.modifiers = JSON.parse(JSON.stringify(product.modifiers))
|
||||
// }
|
||||
// this.cookieService.setCookie('cart', JSON.stringify(cart));
|
||||
}
|
||||
|
||||
changeAmountProduct(productTempId: string,action: ProductAmountAction): void{
|
||||
const cart = this._getCartProducts();
|
||||
if(!cart.products){
|
||||
return;
|
||||
}
|
||||
const product: CartProduct | undefined = cart.products.find((value) => value.guid === productTempId);
|
||||
if(product && action === ProductAmountAction.increment){
|
||||
product.amount++
|
||||
// product.increment();
|
||||
}
|
||||
else if(product && action === ProductAmountAction.decrement){
|
||||
product.amount--
|
||||
// product.decrement();
|
||||
}
|
||||
this.cookieService.setCookie('cart', JSON.stringify(cart));
|
||||
this.cartCount$.next(cart.products.length);
|
||||
}
|
||||
|
||||
clearCart(){
|
||||
this.cart = {products: []};
|
||||
this.cookieService.setCookie('cart', JSON.stringify(this.cart));
|
||||
this.cartCount$.next(0);
|
||||
}
|
||||
|
||||
_getCartProducts(): Cart{
|
||||
if(this.cart){
|
||||
return this.cart;
|
||||
}
|
||||
|
||||
const cartJson = this.cookieService.getItem('cart');
|
||||
this.cart = cartJson ? JSON.parse(cartJson) : {products: []};
|
||||
return this.cart;
|
||||
}
|
||||
|
||||
get cartCount(): number{
|
||||
return this._getCartProducts().products.length;
|
||||
}
|
||||
}
|
||||
113
angular/src/app/services/order.service.ts
Normal file
113
angular/src/app/services/order.service.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {CartService} from "./cart.service";
|
||||
import {WpJsonService} from "./wp-json.service";
|
||||
import {forkJoin, lastValueFrom, Observable, tap} from "rxjs";
|
||||
import {Cart, DeliveryData, DeliveryType, Modifier, Product, UserData} from "../interface/data";
|
||||
import {Order} from "../models/order";
|
||||
import {OrderProduct} from "../models/order-product";
|
||||
import {JsonrpcService, RpcService} from "./jsonrpc.service";
|
||||
import {CookiesService} from "./cookies.service";
|
||||
import {MessageService} from "primeng/api";
|
||||
import {map} from "rxjs/operators";
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class OrderService {
|
||||
|
||||
private order!: Order;
|
||||
|
||||
constructor(
|
||||
private cartService: CartService,
|
||||
private wpJsonService: WpJsonService,
|
||||
private jsonRpcService: JsonrpcService,
|
||||
private cookiesService: CookiesService,
|
||||
private messageService: MessageService,
|
||||
) {
|
||||
}
|
||||
|
||||
async getDeliveryTypes(): Promise<DeliveryType[]> {
|
||||
return await lastValueFrom(this.wpJsonService.getDeliveryTypes());
|
||||
}
|
||||
|
||||
|
||||
async getOrder(refresh = false): Promise<Order> {
|
||||
if (!this.order || refresh) {
|
||||
const cart = this.cartService.getCart();
|
||||
|
||||
if (cart.products.length) {
|
||||
const products = await this.getProducts(cart);
|
||||
const additionalInfo = this.jsonRpcService.rpc({
|
||||
method: 'getAdditionalInfo',
|
||||
params: []
|
||||
}, RpcService.authService, true);
|
||||
|
||||
const tokenData = this.jsonRpcService.rpc({
|
||||
method: 'getTokenData',
|
||||
params: [this.cookiesService.getItem('token')],
|
||||
}, RpcService.authService, true);
|
||||
|
||||
const info = await lastValueFrom(forkJoin([additionalInfo, tokenData, products]));
|
||||
const token = this.cookiesService.getItem('token')
|
||||
this.order = new Order({products: products, userData: info[0]?.data, phone: info[1].data?.mobile_number, token: token});
|
||||
}
|
||||
}
|
||||
return this.order;
|
||||
}
|
||||
|
||||
async getProducts(cart: Cart): Promise<OrderProduct[]> {
|
||||
const allData = await lastValueFrom(this.wpJsonService.getAllData())
|
||||
const products: OrderProduct[] = []
|
||||
for (let i = 0; i < cart.products.length; i++) {
|
||||
const productSub = allData.products.find((product: any) => product.id === cart.products[i].id)
|
||||
const product = Object.assign(cloneDeep(cart.products[i]), {
|
||||
category_id: 0,
|
||||
price: productSub.price,
|
||||
currency_symbol: '₽',
|
||||
description: '',
|
||||
short_description: '',
|
||||
image_gallery: [],
|
||||
image: productSub.image,
|
||||
modifier_data: cart.products[i].modifiers,
|
||||
stock_status: 'instock',
|
||||
groupId: productSub.groupId,
|
||||
modifiers_group: productSub.modifiers_group
|
||||
})
|
||||
const orderProduct: OrderProduct = new OrderProduct(product, cart.products[i].guid, cart.products[i].amount)
|
||||
products.push(orderProduct)
|
||||
}
|
||||
return products
|
||||
}
|
||||
|
||||
removeFromCart(productGuid: string): void {
|
||||
this.order.products = this.order.products.filter(value => value.guid !== productGuid);
|
||||
this.cartService.removeFromCart(productGuid);
|
||||
}
|
||||
|
||||
setUserData(userData: UserData): void {
|
||||
this.order.userData = userData;
|
||||
}
|
||||
|
||||
setDeliveryData(deliveryData: DeliveryData): void {
|
||||
this.order.deliveryData = deliveryData;
|
||||
}
|
||||
|
||||
submit(): Observable<any> {
|
||||
return this.wpJsonService.createOrder(this.order.toJson(), environment.webhookItRetail).pipe(
|
||||
tap({
|
||||
next: (_) => {
|
||||
this.jsonRpcService.rpc({
|
||||
method: 'updateAdditionalInfo',
|
||||
params: [this.order.userData, this.order.deliveryData]
|
||||
}, RpcService.authService, true).subscribe();
|
||||
this.messageService.add({
|
||||
severity:'success',
|
||||
summary: 'Заказ создан',
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -30,8 +30,8 @@ export class WpJsonService {
|
||||
return this._request('orders/delivery-types', 'GET');
|
||||
}
|
||||
|
||||
createOrder(order: any){
|
||||
return this._request('orders', 'POST', order);
|
||||
createOrder(order: any, url: string){
|
||||
return this._request('', 'POST', order);
|
||||
}
|
||||
|
||||
getOrders(): Observable<AcceptedOrder[]>{
|
||||
@ -46,7 +46,7 @@ export class WpJsonService {
|
||||
return this._request('static/nomen_1eb3fb56-3c4c-43b7-9a04-ce532ab7548f.json', 'GET')
|
||||
}
|
||||
|
||||
_request(path: string, method: string, body?: any, auth = false): Observable<any> {
|
||||
_request(path: string, method: string, body?: any, auth = false, baseUrl = null): Observable<any> {
|
||||
const token = decodeURI(this.cookiesService.getItem('token') ?? '');
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.set('Content-Type', 'application/json');
|
||||
@ -60,8 +60,10 @@ export class WpJsonService {
|
||||
body: this.body,
|
||||
};
|
||||
|
||||
const url = environment.production ? window.location.origin + '/' : this.api
|
||||
|
||||
let url = environment.production ? window.location.origin + '/' : this.api
|
||||
if (baseUrl) {
|
||||
url = baseUrl
|
||||
}
|
||||
return this.http
|
||||
.request( method, url + path + urlToken, options);
|
||||
}
|
||||
|
||||
25
angular/src/app/validators/street.validator.ts
Normal file
25
angular/src/app/validators/street.validator.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {AbstractControl, ValidationErrors, AsyncValidator} from '@angular/forms';
|
||||
import {AutocompleteService} from "../services/autocomplete.service";
|
||||
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class StreetValidator implements AsyncValidator {
|
||||
constructor(private autocompleteService: AutocompleteService) {}
|
||||
|
||||
async validate(
|
||||
control: AbstractControl
|
||||
): Promise<ValidationErrors | null> {
|
||||
try{
|
||||
const streets = await this.autocompleteService.queryStreet(control.value);
|
||||
if(streets.includes(control.value)){
|
||||
return null;
|
||||
}
|
||||
return { validStreet: false }
|
||||
}
|
||||
catch (e){
|
||||
return { validStreet: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,5 +20,7 @@ export const environment = {
|
||||
version: packageJson.version,
|
||||
appleWalletEndpoint: 'https://apple-push-notifications.it-retail.tech/apns/api',
|
||||
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
|
||||
clientName: 'Sakura'
|
||||
webhookItRetail: 'https://webhook.it-retail.tech/handlers/tillda/1eb3fb56-3c4c-43b7-9a04-ce532ab7548f',
|
||||
clientName: 'Sakura',
|
||||
cities: ['Менделеевск'],
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ export const environment = {
|
||||
production: false,
|
||||
appAuthEndpoint: 'https://auth.crm4retail.ru/tnt',
|
||||
appBonusEndpoint: 'https://customerapi2.mi.crm4retail.ru/json.rpc/',
|
||||
appWPEndpoint: './assets/',
|
||||
appWPEndpoint: './',
|
||||
hasBonusProgram: true,
|
||||
systemId: 'g6zyv8tj53w28ov7cl',
|
||||
defaultUrl: 'http://192.168.0.179:4200',
|
||||
@ -20,5 +20,7 @@ export const environment = {
|
||||
version: packageJson.version,
|
||||
appleWalletEndpoint: 'http://192.168.0.179:4200/apns/api',
|
||||
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
|
||||
clientName: 'Sakura'
|
||||
webhookItRetail: 'https://webhook.it-retail.tech/handlers/tillda/1eb3fb56-3c4c-43b7-9a04-ce532ab7548f',
|
||||
clientName: 'Sakura',
|
||||
cities: ['Менделеевск'],
|
||||
};
|
||||
|
||||
@ -22,6 +22,29 @@ table{border-collapse:collapse;border-spacing:0}
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.p-dropdown {
|
||||
width: 100%;
|
||||
height: 39px;
|
||||
}
|
||||
|
||||
.p-selectbutton {
|
||||
display: flex;
|
||||
&>.p-button{
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.p-selectbutton .p-button.p-highlight {
|
||||
background: #f9b004;
|
||||
border-color: #f9b004;
|
||||
}
|
||||
|
||||
.p-selectbutton .p-button.p-highlight:hover {
|
||||
background: #f9b004;
|
||||
border-color: #f9b004;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 4px;
|
||||
background: #009688;
|
||||
@ -64,3 +87,16 @@ button {
|
||||
input::-webkit-date-and-time-value {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.p-treeselect {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.p-tree .p-tree-container .p-treenode {
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.p-tree .p-tree-container .p-treenode .p-treenode-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user