dev #12926
dev #12927
изменил название клиента и пути для разворачивания проекта
Наработки по каталогу товаров
наработки по карточке товара
This commit is contained in:
gofnnp 2022-10-24 22:20:41 +04:00
parent 192a7a5d2f
commit ed41ab6627
40 changed files with 848 additions and 175 deletions

View File

@ -1,4 +1,4 @@
# FashionLogica
# Sakura
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6.

View File

@ -4,7 +4,7 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"fashion-logica": {
"sakura": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
@ -19,7 +19,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "/var/www/html/lk.crm4retail.ru/fashion-logica",
"outputPath": "/var/www/html/lk.crm4retail.ru/sakura",
"baseHref": "/",
"index": "src/index.html",
"main": "src/main.ts",
@ -81,10 +81,10 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "fashion-logica:build:production"
"browserTarget": "sakura:build:production"
},
"development": {
"browserTarget": "fashion-logica:build:development"
"browserTarget": "sakura:build:development"
}
},
"defaultConfiguration": "development"
@ -92,7 +92,7 @@
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "fashion-logica:build"
"browserTarget": "sakura:build"
}
},
"test": {

View File

@ -1,11 +1,11 @@
{
"name": "fashion-logica",
"name": "sakura",
"version": "0.0.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "fashion-logica",
"name": "sakura",
"version": "0.0.2",
"dependencies": {
"@angular/animations": "^14.0.0",
@ -44,6 +44,7 @@
"@angular/compiler-cli": "^14.0.0",
"@types/google-libphonenumber": "^7.4.23",
"@types/jasmine": "~4.0.0",
"@types/lodash": "^4.14.186",
"jasmine-core": "~4.1.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
@ -3914,6 +3915,12 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
},
"node_modules/@types/lodash": {
"version": "4.14.186",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz",
"integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==",
"dev": true
},
"node_modules/@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
@ -16521,6 +16528,12 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
},
"@types/lodash": {
"version": "4.14.186",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz",
"integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==",
"dev": true
},
"@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",

View File

@ -1,5 +1,5 @@
{
"name": "fashion-logica",
"name": "sakura",
"version": "0.0.2",
"scripts": {
"ng": "ng",
@ -46,6 +46,7 @@
"@angular/compiler-cli": "^14.0.0",
"@types/google-libphonenumber": "^7.4.23",
"@types/jasmine": "~4.0.0",
"@types/lodash": "^4.14.186",
"jasmine-core": "~4.1.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",

View File

@ -20,16 +20,16 @@ describe('AppComponent', () => {
expect(app).toBeTruthy();
});
it(`should have as title 'fashion-logica'`, () => {
it(`should have as title 'sakura'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('fashion-logica');
expect(app.title).toEqual('sakura');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('fashion-logica app is running!');
expect(compiled.querySelector('.content span')?.textContent).toContain('sakura app is running!');
});
});

View File

@ -6,5 +6,5 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'Fashionlogica';
title = 'Sakura';
}

View File

@ -1,4 +1,4 @@
import {OrderStatus, Page, PageCode} from "./interface/data";
import {MainPageCode, OrderStatus, Page, PageCode} from "./interface/data";
export const PageList: Page[] = [
{
@ -52,6 +52,27 @@ export const PageListWithBonus: Page[] = [
},
];
export const PageListMain: Page[] = [
{
code: MainPageCode.Account,
name: 'Аккаунт',
resName: 'account',
onSideBar: true,
},
{
code: MainPageCode.Products,
name: 'Товары',
resName: 'products',
onSideBar: true,
},
{
code: MainPageCode.Cart,
name: 'Корзина',
resName: 'cart',
onSideBar: true,
},
]
export const orderStatuses: OrderStatus = {
'Cancelled': 'Отменен',
'InProcessing': 'В обработке',

View File

@ -33,6 +33,11 @@ import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
import { ShareIconsModule } from 'ngx-sharebuttons/icons';
import { MessagingService } from './services/messaging.service';
import { NotFoundComponent } from './pages/not-found/not-found.component';
import { ProductsComponent } from './pages/products/products.component';
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';
@NgModule({
declarations: [
@ -49,7 +54,11 @@ import { NotFoundComponent } from './pages/not-found/not-found.component';
FooterButtonsComponent,
UserDataComponent,
RefSystemComponent,
NotFoundComponent
NotFoundComponent,
ProductsComponent,
CartComponent,
ProductModalComponent,
CheckboxGroupComponent
],
imports: [
BrowserModule,
@ -81,7 +90,8 @@ import { NotFoundComponent } from './pages/not-found/not-found.component';
ShareButtonsModule.withConfig({
debug: true
}),
ShareIconsModule
ShareIconsModule,
ListboxModule
],
providers: [DialogService, MessageService, MessagingService ],
bootstrap: [AppComponent]

View File

@ -0,0 +1 @@
<p>checkbox-group works!</p>

View File

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

View File

@ -0,0 +1,35 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Modifier, Option} from 'src/app/interface/data';
import { cloneDeep } from 'lodash/fp';
@Component({
selector: 'app-checkbox-group',
templateUrl: './checkbox-group.component.html',
styleUrls: ['./checkbox-group.component.scss']
})
export class CheckboxGroupComponent implements OnInit {
constructor() { }
@Input() modifier!: Modifier;
@Input() currencySymbol!: string;
@Output() toggle = new EventEmitter<Option>();
@Input() selectedOptions: Option[] = [];
public options: Option[] = [];
ngOnInit() {
// this.options = cloneDeep<Option[]>(this.modifier.options);
}
optionIsSelected(option: Option): boolean{
return !!this.selectedOptions.find(selected => selected.id === option.id);
}
onToggle(option: Option){
this.toggle.emit(option);
}
}

View File

@ -0,0 +1,22 @@
<div class="product-modal">
<img *ngIf="!product.image" src="./assets/no-image.png" alt="{{product.name}}">
<img *ngIf="product.image" src="{{product.image}}" alt="{{product.name}}">
<div class="product-modal__modifiers-container">
<ng-container *ngIf="product.modifiers_group.length">
<div *ngFor="let modifier of modifiersFilter()" class="product-card__modifier">
<h4>{{modifier.name}}</h4>
<!-- <app-checkbox-group [modifier]="modifier" currencySymbol="₽"
[selectedOptions]="selectedOptions(modifier)" (toggle)="addOption(modifier, $event)">
</app-checkbox-group> -->
</div>
</ng-container>
</div>
<div class="product-modal__footer">
<span>{{product.price}}₽</span>
<button class="product-modal__add-to-cart">
Добавить
</button>
</div>
</div>

View File

@ -0,0 +1,29 @@
:host {
.product-modal {
position: relative;
padding-bottom: 60px;
& > img {
width: 100%;
}
&__footer {
width: 100%;
height: auto;
display: flex;
position: absolute;
bottom: 0;
padding-top: 8px;
border-top: solid #d1d1d1 1px;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
&__add-to-cart {
padding: 8px;
border: none;
border-radius: 4px;
background-color: #0d457e;
color: #fff;
}
}
}

View File

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

View File

@ -0,0 +1,36 @@
import { Component, OnInit } from '@angular/core';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
import { AllData, Modifier, ModifiersGroup, Product } from 'src/app/interface/data';
import { WpJsonService } from 'src/app/services/wp-json.service';
@Component({
selector: 'app-product-modal',
templateUrl: './product-modal.component.html',
styleUrls: ['./product-modal.component.scss']
})
export class ProductModalComponent implements OnInit {
public product!: Product;
public allData!: AllData;
public modifiersGroups!: ModifiersGroup[];
public modifiers!: Modifier[];
constructor(
public dialogRef: DynamicDialogRef,
public config: DynamicDialogConfig,
private wpJsonService: WpJsonService
) { }
ngOnInit(): void {
this.product = this.config.data.product
this.modifiersGroups = this.config.data.modifiersGroups
this.modifiers = this.config.data.modifiers
}
modifiersFilter() {
return this.modifiersGroups.filter((value) => this.product.modifiers_group.includes(value.id))
}
// selectedOptions(modifier: Modifier): Option[]{
// return this.cartProduct.modifiers.find(cartModifier => cartModifier.id === modifier.id)?.options ?? [];
// }
}

View File

@ -7,9 +7,15 @@ export enum PageCode {
RefSystem,
UserData
}
export enum MainPageCode {
Account,
Products,
Cart
}
export interface Page {
code: PageCode;
code: PageCode | MainPageCode;
component?: any;
name: string;
description?: string;
@ -30,7 +36,7 @@ export interface BonusProgramAccount {
CardNumber: number;
Bonuses: number;
HoldedBonuses: number;
BonusProgramAccounts: BonusProgramAccount[];
BonusProgramAccounts: BonusProgramAccount[];
DateBonusBurn: string;
_links: any[];
_embedded: any;
@ -61,11 +67,11 @@ export interface Transaction {
DateActiveBonus: string;
AccountBonus: string;
Bonus: string;
ID:string;
HasPurchase?:boolean;
ID: string;
HasPurchase?: boolean;
}
export interface OrderStatus{
export interface OrderStatus {
[key: string]: string;
}
@ -81,48 +87,87 @@ export interface AcceptedOrder {
currency_symbol: string;
total: number;
address: {
city: string;
street: string;
house: number;
flat: number;
city: string;
street: string;
house: number;
flat: number;
};
payment_method: string;
shipping: {
name: string;
total: number;
name: string;
total: number;
};
date_created: string;
items: OrderProduct[]
}
export interface Product{
id: number;
export interface Product {
id: string;
name: string;
price: string;
image_url: string;
price: number;
image: string;
image_gallery: string[];
category_id: number;
description?: string;
description: string;
stock_status: string;
currency_symbol: string;
modifier_data: Modifier[];
short_description?: string;
guid?: string;
short_description: string;
guid: string;
groupId: string;
modifiers_group: string[];
}
export interface Modifier{
id: number;
export interface AllData {
datetime: string;
groups: Group[];
products: Product[];
modifiers_groups: ModifiersGroup[];
modifiers: Modifier[];
categories: any[]
}
export interface Group {
id: string;
name: string;
category_type: string;
minimum_options: number;
maximum_options: number;
global_categories: string;
required: number;
options: Option[];
allOptions?: Option[];
}
export interface Option{
export interface ModifiersGroup {
name: string,
id: string,
restrictions: {
minQuantity: number,
maxQuantity: number,
freeQuantity: number,
byDefault: number
}
}
export interface Modifier {
id: string,
name: string,
groupId: string,
restrictions: {
minQuantity: number,
maxQuantity: number,
freeQuantity: number,
byDefault: number
}
}
// export interface Modifier {
// id: number;
// name: string;
// category_type: string;
// minimum_options: number;
// maximum_options: number;
// global_categories: string;
// required: number;
// options: Option[];
// allOptions?: Option[];
// }
export interface Option {
id: number;
name: string;
price: string;
@ -130,17 +175,17 @@ export interface Option{
active?: boolean;
}
export interface OrderProduct{
export interface OrderProduct {
id: number;
amount: number;
name: string;
price: number;
modifiers: {
[name: string]: OrderModifier[]
[name: string]: OrderModifier[]
}
}
export interface OrderModifier{
export interface OrderModifier {
name: string;
id: number;
price: number;

View File

@ -8,7 +8,7 @@ export class OrderProduct implements Product{
this.description = product.description;
this.id = product.id;
this.image_gallery = product.image_gallery;
this.image_url = product.image_url;
this.image = product.image;
this.modifier_data = product.modifier_data;
this.name = product.name;
this.price = product.price;
@ -16,31 +16,36 @@ export class OrderProduct implements Product{
this.amount = amount;
this.guid = guid;
this.short_description = product.short_description;
this.groupId = product.groupId;
this.modifiers_group = product.modifiers_group;
}
public amount: number;
public category_id: number;
public currency_symbol: string;
public description?: string;
public short_description?: string;
public id: number;
public description: string;
public short_description: string;
public id: string;
public image_gallery: string[];
public image_url: string;
public image: string;
public modifier_data: Modifier[];
public name: string;
public price: string;
public price: number;
public stock_status: string;
public guid: string;
public groupId: string;
public modifiers_group: string[];
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;
// 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()
}
@ -52,7 +57,7 @@ export class OrderProduct implements Product{
modifiers: this.modifier_data?.map(modifier => {
return {
id: modifier.id,
options: modifier.options,
// options: modifier.options,
}
}),
}

View File

@ -2,50 +2,82 @@
woocommerce: true,
'auth-page': currentPage.code === PageCode.Auth
}">
<div *ngIf="currentPage.code !== PageCode.Auth" class="top-left-attribute"></div>
<div [ngSwitch]="currentPage.code" class="">
<ng-container *ngSwitchCase="PageCode.Auth">
<app-auth [handleHttpError]="handleHttpErrorFunc" (phoneConfirmed)="phoneConfirmed()"></app-auth>
</ng-container>
<ng-container *ngSwitchCase="PageCode.Orders">
<app-orders></app-orders>
</ng-container>
<ng-container *ngSwitchCase="PageCode.BonusProgram">
<app-bonus-program></app-bonus-program>
</ng-container>
<ng-container *ngSwitchCase="PageCode.UserData">
<app-user-data></app-user-data>
</ng-container>
<ng-container *ngSwitchCase="PageCode.RefSystem">
<app-ref-system></app-ref-system>
</ng-container>
</div>
<nav *ngIf="currentPage.code !== PageCode.Auth" class="woocommerce-MyAccount-navigation">
<nav *ngIf="currentPage.code !== PageCode.Auth" class="main-menu-container">
<ul>
<ng-container *ngFor="let page of pageList; let index = index">
<li *ngIf="page.onSideBar" class="woocommerce-MyAccount-navigation-link" [ngClass]="{
'is-active': page === currentPage,
first: index === 1
}" (click)="changePage($event, page)">
<div class="container">
<img src="{{ './assets/menu-icons/' + page.resName + '.png' }}" alt="Иконка меню" />
<div class="menu-item-info">
<a href="#">{{ page.name }}</a>
<p>{{ page.description }}</p>
</div>
</div>
<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]="{
'is-active': page === currentPageMain
}"
[ngStyle]="{
border: last && 0,
'border-radius': first ? '6px 0 0 6px' : (last ? '0 6px 6px 0' : 0)
}"
(click)="changeMainPage(page, $event)">
<span>
{{page.name}}
</span>
</li>
</ng-container>
<li class="woocommerce-MyAccount-navigation-link" (click)="logout($event)">
<div class="container">
<img src="./assets/menu-icons/exit.png" alt="Иконка меню" />
<div class="menu-item-info">
<a href="#">Выход</a>
</div>
</div>
</li>
</ul>
</nav>
<div [ngSwitch]="currentPageMain.code">
<ng-container *ngSwitchCase="MainPageCode.Account">
<div class="account-page">
<div *ngIf="currentPage.code !== PageCode.Auth" class="top-left-attribute"></div>
<div [ngSwitch]="currentPage.code" class="">
<ng-container *ngSwitchCase="PageCode.Auth">
<app-auth [handleHttpError]="handleHttpErrorFunc" (phoneConfirmed)="phoneConfirmed()"></app-auth>
</ng-container>
<ng-container *ngSwitchCase="PageCode.Orders">
<app-orders></app-orders>
</ng-container>
<ng-container *ngSwitchCase="PageCode.BonusProgram">
<app-bonus-program (deauthorization)="changePage(pageList[0])"></app-bonus-program>
</ng-container>
<ng-container *ngSwitchCase="PageCode.UserData">
<app-user-data></app-user-data>
</ng-container>
<ng-container *ngSwitchCase="PageCode.RefSystem">
<app-ref-system></app-ref-system>
</ng-container>
</div>
<nav *ngIf="currentPage.code !== PageCode.Auth" class="woocommerce-MyAccount-navigation">
<ul>
<ng-container *ngFor="let page of pageList; let index = index">
<li *ngIf="page.onSideBar" class="woocommerce-MyAccount-navigation-link" [ngClass]="{
'is-active': page === currentPage,
first: index === 1
}" (click)="changePage(page, $event)">
<div class="container">
<img src="{{ './assets/menu-icons/' + page.resName + '.png' }}" alt="Иконка меню" />
<div class="menu-item-info">
<a href="#">{{ page.name }}</a>
<p>{{ page.description }}</p>
</div>
</div>
</li>
</ng-container>
<li class="woocommerce-MyAccount-navigation-link" (click)="logout($event)">
<div class="container">
<img src="./assets/menu-icons/exit.png" alt="Иконка меню" />
<div class="menu-item-info">
<a href="#">Выход</a>
</div>
</div>
</li>
</ul>
</nav>
</div>
</ng-container>
<ng-container *ngSwitchCase="MainPageCode.Products">
<app-products></app-products>
</ng-container>
<ng-container *ngSwitchCase="MainPageCode.Cart">
<app-cart></app-cart>
</ng-container>
</div>
<span class="version" [ngClass]="{
version: true
}">

View File

@ -9,73 +9,100 @@
justify-content: space-between;
}
nav {
margin-bottom: 24px;
margin-top: 26px;
display: flex;
justify-content: center;
.main-menu-container {
max-width: 600px;
height: 50px;
margin: -20px auto 0 auto;
ul {
max-width: 400px;
width: 100%;
border-radius: 6px;
display: flex;
justify-content: space-between;
flex-direction: column;
height: 100%;
align-items: center;
font-size: 14px;
padding: 0 16px;
li {
padding: 12px;
width: 100%;
text-align: center;
border-right: solid #e1e1e1 1px;
padding: 8px 0;
cursor: pointer;
height: 81px;
margin-bottom: 10px;
background: #ffffff;
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
color: #000;
border-radius: 15px;
&.is-active {
border: solid #09467f 1px;
// display: none;
background: #0d457e;
color: #fff;
}
}
}
}
&.first {
// border-radius: 7px 0 0 7px;
}
.account-page {
nav {
margin-bottom: 24px;
margin-top: 26px;
display: flex;
justify-content: center;
.container {
display: flex;
align-items: center;
height: 100%;
ul {
max-width: 400px;
width: 100%;
border-radius: 6px;
display: flex;
justify-content: space-between;
flex-direction: column;
.menu-item-info {
margin-left: 16px;
li {
padding: 12px;
width: 100%;
text-align: center;
cursor: pointer;
height: 81px;
margin-bottom: 10px;
background: #ffffff;
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
color: #000;
border-radius: 15px;
&>a {
font-style: normal;
font-weight: 700;
font-size: 18px;
line-height: 22px;
text-decoration: none;
color: #000;
display: block;
text-align-last: left;
}
&.is-active {
border: solid #09467f 1px;
// display: none;
}
&>p {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
text-align: left;
&.first {
// border-radius: 7px 0 0 7px;
}
.container {
display: flex;
align-items: center;
height: 100%;
.menu-item-info {
margin-left: 16px;
&>a {
font-style: normal;
font-weight: 700;
font-size: 18px;
line-height: 22px;
text-decoration: none;
color: #000;
display: block;
text-align-last: left;
}
&>p {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
text-align: left;
}
}
}
}
}
li:nth-child(odd) {
background-color: #ebebeb;
li:nth-child(odd) {
background-color: #ebebeb;
}
}
}
}

View File

@ -1,9 +1,9 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { CookiesService } from '../../services/cookies.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Page, PageCode } from '../../interface/data';
import { MainPageCode, Page, PageCode } from '../../interface/data';
import { environment } from '../../../environments/environment';
import { PageList, PageListWithBonus } from '../../app.constants';
import { PageList, PageListMain, PageListWithBonus } from '../../app.constants';
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { ExitComponent } from '../../components/exit/exit.component';
import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
@ -28,7 +28,7 @@ export class AccountComponent implements OnInit {
private dialogService: DialogService,
private jsonRpcService: JsonrpcService,
private messageService: MessageService
) {}
) { }
public currentPage!: Page;
public handleHttpErrorFunc = this.handleHttpError.bind(this);
@ -40,6 +40,10 @@ export class AccountComponent implements OnInit {
? PageListWithBonus
: PageList;
readonly MainPageCode = MainPageCode;
readonly mainPageList = PageListMain;
public currentPageMain: Page = this.mainPageList[1];
ngOnInit(): void {
if (!this.getToken()) {
this.currentPage = this.pageList[0];
@ -73,13 +77,12 @@ export class AccountComponent implements OnInit {
async refSystem() {
const additionalInfo = (await lastValueFrom(
this.jsonRpcService.rpc({
method: 'getAdditionalInfo',
params: []
}, RpcService.authService, true)
this.jsonRpcService.rpc({
method: 'getAdditionalInfo',
params: []
}, RpcService.authService, true)
)).data
this.route.queryParams.subscribe((params) => {
console.log('####: ', params)
if (params['refUserId']) {
if (additionalInfo.refSystem?.code.length) {
this.messageService.add({
@ -131,8 +134,24 @@ export class AccountComponent implements OnInit {
});
}
changePage(event: MouseEvent, page: Page): void {
event.preventDefault();
changeMainPage(page: Page, event?: MouseEvent): void {
if (event) {
event.preventDefault();
}
this.currentPageMain = page;
this.router.navigate(['.'], {
relativeTo: this.route,
// queryParams: {
// activePage: this.currentPage.code,
// },
queryParamsHandling: 'merge',
});
}
changePage(page: Page, event?: MouseEvent): void {
if (event) {
event.preventDefault();
}
this.currentPage = page;
// let params = new HttpParams();
// params = params.append('activePage', this.currentPage.code);

View File

@ -1,4 +1,4 @@
import { Component, Inject, OnInit } from '@angular/core';
import { Component, EventEmitter, Inject, OnInit, Output } from '@angular/core';
import { lastValueFrom } from 'rxjs';
import { orderStatuses, PageList, PageListWithBonus } from 'src/app/app.constants';
import { BonusProgramAccount, Page, Purchase, Transaction } from 'src/app/interface/data';
@ -17,7 +17,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
styleUrls: ['./bonus-program.component.scss']
})
export class BonusProgramComponent implements OnInit {
@Output() deauthorization = new EventEmitter<boolean>(false)
public accountData!: BonusProgramAccount;
public purchases: Purchase[] = [];
public loadingBonuses: boolean = false;
@ -36,6 +36,8 @@ export class BonusProgramComponent implements OnInit {
private http: HttpClient,
) { }
ngOnInit(): void {
this.getAccountData();
this.getTypeDevice()
@ -55,6 +57,11 @@ export class BonusProgramComponent implements OnInit {
params: []
}, RpcService.authService, true).subscribe({
next: (res) => {
if (res.code == 334) {
this.cookiesService.deleteCookie('token')
this.deauthorization.emit(true)
return
}
this.userName = res.data.first_name
},
error: (err) => {

View File

@ -30,9 +30,7 @@ export class RefSystemComponent implements OnInit {
RpcService.authService,
true
)
)).data
console.log(accountData);
)).data
this.refUrl += accountData.user_id
this.loading = false
}
@ -41,7 +39,7 @@ export class RefSystemComponent implements OnInit {
if (navigator.share) {
navigator.share({
title: document.title,
text: "Fashionlogica",
text: "Sakura",
url: this.refUrl
})
.then(() => console.log('Successful share'))

View File

@ -79,7 +79,6 @@ export class UserDataComponent implements OnInit {
if (accountData.user_id) {
this.appleWallet.reloadCard(accountData.user_id).subscribe({
next: (value) => {
console.log(value);
},
error: (err) => {
console.error(err);

View File

@ -0,0 +1 @@
<p>cart works!</p>

View File

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

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.scss']
})
export class CartComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,14 @@
<div class="products-container">
<p-listbox [options]="groups" [(ngModel)]="selectedGroup" optionLabel="name" [style]="{'width':'fit-content', 'margin-bottom': '16px'}"></p-listbox>
<div class="products-container__items">
<div *ngFor="let product of filterByGroup()" class="products-container__item">
<img *ngIf="!product.image" src="./assets/no-image.png" alt="{{product.name}}">
<img *ngIf="product.image" src="{{product.image}}" alt="{{product.name}}">
<div class="products-container__name-container">
<p>{{product.name}}</p>
<span>{{product.price}}₽</span>
</div>
<button class="products-container__add-to-cart" (click)="addToCart($event, product)">В корзину</button>
</div>
</div>
</div>

View File

@ -0,0 +1,57 @@
:host {
.products-container {
width: 100%;
display: flex;
flex-direction: column;
margin-bottom: 16px;
&__items {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16px;
}
&__item {
height: auto;
min-height: 200px;
width: 150px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
padding-bottom: 38Px;
img {
width: 100%;
// max-width: 150px;
margin: 0 auto;
}
p {
font-size: 14px;
font-weight: 600;
width: 100%;
}
}
&__name-container {
width: 100%;
display: flex;
align-items: center;
margin-top: 8px;
}
&__add-to-cart {
width: 100%;
height: 30px;
background: #09467f;
border: none;
border-radius: 4px;
color: #fff;
position: absolute;
bottom: 0;
}
}
}

View File

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

View File

@ -0,0 +1,80 @@
import { Component, OnInit } from '@angular/core';
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';
import { WpJsonService } from 'src/app/services/wp-json.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.scss']
})
export class ProductsComponent implements OnInit {
public products!: Product[];
public groups: Group[] = [
{
id: uuidv4(),
name: 'Все'
}
];
public modifiersGroups!: ModifiersGroup[];
public modifiers!: Modifier[];
public selectedGroup: Group = this.groups[0];
constructor(
public dialogService: DialogService,
private wpJsonService: WpJsonService
) { }
ngOnInit(): void {
this.getData()
}
getData() {
this.wpJsonService.getAllData().subscribe({
next: (value) => {
this.products = value.products
this.groups = [...this.groups, ...value.groups]
this.modifiersGroups = value.modifiers_groups
this.modifiers = value.modifiers
}
})
}
filterByGroup() {
if (this.selectedGroup.name === 'Все') return this.products
return this.products.filter((product) => product.groupId === this.selectedGroup.id)
}
addToCart(event: MouseEvent, product: Product) {
if (event) {
event.preventDefault()
}
const ref = this.dialogService.open(ProductModalComponent, {
header: product.name,
width: 'auto',
style: {
'max-width': '90vw',
'max-height': '90vh',
},
contentStyle: {
'max-height': '90vh',
height: 'auto',
'max-width': '90vw',
overflow: 'auto',
},
data: {
product: product,
modifiersGroups: this.modifiersGroups,
modifiers: this.modifiers
},
baseZIndex: 10000,
autoZIndex: true,
dismissableMask: true,
closeOnEscape: true,
});
}
}

View File

@ -83,7 +83,6 @@ export class MessagingService {
*/
receiveMessage() {
this.angularFireMessaging.messages.subscribe((payload: any) => {
console.log('new message received. ', payload);
this.currentMessage.next(payload);
});
}

View File

@ -42,6 +42,10 @@ export class WpJsonService {
return this._request(`products/${id}`, 'GET');
}
getAllData(): Observable<any> {
return this._request('nomen_1eb3fb56-3c4c-43b7-9a04-ce532ab7548f.json', 'GET')
}
_request(path: string, method: string, body?: any, auth = false): Observable<any> {
const token = decodeURI(this.cookiesService.getItem('token') ?? '');
let headers = new HttpHeaders();
@ -56,7 +60,7 @@ export class WpJsonService {
body: this.body,
};
const url = environment.production ? window.location.origin + '/wp-json/woofood/v1/' : this.api
const url = environment.production ? window.location.origin + '/' : this.api
return this.http
.request( method, url + path + urlToken, options);

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,111 @@
{
"groups": [
{
"name": "тестовая категория",
"id": "05126d92-0e17-4c0f-b4c3-db6a9a1afdeb"
}
],
"products": [
{
"name": "Пицца",
"groupId": "05126d92-0e17-4c0f-b4c3-db6a9a1afdeb",
"id": "5d76d34a-d3be-48b9-8856-19ef825671c4",
"price": 234,
"image": "",
"modifiers_group": [
"78dc0a96-85fc-4ae3-87d0-2b4aabbea51a",
"00000000-0000-0000-0000-000000000000"
]
},
{
"name": "Пиво темное Козел",
"groupId": "05126d92-0e17-4c0f-b4c3-db6a9a1afdeb",
"id": "f1a2d10d-235a-4ac7-a9b4-836b025e9684",
"price": 123,
"image": "https://102922.selcdn.ru/ecomm/headline-demo-start/38180/images/items/4c252f382ab3851c3547d03155cf6ae0.jpeg",
"modifiers_group": []
},
{
"name": "Пиво темное Козел",
"groupId": "05126d92-0e17-4c0f-b4c3-db6a9a1afdeb",
"id": "f1a2d10d-235a-4ac7-a9b4-836b025e9684",
"price": 123,
"image": "https://102922.selcdn.ru/ecomm/headline-demo-start/38180/images/items/4c252f382ab3851c3547d03155cf6ae0.jpeg",
"modifiers_group": []
},
{
"name": "Пиво темное Козел",
"groupId": "05126d92-0e17-4c0f-b4c3-db6a9a1afdeb",
"id": "f1a2d10d-235a-4ac7-a9b4-836b025e9684",
"price": 123,
"image": "https://102922.selcdn.ru/ecomm/headline-demo-start/38180/images/items/4c252f382ab3851c3547d03155cf6ae0.jpeg",
"modifiers_group": []
},
{
"name": "Пиво темное Козел",
"groupId": "05126d92-0e17-4c0f-b4c3-db6a9a1afdeb7",
"id": "f1a2d10d-235a-4ac7-a9b4-836b025e9684",
"price": 123,
"image": "https://102922.selcdn.ru/ecomm/headline-demo-start/38180/images/items/4c252f382ab3851c3547d03155cf6ae0.jpeg",
"modifiers_group": []
}
],
"modifiers_groups": [
{
"name": "Добавки к пицце",
"id": "78dc0a96-85fc-4ae3-87d0-2b4aabbea51a",
"restrictions": {
"minQuantity": 0,
"maxQuantity": 1,
"freeQuantity": 0,
"byDefault": 0
}
},
{
"name": "Дополнительно",
"id": "00000000-0000-0000-0000-000000000000",
"restrictions": {
"minQuantity": 0,
"maxQuantity": 0,
"freeQuantity": 0,
"byDefault": 0
}
}
],
"modifiers": [
{
"groupId": "78dc0a96-85fc-4ae3-87d0-2b4aabbea51a",
"name": "С колбасой",
"id": "ab5e5ee1-4772-4b36-a793-751315e35436",
"restrictions": {
"minQuantity": 0,
"maxQuantity": 0,
"freeQuantity": 0,
"byDefault": 0
}
},
{
"groupId": "78dc0a96-85fc-4ae3-87d0-2b4aabbea51a",
"name": "С ветчиной",
"id": "e9fd6d73-eab2-4d3b-81b2-1b88c9c712d2",
"restrictions": {
"minQuantity": 0,
"maxQuantity": 0,
"freeQuantity": 0,
"byDefault": 0
}
},
{
"groupId": "00000000-0000-0000-0000-000000000000",
"name": "С собой",
"id": "d2c369f7-1d05-49ad-9387-157ebf253e10",
"restrictions": {
"minQuantity": 0,
"maxQuantity": 1,
"freeQuantity": 0,
"byDefault": 0
}
}
],
"categories": []
}

View File

@ -7,7 +7,7 @@ export const environment = {
appWPEndpoint: 'http://213.239.210.240:4500/wp-json/woofood/v1/',
hasBonusProgram: true,
systemId: 'g6zyv8tj53w28ov7cl',
defaultUrl: 'https://fashionlogica.lk.crm4retail.ru',
defaultUrl: 'https://sakura.lk.crm4retail.ru',
firebase: {
apiKey: "AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds",
authDomain: "fashionlogicanotification.firebaseapp.com",
@ -20,5 +20,5 @@ export const environment = {
version: packageJson.version,
appleWalletEndpoint: 'https://apple-push-notifications.it-retail.tech/apns/api',
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
clientName: 'fashionlogica'
clientName: 'Sakura'
}

View File

@ -4,7 +4,7 @@ export const environment = {
production: false,
appAuthEndpoint: 'https://auth.crm4retail.ru/tnt',
appBonusEndpoint: 'https://customerapi2.mi.crm4retail.ru/json.rpc/',
appWPEndpoint: 'http://192.168.0.179:4200/wp-json/woofood/v1/',
appWPEndpoint: './assets/',
hasBonusProgram: true,
systemId: 'g6zyv8tj53w28ov7cl',
defaultUrl: 'http://192.168.0.179:4200',
@ -20,5 +20,5 @@ export const environment = {
version: packageJson.version,
appleWalletEndpoint: 'http://192.168.0.179:4200/apns/api',
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
clientName: 'fashionlogica'
clientName: 'Sakura'
};

View File

@ -2,7 +2,7 @@
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Fashionlogica</title>
<title>Sakura</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" href="./assets/icons/apple-icon-180x180.png">

View File

@ -1,6 +1,6 @@
{
"name": "Fashionlogica",
"short_name": "Fashionlogica",
"name": "Sakura",
"short_name": "Sakura",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",