From aea275e705e82ad503c179fac4d72c0c5b61cee9 Mon Sep 17 00:00:00 2001 From: gofnnp Date: Fri, 28 Oct 2022 21:13:08 +0400 Subject: [PATCH] =?UTF-8?q?dev=20#12928=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B8=20=D0=BC=D0=BE=D0=B4=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D1=86=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D0=B7=D0=B8=D0=BD=D1=83=20=D0=B8=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BA=D1=83=20=D0=B7=D0=B0=D0=BA=D0=B0=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- angular/src/app/app.constants.ts | 15 +- angular/src/app/app.module.ts | 12 +- .../user-data-order.component.html | 50 +++++ .../user-data-order.component.scss | 67 ++++++ .../user-data-order.component.spec.ts | 23 ++ .../user-data-order.component.ts | 200 ++++++++++++++++++ angular/src/app/interface/data.ts | 27 ++- angular/src/app/models/cart-product.ts | 44 ++++ angular/src/app/models/order-product.ts | 32 ++- angular/src/app/models/order.ts | 43 ++-- .../app/pages/account/account.component.html | 2 + .../app/pages/account/account.component.scss | 24 +++ .../app/pages/account/account.component.ts | 14 +- .../bonus-program.component.scss | 3 + .../src/app/pages/cart/cart.component.html | 78 ++++++- .../src/app/pages/cart/cart.component.scss | 196 +++++++++++++++++ angular/src/app/pages/cart/cart.component.ts | 48 ++++- .../src/app/services/autocomplete.service.ts | 77 +++++++ angular/src/app/services/cart.service.ts | 111 ++++++++++ angular/src/app/services/order.service.ts | 113 ++++++++++ angular/src/app/services/wp-json.service.ts | 12 +- .../src/app/validators/street.validator.ts | 25 +++ angular/src/environments/environment.prod.ts | 4 +- angular/src/environments/environment.ts | 6 +- angular/src/styles.scss | 36 ++++ 25 files changed, 1198 insertions(+), 64 deletions(-) create mode 100644 angular/src/app/components/user-data-order/user-data-order.component.html create mode 100644 angular/src/app/components/user-data-order/user-data-order.component.scss create mode 100644 angular/src/app/components/user-data-order/user-data-order.component.spec.ts create mode 100644 angular/src/app/components/user-data-order/user-data-order.component.ts create mode 100644 angular/src/app/models/cart-product.ts create mode 100644 angular/src/app/services/autocomplete.service.ts create mode 100644 angular/src/app/services/cart.service.ts create mode 100644 angular/src/app/services/order.service.ts create mode 100644 angular/src/app/validators/street.validator.ts diff --git a/angular/src/app/app.constants.ts b/angular/src/app/app.constants.ts index 6cc6e78..8aff840 100644 --- a/angular/src/app/app.constants.ts +++ b/angular/src/app/app.constants.ts @@ -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': 'Выполнен', -}; \ No newline at end of file +}; + +export const paymentMethods: PaymentMethod[] = [ + { + type: 'Card', + label: 'Безналичный расчет' + }, + { + type: 'Cash', + label: 'Наличными' + } + ] \ No newline at end of file diff --git a/angular/src/app/app.module.ts b/angular/src/app/app.module.ts index 7a796c1..c5f6c43 100644 --- a/angular/src/app/app.module.ts +++ b/angular/src/app/app.module.ts @@ -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] diff --git a/angular/src/app/components/user-data-order/user-data-order.component.html b/angular/src/app/components/user-data-order/user-data-order.component.html new file mode 100644 index 0000000..a6b0d08 --- /dev/null +++ b/angular/src/app/components/user-data-order/user-data-order.component.html @@ -0,0 +1,50 @@ +
+
+

Оформление заказа

+

+ Произошла ошибка. Попробуйте позже. +

+
+

+ +

+

+ +

+

+ +

+

+ +

+
+
+

+ +

+

+ + +

+

+ +

+

+ +

+
+ +

Такой адрес не найден! Введите правильный адрес

+
+
+ + +
+ +
+
\ No newline at end of file diff --git a/angular/src/app/components/user-data-order/user-data-order.component.scss b/angular/src/app/components/user-data-order/user-data-order.component.scss new file mode 100644 index 0000000..5f6e70e --- /dev/null +++ b/angular/src/app/components/user-data-order/user-data-order.component.scss @@ -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; + } + } + +} \ No newline at end of file diff --git a/angular/src/app/components/user-data-order/user-data-order.component.spec.ts b/angular/src/app/components/user-data-order/user-data-order.component.spec.ts new file mode 100644 index 0000000..35e6efe --- /dev/null +++ b/angular/src/app/components/user-data-order/user-data-order.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UserDataOrderComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UserDataOrderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/components/user-data-order/user-data-order.component.ts b/angular/src/app/components/user-data-order/user-data-order.component.ts new file mode 100644 index 0000000..2517133 --- /dev/null +++ b/angular/src/app/components/user-data-order/user-data-order.component.ts @@ -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(); + 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 { + 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 { + 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 { + 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),]], + }); + } +} + diff --git a/angular/src/app/interface/data.ts b/angular/src/app/interface/data.ts index 6de7464..5f9d104 100644 --- a/angular/src/app/interface/data.ts +++ b/angular/src/app/interface/data.ts @@ -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 { diff --git a/angular/src/app/models/cart-product.ts b/angular/src/app/models/cart-product.ts new file mode 100644 index 0000000..33365c2 --- /dev/null +++ b/angular/src/app/models/cart-product.ts @@ -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) + } + } + } +} diff --git a/angular/src/app/models/order-product.ts b/angular/src/app/models/order-product.ts index c71bbda..73174fa 100644 --- a/angular/src/app/models/order-product.ts +++ b/angular/src/app/models/order-product.ts @@ -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((previousValue, currentValue) => { - // return previousValue + currentValue.options.reduce((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((previousValue, currentValue) => { + return previousValue + currentValue.options.reduce((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, } } - - } diff --git a/angular/src/app/models/order.ts b/angular/src/app/models/order.ts index 5d14cad..7134b48 100644 --- a/angular/src/app/models/order.ts +++ b/angular/src/app/models/order.ts @@ -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 + }, } } } diff --git a/angular/src/app/pages/account/account.component.html b/angular/src/app/pages/account/account.component.html index b835cd8..2e22bf7 100644 --- a/angular/src/app/pages/account/account.component.html +++ b/angular/src/app/pages/account/account.component.html @@ -7,12 +7,14 @@