Compare commits
35 Commits
1bec150822
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
394ac5ec9d | ||
|
|
4fb918f5e4 | ||
|
|
3b802b7c7b | ||
|
|
1b2a5af2be | ||
|
|
6410321895 | ||
|
|
51445a7341 | ||
|
|
56df8632cb | ||
|
|
824bed199c | ||
|
|
b5728f1238 | ||
|
|
04814aeeda | ||
|
|
9386fbc2f8 | ||
|
|
a06b654103 | ||
|
|
9aaff4d80a | ||
|
|
7a06843bf5 | ||
|
|
1decc08f77 | ||
|
|
c0cfbcbcbb | ||
|
|
688c225911 | ||
|
|
3e79304e5c | ||
|
|
e7d8ec8c63 | ||
|
|
1e3cd99c69 | ||
|
|
3ab67cbe2d | ||
|
|
b3c056980d | ||
|
|
fb3bb6c77c | ||
|
|
bdc330c885 | ||
|
|
31da7f85cf | ||
|
|
69e63fc5f3 | ||
|
|
fe6fc2cb74 | ||
|
|
80cc90d347 | ||
|
|
9b5c2dd95c | ||
|
|
58e0869916 | ||
|
|
14bdd3bcd0 | ||
|
|
a10216a392 | ||
|
|
e53c8230e6 | ||
|
|
c6bc05560e | ||
|
|
63b0e18396 |
18
angular.json
18
angular.json
@@ -40,6 +40,10 @@
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.production.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/app/interceptors/mock-data.interceptor.ts",
|
||||
"with": "src/app/interceptors/mock-data.interceptor.production.ts"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
@@ -49,7 +53,7 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumWarning": "600kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
@@ -92,6 +96,10 @@
|
||||
{
|
||||
"replace": "src/app/brands/brand-routes.ts",
|
||||
"with": "src/app/brands/brand-routes.novo.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/app/interceptors/mock-data.interceptor.ts",
|
||||
"with": "src/app/interceptors/mock-data.interceptor.production.ts"
|
||||
}
|
||||
],
|
||||
"index": "src/index.novo.html",
|
||||
@@ -124,7 +132,7 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumWarning": "600kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
@@ -158,6 +166,10 @@
|
||||
{
|
||||
"replace": "src/app/brands/brand-routes.ts",
|
||||
"with": "src/app/brands/brand-routes.lavero.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/app/interceptors/mock-data.interceptor.ts",
|
||||
"with": "src/app/interceptors/mock-data.interceptor.production.ts"
|
||||
}
|
||||
],
|
||||
"index": "src/index.lavero.html",
|
||||
@@ -190,7 +202,7 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumWarning": "600kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
|
||||
968
package-lock.json
generated
968
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -12,24 +12,21 @@
|
||||
"build:dexar": "ng build --configuration=production",
|
||||
"build:novo": "ng build --configuration=novo-production",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test",
|
||||
"lavero": "ng serve --configuration=lavero --port 4202 --proxy-config proxy.conf.lavero.json",
|
||||
"start:lavero": "ng serve --configuration=lavero --port 4202",
|
||||
"build:lavero": "ng build --configuration=lavero-production"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^21.1.5",
|
||||
"@angular/cdk": "^21.1.5",
|
||||
"@angular/common": "^21.0.6",
|
||||
"@angular/compiler": "^21.0.6",
|
||||
"@angular/core": "^21.0.6",
|
||||
"@angular/forms": "^21.0.6",
|
||||
"@angular/material": "^21.1.5",
|
||||
"@angular/platform-browser": "^21.0.6",
|
||||
"@angular/platform-browser-dynamic": "^21.1.5",
|
||||
"@angular/router": "^21.0.6",
|
||||
"@angular/service-worker": "^21.0.6",
|
||||
"@angular/animations": "21.1.5",
|
||||
"@angular/cdk": "21.1.5",
|
||||
"@angular/common": "21.1.5",
|
||||
"@angular/compiler": "21.1.5",
|
||||
"@angular/core": "21.1.5",
|
||||
"@angular/forms": "21.1.5",
|
||||
"@angular/platform-browser": "21.1.5",
|
||||
"@angular/router": "21.1.5",
|
||||
"@angular/service-worker": "21.1.5",
|
||||
"primeicons": "^7.0.0",
|
||||
"primeng": "^21.0.3",
|
||||
"rxjs": "~7.8.0",
|
||||
@@ -37,16 +34,9 @@
|
||||
"zone.js": "~0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^21.0.6",
|
||||
"@angular/cli": "^21.0.6",
|
||||
"@angular/compiler-cli": "^21.0.6",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.13.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"@angular/build": "21.1.5",
|
||||
"@angular/cli": "21.1.5",
|
||||
"@angular/compiler-cli": "21.1.5",
|
||||
"typescript": "~5.9.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
}
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
<app-footer></app-footer>
|
||||
<app-telegram-login />
|
||||
@defer (on viewport) {
|
||||
<app-footer></app-footer>
|
||||
} @placeholder {
|
||||
<div class="footer-placeholder" aria-hidden="true"></div>
|
||||
}
|
||||
<!-- <app-telegram-login /> -->
|
||||
}
|
||||
@@ -5,6 +5,10 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footer-placeholder {
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.server-check-overlay,
|
||||
.server-error-overlay {
|
||||
display: flex;
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
import { Component, OnInit, signal, ApplicationRef, inject, DestroyRef } from '@angular/core';
|
||||
import { Router, RouterOutlet, NavigationEnd } from '@angular/router';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { BackButtonComponent } from './components/back-button/back-button.component';
|
||||
import { TelegramLoginComponent } from './components/telegram-login/telegram-login.component';
|
||||
import { ApiService } from './services';
|
||||
import { interval, concat } from 'rxjs';
|
||||
import { filter, first } from 'rxjs/operators';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
@@ -17,7 +16,7 @@ import { TranslateService } from './i18n/translate.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, HeaderComponent, FooterComponent, BackButtonComponent, TelegramLoginComponent, TranslatePipe],
|
||||
imports: [RouterOutlet, HeaderComponent, FooterComponent, BackButtonComponent, TranslatePipe],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss'
|
||||
})
|
||||
@@ -28,7 +27,7 @@ export class App implements OnInit {
|
||||
serverAvailable = signal(false);
|
||||
|
||||
private destroyRef = inject(DestroyRef);
|
||||
private apiService = inject(ApiService);
|
||||
private http = inject(HttpClient);
|
||||
private titleService = inject(Title);
|
||||
private swUpdate = inject(SwUpdate);
|
||||
private appRef = inject(ApplicationRef);
|
||||
@@ -56,7 +55,7 @@ export class App implements OnInit {
|
||||
|
||||
private checkServerHealth(): void {
|
||||
this.checkingServer.set(true);
|
||||
this.apiService.ping()
|
||||
this.http.get<{ message: string }>(`${environment.apiUrl}/ping`)
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
@@ -92,13 +91,5 @@ export class App implements OnInit {
|
||||
console.error('Update check failed:', err);
|
||||
}
|
||||
});
|
||||
|
||||
this.swUpdate.versionUpdates
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(event => {
|
||||
if (event.type === 'VERSION_READY') {
|
||||
console.log('New app version ready');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
<p>We are a rapidly growing marketplace connecting sellers and buyers from different countries. Our platform creates convenient conditions for safe trading of various goods and services.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h3>Our Mission</h3>
|
||||
<p>To create a simple and profitable ecosystem for businesses and buyers, where everyone finds the best deals.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🌍</div>
|
||||
<h3>Geography</h3>
|
||||
<p>We operate in Russia, Armenia, UAE, Turkey, China, Kazakhstan, Kyrgyzstan, and other countries.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💼</div>
|
||||
<h3>For Business</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>For Buyers</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Our Values</h3>
|
||||
<div class="features-list">
|
||||
@@ -86,7 +86,7 @@
|
||||
<p><strong>Address:</strong> ARMENIA, KOTAYK, ABOVYAN, VERIN PTGHNI, 3rd Street, 28</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Contact Us</h3>
|
||||
<a href="mailto:info@lavero.store" class="contact-email">info@lavero.store</a>
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
<p>Մենք դինամիկ զարգացող մարկեթփլեյս ենք, որը միավորում է վաճառողներին և գնորդներին տարբեր երկրներից։ Մեր հարթակը ստեղծում է հարմար պայմաններ տարբեր ապրանքների և ծառայությունների անվտանգ առևտրի համար։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h3>Մեր առաքելությունը</h3>
|
||||
<p>Ստեղծել պարզ և շահավետ էկոհամակարգ բիզնեսի և գնորդների համար, որտեղ բոլորը գտնեն լավագույն առաջարկները։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🌍</div>
|
||||
<h3>Աշխարհագրություն</h3>
|
||||
<p>Մենք աշխատում ենք Ռուսաստանում, Հայաստանում, ԱՀԷ-ում, Թուրքիայում, Չինաստանում, Ղազախստանում, Ղրղզստանում և այլ երկրներում։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💼</div>
|
||||
<h3>Բիզնեսի համար</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Գնորդների համար</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Մեր արժեքները</h3>
|
||||
<div class="features-list">
|
||||
@@ -86,7 +86,7 @@
|
||||
<p><strong>Hasцe՝</strong> ՀАЙАСТАН, КОТАЙК, АБОВЯН, ВЕРИН ПТГНИ, 3-рд ПOЛOC, 28</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Կապվել մեզ հետ</h3>
|
||||
<a href="mailto:info@lavero.store" class="contact-email">info@lavero.store</a>
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
<p>Мы - динамично развивающийся маркетплейс, объединяющий продавцов и покупателей из разных стран. Наша платформа создает удобные условия для безопасной торговли различными товарами и услугами.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h3>Наша миссия</h3>
|
||||
<p>Создавать простую и выгодную экосистему для бизнеса и покупателей, где каждый находит лучшие предложения.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🌍</div>
|
||||
<h3>География</h3>
|
||||
<p>Мы работаем в России, Армении, ОАЭ, Турции, Китае, Казахстане, Кыргызстане и других странах.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💼</div>
|
||||
<h3>Для бизнеса</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Для покупателей</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Наши ценности</h3>
|
||||
<div class="features-list">
|
||||
@@ -86,7 +86,7 @@
|
||||
<p><strong>Адрес:</strong> АРМЕНИЯ, КОТАЙК, АБОВЯН, ВЕРИН ПТГНИ, ул. 3-я, 28</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Связаться с нами</h3>
|
||||
<a href="mailto:info@lavero.store" class="contact-email">info@lavero.store</a>
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Organization</h3>
|
||||
<p class="org-name">LLC «ELECTROMOTORS»</p>
|
||||
<p><strong>TIN:</strong> 03590442</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Phone</h3>
|
||||
<p><a [href]="env.phoneTel">{{ env.phones.support }}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✉️</div>
|
||||
<h3>Email</h3>
|
||||
<p><a href="mailto:info@lavero.store">info@lavero.store</a></p>
|
||||
<p class="note">Response within 24 hours</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Address</h3>
|
||||
<p>Armenia, 0501, Aragatsotn region, Talin, 12 Gaya St.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏰</div>
|
||||
<h3>Working Hours</h3>
|
||||
<p><strong>Support:</strong> 9:00 - 21:00</p>
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Կազմակերպություն</h3>
|
||||
<p class="org-name">ՍՊԸ «ԷԼԵԿՏՌՈՄՈՏՈՌՍ»</p>
|
||||
<p><strong>ՀՎՀՀ՝</strong> 03590442</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Հեռախոս</h3>
|
||||
<p><a [href]="env.phoneTel">{{ env.phones.support }}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✉️</div>
|
||||
<h3>Էլ. փոստ</h3>
|
||||
<p><a href="mailto:info@lavero.store">info@lavero.store</a></p>
|
||||
<p class="note">Պատասխանը 24 ժամվա ընթացքում</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Հասցե</h3>
|
||||
<p>Հայաստան, 0501, Արագածոտնի մարզ, ք. Տալին, Գայայի փող. 12</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏰</div>
|
||||
<h3>Աշխատանքային ժամեր</h3>
|
||||
<p><strong>Աջակցություն՝</strong> 9:00 - 21:00</p>
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Организация</h3>
|
||||
<p class="org-name">ООО «ЛАВЕРО»</p>
|
||||
<p><strong>ИНН:</strong> 03590442</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Телефон</h3>
|
||||
<p><a [href]="env.phoneTel">{{ env.phones.support }}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✉️</div>
|
||||
<h3>Email</h3>
|
||||
<p><a href="mailto:info@lavero.store">info@lavero.store</a></p>
|
||||
<p class="note">Ответ в течение 24 часов</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Адрес</h3>
|
||||
<p>АРМЕНИЯ, КОТАЙК, АБОВЯН, ВЕРИН ПТГНИ, ул. 3-я, 28</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏰</div>
|
||||
<h3>Часы работы</h3>
|
||||
<p><strong>Поддержка:</strong> 9:00 - 21:00</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📧</div>
|
||||
<h3>Digital Products</h3>
|
||||
<div class="features-list">
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ The platform is not responsible for digital products. The seller is responsible for quality and functionality.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📦</div>
|
||||
<h3>Physical Products</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -32,7 +32,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ The platform is not responsible for the actions of shipping companies. Delivery is handled by СДЭК, Почта России, Boxberry, DPD, and other carriers.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h3>Delivery Cost</h3>
|
||||
<div class="delivery-cost">
|
||||
@@ -48,7 +48,7 @@
|
||||
<p class="note">Exact cost is calculated at checkout</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔍</div>
|
||||
<h3>Tracking</h3>
|
||||
<p>After shipping, you will receive a tracking number by email. Track your package on the delivery service website or in your account.</p>
|
||||
@@ -66,7 +66,7 @@
|
||||
<p class="note important">If there are issues - file a report with the courier</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Questions about delivery?</h3>
|
||||
<p>Contact the seller or us:</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📧</div>
|
||||
<h3>Թվային ապրանքներ</h3>
|
||||
<div class="features-list">
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Հարթակը պատասխանատվություն չի կրում թվային ապրանքների համար։ Որակի և գործունակության համար պատասխանատու է վաճառողը։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📦</div>
|
||||
<h3>Ֆիզիկական ապրանքներ</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -32,7 +32,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Հարթակը պատասխանատվություն չի կրում տրանսպորտային ընկերությունների գործողությունների համար։ Առաքման համար պատասխանատու են СДЭК, Почта России, Boxberry, DPD և այլ փոխադրողներ։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h3>Առաքման արժեքը</h3>
|
||||
<div class="delivery-cost">
|
||||
@@ -48,7 +48,7 @@
|
||||
<p class="note">Ճիշտ արժեքը հաշվարկվում է ձևակերպման ժամանակ</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔍</div>
|
||||
<h3>Հետագծում</h3>
|
||||
<p>Ուղարկմանից հետո դուք կստանաք թրեք-համար email-ով։ Հետևեք ծանրութը առաքման ծառայության կայքում կամ անձնական էջում։</p>
|
||||
@@ -66,7 +66,7 @@
|
||||
<p class="note important">Եթե խնդիրներ կան - կազմեք ակտ սուրհանդեսի հետ</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Առաքման հարցեր՞</h3>
|
||||
<p>Կապվեք վաճառողի կամ մեզ հետ՝</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📧</div>
|
||||
<h3>Цифровые товары</h3>
|
||||
<div class="features-list">
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Платформа не несет ответственности за цифровые товары. За качество и работоспособность отвечает продавец.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📦</div>
|
||||
<h3>Физические товары</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -32,7 +32,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Платформа не несет ответственности за действия транспортных компаний. За доставку отвечают СДЭК, Почта России, Boxberry, DPD и другие перевозчики.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h3>Стоимость доставки</h3>
|
||||
<div class="delivery-cost">
|
||||
@@ -48,7 +48,7 @@
|
||||
<p class="note">Точная стоимость рассчитывается при оформлении</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔍</div>
|
||||
<h3>Отслеживание</h3>
|
||||
<p>После отправки вы получите трек-номер на email. Отслеживайте посылку на сайте службы доставки или в личном кабинете.</p>
|
||||
@@ -66,7 +66,7 @@
|
||||
<p class="note important">Если есть проблемы - составьте акт с курьером</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Вопросы по доставке?</h3>
|
||||
<p>Свяжитесь с продавцом или нами:</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>How to place an order?</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Payment Methods</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class="note">*Depends on the seller</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🚚</div>
|
||||
<h3>Delivery</h3>
|
||||
<div class="delivery-info">
|
||||
@@ -44,13 +44,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h3>Product Returns</h3>
|
||||
<p>You can return a product in good condition within 7 days, provided it has not been used and the packaging is intact.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Security</h3>
|
||||
<div class="features-list">
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Order Processing</h3>
|
||||
<p>Your order is processed immediately after payment. The seller ships the product within 1-3 business days.</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Ինչպես կատարել պատվեր՞</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Վճարման եղանակներ</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class="note">*Կախված է վաճառողից</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🚚</div>
|
||||
<h3>Առաքում</h3>
|
||||
<div class="delivery-info">
|
||||
@@ -44,13 +44,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h3>Ապրանքի վերադարձ</h3>
|
||||
<p>Կարելի է վերադարձել որակյալ ապրանքը 7 օրվա ընթացքում, եթե այն չի օգտագործվել և փաթեթավորումը պահպանված է։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Անվտանգություն</h3>
|
||||
<div class="features-list">
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Պատվերի մշակում</h3>
|
||||
<p>Պատվերը մշակվում է վճարումից անմիջապես հետո։ Վաճառողը ապրանքը ուղարկում է 1-3 աշխատանքային օրվա ընթացքում։</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Как сделать заказ?</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Способы оплаты</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class="note">*Зависит от продавца</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🚚</div>
|
||||
<h3>Доставка</h3>
|
||||
<div class="delivery-info">
|
||||
@@ -44,13 +44,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h3>Возврат товара</h3>
|
||||
<p>Можно вернуть качественный товар в течение 7 дней, если он не использовался и сохранена упаковка.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Безопасность</h3>
|
||||
<div class="features-list">
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Обработка заказа</h3>
|
||||
<p>Заказ обрабатывается сразу после оплаты. Продавец отправляет товар в течение 1-3 рабочих дней.</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏷️</div>
|
||||
<h3>Warranty Periods</h3>
|
||||
<div class="warranty-periods">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✓</div>
|
||||
<h3>Warranty Conditions</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛠️</div>
|
||||
<h3>Your Rights for Defects</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -48,7 +48,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Repair Timeframe</h3>
|
||||
<p>Maximum 45 days by law. If the deadline is violated, you can request a replacement or a refund.</p>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h3>How to File a Claim</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Need Help?</h3>
|
||||
<p>In case of disputes:</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏷️</div>
|
||||
<h3>Երաշխիքի ժամկետներ</h3>
|
||||
<div class="warranty-periods">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✓</div>
|
||||
<h3>Երաշխիքի պայմաններ</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛠️</div>
|
||||
<h3>Ձեր իրավունքները թերության դեպքում</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -48,7 +48,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Նորոգման ժամկետ</h3>
|
||||
<p>Օրենքով առավելագույնը 45 օր։ Եթե ժամկետը խախտվի ՝ կարող եք պահանջել փոխարինում կամ գումարի վերադարձ։</p>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h3>Ինչպես դիմել հայտ</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Օգնությու՞ն պետք է՞</h3>
|
||||
<p>Վեճերի դեպքում՝</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lavero-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏷️</div>
|
||||
<h3>Сроки гарантии</h3>
|
||||
<div class="warranty-periods">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✓</div>
|
||||
<h3>Условия гарантии</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛠️</div>
|
||||
<h3>Ваши права при браке</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -48,7 +48,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Срок ремонта</h3>
|
||||
<p>Максимум 45 дней по закону. Если срок нарушен - можно требовать замену или возврат денег.</p>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h3>Как подать заявку</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Нужна помощь?</h3>
|
||||
<p>При возникновении споров:</p>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<p>2.3. All payments are processed through certified payment systems in compliance with PCI DSS security standards.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚙️</div>
|
||||
<h2>3. Payment Process</h2>
|
||||
<p>3.1. The order payment procedure includes the following steps:</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>3.3. The Buyer's payment obligation is considered fulfilled from the moment the funds are received in the payment system account.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>4. Payment Security</h2>
|
||||
<p>4.1. All payments are processed through a secure HTTPS connection using TLS 1.2 protocol and above.</p>
|
||||
@@ -84,7 +84,7 @@
|
||||
<p>4.4. In case of suspicious activity, the payment system has the right to request additional identity verification of the Buyer.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>5. Payment Confirmation</h2>
|
||||
<p>5.1. After successful payment, the Buyer receives a confirmation to the email address provided during checkout.</p>
|
||||
@@ -124,7 +124,7 @@
|
||||
<p>6.4. The Marketplace does not charge a fee for processing refunds. Payment system and bank fees may apply in accordance with their tariffs.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">❌</div>
|
||||
<h2>7. Failed Payments</h2>
|
||||
<p>7.1. A payment may be declined for the following reasons:</p>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<p>2.3. Բոլոր վճարումները մշակվում են սերտիֆիկացված վճարման համակարգերի միջոցով՝ PCI DSS անվտանգության ստանդարտներին համապատասխան։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚙️</div>
|
||||
<h2>3. Վճարման գործընթացը</h2>
|
||||
<p>3.1. Պատվերի վճարման գործընթացը ներառում է հետևյալ քայլերը՝</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>3.3. Գնորդի վճարման պարտավորությունը համարվում է կատարված վճարման համակարգի հաշվին դրամական միջոցների մուտքագրման պահից։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>4. Վճարումների անվտանգություն</h2>
|
||||
<p>4.1. Բոլոր վճարումները մշակվում են պաշտպանված HTTPS կապակցով՝ TLS 1.2 և ավելի բարձր արթանագրի օգտագործմամբ։</p>
|
||||
@@ -84,7 +84,7 @@
|
||||
<p>4.4. Կասկածելի գործունեության դեպքում վճարման համակարգը իրավունք ունի պահանջել Գնորդի ինքնության լրացուցիչ ստուգում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>5. Վճարման հաստատում</h2>
|
||||
<p>5.1. Հաջող վճարմանից հետո Գնորդը ստանում է հաստատում պատվերի ձևակերպման թելադրված էլ. փոստի հասցեին։</p>
|
||||
@@ -124,7 +124,7 @@
|
||||
<p>6.4. Միջոցների վերադարձի մշակման համար Մարկեթպլեյսը միջնորդավճար չի գանձում։ Վճարման համակարգերի և բանկերի միջնորդավճարները կարող են կիրառվել դրանց սակագներին համապատասխան։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">❌</div>
|
||||
<h2>7. Անհաջող վճարումներ</h2>
|
||||
<p>7.1. Վճարումը կարող է մերժվել հետևյալ պատճառներով՝</p>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<p>2.3. Все платежи обрабатываются через сертифицированные платежные системы с соблюдением стандартов безопасности PCI DSS.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚙️</div>
|
||||
<h2>3. Процесс оплаты</h2>
|
||||
<p>3.1. Процедура оплаты заказа включает следующие этапы:</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>3.3. Обязательство Покупателя по оплате считается исполненным с момента поступления денежных средств на счет платежной системы.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>4. Безопасность платежей</h2>
|
||||
<p>4.1. Все платежи обрабатываются через защищенное HTTPS-соединение с использованием протокола TLS 1.2 и выше.</p>
|
||||
@@ -84,7 +84,7 @@
|
||||
<p>4.4. В случае подозрительной активности платежная система имеет право запросить дополнительную верификацию личности Покупателя.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>5. Подтверждение оплаты</h2>
|
||||
<p>5.1. После успешной оплаты Покупатель получает подтверждение на указанный при оформлении заказа адрес электронной почты.</p>
|
||||
@@ -124,7 +124,7 @@
|
||||
<p>6.4. За обработку возврата средств Маркетплейс комиссию не взимает. Комиссии платежных систем и банков могут применяться в соответствии с их тарифами.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">❌</div>
|
||||
<h2>7. Неуспешные платежи</h2>
|
||||
<p>7.1. Платеж может быть отклонен по следующим причинам:</p>
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<p>3.1.5. An agreement between the Operator and a third party, where the latter entrusts the Operator with the processing of personal data of the Personal Data Subject or transfers personal data of the Personal Data Subject based on the concluded agreement.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">👥</div>
|
||||
<h2>4. CATEGORIES OF PERSONAL DATA SUBJECTS</h2>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h2>5. CATEGORIES OF PROCESSED PERSONAL DATA</h2>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<p>Include conditions for processing, storage, cross-border data transfer, work with publicly available sources, special and biometric data, as well as the procedure for obtaining clarifications in accordance with the legislation of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>7. RIGHTS OF THE PERSONAL DATA SUBJECT</h2>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
<p>The Subject also has the right to protect their rights, recover damages, and receive compensation for moral harm.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h2>8. OPERATOR'S OBLIGATIONS</h2>
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
<p>8.3. The Operator bears other obligations established by FZ-152.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>9. SECURITY ASSURANCE</h2>
|
||||
|
||||
@@ -222,14 +222,14 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚠️</div>
|
||||
<h2>10. LIABILITY</h2>
|
||||
|
||||
<p>10.1. Persons guilty of violating the norms of processing and protection of Personal Data bear liability in accordance with the legislation of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>11. PURPOSES OF PROCESSING</h2>
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🍪</div>
|
||||
<h2>12. AUTOMATICALLY COLLECTED INFORMATION</h2>
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<p>3.1.5. Договор между оператором и третьим лицом, где последнее поручает Оператору обработку персональных данных Субъекта Персональных данных или передает на основании заключенного договора персональные данные Субъекта Персональных данных.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">👥</div>
|
||||
<h2>4. КАТЕГОРИИ СУБЪЕКТОВ ПЕРСОНАЛЬНЫХ ДАННЫХ</h2>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h2>5. КАТЕГОРИИ ОБРАБАТЫВАЕМЫХ ПЕРСОНАЛЬНЫХ ДАННЫХ</h2>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<p>Включают условия обработки, хранения, трансграничной передачи данных, работы с общедоступными источниками, специальными и биометрическими данными, а также порядок получения разъяснений согласно законодательству РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>7. ПРАВА СУБЪЕКТА ПЕРСОНАЛЬНЫХ ДАННЫХ</h2>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
<p>Субъект также имеет право на защиту своих прав, возмещение убытков и компенсацию морального вреда.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h2>8. ОБЯЗАННОСТИ ОПЕРАТОРА</h2>
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
<p>8.3. Оператор несет иные обязанности, установленные ФЗ-152.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>9. ОБЕСПЕЧЕНИЕ БЕЗОПАСНОСТИ</h2>
|
||||
|
||||
@@ -222,14 +222,14 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚠️</div>
|
||||
<h2>10. ОТВЕТСТВЕННОСТЬ</h2>
|
||||
|
||||
<p>10.1. Лица, виновные в нарушении норм обработки и защиты Персональных данных, несут ответственность согласно законодательству РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>11. ЦЕЛИ ОБРАБОТКИ</h2>
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🍪</div>
|
||||
<h2>12. АВТОМАТИЧЕСКИ СОБИРАЕМАЯ ИНФОРМАЦИЯ</h2>
|
||||
|
||||
|
||||
@@ -36,12 +36,16 @@
|
||||
<p>1.9. Promotional campaigns may have special rules.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>2. Subject of the Agreement</h2>
|
||||
<p>2.1. The purpose is to provide users with the ability to purchase goods and services presented on the resource.</p>
|
||||
<p>2.2. The agreement regulates the use of the website and provided features.</p>
|
||||
<p>2.3. It applies to all types of goods, services, and products on the website.</p>
|
||||
<p>2.4. The Marketplace is an agent acting under agency agreements with Sellers (Third Parties). The Marketplace does not acquire ownership rights to the Goods/Services sold through the Platform and only provides the technical ability to conclude a transaction between the Seller and the Buyer.</p>
|
||||
<p>2.5. The Seller is an independent business entity and bears full responsibility for the quality, safety, conformity of the Goods/Services to the stated characteristics, and fulfillment of obligations to the Buyer (warranties, returns, claims).</p>
|
||||
<p>2.6. The Marketplace is not a party to the sale and purchase agreement between the Seller and the Buyer. All claims regarding the Goods/Services are submitted by the Buyer directly to the Seller.</p>
|
||||
<p>2.7. The Marketplace's agency remuneration is withheld from the amount paid by the Buyer and does not increase the price for the Buyer beyond the price set by the Seller.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card wide">
|
||||
@@ -57,7 +61,7 @@
|
||||
<p><strong>3.8. Applicable laws:</strong> Governed by Federal Law "On Consumer Rights Protection" No. 2300-1 and the Civil Code of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📱</div>
|
||||
<h2>4. Registration and Personal Account</h2>
|
||||
<p><strong>4.1. Procedure:</strong> Registration is not required for ordering but provides access to the personal account.</p>
|
||||
@@ -114,7 +118,7 @@
|
||||
<p><strong>Rights:</strong> Opt out of advertising messages through the website or by writing to <a href="mailto:info@lavero.store">info@lavero.store</a>.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">©</div>
|
||||
<h2>7. Exclusive Rights to Content</h2>
|
||||
<p><strong>7.1. Intellectual property:</strong> All content (design, texts, graphics, video, software, databases) is subject to the copyright of the owner and rights holders.</p>
|
||||
@@ -122,7 +126,7 @@
|
||||
<p><strong>7.3. Personal use:</strong> For personal non-commercial use while preserving copyright notices.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔗</div>
|
||||
<h2>8. Third-Party Websites and Content</h2>
|
||||
<p><strong>8.1. External links:</strong> The website may contain links to third-party resources. The website owner is not responsible for their content.</p>
|
||||
@@ -218,14 +222,14 @@
|
||||
<p><strong>13.7. Return method:</strong> The return method is specified in the application.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⏳</div>
|
||||
<h2>14. Term of the Agreement</h2>
|
||||
<p><strong>14.1. Commencement and termination:</strong> The agreement is active from the moment of acceptance by the user until revocation of acceptance.</p>
|
||||
<p><strong>14.2. Right of revocation:</strong> The owner may revoke the offer pursuant to Art. 436 of the Civil Code of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚡</div>
|
||||
<h2>15. Dispute Resolution Procedure</h2>
|
||||
<p><strong>15.1. Voluntary settlement:</strong> Mandatory pre-trial dispute resolution procedure.</p>
|
||||
@@ -247,12 +251,12 @@
|
||||
<p><strong>16.3. Legislation:</strong> Issues are resolved under the legislation of Armenia.</p>
|
||||
<p><strong>16.4. The term "Legislation":</strong> Refers to the laws of Armenia.</p>
|
||||
<p><strong>16.5. Free services:</strong> Do not imply the application of consumer protection regulations.</p>
|
||||
<p><strong>16.6. Absence of relationships:</strong> Do not establish agency, partnership, or employment relationships.</p>
|
||||
<p><strong>16.6. Absence of relationships:</strong> Except for the agency model under clause 2.4, the terms do not establish partnership, joint venture, or employment relationships.</p>
|
||||
<p><strong>16.7. Invalidity of clauses:</strong> Does not affect the legal force of the remaining provisions.</p>
|
||||
<p><strong>16.8. Response to violations:</strong> Non-intervention does not prevent subsequent protective measures.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h2>Contact Us</h2>
|
||||
<p>Questions about the agreement:</p>
|
||||
|
||||
@@ -36,12 +36,16 @@
|
||||
<p>1.9. Գովազդային ակցիաները կարող են ունենալ հատուկ կանոններ։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>2. Համաձայնագրի առարկան</h2>
|
||||
<p>2.1. Նպատակն է օգտագործողներին հնարավորություն տալ ռեսուրսում ներկայացված ապրանքներ և ծառայություններ ձեռք բերելու։</p>
|
||||
<p>2.2. Համաձայնագիրը կարգավորում է կայքի օգտագործման և տրամադրվող գործառույթների կարգը։</p>
|
||||
<p>2.3. Գործում է կայքում ներկայացված ապրանքների՝ ծառայությունների և պրոդուկտների բոլոր տեսակների նկատմամբ։</p>
|
||||
<p>2.4. Մարկետփլեյսը հանդիսանում է գործակալ, որը գործում է Վաճառողների (Երրորդ անձանց) հետ կնքված գործակալության պայմանագրի հիման վրա։ Մարկետփլեյսը չի ձեռք բերում Հարթակի միջոցով իրացվող Ապրանքների/Ծառայությունների նկատմամբ սեփականության իրավունք, այլ միայն ապահովում է Վաճառողի և Գնորդի միջև գործարք կնքելու տեխնիկական հնարավորությունը։</p>
|
||||
<p>2.5. Վաճառողը հանդիսանում է ինքնուրույն տնտեսվարող սուբյեկտ և կրում է ամբողջական պատասխանատվություն Ապրանքի/Ծառայության որակի, անվտանգության, հայտարարված բնութագրերին համապատասխանության, ինչպես նաև Գնորդի նկատմամբ պարտավորությունների կատարման համար (երաշխիքներ, վերադարձ, պահանջներ)։</p>
|
||||
<p>2.6. Մարկետփլեյսը չի հանդիսանում Վաճառողի և Գնորդի միջև առուվաճառքի պայմանագրի կողմ։ Ապրանքի/Ծառայության վերաբերյալ բոլոր պահանջները Գնորդը ներկայացնում է անմիջապես Վաճառողին։</p>
|
||||
<p>2.7. Մարկետփլեյսի գործակալական վարձատրությունը պահվում է Գնորդի վճարած գումարից և չի ավելացնում Գնորդի համար գինը Վաճառողի սահմանած գնից ավել։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card wide">
|
||||
@@ -57,7 +61,7 @@
|
||||
<p><strong>3.8. Կիրառվող օրենքներ՝</strong> Կարգավորվում են ՌԴ «Սպառողների իրավունքների պաշտպանության մասին» թիվ 2300-1 և ՌԴ քաղաքացիական օրենսգիրքով։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📱</div>
|
||||
<h2>4. Գրանցում և անձնական հաշիվ</h2>
|
||||
<p><strong>4.1. Ըթացակարգ՝</strong> Գրանցումը պարտադիր չէ պատվերի համար՝ սակայն հնարավորություն է տալիս անձնական հաշիվ մուտք գործելու։</p>
|
||||
@@ -114,7 +118,7 @@
|
||||
<p><strong>Իրավունքներ՝</strong> Հրաժարվել գովազդային հաղորդագրություններից կայքի միճոցով կամ նամակով <a href="mailto:info@lavero.store">info@lavero.store</a>։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">©</div>
|
||||
<h2>7. Բովանդակության բացառիկ իրավունքներ</h2>
|
||||
<p><strong>7.1. Մտավոր սեփականություն՝</strong> Բոլոր բովանդակությունը (դիզայն՝ տեքստեր՝ գրաֆիկա՝ տեսանյութ՝ ծրագրեր՝ տվյալների բազաներ) սեփականատիրոջ և իրավատերերի հեղինակային իրավունքների առարկան է։</p>
|
||||
@@ -122,7 +126,7 @@
|
||||
<p><strong>7.3. Անձնական օգտագործում՝</strong> Անձնական ոչ առևտրային օգտագործման համար՝ հեղինակային իրավունքների նիշերի պահպանմամբ։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔗</div>
|
||||
<h2>8. Երրորդ կողմերի կայքեր և բովանդակություն</h2>
|
||||
<p><strong>8.1. Արտաքին հղումներ՝</strong> Կայքը կարող է պարունակել երրորդ կողմի ռեսուրսների հղումներ։ Կայքի սեփականատերը պատասխանատու չէ դրանց բովանդակության համար։</p>
|
||||
@@ -218,14 +222,14 @@
|
||||
<p><strong>13.7. Վերադարձի մեխանիզմ՝</strong> Վերադարձի եղանակը նշվում է դիմումում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⌛</div>
|
||||
<h2>14. Համաձայնագրի գործողության ժամկետը</h2>
|
||||
<p><strong>14.1. Սկիզբ և դադարեցում՝</strong> Համաձայնագիրը գործում է օգտագործողի ընդունման պահից մինչև ընդունման հետկանչում։</p>
|
||||
<p><strong>14.2. Հետկանչման իրավունք՝</strong> Սեփականատերը կարող է օֆերտան հետ կանչել ՌԴ 436 հոդվածի համաձայն։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚡</div>
|
||||
<h2>15. Վեճերի լուծման կարգը</h2>
|
||||
<p><strong>15.1. Կամավոր կարգավորում՝</strong> Պարտադիր նախադատական վեճերի լուծման ընթացակարգ։</p>
|
||||
@@ -247,12 +251,12 @@
|
||||
<p><strong>16.3. Օրենսդրություն՝</strong> Հարցերը լուծվում են Հայաստանի օրենսդրությամբ։</p>
|
||||
<p><strong>16.4. «Օրենսդրություն» տերմին՝</strong> Նշանակում է Հայաստանի օրենքները։</p>
|
||||
<p><strong>16.5. Անվճար ծառայություններ՝</strong> Չեն ենթադրում սպառողների իրավունքների պաշտպանության նորմերի կիրառում։</p>
|
||||
<p><strong>16.6. Հարաբերությունների բացակայություն՝</strong> Չեն հաստատում գործակալային՝ գործընկերային կամ աշխատանքային հարաբերություններ։</p>
|
||||
<p><strong>16.6. Հարաբերությունների բացակայություն՝</strong> Բացի 2.4 կետով նախատեսված գործակալական մոդելից՝ պայմանները չեն հաստատում գործընկերային, համատեղ ձեռնարկության կամ աշխատանքային հարաբերություններ։</p>
|
||||
<p><strong>16.7. Կետերի անվավերություն՝</strong> Չի ազդում մնացած դրույթների իրավական ուժի վրա։</p>
|
||||
<p><strong>16.8. Խախտումների նկատմամբ արձագանք՝</strong> Անգործունեությունը չի խանգարում հետագա պաշտպանության միջոցներին։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h2>Կապ</h2>
|
||||
<p>Համաձայնագրի վերաբերյալ հարցեր՝</p>
|
||||
|
||||
@@ -36,12 +36,16 @@
|
||||
<p>1.9. Промо-кампании могут иметь специальные правила.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>2. Предмет соглашения</h2>
|
||||
<p>2.1. Целью является предоставление пользователям возможности покупать товары и услуги, представленные на ресурсе.</p>
|
||||
<p>2.2. Соглашение регулирует порядок использования сайта и предоставляемых функций.</p>
|
||||
<p>2.3. Действие распространяется на все типы товаров, услуг и продуктов на сайте.</p>
|
||||
<p>2.4. Маркетплейс является агентом, действующим на основании договора агентирования с Продавцами (Третьими лицами). Маркетплейс не приобретает право собственности на Товары/Услуги, реализуемые через Платформу, а лишь обеспечивает техническую возможность заключения сделки между Продавцом и Покупателем.</p>
|
||||
<p>2.5. Продавец является самостоятельным хозяйствующим субъектом и несёт полную ответственность за качество, безопасность, соответствие Товара/Услуги заявленным характеристикам, а также за исполнение обязательств перед Покупателем (гарантии, возврат, претензии).</p>
|
||||
<p>2.6. Маркетплейс не выступает стороной договора купли-продажи между Продавцом и Покупателем. Все претензии по Товару/Услуге Покупатель предъявляет непосредственно Продавцу.</p>
|
||||
<p>2.7. Агентское вознаграждение Маркетплейса удерживается из суммы, уплаченной Покупателем, и не увеличивает цену для Покупателя сверх установленной Продавцом.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card wide">
|
||||
@@ -57,7 +61,7 @@
|
||||
<p><strong>3.8. Применение законов:</strong> Регулируются ФЗ "О защите прав потребителей" №2300-1 и ГК РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📱</div>
|
||||
<h2>4. Регистрация и личный кабинет</h2>
|
||||
<p><strong>4.1. Процедура:</strong> Регистрация необязательна для заказа, но открывает доступ к личному кабинету.</p>
|
||||
@@ -114,7 +118,7 @@
|
||||
<p><strong>Права:</strong> Отказаться от рекламных сообщений через сайт или письмом на <a href="mailto:info@lavero.store">info@lavero.store</a>.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">©</div>
|
||||
<h2>7. Исключительные права на контент</h2>
|
||||
<p><strong>7.1. Интеллектуальная собственность:</strong> Весь контент (дизайн, тексты, графика, видео, программы, базы данных) является объектом авторских прав владельца и правообладателей.</p>
|
||||
@@ -122,7 +126,7 @@
|
||||
<p><strong>7.3. Личное использование:</strong> Для личного некоммерческого использования при сохранении обозначений авторских прав.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔗</div>
|
||||
<h2>8. Сайты и контент третьих лиц</h2>
|
||||
<p><strong>8.1. Внешние ссылки:</strong> Сайт может содержать ссылки на сторонние ресурсы. Ответственность за их содержание не относится к владельцу сайта.</p>
|
||||
@@ -218,14 +222,14 @@
|
||||
<p><strong>13.7. Механизм возврата:</strong> Способ возврата указывается в заявлении.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⏳</div>
|
||||
<h2>14. Срок действия соглашения</h2>
|
||||
<p><strong>14.1. Начало и прекращение:</strong> Соглашение активно с момента принятия пользователем до отзыва акцепта.</p>
|
||||
<p><strong>14.2. Право отзыва:</strong> Владелец может отозвать оферту согласно ст. 436 ГК РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚡</div>
|
||||
<h2>15. Порядок разрешения споров</h2>
|
||||
<p><strong>15.1. Добровольное урегулирование:</strong> Обязательный досудебный порядок рассмотрения споров.</p>
|
||||
@@ -247,12 +251,12 @@
|
||||
<p><strong>16.3. Законодательство:</strong> Вопросы разрешаются по законодательству Армении.</p>
|
||||
<p><strong>16.4. Термин "Законодательство":</strong> Подразумевает законы Армении.</p>
|
||||
<p><strong>16.5. Бесплатные услуги:</strong> Не предполагают применение норм о защите прав потребителей.</p>
|
||||
<p><strong>16.6. Отсутствие отношений:</strong> Не устанавливают агентских связей, партнёрства или трудовых отношений.</p>
|
||||
<p><strong>16.6. Отсутствие отношений:</strong> Кроме агентской модели по п. 2.4, условия не устанавливают партнёрства, совместного предприятия или трудовых отношений.</p>
|
||||
<p><strong>16.7. Недействительность пунктов:</strong> Не влияет на законную силу остальных положений.</p>
|
||||
<p><strong>16.8. Реакция на нарушения:</strong> Невмешательство не препятствует последующим мерам защиты.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h2>Контакты</h2>
|
||||
<p>Вопросы по соглашению:</p>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>1.5. Digital products are returned under special rules prescribed by legislation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📅</div>
|
||||
<h2>2. Time Frames</h2>
|
||||
<p><strong>2.1. Quality products:</strong></p>
|
||||
@@ -112,7 +112,7 @@
|
||||
<p>Our platform is always ready to help resolve disputes.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h2>6. Payment Refund</h2>
|
||||
<p>Funds are refunded to the same payment method:</p>
|
||||
@@ -134,14 +134,14 @@
|
||||
<p>The customer has the right to claim compensation for defective products and shipping costs.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔄</div>
|
||||
<h2>7. Product Exchange</h2>
|
||||
<p>If a non-defective product does not match in dimensions, size, or specifications, the customer may exchange it with the supplier for a suitable option.</p>
|
||||
<p>If the desired model is unavailable, the customer has the right to cancel the order and receive a refund.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>8. Warranty Service</h2>
|
||||
<p>Warranty information is displayed on the product page or available from the seller.</p>
|
||||
@@ -149,7 +149,7 @@
|
||||
<p>Warranty service does not cover physical damage, improper use, or unauthorized modifications.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚖️</div>
|
||||
<h2>9. Responsibilities of Parties</h2>
|
||||
<p>The supplier is responsible for product quality and timely refunds.</p>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>1.5. Թվային ապրանքները վերադարձվում են օրենսդրությամբ նախատեսված հատուկ կանոններով։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📅</div>
|
||||
<h2>2. Ժամանակային սահմաններ</h2>
|
||||
<p><strong>2.1. Որակյալ ապրանքներ՝</strong></p>
|
||||
@@ -112,7 +112,7 @@
|
||||
<p>Մեր հարթակը միշտ պատրաստ է օգնել վիճելի իրավիճակների կարգավորման հարցում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h2>6. Վճարման փոխհատուցում</h2>
|
||||
<p>Գումարը վերադարձվում է նույն վճարման եղանակով՝</p>
|
||||
@@ -134,14 +134,14 @@
|
||||
<p>Գնորդը իրավունք ունի պահանջել փոխհատուցում անորակ ապրանքների և առաքման ծախսերի համար։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔄</div>
|
||||
<h2>7. Ապրանքի փոխանակում</h2>
|
||||
<p>Եթե որակյալ ապրանքը չի համապատասխանում չափսերով, չափորոշներով կամ հատկանիշներով, գնորդը կարող է այն փոխանակել մատակարարի մոտ համապատասխան տարբերակով։</p>
|
||||
<p>Համապատասխան մոդելի բացակայության դեպքում գնորդը կարող է հրաժարվել պատվերից և ստանալ փոխհատուցում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>8. Երաշխիքային սպասարկում</h2>
|
||||
<p>Երաշխիքի պայմանները նշված են ապրանքի էջում կամ հասանելի են վաճառողից։</p>
|
||||
@@ -149,7 +149,7 @@
|
||||
<p>Երաշխիքային սպասարկումը չի տարածվում ֆիզիկական վնասների, ոչ պատշաճ օգտագործման կամ ինքնուրույն միջամտության դեպքում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚖️</div>
|
||||
<h2>9. Կողմերի պարտականություններ</h2>
|
||||
<p>Մատակարարը պատասխանատու է ապրանքի որակի և ժամանակային գումարի վերադարձի համար։</p>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>1.5. Электронные товары возвращаются по специальным правилам, прописанным в законодательстве.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📅</div>
|
||||
<h2>2. Временные рамки</h2>
|
||||
<p><strong>2.1. Качественная продукция:</strong></p>
|
||||
@@ -112,7 +112,7 @@
|
||||
<p>Наша площадка всегда готова помочь в урегулировании спорных ситуаций.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h2>6. Компенсация платежа</h2>
|
||||
<p>Деньги возмещаются на тот же способ оплаты:</p>
|
||||
@@ -134,14 +134,14 @@
|
||||
<p>Клиент имеет право потребовать компенсацию за некачественную продукцию и расходы по пересылке.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔄</div>
|
||||
<h2>7. Обмен продукции</h2>
|
||||
<p>При несоответствии исправного изделия по габаритам, размерам или параметрам, клиент может заменить его у поставщика на подходящий вариант.</p>
|
||||
<p>При отсутствии нужной модели клиент вправе отказаться от заказа и получить компенсацию.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>8. Сервисное обслуживание</h2>
|
||||
<p>Информация о гарантийных условиях размещена в карточке товара или доступна у продавца.</p>
|
||||
@@ -149,7 +149,7 @@
|
||||
<p>Гарантийное обслуживание не распространяется на физические ущербы, неправильное применение или самостоятельное вмешательство.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚖️</div>
|
||||
<h2>9. Обязанности участников</h2>
|
||||
<p>Поставщик несет ответственность за качество продукции и своевременное возмещение платежей.</p>
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
<p>We are a rapidly growing marketplace connecting sellers and buyers from different countries. Our platform creates convenient conditions for safe trading of various goods and services.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h3>Our Mission</h3>
|
||||
<p>To create a simple and profitable ecosystem for businesses and buyers, where everyone finds the best deals.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🌍</div>
|
||||
<h3>Geography</h3>
|
||||
<p>We operate in Russia, Armenia, UAE, Turkey, China, Kazakhstan, Kyrgyzstan, and other countries.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💼</div>
|
||||
<h3>For Business</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>For Buyers</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Our Values</h3>
|
||||
<div class="features-list">
|
||||
@@ -87,7 +87,7 @@
|
||||
<p><strong>Bank:</strong> To be confirmed</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Contact Us</h3>
|
||||
<a href="mailto:info@novo.market" class="contact-email">info@novo.market</a>
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
<p>Մենք դինամիկ զարգացող մարկեթփլեյս ենք, որը միավորում է վաճառողներին և գնորդներին տարբեր երկրներից։ Մեր հարթակը ստեղծում է հարմար պայմաններ տարբեր ապրանքների և ծառայությունների անվտանգ առևտրի համար։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h3>Մեր առաքելությունը</h3>
|
||||
<p>Ստեղծել պարզ և շահավետ էկոհամակարգ բիզնեսի և գնորդների համար, որտեղ բոլորը գտնեն լավագույն առաջարկները։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🌍</div>
|
||||
<h3>Աշխարհագրություն</h3>
|
||||
<p>Մենք աշխատում ենք Ռուսաստանում, Հայաստանում, ԱՀԷ-ում, Թուրքիայում, Չինաստանում, Ղազախստանում, Ղրղզստանում և այլ երկրներում։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💼</div>
|
||||
<h3>Բիզնեսի համար</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Գնորդների համար</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Մեր արժեքները</h3>
|
||||
<div class="features-list">
|
||||
@@ -87,7 +87,7 @@
|
||||
<p><strong>Բանկ՝</strong> Ճշտում է</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Կապվել մեզ հետ</h3>
|
||||
<a href="mailto:info@novo.market" class="contact-email">info@novo.market</a>
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
<p>Мы - динамично развивающийся маркетплейс, объединяющий продавцов и покупателей из разных стран. Наша платформа создает удобные условия для безопасной торговли различными товарами и услугами.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h3>Наша миссия</h3>
|
||||
<p>Создавать простую и выгодную экосистему для бизнеса и покупателей, где каждый находит лучшие предложения.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🌍</div>
|
||||
<h3>География</h3>
|
||||
<p>Мы работаем в России, Армении, ОАЭ, Турции, Китае, Казахстане, Кыргызстане и других странах.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💼</div>
|
||||
<h3>Для бизнеса</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Для покупателей</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -46,7 +46,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Наши ценности</h3>
|
||||
<div class="features-list">
|
||||
@@ -87,7 +87,7 @@
|
||||
<p><strong>Банк:</strong> Уточняется</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Связаться с нами</h3>
|
||||
<a href="mailto:info@novo.market" class="contact-email">info@novo.market</a>
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Organization</h3>
|
||||
<p class="org-name">LLC «ELECTROMOTORS»</p>
|
||||
<p><strong>TIN:</strong> 9909687443</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Phone</h3>
|
||||
<p><a [href]="env.phoneTel">{{ env.phones.support }}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✉️</div>
|
||||
<h3>Email</h3>
|
||||
<p><a href="mailto:info@novo.market">info@novo.market</a></p>
|
||||
<p class="note">Response within 24 hours</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Address</h3>
|
||||
<p>Armenia, 0501, Aragatsotn region, Talin, 12 Gaya St.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏰</div>
|
||||
<h3>Working Hours</h3>
|
||||
<p><strong>Support:</strong> 9:00 - 21:00</p>
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Կազմակերպություն</h3>
|
||||
<p class="org-name">ՍՊԸ «ԷԼԵԿՏՌՈՄՈՏՈՌՍ»</p>
|
||||
<p><strong>ՀՎՀՀ՝</strong> 9909687443</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Հեռախոս</h3>
|
||||
<p><a [href]="env.phoneTel">{{ env.phones.support }}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✉️</div>
|
||||
<h3>Էլ. փոստ</h3>
|
||||
<p><a href="mailto:info@novo.market">info@novo.market</a></p>
|
||||
<p class="note">Պատասխանը 24 ժամվա ընթացքում</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Հասցե</h3>
|
||||
<p>Հայաստան, 0501, Արագածոտնի մարզ, ք. Տալին, Գայայի փող. 12</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏰</div>
|
||||
<h3>Աշխատանքային ժամեր</h3>
|
||||
<p><strong>Աջակցություն՝</strong> 9:00 - 21:00</p>
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Организация</h3>
|
||||
<p class="org-name">ООО «ЭЛЕКТРОМОТОРС»</p>
|
||||
<p><strong>ИНН:</strong> 9909687443</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Телефон</h3>
|
||||
<p><a [href]="env.phoneTel">{{ env.phones.support }}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✉️</div>
|
||||
<h3>Email</h3>
|
||||
<p><a href="mailto:info@novo.market">info@novo.market</a></p>
|
||||
<p class="note">Ответ в течение 24 часов</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Адрес</h3>
|
||||
<p>Армения, 0501, Арагацотиская обл., г. Талин, ул. Гая, д. 12</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏰</div>
|
||||
<h3>Часы работы</h3>
|
||||
<p><strong>Поддержка:</strong> 9:00 - 21:00</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📧</div>
|
||||
<h3>Digital Products</h3>
|
||||
<div class="features-list">
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ The platform is not responsible for digital products. The seller is responsible for quality and functionality.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📦</div>
|
||||
<h3>Physical Products</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -32,7 +32,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ The platform is not responsible for the actions of shipping companies. Delivery is handled by СДЭК, Почта России, Boxberry, DPD, and other carriers.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h3>Delivery Cost</h3>
|
||||
<div class="delivery-cost">
|
||||
@@ -48,7 +48,7 @@
|
||||
<p class="note">Exact cost is calculated at checkout</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔍</div>
|
||||
<h3>Tracking</h3>
|
||||
<p>After shipping, you will receive a tracking number by email. Track your package on the delivery service website or in your account.</p>
|
||||
@@ -66,7 +66,7 @@
|
||||
<p class="note important">If there are issues - file a report with the courier</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Questions about delivery?</h3>
|
||||
<p>Contact the seller or us:</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📧</div>
|
||||
<h3>Թվային ապրանքներ</h3>
|
||||
<div class="features-list">
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Հարթակը պատասխանատվություն չի կրում թվային ապրանքների համար։ Որակի և գործունակության համար պատասխանատու է վաճառողը։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📦</div>
|
||||
<h3>Ֆիզիկական ապրանքներ</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -32,7 +32,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Հարթակը պատասխանատվություն չի կրում տրանսպորտային ընկերությունների գործողությունների համար։ Առաքման համար պատասխանատու են СДЭК, Почта России, Boxberry, DPD և այլ փոխադրողներ։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h3>Առաքման արժեքը</h3>
|
||||
<div class="delivery-cost">
|
||||
@@ -48,7 +48,7 @@
|
||||
<p class="note">Ճիշտ արժեքը հաշվարկվում է ձևակերպման ժամանակ</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔍</div>
|
||||
<h3>Հետագծում</h3>
|
||||
<p>Ուղարկմանից հետո դուք կստանաք թրեք-համար email-ով։ Հետևեք ծանրութը առաքման ծառայության կայքում կամ անձնական էջում։</p>
|
||||
@@ -66,7 +66,7 @@
|
||||
<p class="note important">Եթե խնդիրներ կան - կազմեք ակտ սուրհանդեսի հետ</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Առաքման հարցեր՞</h3>
|
||||
<p>Կապվեք վաճառողի կամ մեզ հետ՝</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📧</div>
|
||||
<h3>Цифровые товары</h3>
|
||||
<div class="features-list">
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Платформа не несет ответственности за цифровые товары. За качество и работоспособность отвечает продавец.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📦</div>
|
||||
<h3>Физические товары</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -32,7 +32,7 @@
|
||||
<p class="note important" style="margin-top: 12px;">⚠️ Платформа не несет ответственности за действия транспортных компаний. За доставку отвечают СДЭК, Почта России, Boxberry, DPD и другие перевозчики.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h3>Стоимость доставки</h3>
|
||||
<div class="delivery-cost">
|
||||
@@ -48,7 +48,7 @@
|
||||
<p class="note">Точная стоимость рассчитывается при оформлении</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔍</div>
|
||||
<h3>Отслеживание</h3>
|
||||
<p>После отправки вы получите трек-номер на email. Отслеживайте посылку на сайте службы доставки или в личном кабинете.</p>
|
||||
@@ -66,7 +66,7 @@
|
||||
<p class="note important">Если есть проблемы - составьте акт с курьером</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Вопросы по доставке?</h3>
|
||||
<p>Свяжитесь с продавцом или нами:</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>How to place an order?</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Payment Methods</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class="note">*Depends on the seller</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🚚</div>
|
||||
<h3>Delivery</h3>
|
||||
<div class="delivery-info">
|
||||
@@ -44,13 +44,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h3>Product Returns</h3>
|
||||
<p>You can return a product in good condition within 7 days, provided it has not been used and the packaging is intact.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Security</h3>
|
||||
<div class="features-list">
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Order Processing</h3>
|
||||
<p>Your order is processed immediately after payment. The seller ships the product within 1-3 business days.</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Ինչպես կատարել պատվեր՞</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Վճարման եղանակներ</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class="note">*Կախված է վաճառողից</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🚚</div>
|
||||
<h3>Առաքում</h3>
|
||||
<div class="delivery-info">
|
||||
@@ -44,13 +44,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h3>Ապրանքի վերադարձ</h3>
|
||||
<p>Կարելի է վերադարձել որակյալ ապրանքը 7 օրվա ընթացքում, եթե այն չի օգտագործվել և փաթեթավորումը պահպանված է։</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Անվտանգություն</h3>
|
||||
<div class="features-list">
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Պատվերի մշակում</h3>
|
||||
<p>Պատվերը մշակվում է վճարումից անմիջապես հետո։ Վաճառողը ապրանքը ուղարկում է 1-3 աշխատանքային օրվա ընթացքում։</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛍️</div>
|
||||
<h3>Как сделать заказ?</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Способы оплаты</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class="note">*Зависит от продавца</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🚚</div>
|
||||
<h3>Доставка</h3>
|
||||
<div class="delivery-info">
|
||||
@@ -44,13 +44,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h3>Возврат товара</h3>
|
||||
<p>Можно вернуть качественный товар в течение 7 дней, если он не использовался и сохранена упаковка.</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🔒</div>
|
||||
<h3>Безопасность</h3>
|
||||
<div class="features-list">
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Обработка заказа</h3>
|
||||
<p>Заказ обрабатывается сразу после оплаты. Продавец отправляет товар в течение 1-3 рабочих дней.</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏷️</div>
|
||||
<h3>Warranty Periods</h3>
|
||||
<div class="warranty-periods">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✓</div>
|
||||
<h3>Warranty Conditions</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛠️</div>
|
||||
<h3>Your Rights for Defects</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -48,7 +48,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Repair Timeframe</h3>
|
||||
<p>Maximum 45 days by law. If the deadline is violated, you can request a replacement or a refund.</p>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h3>How to File a Claim</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Need Help?</h3>
|
||||
<p>In case of disputes:</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏷️</div>
|
||||
<h3>Երաշխիքի ժամկետներ</h3>
|
||||
<div class="warranty-periods">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✓</div>
|
||||
<h3>Երաշխիքի պայմաններ</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛠️</div>
|
||||
<h3>Ձեր իրավունքները թերության դեպքում</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -48,7 +48,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Նորոգման ժամկետ</h3>
|
||||
<p>Օրենքով առավելագույնը 45 օր։ Եթե ժամկետը խախտվի ՝ կարող եք պահանջել փոխարինում կամ գումարի վերադարձ։</p>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h3>Ինչպես դիմել հայտ</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Օգնությու՞ն պետք է՞</h3>
|
||||
<p>Վեճերի դեպքում՝</p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏷️</div>
|
||||
<h3>Сроки гарантии</h3>
|
||||
<div class="warranty-periods">
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">✓</div>
|
||||
<h3>Условия гарантии</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🛠️</div>
|
||||
<h3>Ваши права при браке</h3>
|
||||
<ul class="compact-list">
|
||||
@@ -48,7 +48,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">⏱️</div>
|
||||
<h3>Срок ремонта</h3>
|
||||
<p>Максимум 45 дней по закону. Если срок нарушен - можно требовать замену или возврат денег.</p>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h3>Как подать заявку</h3>
|
||||
<div class="process-steps-compact">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h3>Нужна помощь?</h3>
|
||||
<p>При возникновении споров:</p>
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Company Name</h3>
|
||||
<p class="org-name">ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ «ЭЛЕКТРОМОТОРС»</p>
|
||||
<p class="org-short">Abbreviated: ООО «ЭЛЕКТРОМОТОРС»</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Registered Address</h3>
|
||||
<p>АРМЕНИЯ, 0501, АРАГАЦОТИСКАЯ ОБЛАСТЬ, ТАЛИН, ул. ГАЯ, д. 12</p>
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h3>Details</h3>
|
||||
<div class="requisites">
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Bank Details</h3>
|
||||
<div class="requisites">
|
||||
@@ -101,7 +101,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">👤</div>
|
||||
<h3>Management</h3>
|
||||
<p><strong>General Director</strong></p>
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Անվանում</h3>
|
||||
<p class="org-name">ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ «ЭЛЕКТРОМОТОРС»</p>
|
||||
<p class="org-short">Կրճատ՝ ООО «ЭЛЕКТРОМОТОРС»</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Գրանցման հասցե</h3>
|
||||
<p>АРМЕНИЯ, 0501, АРАГАЦОТИСКАЯ ОБЛАСТЬ, ТАЛИН, ул. ГАЯ, д. 12</p>
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h3>Ռեկվիզիտներ</h3>
|
||||
<div class="requisites">
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Բանկային տվյալներ</h3>
|
||||
<div class="requisites">
|
||||
@@ -101,7 +101,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">👤</div>
|
||||
<h3>Ղեկավարություն</h3>
|
||||
<p><strong>Գլխավոր տնօրեն</strong></p>
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-cards">
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">🏢</div>
|
||||
<h3>Наименование</h3>
|
||||
<p class="org-name">ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ «ЭЛЕКТРОМОТОРС»</p>
|
||||
<p class="org-short">Сокращенно: ООО «ЭЛЕКТРОМОТОРС»</p>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📍</div>
|
||||
<h3>Адрес регистрации</h3>
|
||||
<p>АРМЕНИЯ, 0501, АРАГАЦОТИСКАЯ ОБЛАСТЬ, ТАЛИН, ул. ГАЯ, д. 12</p>
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h3>Реквизиты</h3>
|
||||
<div class="requisites">
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">💳</div>
|
||||
<h3>Банковские реквизиты</h3>
|
||||
<div class="requisites">
|
||||
@@ -101,7 +101,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-card wide">
|
||||
<div class="card-icon">👤</div>
|
||||
<h3>Руководство</h3>
|
||||
<p><strong>Генеральный директор</strong></p>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<p>2.3. All payments are processed through certified payment systems in compliance with PCI DSS security standards.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚙️</div>
|
||||
<h2>3. Payment Process</h2>
|
||||
<p>3.1. The order payment procedure includes the following steps:</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>3.3. The Buyer's payment obligation is considered fulfilled from the moment the funds are received in the payment system account.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>4. Payment Security</h2>
|
||||
<p>4.1. All payments are processed through a secure HTTPS connection using TLS 1.2 protocol and above.</p>
|
||||
@@ -84,7 +84,7 @@
|
||||
<p>4.4. In case of suspicious activity, the payment system has the right to request additional identity verification of the Buyer.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>5. Payment Confirmation</h2>
|
||||
<p>5.1. After successful payment, the Buyer receives a confirmation to the email address provided during checkout.</p>
|
||||
@@ -124,7 +124,7 @@
|
||||
<p>6.4. The Marketplace does not charge a fee for processing refunds. Payment system and bank fees may apply in accordance with their tariffs.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">❌</div>
|
||||
<h2>7. Failed Payments</h2>
|
||||
<p>7.1. A payment may be declined for the following reasons:</p>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<p>2.3. Բոլոր վճարումները մշակվում են սերտիֆիկացված վճարման համակարգերի միջոցով՝ PCI DSS անվտանգության ստանդարտներին համապատասխան։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚙️</div>
|
||||
<h2>3. Վճարման գործընթացը</h2>
|
||||
<p>3.1. Պատվերի վճարման գործընթացը ներառում է հետևյալ քայլերը՝</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>3.3. Գնորդի վճարման պարտավորությունը համարվում է կատարված վճարման համակարգի հաշվին դրամական միջոցների մուտքագրման պահից։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>4. Վճարումների անվտանգություն</h2>
|
||||
<p>4.1. Բոլոր վճարումները մշակվում են պաշտպանված HTTPS կապակցով՝ TLS 1.2 և ավելի բարձր արթանագրի օգտագործմամբ։</p>
|
||||
@@ -84,7 +84,7 @@
|
||||
<p>4.4. Կասկածելի գործունեության դեպքում վճարման համակարգը իրավունք ունի պահանջել Գնորդի ինքնության լրացուցիչ ստուգում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>5. Վճարման հաստատում</h2>
|
||||
<p>5.1. Հաջող վճարմանից հետո Գնորդը ստանում է հաստատում պատվերի ձևակերպման թելադրված էլ. փոստի հասցեին։</p>
|
||||
@@ -124,7 +124,7 @@
|
||||
<p>6.4. Միջոցների վերադարձի մշակման համար Մարկեթպլեյսը միջնորդավճար չի գանձում։ Վճարման համակարգերի և բանկերի միջնորդավճարները կարող են կիրառվել դրանց սակագներին համապատասխան։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">❌</div>
|
||||
<h2>7. Անհաջող վճարումներ</h2>
|
||||
<p>7.1. Վճարումը կարող է մերժվել հետևյալ պատճառներով՝</p>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<p>2.3. Все платежи обрабатываются через сертифицированные платежные системы с соблюдением стандартов безопасности PCI DSS.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚙️</div>
|
||||
<h2>3. Процесс оплаты</h2>
|
||||
<p>3.1. Процедура оплаты заказа включает следующие этапы:</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
<p>3.3. Обязательство Покупателя по оплате считается исполненным с момента поступления денежных средств на счет платежной системы.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>4. Безопасность платежей</h2>
|
||||
<p>4.1. Все платежи обрабатываются через защищенное HTTPS-соединение с использованием протокола TLS 1.2 и выше.</p>
|
||||
@@ -84,7 +84,7 @@
|
||||
<p>4.4. В случае подозрительной активности платежная система имеет право запросить дополнительную верификацию личности Покупателя.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>5. Подтверждение оплаты</h2>
|
||||
<p>5.1. После успешной оплаты Покупатель получает подтверждение на указанный при оформлении заказа адрес электронной почты.</p>
|
||||
@@ -124,7 +124,7 @@
|
||||
<p>6.4. За обработку возврата средств Маркетплейс комиссию не взимает. Комиссии платежных систем и банков могут применяться в соответствии с их тарифами.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">❌</div>
|
||||
<h2>7. Неуспешные платежи</h2>
|
||||
<p>7.1. Платеж может быть отклонен по следующим причинам:</p>
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<p>3.1.5. An agreement between the Operator and a third party, where the latter entrusts the Operator with the processing of personal data of the Personal Data Subject or transfers personal data of the Personal Data Subject based on the concluded agreement.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">👥</div>
|
||||
<h2>4. CATEGORIES OF PERSONAL DATA SUBJECTS</h2>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h2>5. CATEGORIES OF PROCESSED PERSONAL DATA</h2>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<p>Include conditions for processing, storage, cross-border data transfer, work with publicly available sources, special and biometric data, as well as the procedure for obtaining clarifications in accordance with the legislation of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>7. RIGHTS OF THE PERSONAL DATA SUBJECT</h2>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
<p>The Subject also has the right to protect their rights, recover damages, and receive compensation for moral harm.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h2>8. OPERATOR'S OBLIGATIONS</h2>
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
<p>8.3. The Operator bears other obligations established by FZ-152.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>9. SECURITY ASSURANCE</h2>
|
||||
|
||||
@@ -222,14 +222,14 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚠️</div>
|
||||
<h2>10. LIABILITY</h2>
|
||||
|
||||
<p>10.1. Persons guilty of violating the norms of processing and protection of Personal Data bear liability in accordance with the legislation of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>11. PURPOSES OF PROCESSING</h2>
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🍪</div>
|
||||
<h2>12. AUTOMATICALLY COLLECTED INFORMATION</h2>
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<p>3.1.5. Договор между оператором и третьим лицом, где последнее поручает Оператору обработку персональных данных Субъекта Персональных данных или передает на основании заключенного договора персональные данные Субъекта Персональных данных.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">👥</div>
|
||||
<h2>4. КАТЕГОРИИ СУБЪЕКТОВ ПЕРСОНАЛЬНЫХ ДАННЫХ</h2>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📝</div>
|
||||
<h2>5. КАТЕГОРИИ ОБРАБАТЫВАЕМЫХ ПЕРСОНАЛЬНЫХ ДАННЫХ</h2>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<p>Включают условия обработки, хранения, трансграничной передачи данных, работы с общедоступными источниками, специальными и биометрическими данными, а также порядок получения разъяснений согласно законодательству РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">✅</div>
|
||||
<h2>7. ПРАВА СУБЪЕКТА ПЕРСОНАЛЬНЫХ ДАННЫХ</h2>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
<p>Субъект также имеет право на защиту своих прав, возмещение убытков и компенсацию морального вреда.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📋</div>
|
||||
<h2>8. ОБЯЗАННОСТИ ОПЕРАТОРА</h2>
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
<p>8.3. Оператор несет иные обязанности, установленные ФЗ-152.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>9. ОБЕСПЕЧЕНИЕ БЕЗОПАСНОСТИ</h2>
|
||||
|
||||
@@ -222,14 +222,14 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚠️</div>
|
||||
<h2>10. ОТВЕТСТВЕННОСТЬ</h2>
|
||||
|
||||
<p>10.1. Лица, виновные в нарушении норм обработки и защиты Персональных данных, несут ответственность согласно законодательству РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>11. ЦЕЛИ ОБРАБОТКИ</h2>
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🍪</div>
|
||||
<h2>12. АВТОМАТИЧЕСКИ СОБИРАЕМАЯ ИНФОРМАЦИЯ</h2>
|
||||
|
||||
|
||||
@@ -36,12 +36,16 @@
|
||||
<p>1.9. Promotional campaigns may have special rules.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>2. Subject of the Agreement</h2>
|
||||
<p>2.1. The purpose is to provide users with the ability to purchase goods and services presented on the resource.</p>
|
||||
<p>2.2. The agreement regulates the use of the website and provided features.</p>
|
||||
<p>2.3. It applies to all types of goods, services, and products on the website.</p>
|
||||
<p>2.4. The Marketplace is an agent acting under agency agreements with Sellers (Third Parties). The Marketplace does not acquire ownership rights to the Goods/Services sold through the Platform and only provides the technical ability to conclude a transaction between the Seller and the Buyer.</p>
|
||||
<p>2.5. The Seller is an independent business entity and bears full responsibility for the quality, safety, conformity of the Goods/Services to the stated characteristics, and fulfillment of obligations to the Buyer (warranties, returns, claims).</p>
|
||||
<p>2.6. The Marketplace is not a party to the sale and purchase agreement between the Seller and the Buyer. All claims regarding the Goods/Services are submitted by the Buyer directly to the Seller.</p>
|
||||
<p>2.7. The Marketplace's agency remuneration is withheld from the amount paid by the Buyer and does not increase the price for the Buyer beyond the price set by the Seller.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card wide">
|
||||
@@ -57,7 +61,7 @@
|
||||
<p><strong>3.8. Applicable laws:</strong> Governed by Federal Law "On Consumer Rights Protection" No. 2300-1 and the Civil Code of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📱</div>
|
||||
<h2>4. Registration and Personal Account</h2>
|
||||
<p><strong>4.1. Procedure:</strong> Registration is not required for ordering but provides access to the personal account.</p>
|
||||
@@ -114,7 +118,7 @@
|
||||
<p><strong>Rights:</strong> Opt out of advertising messages through the website or by writing to <a href="mailto:info@novo.market">info@novo.market</a>.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">©</div>
|
||||
<h2>7. Exclusive Rights to Content</h2>
|
||||
<p><strong>7.1. Intellectual property:</strong> All content (design, texts, graphics, video, software, databases) is subject to the copyright of the owner and rights holders.</p>
|
||||
@@ -122,7 +126,7 @@
|
||||
<p><strong>7.3. Personal use:</strong> For personal non-commercial use while preserving copyright notices.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔗</div>
|
||||
<h2>8. Third-Party Websites and Content</h2>
|
||||
<p><strong>8.1. External links:</strong> The website may contain links to third-party resources. The website owner is not responsible for their content.</p>
|
||||
@@ -218,14 +222,14 @@
|
||||
<p><strong>13.7. Return method:</strong> The return method is specified in the application.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⏳</div>
|
||||
<h2>14. Term of the Agreement</h2>
|
||||
<p><strong>14.1. Commencement and termination:</strong> The agreement is active from the moment of acceptance by the user until revocation of acceptance.</p>
|
||||
<p><strong>14.2. Right of revocation:</strong> The owner may revoke the offer pursuant to Art. 436 of the Civil Code of the Russian Federation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚡</div>
|
||||
<h2>15. Dispute Resolution Procedure</h2>
|
||||
<p><strong>15.1. Voluntary settlement:</strong> Mandatory pre-trial dispute resolution procedure.</p>
|
||||
@@ -247,12 +251,12 @@
|
||||
<p><strong>16.3. Legislation:</strong> Issues are resolved under the legislation of Armenia.</p>
|
||||
<p><strong>16.4. The term "Legislation":</strong> Refers to the laws of Armenia.</p>
|
||||
<p><strong>16.5. Free services:</strong> Do not imply the application of consumer protection regulations.</p>
|
||||
<p><strong>16.6. Absence of relationships:</strong> Do not establish agency, partnership, or employment relationships.</p>
|
||||
<p><strong>16.6. Absence of relationships:</strong> Except for the agency model under clause 2.4, the terms do not establish partnership, joint venture, or employment relationships.</p>
|
||||
<p><strong>16.7. Invalidity of clauses:</strong> Does not affect the legal force of the remaining provisions.</p>
|
||||
<p><strong>16.8. Response to violations:</strong> Non-intervention does not prevent subsequent protective measures.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h2>Contact Us</h2>
|
||||
<p>Questions about the agreement:</p>
|
||||
|
||||
@@ -36,12 +36,16 @@
|
||||
<p>1.9. Գովազդային ակցիաները կարող են ունենալ հատուկ կանոններ։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>2. Համաձայնագրի առարկան</h2>
|
||||
<p>2.1. Նպատակն է օգտագործողներին հնարավորություն տալ ռեսուրսում ներկայացված ապրանքներ և ծառայություններ ձեռք բերելու։</p>
|
||||
<p>2.2. Համաձայնագիրը կարգավորում է կայքի օգտագործման և տրամադրվող գործառույթների կարգը։</p>
|
||||
<p>2.3. Գործում է կայքում ներկայացված ապրանքների՝ ծառայությունների և պրոդուկտների բոլոր տեսակների նկատմամբ։</p>
|
||||
<p>2.4. Մարկետփլեյսը հանդիսանում է գործակալ, որը գործում է Վաճառողների (Երրորդ անձանց) հետ կնքված գործակալության պայմանագրի հիման վրա։ Մարկետփլեյսը չի ձեռք բերում Հարթակի միջոցով իրացվող Ապրանքների/Ծառայությունների նկատմամբ սեփականության իրավունք, այլ միայն ապահովում է Վաճառողի և Գնորդի միջև գործարք կնքելու տեխնիկական հնարավորությունը։</p>
|
||||
<p>2.5. Վաճառողը հանդիսանում է ինքնուրույն տնտեսվարող սուբյեկտ և կրում է ամբողջական պատասխանատվություն Ապրանքի/Ծառայության որակի, անվտանգության, հայտարարված բնութագրերին համապատասխանության, ինչպես նաև Գնորդի նկատմամբ պարտավորությունների կատարման համար (երաշխիքներ, վերադարձ, պահանջներ)։</p>
|
||||
<p>2.6. Մարկետփլեյսը չի հանդիսանում Վաճառողի և Գնորդի միջև առուվաճառքի պայմանագրի կողմ։ Ապրանքի/Ծառայության վերաբերյալ բոլոր պահանջները Գնորդը ներկայացնում է անմիջապես Վաճառողին։</p>
|
||||
<p>2.7. Մարկետփլեյսի գործակալական վարձատրությունը պահվում է Գնորդի վճարած գումարից և չի ավելացնում Գնորդի համար գինը Վաճառողի սահմանած գնից ավել։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card wide">
|
||||
@@ -57,7 +61,7 @@
|
||||
<p><strong>3.8. Կիրառվող օրենքներ՝</strong> Կարգավորվում են ՌԴ «Սպառողների իրավունքների պաշտպանության մասին» թիվ 2300-1 և ՌԴ քաղաքացիական օրենսգիրքով։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📱</div>
|
||||
<h2>4. Գրանցում և անձնական հաշիվ</h2>
|
||||
<p><strong>4.1. Ըթացակարգ՝</strong> Գրանցումը պարտադիր չէ պատվերի համար՝ սակայն հնարավորություն է տալիս անձնական հաշիվ մուտք գործելու։</p>
|
||||
@@ -114,7 +118,7 @@
|
||||
<p><strong>Իրավունքներ՝</strong> Հրաժարվել գովազդային հաղորդագրություններից կայքի միճոցով կամ նամակով <a href="mailto:info@novo.market">info@novo.market</a>։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">©</div>
|
||||
<h2>7. Բովանդակության բացառիկ իրավունքներ</h2>
|
||||
<p><strong>7.1. Մտավոր սեփականություն՝</strong> Բոլոր բովանդակությունը (դիզայն՝ տեքստեր՝ գրաֆիկա՝ տեսանյութ՝ ծրագրեր՝ տվյալների բազաներ) սեփականատիրոջ և իրավատերերի հեղինակային իրավունքների առարկան է։</p>
|
||||
@@ -122,7 +126,7 @@
|
||||
<p><strong>7.3. Անձնական օգտագործում՝</strong> Անձնական ոչ առևտրային օգտագործման համար՝ հեղինակային իրավունքների նիշերի պահպանմամբ։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔗</div>
|
||||
<h2>8. Երրորդ կողմերի կայքեր և բովանդակություն</h2>
|
||||
<p><strong>8.1. Արտաքին հղումներ՝</strong> Կայքը կարող է պարունակել երրորդ կողմի ռեսուրսների հղումներ։ Կայքի սեփականատերը պատասխանատու չէ դրանց բովանդակության համար։</p>
|
||||
@@ -218,14 +222,14 @@
|
||||
<p><strong>13.7. Վերադարձի մեխանիզմ՝</strong> Վերադարձի եղանակը նշվում է դիմումում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⌛</div>
|
||||
<h2>14. Համաձայնագրի գործողության ժամկետը</h2>
|
||||
<p><strong>14.1. Սկիզբ և դադարեցում՝</strong> Համաձայնագիրը գործում է օգտագործողի ընդունման պահից մինչև ընդունման հետկանչում։</p>
|
||||
<p><strong>14.2. Հետկանչման իրավունք՝</strong> Սեփականատերը կարող է օֆերտան հետ կանչել ՌԴ 436 հոդվածի համաձայն։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚡</div>
|
||||
<h2>15. Վեճերի լուծման կարգը</h2>
|
||||
<p><strong>15.1. Կամավոր կարգավորում՝</strong> Պարտադիր նախադատական վեճերի լուծման ընթացակարգ։</p>
|
||||
@@ -247,12 +251,12 @@
|
||||
<p><strong>16.3. Օրենսդրություն՝</strong> Հարցերը լուծվում են Հայաստանի օրենսդրությամբ։</p>
|
||||
<p><strong>16.4. «Օրենսդրություն» տերմին՝</strong> Նշանակում է Հայաստանի օրենքները։</p>
|
||||
<p><strong>16.5. Անվճար ծառայություններ՝</strong> Չեն ենթադրում սպառողների իրավունքների պաշտպանության նորմերի կիրառում։</p>
|
||||
<p><strong>16.6. Հարաբերությունների բացակայություն՝</strong> Չեն հաստատում գործակալային՝ գործընկերային կամ աշխատանքային հարաբերություններ։</p>
|
||||
<p><strong>16.6. Հարաբերությունների բացակայություն՝</strong> Բացի 2.4 կետով նախատեսված գործակալական մոդելից՝ պայմանները չեն հաստատում գործընկերային, համատեղ ձեռնարկության կամ աշխատանքային հարաբերություններ։</p>
|
||||
<p><strong>16.7. Կետերի անվավերություն՝</strong> Չի ազդում մնացած դրույթների իրավական ուժի վրա։</p>
|
||||
<p><strong>16.8. Խախտումների նկատմամբ արձագանք՝</strong> Անգործունեությունը չի խանգարում հետագա պաշտպանության միջոցներին։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h2>Կապ</h2>
|
||||
<p>Համաձայնագրի վերաբերյալ հարցեր՝</p>
|
||||
|
||||
@@ -36,12 +36,16 @@
|
||||
<p>1.9. Промо-кампании могут иметь специальные правила.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🎯</div>
|
||||
<h2>2. Предмет соглашения</h2>
|
||||
<p>2.1. Целью является предоставление пользователям возможности покупать товары и услуги, представленные на ресурсе.</p>
|
||||
<p>2.2. Соглашение регулирует порядок использования сайта и предоставляемых функций.</p>
|
||||
<p>2.3. Действие распространяется на все типы товаров, услуг и продуктов на сайте.</p>
|
||||
<p>2.4. Маркетплейс является агентом, действующим на основании договора агентирования с Продавцами (Третьими лицами). Маркетплейс не приобретает право собственности на Товары/Услуги, реализуемые через Платформу, а лишь обеспечивает техническую возможность заключения сделки между Продавцом и Покупателем.</p>
|
||||
<p>2.5. Продавец является самостоятельным хозяйствующим субъектом и несёт полную ответственность за качество, безопасность, соответствие Товара/Услуги заявленным характеристикам, а также за исполнение обязательств перед Покупателем (гарантии, возврат, претензии).</p>
|
||||
<p>2.6. Маркетплейс не выступает стороной договора купли-продажи между Продавцом и Покупателем. Все претензии по Товару/Услуге Покупатель предъявляет непосредственно Продавцу.</p>
|
||||
<p>2.7. Агентское вознаграждение Маркетплейса удерживается из суммы, уплаченной Покупателем, и не увеличивает цену для Покупателя сверх установленной Продавцом.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card wide">
|
||||
@@ -57,7 +61,7 @@
|
||||
<p><strong>3.8. Применение законов:</strong> Регулируются ФЗ "О защите прав потребителей" №2300-1 и ГК РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📱</div>
|
||||
<h2>4. Регистрация и личный кабинет</h2>
|
||||
<p><strong>4.1. Процедура:</strong> Регистрация необязательна для заказа, но открывает доступ к личному кабинету.</p>
|
||||
@@ -114,7 +118,7 @@
|
||||
<p><strong>Права:</strong> Отказаться от рекламных сообщений через сайт или письмом на <a href="mailto:info@novo.market">info@novo.market</a>.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">©</div>
|
||||
<h2>7. Исключительные права на контент</h2>
|
||||
<p><strong>7.1. Интеллектуальная собственность:</strong> Весь контент (дизайн, тексты, графика, видео, программы, базы данных) является объектом авторских прав владельца и правообладателей.</p>
|
||||
@@ -122,7 +126,7 @@
|
||||
<p><strong>7.3. Личное использование:</strong> Для личного некоммерческого использования при сохранении обозначений авторских прав.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔗</div>
|
||||
<h2>8. Сайты и контент третьих лиц</h2>
|
||||
<p><strong>8.1. Внешние ссылки:</strong> Сайт может содержать ссылки на сторонние ресурсы. Ответственность за их содержание не относится к владельцу сайта.</p>
|
||||
@@ -218,14 +222,14 @@
|
||||
<p><strong>13.7. Механизм возврата:</strong> Способ возврата указывается в заявлении.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⏳</div>
|
||||
<h2>14. Срок действия соглашения</h2>
|
||||
<p><strong>14.1. Начало и прекращение:</strong> Соглашение активно с момента принятия пользователем до отзыва акцепта.</p>
|
||||
<p><strong>14.2. Право отзыва:</strong> Владелец может отозвать оферту согласно ст. 436 ГК РФ.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚡</div>
|
||||
<h2>15. Порядок разрешения споров</h2>
|
||||
<p><strong>15.1. Добровольное урегулирование:</strong> Обязательный досудебный порядок рассмотрения споров.</p>
|
||||
@@ -247,12 +251,12 @@
|
||||
<p><strong>16.3. Законодательство:</strong> Вопросы разрешаются по законодательству Армении.</p>
|
||||
<p><strong>16.4. Термин "Законодательство":</strong> Подразумевает законы Армении.</p>
|
||||
<p><strong>16.5. Бесплатные услуги:</strong> Не предполагают применение норм о защите прав потребителей.</p>
|
||||
<p><strong>16.6. Отсутствие отношений:</strong> Не устанавливают агентских связей, партнёрства или трудовых отношений.</p>
|
||||
<p><strong>16.6. Отсутствие отношений:</strong> Кроме агентской модели по п. 2.4, условия не устанавливают партнёрства, совместного предприятия или трудовых отношений.</p>
|
||||
<p><strong>16.7. Недействительность пунктов:</strong> Не влияет на законную силу остальных положений.</p>
|
||||
<p><strong>16.8. Реакция на нарушения:</strong> Невмешательство не препятствует последующим мерам защиты.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📞</div>
|
||||
<h2>Контакты</h2>
|
||||
<p>Вопросы по соглашению:</p>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>1.5. Digital products are returned under special rules prescribed by legislation.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📅</div>
|
||||
<h2>2. Time Frames</h2>
|
||||
<p><strong>2.1. Quality products:</strong></p>
|
||||
@@ -112,7 +112,7 @@
|
||||
<p>Our platform is always ready to help resolve disputes.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h2>6. Payment Refund</h2>
|
||||
<p>Funds are refunded to the same payment method:</p>
|
||||
@@ -134,14 +134,14 @@
|
||||
<p>The customer has the right to claim compensation for defective products and shipping costs.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔄</div>
|
||||
<h2>7. Product Exchange</h2>
|
||||
<p>If a non-defective product does not match in dimensions, size, or specifications, the customer may exchange it with the supplier for a suitable option.</p>
|
||||
<p>If the desired model is unavailable, the customer has the right to cancel the order and receive a refund.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>8. Warranty Service</h2>
|
||||
<p>Warranty information is displayed on the product page or available from the seller.</p>
|
||||
@@ -149,7 +149,7 @@
|
||||
<p>Warranty service does not cover physical damage, improper use, or unauthorized modifications.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚖️</div>
|
||||
<h2>9. Responsibilities of Parties</h2>
|
||||
<p>The supplier is responsible for product quality and timely refunds.</p>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>1.5. Թվային ապրանքները վերադարձվում են օրենսդրությամբ նախատեսված հատուկ կանոններով։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📅</div>
|
||||
<h2>2. Ժամանակային սահմաններ</h2>
|
||||
<p><strong>2.1. Որակյալ ապրանքներ՝</strong></p>
|
||||
@@ -112,7 +112,7 @@
|
||||
<p>Մեր հարթակը միշտ պատրաստ է օգնել վիճելի իրավիճակների կարգավորման հարցում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h2>6. Վճարման փոխհատուցում</h2>
|
||||
<p>Գումարը վերադարձվում է նույն վճարման եղանակով՝</p>
|
||||
@@ -134,14 +134,14 @@
|
||||
<p>Գնորդը իրավունք ունի պահանջել փոխհատուցում անորակ ապրանքների և առաքման ծախսերի համար։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔄</div>
|
||||
<h2>7. Ապրանքի փոխանակում</h2>
|
||||
<p>Եթե որակյալ ապրանքը չի համապատասխանում չափսերով, չափորոշներով կամ հատկանիշներով, գնորդը կարող է այն փոխանակել մատակարարի մոտ համապատասխան տարբերակով։</p>
|
||||
<p>Համապատասխան մոդելի բացակայության դեպքում գնորդը կարող է հրաժարվել պատվերից և ստանալ փոխհատուցում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>8. Երաշխիքային սպասարկում</h2>
|
||||
<p>Երաշխիքի պայմանները նշված են ապրանքի էջում կամ հասանելի են վաճառողից։</p>
|
||||
@@ -149,7 +149,7 @@
|
||||
<p>Երաշխիքային սպասարկումը չի տարածվում ֆիզիկական վնասների, ոչ պատշաճ օգտագործման կամ ինքնուրույն միջամտության դեպքում։</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚖️</div>
|
||||
<h2>9. Կողմերի պարտականություններ</h2>
|
||||
<p>Մատակարարը պատասխանատու է ապրանքի որակի և ժամանակային գումարի վերադարձի համար։</p>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>1.5. Электронные товары возвращаются по специальным правилам, прописанным в законодательстве.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">📅</div>
|
||||
<h2>2. Временные рамки</h2>
|
||||
<p><strong>2.1. Качественная продукция:</strong></p>
|
||||
@@ -112,7 +112,7 @@
|
||||
<p>Наша площадка всегда готова помочь в урегулировании спорных ситуаций.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">💰</div>
|
||||
<h2>6. Компенсация платежа</h2>
|
||||
<p>Деньги возмещаются на тот же способ оплаты:</p>
|
||||
@@ -134,14 +134,14 @@
|
||||
<p>Клиент имеет право потребовать компенсацию за некачественную продукцию и расходы по пересылке.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🔄</div>
|
||||
<h2>7. Обмен продукции</h2>
|
||||
<p>При несоответствии исправного изделия по габаритам, размерам или параметрам, клиент может заменить его у поставщика на подходящий вариант.</p>
|
||||
<p>При отсутствии нужной модели клиент вправе отказаться от заказа и получить компенсацию.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">🛡️</div>
|
||||
<h2>8. Сервисное обслуживание</h2>
|
||||
<p>Информация о гарантийных условиях размещена в карточке товара или доступна у продавца.</p>
|
||||
@@ -149,7 +149,7 @@
|
||||
<p>Гарантийное обслуживание не распространяется на физические ущербы, неправильное применение или самостоятельное вмешательство.</p>
|
||||
</section>
|
||||
|
||||
<section class="info-card">
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">⚖️</div>
|
||||
<h2>9. Обязанности участников</h2>
|
||||
<p>Поставщик несет ответственность за качество продукции и своевременное возмещение платежей.</p>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="delivery-selector">
|
||||
@if (isDigital) {
|
||||
<div class="delivery-chip delivery-chip--digital">{{ 'cart.digitalDelivery' | translate }}</div>
|
||||
} @else if (options.length > 0) {
|
||||
<label class="delivery-label" [for]="selectId">{{ 'cart.deliveryMethod' | translate }}</label>
|
||||
|
||||
<div class="delivery-control">
|
||||
<select [id]="selectId" [ngModel]="selectedKey" (ngModelChange)="onSelectionChange($event)">
|
||||
@if (required) {
|
||||
<option value="">{{ 'cart.selectDelivery' | translate }}</option>
|
||||
}
|
||||
|
||||
@for (option of options; track trackByOption($index, option)) {
|
||||
<option [value]="optionKey(option)">{{ optionLabel(option) }}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@if (selectedDelivery) {
|
||||
<div class="delivery-meta">
|
||||
@if (selectedDelivery.deliveryPlace) {
|
||||
<span>{{ 'cart.deliveryPlace' | translate }}: {{ selectedDelivery.deliveryPlace }}</span>
|
||||
}
|
||||
@if (selectedDelivery.deliveryTime) {
|
||||
<span>{{ 'cart.deliveryTime' | translate }}: {{ selectedDelivery.deliveryTime }}</span>
|
||||
}
|
||||
<span>
|
||||
{{ 'cart.deliveryLabel' | translate }}:
|
||||
{{ selectedDeliveryTotal | number:'1.2-2' }} {{ currency }}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,81 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.delivery-selector {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #d3dad9;
|
||||
background: #fafbfb;
|
||||
}
|
||||
|
||||
.delivery-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
color: #3f5f5c;
|
||||
}
|
||||
|
||||
.delivery-control select {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #c9d5d3;
|
||||
background: #fff;
|
||||
color: #1f2937;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.delivery-control select:focus {
|
||||
outline: none;
|
||||
border-color: #497671;
|
||||
box-shadow: 0 0 0 3px rgba(73, 118, 113, 0.12);
|
||||
}
|
||||
|
||||
.delivery-meta {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
margin-top: 10px;
|
||||
font-size: 0.82rem;
|
||||
color: #697777;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.delivery-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.delivery-chip--digital {
|
||||
color: #2563eb;
|
||||
background: rgba(37, 99, 235, 0.12);
|
||||
}
|
||||
|
||||
:host-context(.cart-container.novo) .delivery-selector {
|
||||
border-color: #d1fae5;
|
||||
background: #f9fffc;
|
||||
}
|
||||
|
||||
:host-context(.cart-container.novo) .delivery-label {
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
:host-context(.cart-container.novo) .delivery-control select {
|
||||
border-color: #bbf7d0;
|
||||
}
|
||||
|
||||
:host-context(.cart-container.novo) .delivery-control select:focus {
|
||||
border-color: #10b981;
|
||||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
|
||||
}
|
||||
|
||||
:host-context(.cart-container.novo) .delivery-meta {
|
||||
color: #4b5563;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CartItem, DeliveryOption } from '../../models';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
|
||||
let nextDeliverySelectorId = 0;
|
||||
|
||||
@Component({
|
||||
selector: 'app-delivery-selector',
|
||||
standalone: true,
|
||||
imports: [FormsModule, DecimalPipe, TranslatePipe],
|
||||
templateUrl: './delivery-selector.component.html',
|
||||
styleUrls: ['./delivery-selector.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DeliverySelectorComponent {
|
||||
@Input({ required: true }) item: CartItem | null = null;
|
||||
|
||||
@Output() selectedDeliveryChange = new EventEmitter<DeliveryOption | null>();
|
||||
|
||||
readonly selectId = `delivery-select-${nextDeliverySelectorId++}`;
|
||||
|
||||
get options(): DeliveryOption[] {
|
||||
return this.item?.deliveryOptions ?? [];
|
||||
}
|
||||
|
||||
get selectedDelivery(): DeliveryOption | null {
|
||||
return this.item?.selectedDelivery ?? null;
|
||||
}
|
||||
|
||||
get currency(): string {
|
||||
return this.item?.currency || 'RUB';
|
||||
}
|
||||
|
||||
get required(): boolean {
|
||||
return this.item?.deliveryMode !== 'digital'
|
||||
&& this.options.length > 0
|
||||
&& this.item?.deliverySelectionRequired !== false;
|
||||
}
|
||||
|
||||
get isDigital(): boolean {
|
||||
return this.item?.deliveryMode === 'digital';
|
||||
}
|
||||
|
||||
get selectedKey(): string {
|
||||
return this.selectedDelivery ? this.optionKey(this.selectedDelivery) : '';
|
||||
}
|
||||
|
||||
get selectedDeliveryTotal(): number {
|
||||
return (this.selectedDelivery?.deliveryPrice ?? 0) * (this.item?.quantity ?? 1);
|
||||
}
|
||||
|
||||
optionKey(option: DeliveryOption): string {
|
||||
return `${option.deliveryPlace}__${option.deliveryTime}__${option.deliveryPrice}`;
|
||||
}
|
||||
|
||||
optionLabel(option: DeliveryOption): string {
|
||||
const details = [option.deliveryPlace, option.deliveryTime].filter(Boolean);
|
||||
details.push(`${option.deliveryPrice.toFixed(2)} ${this.currency}`);
|
||||
return details.join(' • ');
|
||||
}
|
||||
|
||||
trackByOption(index: number, option: DeliveryOption): string {
|
||||
return `${index}-${this.optionKey(option)}`;
|
||||
}
|
||||
|
||||
onSelectionChange(nextKey: string): void {
|
||||
if (!nextKey) {
|
||||
this.selectedDeliveryChange.emit(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedDeliveryChange.emit(
|
||||
this.options.find(option => this.optionKey(option) === nextKey) ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<header class="novo-header">
|
||||
<div class="novo-header-container">
|
||||
<div class="novo-left">
|
||||
<a [routerLink]="'/' | langRoute" class="novo-logo" (click)="closeMenu()">
|
||||
<a [attr.href]="homeUrl" class="novo-logo" (click)="navigateHome($event)">
|
||||
<app-logo />
|
||||
<!-- <span class="novo-brand">{{ brandName }}</span> -->
|
||||
</a>
|
||||
@@ -24,6 +24,11 @@
|
||||
{{ 'header.contacts' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="novo-mobile-controls">
|
||||
<app-region-selector />
|
||||
<app-language-selector />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="novo-right">
|
||||
@@ -31,17 +36,22 @@
|
||||
<app-language-selector />
|
||||
|
||||
<a [routerLink]="'/cart' | langRoute" routerLinkActive="novo-cart-active" class="novo-cart" (click)="closeMenu()" [attr.aria-label]="'header.cart' | translate">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<circle cx="9" cy="21" r="1"></circle>
|
||||
<circle cx="20" cy="21" r="1"></circle>
|
||||
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
|
||||
</svg>
|
||||
<span class="novo-cart-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<circle cx="9" cy="21" r="1"></circle>
|
||||
<circle cx="20" cy="21" r="1"></circle>
|
||||
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
|
||||
</svg>
|
||||
@if (cartItemCount() > 0) {
|
||||
<span class="novo-cart-badge">{{ cartItemCount() }}</span>
|
||||
}
|
||||
</span>
|
||||
@if (cartItemCount() > 0) {
|
||||
<span class="novo-cart-badge">{{ cartItemCount() }}</span>
|
||||
<span class="novo-cart-total">{{ formatCartTotal(cartTotal()) }}</span>
|
||||
}
|
||||
</a>
|
||||
|
||||
<button class="menu-toggle" (click)="toggleMenu()" [class.active]="menuOpen" [attr.aria-label]="menuOpen ? 'Close menu' : 'Open menu'" [attr.aria-expanded]="menuOpen">
|
||||
<button class="menu-toggle novo-menu-toggle" (click)="toggleMenu()" [class.active]="menuOpen" [class.novo-active]="menuOpen" [attr.aria-label]="menuOpen ? 'Close menu' : 'Open menu'" [attr.aria-expanded]="menuOpen">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
@@ -54,7 +64,7 @@
|
||||
<header class="dexar-header">
|
||||
<div class="dexar-header-container">
|
||||
<!-- Logo -->
|
||||
<a [routerLink]="'/' | langRoute" class="dexar-logo" (click)="closeMenu()">
|
||||
<a [attr.href]="homeUrl" class="dexar-logo" (click)="navigateHome($event)">
|
||||
<app-logo />
|
||||
</a>
|
||||
|
||||
@@ -97,13 +107,18 @@
|
||||
<div class="dexar-actions">
|
||||
<!-- Cart Button -->
|
||||
<a [routerLink]="'/cart' | langRoute" routerLinkActive="dexar-cart-active" class="dexar-cart-btn" (click)="closeMenu()">
|
||||
<svg width="32" height="24" viewBox="0 0 48 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0.5H36C42.3513 0.5 47.5 5.64873 47.5 12V20C47.5 26.3513 42.3513 31.5 36 31.5H12C5.64873 31.5 0.5 26.3513 0.5 20V12C0.5 5.64873 5.64873 0.5 12 0.5Z" fill="transparent" />
|
||||
<path d="M12 0.5H36C42.3513 0.5 47.5 5.64873 47.5 12V20C47.5 26.3513 42.3513 31.5 36 31.5H12C5.64873 31.5 0.5 26.3513 0.5 20V12C0.5 5.64873 5.64873 0.5 12 0.5Z" stroke="#677B78" />
|
||||
<path d="M10 3.9C10 3.40294 10.4029 3 10.9 3H13.6C14.013 3 14.373 3.28107 14.4731 3.68172L15.2027 6.6H36.1C36.3677 6.6 36.6216 6.7192 36.7925 6.92523C36.9635 7.13125 37.0339 7.40271 36.9846 7.66586L34.2846 22.0659C34.2048 22.4915 33.8331 22.8 33.4 22.8H31.6H19H17.2C16.7669 22.8 16.3952 22.4915 16.3154 22.0659L13.6204 7.69224L12.8973 4.8H10.9C10.4029 4.8 10 4.39706 10 3.9ZM15.5844 8.4L17.9469 21H32.6531L35.0156 8.4H15.5844ZM19 22.8C17.0118 22.8 15.4 24.4118 15.4 26.4C15.4 28.3882 17.0118 30 19 30C20.9882 30 22.6 28.3882 22.6 26.4C22.6 24.4118 20.9882 22.8 19 22.8ZM31.6 22.8C29.6118 22.8 28 24.4118 28 26.4C28 28.3882 29.6118 30 31.6 30C33.5882 30 35.2 28.3882 35.2 26.4C35.2 24.4118 33.5882 22.8 31.6 22.8ZM19 24.6C19.9941 24.6 20.8 25.4059 20.8 26.4C20.8 27.3941 19.9941 28.2 19 28.2C18.0059 28.2 17.2 27.3941 17.2 26.4C17.2 25.4059 18.0059 24.6 19 24.6ZM31.6 24.6C32.5941 24.6 33.4 25.4059 33.4 26.4C33.4 27.3941 32.5941 28.2 31.6 28.2C30.6059 28.2 29.8 27.3941 29.8 26.4C29.8 25.4059 30.6059 24.6 31.6 24.6Z" fill="#1E3C38" />
|
||||
</svg>
|
||||
<span class="dexar-cart-icon">
|
||||
<svg width="32" height="24" viewBox="0 0 48 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0.5H36C42.3513 0.5 47.5 5.64873 47.5 12V20C47.5 26.3513 42.3513 31.5 36 31.5H12C5.64873 31.5 0.5 26.3513 0.5 20V12C0.5 5.64873 5.64873 0.5 12 0.5Z" fill="transparent" />
|
||||
<path d="M12 0.5H36C42.3513 0.5 47.5 5.64873 47.5 12V20C47.5 26.3513 42.3513 31.5 36 31.5H12C5.64873 31.5 0.5 26.3513 0.5 20V12C0.5 5.64873 5.64873 0.5 12 0.5Z" stroke="#677B78" />
|
||||
<path d="M10 3.9C10 3.40294 10.4029 3 10.9 3H13.6C14.013 3 14.373 3.28107 14.4731 3.68172L15.2027 6.6H36.1C36.3677 6.6 36.6216 6.7192 36.7925 6.92523C36.9635 7.13125 37.0339 7.40271 36.9846 7.66586L34.2846 22.0659C34.2048 22.4915 33.8331 22.8 33.4 22.8H31.6H19H17.2C16.7669 22.8 16.3952 22.4915 16.3154 22.0659L13.6204 7.69224L12.8973 4.8H10.9C10.4029 4.8 10 4.39706 10 3.9ZM15.5844 8.4L17.9469 21H32.6531L35.0156 8.4H15.5844ZM19 22.8C17.0118 22.8 15.4 24.4118 15.4 26.4C15.4 28.3882 17.0118 30 19 30C20.9882 30 22.6 28.3882 22.6 26.4C22.6 24.4118 20.9882 22.8 19 22.8ZM31.6 22.8C29.6118 22.8 28 24.4118 28 26.4C28 28.3882 29.6118 30 31.6 30C33.5882 30 35.2 28.3882 35.2 26.4C35.2 24.4118 33.5882 22.8 31.6 22.8ZM19 24.6C19.9941 24.6 20.8 25.4059 20.8 26.4C20.8 27.3941 19.9941 28.2 19 28.2C18.0059 28.2 17.2 27.3941 17.2 26.4C17.2 25.4059 18.0059 24.6 19 24.6ZM31.6 24.6C32.5941 24.6 33.4 25.4059 33.4 26.4C33.4 27.3941 32.5941 28.2 31.6 28.2C30.6059 28.2 29.8 27.3941 29.8 26.4C29.8 25.4059 30.6059 24.6 31.6 24.6Z" fill="#1E3C38" />
|
||||
</svg>
|
||||
@if (cartItemCount() > 0) {
|
||||
<span class="dexar-cart-badge">{{ cartItemCount() }}</span>
|
||||
}
|
||||
</span>
|
||||
@if (cartItemCount() > 0) {
|
||||
<span class="dexar-cart-badge">{{ cartItemCount() }}</span>
|
||||
<span class="dexar-cart-total">{{ formatCartTotal(cartTotal()) }}</span>
|
||||
}
|
||||
</a>
|
||||
|
||||
@@ -177,14 +192,13 @@
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- Region Selector in mobile menu -->
|
||||
<div class="dexar-mobile-lang">
|
||||
<app-region-selector />
|
||||
</div>
|
||||
|
||||
<!-- Language Selector in mobile menu -->
|
||||
<div class="dexar-mobile-lang">
|
||||
<app-language-selector />
|
||||
<div class="dexar-mobile-controls">
|
||||
<div class="dexar-mobile-lang">
|
||||
<app-region-selector />
|
||||
</div>
|
||||
<div class="dexar-mobile-lang">
|
||||
<app-language-selector />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -328,43 +328,65 @@
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.novo-mobile-controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.novo-cart {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.2rem;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--radius-md);
|
||||
transition: all 0.3s;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.novo-cart-active {
|
||||
color: var(--primary-color);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.novo-cart-badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 5px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.novo-cart-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.novo-cart-badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 5px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.novo-cart-total {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.novo-menu-toggle {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
@@ -409,7 +431,8 @@
|
||||
transition: max-height 0.3s;
|
||||
|
||||
&.novo-nav-open {
|
||||
max-height: 300px;
|
||||
max-height: calc(100dvh - 65px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.novo-nav-links {
|
||||
@@ -434,6 +457,25 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.novo-right {
|
||||
flex: 0 0 auto;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.novo-right > app-region-selector,
|
||||
.novo-right > app-language-selector {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.novo-mobile-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 1rem 2rem 1.25rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.novo-menu-toggle {
|
||||
display: flex;
|
||||
@@ -468,7 +510,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.dexar-logo {
|
||||
@@ -591,15 +633,15 @@
|
||||
}
|
||||
|
||||
.dexar-cart-btn {
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 36px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s ease;
|
||||
gap: 2px;
|
||||
|
||||
svg {
|
||||
width: 36px;
|
||||
@@ -615,6 +657,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dexar-cart-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.dexar-cart-badge {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
@@ -634,6 +685,15 @@
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.dexar-cart-total {
|
||||
font-family: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
color: #1e3c38;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dexar-lang-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -714,10 +774,20 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.dexar-mobile-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.dexar-menu-toggle {
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
@@ -841,7 +911,7 @@
|
||||
|
||||
// Hide desktop language selector
|
||||
.dexar-lang-desktop {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.dexar-search-wrapper {
|
||||
@@ -903,9 +973,10 @@
|
||||
transition: max-height 0.35s ease, opacity 0.25s ease, padding 0.35s ease;
|
||||
|
||||
&.dexar-mobile-menu-open {
|
||||
max-height: 700px;
|
||||
max-height: calc(100dvh - 84px);
|
||||
padding: 28px 20px 32px;
|
||||
opacity: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,8 +985,8 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.dexar-mobile-lang {
|
||||
margin-top: 16px;
|
||||
.dexar-mobile-controls {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, Renderer2, inject, DOCUMENT } from '@angular/core';
|
||||
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { CartService, LanguageService } from '../../services';
|
||||
import { CartService } from '../../services/cart.service';
|
||||
import { LanguageService } from '../../services/language.service';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { LogoComponent } from '../logo/logo.component';
|
||||
import { LanguageSelectorComponent } from '../language-selector/language-selector.component';
|
||||
@@ -17,6 +18,7 @@ import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
})
|
||||
export class HeaderComponent {
|
||||
cartItemCount;
|
||||
cartTotal;
|
||||
menuOpen = false;
|
||||
brandName = environment.brandFullName;
|
||||
logo = environment.logo;
|
||||
@@ -28,6 +30,11 @@ export class HeaderComponent {
|
||||
|
||||
constructor(private cartService: CartService, private router: Router) {
|
||||
this.cartItemCount = this.cartService.itemCount;
|
||||
this.cartTotal = this.cartService.totalPrice;
|
||||
}
|
||||
|
||||
get homeUrl(): string {
|
||||
return `/${this.langService.currentLanguage()}`;
|
||||
}
|
||||
|
||||
toggleMenu(): void {
|
||||
@@ -44,6 +51,23 @@ export class HeaderComponent {
|
||||
this.renderer.removeClass(this.document.body, 'dexar-menu-open');
|
||||
}
|
||||
|
||||
navigateHome(event?: Event): void {
|
||||
event?.preventDefault();
|
||||
this.closeMenu();
|
||||
|
||||
const homeUrl = this.homeUrl;
|
||||
const currentUrl = this.router.url.split('?')[0].split('#')[0];
|
||||
|
||||
if (currentUrl === homeUrl || currentUrl === `${homeUrl}/`) {
|
||||
this.document.defaultView?.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.router.navigateByUrl(homeUrl).then(() => {
|
||||
this.document.defaultView?.scrollTo({ top: 0, behavior: 'auto' });
|
||||
});
|
||||
}
|
||||
|
||||
navigateToSearch(): void {
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}/search`]);
|
||||
@@ -58,4 +82,20 @@ export class HeaderComponent {
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
formatCartTotal(total: number): string {
|
||||
const locale = this.langService.currentLanguage() === 'en'
|
||||
? 'en-US'
|
||||
: this.langService.currentLanguage() === 'hy'
|
||||
? 'hy-AM'
|
||||
: 'ru-RU';
|
||||
const fractionDigits = Number.isInteger(total) ? 0 : 2;
|
||||
const amount = new Intl.NumberFormat(locale, {
|
||||
minimumFractionDigits: fractionDigits,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(total);
|
||||
const currencySymbol = this.langService.getCurrentCurrency()?.symbol ?? this.langService.currentCurrency();
|
||||
|
||||
return `${amount} ${currencySymbol}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,13 +262,17 @@
|
||||
// Inside mobile menu: use inline dropdown instead of absolute
|
||||
// to avoid being clipped by overflow: hidden on the menu panel
|
||||
:host-context(.dexar-mobile-menu),
|
||||
:host-context(.dexar-mobile-lang) {
|
||||
:host-context(.dexar-mobile-lang),
|
||||
:host-context(.novo-mobile-controls) {
|
||||
.language-selector {
|
||||
width: auto;
|
||||
height: auto;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.language-dropdown {
|
||||
.language-dropdown,
|
||||
.currency-dropdown {
|
||||
position: static;
|
||||
opacity: 1;
|
||||
visibility: hidden;
|
||||
@@ -289,7 +293,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.language-option {
|
||||
.language-option,
|
||||
.currency-option {
|
||||
color: #1e3c38;
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
|
||||
@@ -3,12 +3,15 @@ import { environment } from '../../../environments/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-logo',
|
||||
template: `<img [src]="logoPath" [alt]="brandName + ' logo'" class="logo-img" fetchpriority="high" />`,
|
||||
template: `<img [src]="logoPath" [alt]="brandName + ' logo'" class="logo-img" fetchpriority="high" draggable="false" />`,
|
||||
styles: [`
|
||||
.logo-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
{{ 'auth.loginWithTelegram' | translate }}
|
||||
</button>
|
||||
|
||||
@if (loginUrl()) {
|
||||
<!-- @if (loginUrl()) {
|
||||
<a class="bot-link" [href]="loginUrl()" target="_blank" rel="noopener noreferrer">
|
||||
{{ loginUrl() }}
|
||||
</a>
|
||||
}
|
||||
} -->
|
||||
|
||||
<div class="qr-section">
|
||||
<p class="qr-hint">{{ 'auth.orScanQr' | translate }}</p>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Component, ChangeDetectionStrategy, inject, signal, computed, effect, OnDestroy } from '@angular/core';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { CartService } from '../../services/cart.service';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
import { getDiscountedPrice } from '../../utils/item.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-telegram-login',
|
||||
@@ -13,7 +11,6 @@ import { getDiscountedPrice } from '../../utils/item.utils';
|
||||
})
|
||||
export class TelegramLoginComponent implements OnDestroy {
|
||||
private authService = inject(AuthService);
|
||||
private cartService = inject(CartService);
|
||||
|
||||
showDialog = this.authService.showLoginDialog;
|
||||
status = this.authService.status;
|
||||
@@ -22,48 +19,76 @@ export class TelegramLoginComponent implements OnDestroy {
|
||||
webSessionID = signal('');
|
||||
qrStatus = signal<'loading' | 'ready' | 'expired' | 'error'>('loading');
|
||||
encodedQrUrl = computed(() => encodeURIComponent(this.loginUrl()));
|
||||
awaitingTelegramReturn = signal(false);
|
||||
|
||||
private readonly pollIntervalMs = 5000;
|
||||
private pollTimer?: ReturnType<typeof setInterval>;
|
||||
private readonly handleVisibilityChange = () => {
|
||||
if (typeof document !== 'undefined' && document.visibilityState === 'visible') {
|
||||
this.checkLoginAfterReturn();
|
||||
}
|
||||
};
|
||||
private readonly handleWindowFocus = () => {
|
||||
this.checkLoginAfterReturn();
|
||||
};
|
||||
private readonly handlePageShow = () => {
|
||||
this.checkLoginAfterReturn();
|
||||
};
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
if (this.showDialog()) {
|
||||
this.initQrLogin();
|
||||
} else {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.stopPolling();
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityChange);
|
||||
window.addEventListener('focus', this.handleWindowFocus);
|
||||
window.addEventListener('pageshow', this.handlePageShow);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.stopPolling();
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
|
||||
window.removeEventListener('focus', this.handleWindowFocus);
|
||||
window.removeEventListener('pageshow', this.handlePageShow);
|
||||
}
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.authService.hideLogin();
|
||||
this.stopPolling();
|
||||
}
|
||||
|
||||
openTelegramLogin(): void {
|
||||
const url = this.loginUrl();
|
||||
if (!url) return;
|
||||
const webSessionID = this.webSessionID();
|
||||
if (!webSessionID || typeof window === 'undefined') return;
|
||||
|
||||
window.open(url, '_blank');
|
||||
if (!this.pollTimer) {
|
||||
const webSessionID = this.webSessionID();
|
||||
if (webSessionID) {
|
||||
this.startPolling(webSessionID);
|
||||
}
|
||||
this.startPolling(webSessionID);
|
||||
}
|
||||
|
||||
this.awaitingTelegramReturn.set(true);
|
||||
window.location.href = this.authService.getTelegramAppLoginUrl(webSessionID);
|
||||
}
|
||||
|
||||
refreshQr(): void {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.stopPolling();
|
||||
this.initQrLogin();
|
||||
}
|
||||
|
||||
private initQrLogin(): void {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.qrStatus.set('loading');
|
||||
this.loginUrl.set('');
|
||||
this.webSessionID.set('');
|
||||
@@ -97,8 +122,9 @@ export class TelegramLoginComponent implements OnDestroy {
|
||||
this.authService.checkSessionOnce(webSessionID).subscribe({
|
||||
next: (session) => {
|
||||
if (session?.active) {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.stopPolling();
|
||||
this.syncCartAndComplete(session.sessionId);
|
||||
this.authService.onTelegramLoginComplete();
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
@@ -108,26 +134,34 @@ export class TelegramLoginComponent implements OnDestroy {
|
||||
}, this.pollIntervalMs);
|
||||
}
|
||||
|
||||
private syncCartAndComplete(sessionId: string): void {
|
||||
const cartItems = this.cartService.items().map(item => ({
|
||||
itemID: item.itemID,
|
||||
quantity: item.quantity,
|
||||
colour: item.colour || '',
|
||||
size: item.size || '',
|
||||
price: item.discount > 0
|
||||
? item.price * (1 - item.discount / 100)
|
||||
: item.price,
|
||||
}));
|
||||
|
||||
this.authService.syncCart(sessionId, cartItems).subscribe(() => {
|
||||
this.authService.onTelegramLoginComplete();
|
||||
});
|
||||
}
|
||||
|
||||
private stopPolling(): void {
|
||||
if (this.pollTimer) {
|
||||
clearInterval(this.pollTimer);
|
||||
this.pollTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private checkLoginAfterReturn(): void {
|
||||
if (!this.showDialog() || !this.awaitingTelegramReturn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const webSessionID = this.webSessionID();
|
||||
if (!webSessionID) {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pollTimer) {
|
||||
this.startPolling(webSessionID);
|
||||
}
|
||||
|
||||
this.authService.checkSessionOnce(webSessionID).subscribe(session => {
|
||||
if (session?.active) {
|
||||
this.awaitingTelegramReturn.set(false);
|
||||
this.stopPolling();
|
||||
this.authService.onTelegramLoginComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
export const PAYMENT_POLL_INTERVAL_MS = 5000;
|
||||
export const PAYMENT_MAX_CHECKS = 36;
|
||||
export const PAYMENT_TIMEOUT_CLOSE_MS = 3000;
|
||||
export const PAYMENT_ERROR_CLOSE_MS = 4000;
|
||||
export const LINK_COPIED_DURATION_MS = 2000;
|
||||
|
||||
// Infinite scroll
|
||||
@@ -12,7 +11,6 @@ export const ITEMS_PER_PAGE = 50;
|
||||
|
||||
// Search
|
||||
export const SEARCH_DEBOUNCE_MS = 300;
|
||||
export const SEARCH_MIN_LENGTH = 3;
|
||||
|
||||
// Cache
|
||||
export const CACHE_DURATION_MS = 5 * 60 * 1000;
|
||||
|
||||
@@ -64,6 +64,12 @@ export const en: Translations = {
|
||||
total: 'Total',
|
||||
items: 'Products',
|
||||
deliveryLabel: 'Delivery',
|
||||
deliveryMethod: 'Delivery option',
|
||||
selectDelivery: 'Select delivery option',
|
||||
deliveryPlace: 'Place',
|
||||
deliveryTime: 'Delivery time',
|
||||
digitalDelivery: 'Digital delivery',
|
||||
deliveryRequired: 'Select delivery for every shippable item before checkout.',
|
||||
toPay: 'To pay',
|
||||
agreeWith: 'I agree with the',
|
||||
publicOffer: 'public offer',
|
||||
@@ -85,6 +91,9 @@ export const en: Translations = {
|
||||
paymentSuccessDesc: 'Enter your contact details and we will send your purchase within a few minutes',
|
||||
sending: 'Sending...',
|
||||
send: 'Send',
|
||||
paymentError: 'Unable to create payment',
|
||||
paymentErrorDesc: 'We could not prepare the payment QR code right now. Please try again in a moment.',
|
||||
retryPayment: 'Try again',
|
||||
paymentTimeout: 'Payment timed out',
|
||||
paymentTimeoutDesc: 'We did not receive payment confirmation within 3 minutes.',
|
||||
autoClose: 'Window will close automatically...',
|
||||
|
||||
@@ -64,6 +64,12 @@ export const hy: Translations = {
|
||||
total: 'Ընդամենը',
|
||||
items: 'Ապրանքներ',
|
||||
deliveryLabel: 'Առաքում',
|
||||
deliveryMethod: 'Առաքման տարբերակ',
|
||||
selectDelivery: 'Ընտրեք առաքման տարբերակը',
|
||||
deliveryPlace: 'Վայր',
|
||||
deliveryTime: 'Առաքման ժամկետ',
|
||||
digitalDelivery: 'Թվային առաքում',
|
||||
deliveryRequired: 'Մինչ պատվերը ձևակերպելը ընտրեք առաքումը բոլոր առաքվող ապրանքների համար։',
|
||||
toPay: 'Վճարման ենթակա',
|
||||
agreeWith: 'Ես համաձայն եմ',
|
||||
publicOffer: 'հանրային օֆերտայի',
|
||||
@@ -85,6 +91,9 @@ export const hy: Translations = {
|
||||
paymentSuccessDesc: 'Մուտքագրեք ձեր տվյալները, և մենք կուղարկենք գնումը մի քանի րոպեի ընթացքում',
|
||||
sending: 'Ուղարկվում է...',
|
||||
send: 'Ուղարկել',
|
||||
paymentError: 'Չհաջողվեց ստեղծել վճարումը',
|
||||
paymentErrorDesc: 'Այս պահին չհաջողվեց պատրաստել վճարման QR կոդը։ Խնդրում ենք մի փոքր հետո կրկին փորձել։',
|
||||
retryPayment: 'Փորձել կրկին',
|
||||
paymentTimeout: 'Ժամանակը սպառվեց',
|
||||
paymentTimeoutDesc: 'Մենք չստացանք վճարման հաստատում 3 րոպեի ընթացքում։',
|
||||
autoClose: 'Պատուհանը կփակվի ավտոմատ...',
|
||||
|
||||
@@ -64,6 +64,12 @@ export const ru: Translations = {
|
||||
total: 'Итого',
|
||||
items: 'Товары',
|
||||
deliveryLabel: 'Доставка',
|
||||
deliveryMethod: 'Способ доставки',
|
||||
selectDelivery: 'Выберите способ доставки',
|
||||
deliveryPlace: 'Место',
|
||||
deliveryTime: 'Срок доставки',
|
||||
digitalDelivery: 'Цифровая доставка',
|
||||
deliveryRequired: 'Выберите доставку для всех товаров с доставкой перед оформлением заказа.',
|
||||
toPay: 'К оплате',
|
||||
agreeWith: 'Я согласен с',
|
||||
publicOffer: 'публичной офертой',
|
||||
@@ -85,6 +91,9 @@ export const ru: Translations = {
|
||||
paymentSuccessDesc: 'Введите ваши контактные данные, и мы отправим вам покупку в течение нескольких минут',
|
||||
sending: 'Отправка...',
|
||||
send: 'Отправить',
|
||||
paymentError: 'Не удалось создать платеж',
|
||||
paymentErrorDesc: 'Сейчас не получилось подготовить QR-код для оплаты. Попробуйте еще раз через минуту.',
|
||||
retryPayment: 'Попробовать снова',
|
||||
paymentTimeout: 'Время ожидания истекло',
|
||||
paymentTimeoutDesc: 'Мы не получили подтверждение оплаты в течение 3 минут.',
|
||||
autoClose: 'Окно закроется автоматически...',
|
||||
|
||||
@@ -62,6 +62,12 @@ export interface Translations {
|
||||
total: string;
|
||||
items: string;
|
||||
deliveryLabel: string;
|
||||
deliveryMethod: string;
|
||||
selectDelivery: string;
|
||||
deliveryPlace: string;
|
||||
deliveryTime: string;
|
||||
digitalDelivery: string;
|
||||
deliveryRequired: string;
|
||||
toPay: string;
|
||||
agreeWith: string;
|
||||
publicOffer: string;
|
||||
@@ -83,6 +89,9 @@ export interface Translations {
|
||||
paymentSuccessDesc: string;
|
||||
sending: string;
|
||||
send: string;
|
||||
paymentError: string;
|
||||
paymentErrorDesc: string;
|
||||
retryPayment: string;
|
||||
paymentTimeout: string;
|
||||
paymentTimeoutDesc: string;
|
||||
autoClose: string;
|
||||
|
||||
@@ -50,11 +50,6 @@ export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
);
|
||||
};
|
||||
|
||||
/** Clear all cached responses */
|
||||
export function clearCache(): void {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
function cleanupExpiredCache(): void {
|
||||
const now = Date.now();
|
||||
for (const [url, data] of cache.entries()) {
|
||||
|
||||
3
src/app/interceptors/mock-data.interceptor.production.ts
Normal file
3
src/app/interceptors/mock-data.interceptor.production.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { HttpInterceptorFn } from '@angular/common/http';
|
||||
|
||||
export const mockDataInterceptor: HttpInterceptorFn = (req, next) => next(req);
|
||||
@@ -780,8 +780,8 @@ export const mockDataInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
return respond([]);
|
||||
}
|
||||
|
||||
// ── POST /websession/:id (add to cart)
|
||||
if (url.match(/\/websession\/[^/]+$/) && req.method === 'POST') {
|
||||
// ── POST /usersession/:id or /websession/:id (sync cart)
|
||||
if (url.match(/\/(?:user|web)session\/[^/]+$/) && req.method === 'POST') {
|
||||
return respond({
|
||||
sessionId: 'mock-session',
|
||||
Status: true,
|
||||
@@ -795,11 +795,41 @@ export const mockDataInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
qrId: 'mock-qr-' + Date.now(),
|
||||
qrStatus: 'NEW',
|
||||
qrExpirationDate: new Date(Date.now() + 180000).toISOString(),
|
||||
Payload: 'https://example.com/pay/mock',
|
||||
payload: 'https://example.com/pay/mock',
|
||||
qrUrl: 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=mock-payment'
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// ── POST /qr (create payment QR directly)
|
||||
if (url.endsWith('/qr') && req.method === 'POST') {
|
||||
return respond({
|
||||
qrId: 'mock-qr-' + Date.now(),
|
||||
qrStatus: 'NEW',
|
||||
qrExpirationDate: new Date(Date.now() + 180000).toISOString(),
|
||||
payload: 'https://example.com/pay/mock',
|
||||
qrUrl: 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=mock-payment'
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// ── POST /cart (legacy create payment QR)
|
||||
if (url.endsWith('/cart') && req.method === 'POST') {
|
||||
const body = req.body;
|
||||
const looksLikePaymentRequest = !!body
|
||||
&& typeof body === 'object'
|
||||
&& !Array.isArray(body)
|
||||
&& 'amount' in body
|
||||
&& 'items' in body;
|
||||
if (looksLikePaymentRequest) {
|
||||
return respond({
|
||||
qrId: 'mock-qr-' + Date.now(),
|
||||
qrStatus: 'NEW',
|
||||
qrExpirationDate: new Date(Date.now() + 180000).toISOString(),
|
||||
payload: 'https://example.com/pay/mock',
|
||||
qrUrl: 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=mock-payment'
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// ── POST /items/:id/callback (review)
|
||||
if (url.match(/\/items\/\d+\/callback$/) && req.method === 'POST') {
|
||||
return respond({ message: 'Review submitted (mock)' }, 200);
|
||||
@@ -813,7 +843,7 @@ export const mockDataInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
// ── GET /websession/:id/:qrId (check QR payment status)
|
||||
if (url.match(/\/websession\/[^/]+\/[^/]+$/) && !url.match(/\/websession\/[^/]+\/qr$/) && req.method === 'GET') {
|
||||
return respond({
|
||||
paymentStatus: 'SUCCESS',
|
||||
paymentStatus: 'COMPLETED',
|
||||
code: 'SUCCESS',
|
||||
amount: 0,
|
||||
currency: 'RUB',
|
||||
@@ -828,6 +858,25 @@ export const mockDataInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// ── GET /qr/payment/:qrId (payment status)
|
||||
if (url.match(/\/qr\/payment\/[^/]+$/) && req.method === 'GET') {
|
||||
return respond({
|
||||
paymentStatus: 'COMPLETED',
|
||||
code: 'SUCCESS',
|
||||
amount: 0,
|
||||
currency: 'RUB',
|
||||
qrId: 'mock',
|
||||
transactionId: 999,
|
||||
transactionDate: new Date().toISOString(),
|
||||
additionalInfo: '',
|
||||
paymentPurpose: '',
|
||||
createDate: new Date().toISOString(),
|
||||
order: 'mock-order',
|
||||
qrExpirationDate: new Date().toISOString(),
|
||||
phoneNumber: '+70000000000'
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Fallback — pass through
|
||||
return next(req);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export interface AuthSession {
|
||||
sessionId: string;
|
||||
telegramUserId: number;
|
||||
userId: number | null;
|
||||
username: string | null;
|
||||
displayName: string;
|
||||
active: boolean;
|
||||
expiresAt: string;
|
||||
expires: string;
|
||||
}
|
||||
|
||||
export interface WebSessionStart {
|
||||
@@ -12,14 +12,4 @@ export interface WebSessionStart {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface TelegramAuthData {
|
||||
id: number;
|
||||
first_name: string;
|
||||
last_name?: string;
|
||||
username?: string;
|
||||
photo_url?: string;
|
||||
auth_date: number;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export type AuthStatus = 'unknown' | 'checking' | 'authenticated' | 'expired' | 'unauthenticated';
|
||||
|
||||
@@ -33,6 +33,6 @@ export interface Subcategory {
|
||||
subcategories?: Subcategory[];
|
||||
}
|
||||
|
||||
export interface CategoryTranslation {
|
||||
interface CategoryTranslation {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface Photo {
|
||||
interface Photo {
|
||||
photo?: string;
|
||||
video?: string;
|
||||
url: string;
|
||||
@@ -10,7 +10,7 @@ export interface DescriptionField {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
interface Comment {
|
||||
id?: string;
|
||||
text: string;
|
||||
author?: string;
|
||||
@@ -18,13 +18,13 @@ export interface Comment {
|
||||
createdAt?: string;
|
||||
}
|
||||
|
||||
export interface ItemTranslation {
|
||||
interface ItemTranslation {
|
||||
name?: string;
|
||||
simpleDescription?: string;
|
||||
description?: DescriptionField[];
|
||||
}
|
||||
|
||||
export interface Review {
|
||||
interface Review {
|
||||
rating?: number;
|
||||
content?: string;
|
||||
userID?: string;
|
||||
@@ -32,10 +32,7 @@ export interface Review {
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link Review} instead */
|
||||
export type Callback = Review;
|
||||
|
||||
export interface Question {
|
||||
interface Question {
|
||||
question: string;
|
||||
answer: string;
|
||||
upvotes: number;
|
||||
@@ -51,23 +48,32 @@ export interface ItemName {
|
||||
}
|
||||
|
||||
/** Localized description entry from backend */
|
||||
export interface ItemDescription {
|
||||
interface ItemDescription {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/** Key-value attribute pair */
|
||||
export interface ItemAttribute {
|
||||
interface ItemAttribute {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface DeliveryOption {
|
||||
deliveryPrice: number;
|
||||
deliveryPlace: string;
|
||||
deliveryTime: string;
|
||||
}
|
||||
|
||||
type DeliveryMode = 'selectable' | 'digital';
|
||||
|
||||
/** Item variant detail (price, size, colour per variant) */
|
||||
export interface ItemDetail {
|
||||
color?: string;
|
||||
colour?: string;
|
||||
size?: string;
|
||||
price: number;
|
||||
deliveryPrice?: number;
|
||||
currency: string;
|
||||
remaining: number;
|
||||
}
|
||||
@@ -80,6 +86,7 @@ export interface Item {
|
||||
description: string;
|
||||
currency: string;
|
||||
price: number;
|
||||
deliveryPrice?: number;
|
||||
discount: number;
|
||||
remainings?: string;
|
||||
rating: number;
|
||||
@@ -90,6 +97,9 @@ export interface Item {
|
||||
// Backend API fields
|
||||
colour?: string;
|
||||
size?: string;
|
||||
deliveryOptions?: DeliveryOption[];
|
||||
deliveryMode?: DeliveryMode;
|
||||
deliverySelectionRequired?: boolean;
|
||||
language?: string;
|
||||
names?: ItemName[];
|
||||
descriptions?: ItemDescription[];
|
||||
@@ -113,4 +123,5 @@ export interface Item {
|
||||
|
||||
export interface CartItem extends Item {
|
||||
quantity: number;
|
||||
selectedDelivery?: DeliveryOption | null;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,13 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (item.deliveryMode === 'digital' || item.deliveryOptions?.length) {
|
||||
<app-delivery-selector
|
||||
[item]="item"
|
||||
(selectedDeliveryChange)="selectDelivery(item.itemID, $event)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -116,14 +123,20 @@
|
||||
<span class="value">{{ totalPrice() | number:'1.2-2' }} {{ currentCurrency }}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row delivery">
|
||||
<span>{{ 'cart.deliveryLabel' | translate }}</span>
|
||||
<span>0 {{ currentCurrency }}</span>
|
||||
</div>
|
||||
@if (hasDeliveryPrice()) {
|
||||
<div class="summary-row delivery">
|
||||
<span>{{ 'cart.deliveryLabel' | translate }}</span>
|
||||
<span class="value">{{ totalDeliveryPrice() | number:'1.2-2' }} {{ currentCurrency }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!allRequiredDeliveriesSelected()) {
|
||||
<p class="delivery-warning">{{ 'cart.deliveryRequired' | translate }}</p>
|
||||
}
|
||||
|
||||
<div class="summary-row total">
|
||||
<span>{{ 'cart.toPay' | translate }}</span>
|
||||
<span class="total-price">{{ totalPrice() | number:'1.2-2' }} {{ currentCurrency }}</span>
|
||||
<span class="total-price">{{ totalWithDelivery() | number:'1.2-2' }} {{ currentCurrency }}</span>
|
||||
</div>
|
||||
|
||||
<div class="terms-agreement">
|
||||
@@ -147,8 +160,8 @@
|
||||
<button
|
||||
class="checkout-btn"
|
||||
(click)="checkout()"
|
||||
[class.disabled]="!termsAccepted"
|
||||
[disabled]="!termsAccepted"
|
||||
[class.disabled]="isCheckoutDisabled"
|
||||
[disabled]="isCheckoutDisabled"
|
||||
>
|
||||
{{ 'cart.checkout' | translate }}
|
||||
</button>
|
||||
@@ -207,7 +220,7 @@
|
||||
<div class="payment-info">
|
||||
<div class="payment-amount">
|
||||
<span class="label">{{ 'cart.amountToPay' | translate }}</span>
|
||||
<span class="amount">{{ totalPrice() | number:'1.2-2' }} {{ currentCurrency }}</span>
|
||||
<span class="amount">{{ totalWithDelivery() | number:'1.2-2' }} {{ currentCurrency }}</span>
|
||||
</div>
|
||||
|
||||
<div class="waiting-indicator">
|
||||
@@ -227,13 +240,27 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (paymentStatus() === 'error') {
|
||||
<div class="payment-status-screen error">
|
||||
<div class="error-icon">!</div>
|
||||
<h2>{{ 'cart.paymentError' | translate }}</h2>
|
||||
<p>{{ 'cart.paymentErrorDesc' | translate }}</p>
|
||||
|
||||
<div class="payment-error-actions">
|
||||
<button class="retry-payment-btn" (click)="retryPayment()">
|
||||
{{ 'cart.retryPayment' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (paymentStatus() === 'success') {
|
||||
<div class="payment-status-screen success">
|
||||
<div class="success-icon">✓</div>
|
||||
<h2>{{ 'cart.paymentSuccess' | translate }}</h2>
|
||||
<p class="success-text">{{ 'cart.paymentSuccessDesc' | translate }}</p>
|
||||
<!-- <p class="success-text">{{ 'cart.paymentSuccessDesc' | translate }}</p> -->
|
||||
|
||||
<div class="email-form">
|
||||
<!-- <div class="email-form">
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="email"
|
||||
@@ -282,7 +309,7 @@
|
||||
{{ 'cart.send' | translate }}
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -225,14 +225,14 @@
|
||||
|
||||
.cart-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 350px;
|
||||
grid-template-columns: minmax(0, 1fr) 350px;
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
// Novo wider summary
|
||||
.cart-container.novo .cart-content {
|
||||
grid-template-columns: 1fr 400px;
|
||||
grid-template-columns: minmax(0, 1fr) 400px;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
@@ -240,6 +240,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// Novo larger gap
|
||||
@@ -554,6 +555,12 @@
|
||||
font-weight: 700;
|
||||
color: #497671;
|
||||
}
|
||||
|
||||
.delivery-price {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: #697777;
|
||||
}
|
||||
}
|
||||
|
||||
// Dexar quantity controls
|
||||
@@ -837,7 +844,7 @@
|
||||
color: #6b7280;
|
||||
|
||||
&.delivery {
|
||||
display: none; // Hide delivery in Novo
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&.total {
|
||||
@@ -1009,6 +1016,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
.delivery-warning {
|
||||
margin: -4px 0 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.cart-container.dexar .cart-summary .delivery-warning {
|
||||
color: #9a6700;
|
||||
background: #fff8e8;
|
||||
border: 1px solid #f1ddb2;
|
||||
font-family: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
|
||||
.cart-container.novo .cart-summary .delivery-warning {
|
||||
color: #92400e;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
}
|
||||
|
||||
// Dexar checkbox colors
|
||||
.cart-container.dexar .terms-agreement .checkbox-container {
|
||||
input[type="checkbox"]:checked ~ .checkmark {
|
||||
@@ -1196,6 +1224,45 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
.error-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: #f97316;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.payment-error-actions {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.retry-payment-btn {
|
||||
padding: 14px 24px;
|
||||
background: #497671;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 13px;
|
||||
font-family: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #3a5f5b;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(73, 118, 113, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.timeout {
|
||||
.timeout-icon {
|
||||
font-size: 4rem;
|
||||
@@ -1488,11 +1555,31 @@
|
||||
// Mobile responsive
|
||||
@media (max-width: 768px) {
|
||||
.cart-content {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.cart-container.novo .cart-content {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.cart-summary {
|
||||
position: static;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cart-container.novo,
|
||||
.cart-container.dexar {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.cart-container.novo .cart-header,
|
||||
.cart-container.dexar .cart-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.remove-btn-desktop {
|
||||
@@ -1770,6 +1857,7 @@
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
line-clamp: 2;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Component, computed, ChangeDetectionStrategy, signal, OnDestroy, inject } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, signal, OnDestroy, inject } from '@angular/core';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CartService, ApiService, LanguageService, AuthService } from '../../services';
|
||||
import { Item, CartItem } from '../../models';
|
||||
import { interval, Subscription } from 'rxjs';
|
||||
import { switchMap, take } from 'rxjs/operators';
|
||||
import { Item, CartItem, DeliveryOption } from '../../models';
|
||||
import { interval, of, Subscription } from 'rxjs';
|
||||
import { catchError, exhaustMap, take, timeout } from 'rxjs/operators';
|
||||
import { DeliverySelectorComponent } from '../../components/delivery-selector/delivery-selector.component';
|
||||
import { EmptyCartIconComponent } from '../../components/empty-cart-icon/empty-cart-icon.component';
|
||||
import { TelegramLoginComponent } from '../../components/telegram-login/telegram-login.component';
|
||||
import { environment } from '../../../environments/environment';
|
||||
@@ -13,11 +14,11 @@ import { getDiscountedPrice, getMainImage, trackByItemId, getBadgeClass, getTran
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
import { TranslateService } from '../../i18n/translate.service';
|
||||
import { PAYMENT_POLL_INTERVAL_MS, PAYMENT_MAX_CHECKS, PAYMENT_TIMEOUT_CLOSE_MS, PAYMENT_ERROR_CLOSE_MS, LINK_COPIED_DURATION_MS } from '../../config/constants';
|
||||
import { PAYMENT_POLL_INTERVAL_MS, PAYMENT_MAX_CHECKS, PAYMENT_TIMEOUT_CLOSE_MS, LINK_COPIED_DURATION_MS } from '../../config/constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cart',
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, EmptyCartIconComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe],
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, EmptyCartIconComponent, DeliverySelectorComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe],
|
||||
templateUrl: './cart.component.html',
|
||||
styleUrls: ['./cart.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -26,6 +27,10 @@ export class CartComponent implements OnDestroy {
|
||||
items;
|
||||
itemCount;
|
||||
totalPrice;
|
||||
totalDeliveryPrice;
|
||||
totalWithDelivery;
|
||||
hasDeliveryPrice;
|
||||
allRequiredDeliveriesSelected;
|
||||
termsAccepted = false;
|
||||
isnovo = environment.theme === 'novo';
|
||||
|
||||
@@ -39,7 +44,7 @@ export class CartComponent implements OnDestroy {
|
||||
|
||||
// Payment popup states
|
||||
showPaymentPopup = signal<boolean>(false);
|
||||
paymentStatus = signal<'creating' | 'waiting' | 'success' | 'timeout'>('creating');
|
||||
paymentStatus = signal<'creating' | 'waiting' | 'success' | 'timeout' | 'error' | null>('creating');
|
||||
qrCodeUrl = signal<string>('');
|
||||
paymentUrl = signal<string>('');
|
||||
paymentId = signal<string>('');
|
||||
@@ -68,6 +73,10 @@ export class CartComponent implements OnDestroy {
|
||||
this.items = this.cartService.items;
|
||||
this.itemCount = this.cartService.itemCount;
|
||||
this.totalPrice = this.cartService.totalPrice;
|
||||
this.totalDeliveryPrice = this.cartService.totalDeliveryPrice;
|
||||
this.totalWithDelivery = this.cartService.totalWithDelivery;
|
||||
this.hasDeliveryPrice = this.cartService.hasDeliveryPrice;
|
||||
this.allRequiredDeliveriesSelected = this.cartService.allRequiredDeliveriesSelected;
|
||||
}
|
||||
|
||||
requestLogin(): void {
|
||||
@@ -103,7 +112,6 @@ export class CartComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
onSwipeStart(itemID: number, event: TouchEvent): void {
|
||||
const item = event.currentTarget as HTMLElement;
|
||||
const startX = event.touches[0].clientX;
|
||||
|
||||
const onMove = (e: TouchEvent) => {
|
||||
@@ -142,15 +150,20 @@ export class CartComponent implements OnDestroy {
|
||||
itemName(item: Item): string { return getTranslatedField(item, 'name', this.langService.currentLanguage()); }
|
||||
itemDesc(item: Item): string { return getTranslatedField(item, 'simpleDescription', this.langService.currentLanguage()); }
|
||||
get currentCurrency(): string { return this.langService.currentCurrency(); }
|
||||
get isCheckoutDisabled(): boolean { return !this.termsAccepted || !this.isAuthenticated() || !this.allRequiredDeliveriesSelected(); }
|
||||
|
||||
selectDelivery(itemID: number, selectedDelivery: DeliveryOption | null): void {
|
||||
this.cartService.setSelectedDelivery(itemID, selectedDelivery);
|
||||
}
|
||||
|
||||
checkout(): void {
|
||||
if (!this.termsAccepted) {
|
||||
alert(this.i18n.t('cart.acceptTerms'));
|
||||
if (!this.allRequiredDeliveriesSelected()) {
|
||||
alert(this.i18n.t('cart.deliveryRequired'));
|
||||
return;
|
||||
}
|
||||
// Auth gate: require Telegram login before payment
|
||||
if (!this.authService.isAuthenticated()) {
|
||||
this.authService.requestLogin();
|
||||
|
||||
if (!this.termsAccepted) {
|
||||
alert(this.i18n.t('cart.acceptTerms'));
|
||||
return;
|
||||
}
|
||||
this.openPaymentPopup();
|
||||
@@ -159,6 +172,10 @@ export class CartComponent implements OnDestroy {
|
||||
openPaymentPopup(): void {
|
||||
this.showPaymentPopup.set(true);
|
||||
this.paymentStatus.set('creating');
|
||||
this.paymentId.set('');
|
||||
this.qrCodeUrl.set('');
|
||||
this.paymentUrl.set('');
|
||||
this.linkCopied.set(false);
|
||||
this.userEmail.set('');
|
||||
this.userPhone.set('');
|
||||
this.emailTouched.set(false);
|
||||
@@ -179,73 +196,110 @@ export class CartComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
createPayment(): void {
|
||||
const sessionId = this.authService.session()?.sessionId || '';
|
||||
if (!sessionId) {
|
||||
this.paymentStatus.set('timeout');
|
||||
return;
|
||||
retryPayment(): void {
|
||||
if (this.closeTimeout) {
|
||||
clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = undefined;
|
||||
}
|
||||
|
||||
// First sync cart items to server via websession, then create QR
|
||||
const cartItems = this.items().map((item: CartItem) => ({
|
||||
itemID: item.itemID,
|
||||
quantity: item.quantity,
|
||||
colour: item.colour || '',
|
||||
size: item.size || '',
|
||||
price: item.discount > 0
|
||||
? item.price * (1 - item.discount / 100)
|
||||
: item.price,
|
||||
}));
|
||||
this.paymentStatus.set('creating');
|
||||
this.paymentId.set('');
|
||||
this.qrCodeUrl.set('');
|
||||
this.paymentUrl.set('');
|
||||
this.linkCopied.set(false);
|
||||
this.createPayment();
|
||||
}
|
||||
|
||||
this.apiService.addToCart(sessionId, cartItems).subscribe({
|
||||
next: () => {
|
||||
this.apiService.createPayment(sessionId).subscribe({
|
||||
createPayment(): void {
|
||||
const orderId = this.generateOrderId();
|
||||
const paymentPayload = {
|
||||
amount: Number(this.totalWithDelivery()),
|
||||
currency: 'RUB' as const,
|
||||
siteuserID: this.getPaymentUserId(),
|
||||
siteorderID: orderId,
|
||||
redirectUrl: '',
|
||||
telegramUsername: this.getTelegramUsername(),
|
||||
items: this.buildPaymentItems(),
|
||||
};
|
||||
|
||||
this.apiService.createCartPayment(paymentPayload)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.paymentId.set(response.qrId);
|
||||
this.qrCodeUrl.set(response.qrUrl);
|
||||
this.paymentUrl.set(response.Payload);
|
||||
const qrId = this.apiService.resolvePaymentQrId(response);
|
||||
const qrUrl = this.apiService.resolvePaymentQrUrl(response);
|
||||
const paymentLink = this.apiService.resolvePaymentLink(response);
|
||||
|
||||
if (!qrId || !qrUrl) {
|
||||
console.error('Payment response missing qr fields:', response);
|
||||
this.setPaymentError();
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentId.set(qrId);
|
||||
this.qrCodeUrl.set(qrUrl);
|
||||
this.paymentUrl.set(paymentLink);
|
||||
|
||||
this.paymentStatus.set('waiting');
|
||||
this.startPolling();
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error creating payment:', err);
|
||||
this.paymentStatus.set('timeout');
|
||||
if (this.closeTimeout) clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = setTimeout(() => {
|
||||
this.closePaymentPopup();
|
||||
}, PAYMENT_ERROR_CLOSE_MS);
|
||||
this.setPaymentError();
|
||||
}
|
||||
});
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error syncing cart:', err);
|
||||
this.paymentStatus.set('timeout');
|
||||
if (this.closeTimeout) clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = setTimeout(() => {
|
||||
this.closePaymentPopup();
|
||||
}, PAYMENT_ERROR_CLOSE_MS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startPolling(): void {
|
||||
this.stopPolling();
|
||||
if (!this.paymentId()) {
|
||||
this.setPaymentError();
|
||||
return;
|
||||
}
|
||||
|
||||
this.pollingSubscription = interval(PAYMENT_POLL_INTERVAL_MS)
|
||||
.pipe(
|
||||
take(this.maxChecks), // maximum 36 checks (3 minutes)
|
||||
switchMap(() => {
|
||||
const sessionId = this.authService.session()?.sessionId || '';
|
||||
return this.apiService.checkPaymentStatus(sessionId, this.paymentId());
|
||||
exhaustMap(() => {
|
||||
return this.apiService.checkCartPaymentStatus(this.paymentId()).pipe(
|
||||
timeout(8000),
|
||||
catchError((err) => {
|
||||
console.error('Error checking payment status:', err);
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
|
||||
const paymentStatus = response.status?.toUpperCase() || '';
|
||||
const paymentCode = response.code?.toUpperCase() || '';
|
||||
|
||||
if (paymentStatus === 'FAILED' || paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED' || paymentStatus === 'REJECTED') {
|
||||
this.paymentStatus.set('timeout');
|
||||
this.stopPolling();
|
||||
if (this.closeTimeout) clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = setTimeout(() => {
|
||||
this.closePaymentPopup();
|
||||
}, PAYMENT_TIMEOUT_CLOSE_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if payment is successful
|
||||
if (response.paymentStatus === 'SUCCESS' && response.code === 'SUCCESS') {
|
||||
if (paymentStatus === 'COMPLETED' || paymentStatus === 'APPROVED' || paymentStatus === 'PAID' || paymentCode === 'SUCCESS') {
|
||||
this.paymentStatus.set('success');
|
||||
this.stopPolling();
|
||||
// Clear cart but don't close popup - wait for email submission
|
||||
// Auto-submit purchase after 5 seconds
|
||||
if (this.closeTimeout) clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = setTimeout(() => {
|
||||
this.autoSubmitPurchase();
|
||||
}, 5000);
|
||||
this.cartService.clearCart();
|
||||
|
||||
|
||||
}
|
||||
// Continue checking for 3 minutes regardless of other statuses
|
||||
},
|
||||
@@ -260,14 +314,6 @@ export class CartComponent implements OnDestroy {
|
||||
this.closePaymentPopup();
|
||||
}, PAYMENT_TIMEOUT_CLOSE_MS);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error checking payment status:', err);
|
||||
// Continue checking even on error until time runs out
|
||||
if (this.closeTimeout) clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = setTimeout(() => {
|
||||
this.closePaymentPopup();
|
||||
}, PAYMENT_TIMEOUT_CLOSE_MS);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -275,9 +321,71 @@ export class CartComponent implements OnDestroy {
|
||||
stopPolling(): void {
|
||||
if (this.pollingSubscription) {
|
||||
this.pollingSubscription.unsubscribe();
|
||||
this.pollingSubscription = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private setPaymentError(): void {
|
||||
this.paymentStatus.set('error');
|
||||
this.stopPolling();
|
||||
if (this.closeTimeout) {
|
||||
clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private autoSubmitPurchase(): void {
|
||||
setTimeout(() => {
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}`]);}, 0);
|
||||
const telegramUserId = this.getTelegramUserId();
|
||||
|
||||
// Telegram ID is mandatory
|
||||
if (!telegramUserId) {
|
||||
console.error('Cannot submit purchase: Telegram ID is required');
|
||||
this.emailSubmitting.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.emailSubmitting.set(true);
|
||||
|
||||
const emailData = {
|
||||
email: '',
|
||||
phone: '',
|
||||
telegramUserId: telegramUserId,
|
||||
items: this.paidItems.map((item: CartItem) => ({
|
||||
itemID: item.itemID,
|
||||
name: item.name,
|
||||
price: item.discount > 0
|
||||
? item.price * (1 - item.discount / 100)
|
||||
: item.price,
|
||||
currency: item.currency,
|
||||
quantity: item.quantity,
|
||||
...(item.selectedDelivery ? { delivery: item.selectedDelivery } : {})
|
||||
}))
|
||||
};
|
||||
|
||||
this.apiService.submitPurchaseEmail(emailData).subscribe({
|
||||
next: () => {
|
||||
this.emailSubmitting.set(false);
|
||||
this.closePaymentPopup();
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}`]);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error submitting purchase:', err);
|
||||
this.emailSubmitting.set(false);
|
||||
// Still close popup and redirect even if submission fails
|
||||
this.closePaymentPopup();
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}`]);
|
||||
}
|
||||
});
|
||||
this.paymentStatus.set(null);
|
||||
|
||||
|
||||
}
|
||||
|
||||
copyPaymentLink(): void {
|
||||
const url = this.paymentUrl();
|
||||
if (url) {
|
||||
@@ -321,7 +429,8 @@ export class CartComponent implements OnDestroy {
|
||||
? item.price * (1 - item.discount / 100)
|
||||
: item.price,
|
||||
currency: item.currency,
|
||||
quantity: item.quantity
|
||||
quantity: item.quantity,
|
||||
...(item.selectedDelivery ? { delivery: item.selectedDelivery } : {})
|
||||
}))
|
||||
};
|
||||
|
||||
@@ -346,12 +455,60 @@ export class CartComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private getTelegramUserId(): string | null {
|
||||
const sessionTelegramUserId = this.authService.session()?.userId;
|
||||
if (sessionTelegramUserId !== null && sessionTelegramUserId !== undefined) {
|
||||
return sessionTelegramUserId.toString();
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.Telegram?.WebApp?.initDataUnsafe?.user) {
|
||||
return window.Telegram.WebApp.initDataUnsafe.user.id.toString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private getTelegramUsername(): string {
|
||||
const sessionUsername = this.authService.session()?.username;
|
||||
if (sessionUsername) {
|
||||
return sessionUsername;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.Telegram?.WebApp?.initDataUnsafe?.user) {
|
||||
return window.Telegram.WebApp.initDataUnsafe.user.username || 'nontelegram';
|
||||
}
|
||||
|
||||
return 'nontelegram';
|
||||
}
|
||||
|
||||
private getPaymentUserId(): string {
|
||||
return this.getTelegramUserId() ?? `web_${Date.now()}`;
|
||||
}
|
||||
|
||||
private generateOrderId(): string {
|
||||
const timestamp = Date.now();
|
||||
const random = Math.random().toString(36).substring(2, 8);
|
||||
return `order_${timestamp}_${random}`;
|
||||
}
|
||||
|
||||
private buildPaymentItems(): Array<{ itemID: number; price: number; name: string; quantity: number; delivery?: DeliveryOption }> {
|
||||
return this.items().map((item: CartItem) => {
|
||||
const unitPrice = item.discount > 0
|
||||
? item.price * (1 - item.discount / 100)
|
||||
: item.price;
|
||||
const details = [item.colour, item.size].filter(Boolean).join(', ');
|
||||
const translatedName = this.itemName(item).trim() || `Item ${item.itemID}`;
|
||||
const name = details ? `${item.quantity} x ${translatedName} (${details})` : `${item.quantity} x ${translatedName}`;
|
||||
|
||||
return {
|
||||
itemID: item.itemID,
|
||||
price: unitPrice * item.quantity,
|
||||
name,
|
||||
quantity: item.quantity,
|
||||
...(item.selectedDelivery ? { delivery: item.selectedDelivery } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onPhoneInput(event: Event): void {
|
||||
const input = event.target as HTMLInputElement;
|
||||
let value = input.value.replace(/\D/g, ''); // Remove all non-digits
|
||||
|
||||
@@ -63,10 +63,10 @@ export class SubcategoriesComponent implements OnInit, OnDestroy {
|
||||
|
||||
// Check for nested subcategories from API response (backOffice format)
|
||||
const nested = parent?.subcategories || [];
|
||||
const visibleNested = nested.filter(s => s.visible !== false);
|
||||
const visibleNested = nested.filter(s => this.isDisplayableNestedSubcategory(s));
|
||||
|
||||
// Also check flat legacy subcategories
|
||||
const flatSubs = cats.filter(c => c.parentID === parentID);
|
||||
const flatSubs = cats.filter(c => c.parentID === parentID && this.isDisplayableFlatSubcategory(c));
|
||||
|
||||
if (visibleNested.length > 0) {
|
||||
// Use nested subcategories from API
|
||||
@@ -110,6 +110,20 @@ export class SubcategoriesComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private isDisplayableFlatSubcategory(category: Category): boolean {
|
||||
return category.visible !== false
|
||||
&& ((category.itemCount ?? 0) > 0 || (category.subcategories?.length ?? 0) > 0);
|
||||
}
|
||||
|
||||
private isDisplayableNestedSubcategory(subcategory: Subcategory): boolean {
|
||||
return subcategory.visible !== false
|
||||
&& (
|
||||
(subcategory.itemCount ?? 0) > 0
|
||||
|| subcategory.hasItems === true
|
||||
|| (subcategory.subcategories?.length ?? 0) > 0
|
||||
);
|
||||
}
|
||||
|
||||
hasSubcategories(): boolean {
|
||||
return this.subcategories().length > 0 || this.nestedSubcategories().length > 0;
|
||||
}
|
||||
@@ -121,11 +135,11 @@ export class SubcategoriesComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
// TrackBy function for performance optimization
|
||||
trackByCategoryId(index: number, category: Category): number {
|
||||
trackByCategoryId(_index: number, category: Category): number {
|
||||
return category.categoryID;
|
||||
}
|
||||
|
||||
trackBySubId(index: number, sub: Subcategory): string {
|
||||
trackBySubId(_index: number, sub: Subcategory): string {
|
||||
return sub.id;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
topLevelCategories = computed(() => {
|
||||
return this.categories()
|
||||
.filter(cat => cat.parentID === 0)
|
||||
.filter(cat => this.isDisplayableTopLevelCategory(cat))
|
||||
.sort((a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity));
|
||||
});
|
||||
|
||||
@@ -42,7 +43,7 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
private subcategoriesCache = computed(() => {
|
||||
const cache = new Map<number, Category[]>();
|
||||
this.categories().forEach(cat => {
|
||||
if (cat.parentID !== 0) {
|
||||
if (cat.parentID !== 0 && this.isDisplayableFlatSubcategory(cat)) {
|
||||
if (!cache.has(cat.parentID)) {
|
||||
cache.set(cat.parentID, []);
|
||||
}
|
||||
@@ -90,6 +91,21 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
return this.subcategoriesCache().get(parentID) || [];
|
||||
}
|
||||
|
||||
private isDisplayableFlatSubcategory(category: Category): boolean {
|
||||
return category.visible !== false
|
||||
&& ((category.itemCount ?? 0) > 0 || (category.subcategories?.length ?? 0) > 0);
|
||||
}
|
||||
|
||||
private isDisplayableTopLevelCategory(category: Category): boolean {
|
||||
return category.visible !== false
|
||||
&& (
|
||||
(category.itemCount ?? 0) > 0
|
||||
|| (category.categoriesCount ?? 0) > 0
|
||||
|| (category.subcategories?.length ?? 0) > 0
|
||||
|| this.getSubCategories(category.categoryID).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
isWideCategory(categoryID: number): boolean {
|
||||
return this.wideCategories().has(categoryID);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { SecurityContext } from '@angular/core';
|
||||
import { getDiscountedPrice, getAllImages, getStockStatus, getBadgeClass, getTranslatedField } from '../../utils/item.utils';
|
||||
import { getStockStatus, getBadgeClass, getTranslatedField } from '../../utils/item.utils';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
import { TranslateService } from '../../i18n/translate.service';
|
||||
@@ -297,7 +297,7 @@ export class ItemDetailComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
this.apiService.submitReview(reviewData).subscribe({
|
||||
next: (response) => {
|
||||
next: () => {
|
||||
this.reviewSubmitStatus.set('success');
|
||||
this.newReview = { rating: 0, comment: '', anonymous: false };
|
||||
|
||||
|
||||
@@ -70,6 +70,14 @@
|
||||
<p>2.2. The agreement regulates the procedure for using the site and the functions provided by the Owner.</p>
|
||||
|
||||
<p>2.3. The document applies to all types of goods, services and products available on the site.</p>
|
||||
|
||||
<p>2.4. The Marketplace is an agent acting under agency agreements with Sellers (Third Parties). The Marketplace does not acquire ownership rights to the Goods/Services sold through the Platform and only provides the technical ability to conclude a transaction between the Seller and the Buyer.</p>
|
||||
|
||||
<p>2.5. The Seller is an independent business entity and bears full responsibility for the quality, safety, conformity of the Goods/Services to the stated characteristics, and fulfillment of obligations to the Buyer (warranties, returns, claims).</p>
|
||||
|
||||
<p>2.6. The Marketplace is not a party to the sale and purchase agreement between the Seller and the Buyer. All claims regarding the Goods/Services are submitted by the Buyer directly to the Seller.</p>
|
||||
|
||||
<p>2.7. The Marketplace's agency remuneration is withheld from the amount paid by the Buyer and does not increase the price for the Buyer beyond the price set by the Seller.</p>
|
||||
</section>
|
||||
|
||||
<section class="legal-section">
|
||||
@@ -509,7 +517,7 @@
|
||||
<p>Free services provided under the agreement do not imply the application of consumer protection legislation to the relationship between the User and the Seller.</p>
|
||||
|
||||
<p><strong>16.6. Absent Relationships</strong></p>
|
||||
<p>No provisions of the agreement may be construed as establishing agency relationships, partnerships, joint ventures, employment relationships or other types of relationships not expressly provided for by the agreement.</p>
|
||||
<p>No provisions of the agreement, except for the agency model with Sellers expressly provided in clause 2.4, may be construed as establishing partnerships, joint ventures, employment relationships or other types of relationships not expressly provided for by the agreement.</p>
|
||||
|
||||
<p><strong>16.7. Invalidity of Provisions</strong></p>
|
||||
<p>The recognition of one or more provisions of the agreement as invalid does not affect the legal force and applicability of the remaining provisions.</p>
|
||||
|
||||
@@ -70,6 +70,14 @@
|
||||
<p>2.2. Համաձայնագիրը կարգավորում է կայքի և Սեփականատիրոջ կողմից տրամադրվող գործառույթների օգտագործման կարգը:</p>
|
||||
|
||||
<p>2.3. Փաստաթղթի գործողությունը տարածվում է կայքում առկա ապրանքների, ծառայությունների և պրոդուկտների բոլոր տեսակների վրա:</p>
|
||||
|
||||
<p>2.4. Մարկետփլեյսը հանդիսանում է գործակալ, որը գործում է Վաճառողների (Երրորդ անձանց) հետ կնքված գործակալության պայմանագրի հիման վրա։ Մարկետփլեյսը չի ձեռք բերում Հարթակի միջոցով իրացվող Ապրանքների/Ծառայությունների նկատմամբ սեփականության իրավունք, այլ միայն ապահովում է Վաճառողի և Գնորդի միջև գործարք կնքելու տեխնիկական հնարավորությունը։</p>
|
||||
|
||||
<p>2.5. Վաճառողը հանդիսանում է ինքնուրույն տնտեսվարող սուբյեկտ և կրում է ամբողջական պատասխանատվություն Ապրանքի/Ծառայության որակի, անվտանգության, հայտարարված բնութագրերին համապատասխանության, ինչպես նաև Գնորդի նկատմամբ պարտավորությունների կատարման համար (երաշխիքներ, վերադարձ, պահանջներ)։</p>
|
||||
|
||||
<p>2.6. Մարկետփլեյսը չի հանդիսանում Վաճառողի և Գնորդի միջև առուվաճառքի պայմանագրի կողմ։ Ապրանքի/Ծառայության վերաբերյալ բոլոր պահանջները Գնորդը ներկայացնում է անմիջապես Վաճառողին։</p>
|
||||
|
||||
<p>2.7. Մարկետփլեյսի գործակալական վարձատրությունը պահվում է Գնորդի վճարած գումարից և չի ավելացնում Գնորդի համար գինը Վաճառողի սահմանած գնից ավել։</p>
|
||||
</section>
|
||||
|
||||
<section class="legal-section">
|
||||
@@ -509,7 +517,7 @@
|
||||
<p>Համաձայնագրի շրջանակում տրամադրվող անվճար ծառայությունները չեն ենթադրում սպառողների իրավունքների պաշտպանության մասին օրենսդրության նորմերի կիրառում Օգտատիրոջ և Վաճառողի հարաբերությունների նկատմամբ:</p>
|
||||
|
||||
<p><strong>16.6. Բացակայող հարաբերություններ</strong></p>
|
||||
<p>Համաձայնագրի ոչ մի դրույթ չի կարող մեկնաբանվել որպես գործակալական հարաբերությունների, գործընկերության, համատեղ ձեռնարկության, աշխատանքային հարաբերությունների կամ այլ հարաբերությունների հաստատում, որոնք ուղղակիորեն նախատեսված չեն համաձայնագրով:</p>
|
||||
<p>Համաձայնագրի ոչ մի դրույթ, բացի 2.4 կետով Վաճառողների հետ ուղղակիորեն նախատեսված գործակալական մոդելից, չի կարող մեկնաբանվել որպես գործընկերության, համատեղ ձեռնարկության, աշխատանքային հարաբերությունների կամ համաձայնագրով ուղղակիորեն չնախատեսված այլ հարաբերությունների հաստատում:</p>
|
||||
|
||||
<p><strong>16.7. Կետերի անվավեր ճանաչում</strong></p>
|
||||
<p>Համաձայնագրի մեկ կամ մի քանի դրույթների անվավեր ճանաչումը չի ազդում մնացած կետերի իրավաբանական ուժի և կիրառելիության վրա:</p>
|
||||
|
||||
@@ -70,6 +70,14 @@
|
||||
<p>2.2. Соглашение регулирует порядок использования сайта и функций, предоставляемых Владельцем.</p>
|
||||
|
||||
<p>2.3. Действие документа распространяется на все типы товаров, услуг и продуктов, имеющихся на сайте.</p>
|
||||
|
||||
<p>2.4. Маркетплейс является агентом, действующим на основании договора агентирования с Продавцами (Третьими лицами). Маркетплейс не приобретает право собственности на Товары/Услуги, реализуемые через Платформу, а лишь обеспечивает техническую возможность заключения сделки между Продавцом и Покупателем.</p>
|
||||
|
||||
<p>2.5. Продавец является самостоятельным хозяйствующим субъектом и несёт полную ответственность за качество, безопасность, соответствие Товара/Услуги заявленным характеристикам, а также за исполнение обязательств перед Покупателем (гарантии, возврат, претензии).</p>
|
||||
|
||||
<p>2.6. Маркетплейс не выступает стороной договора купли-продажи между Продавцом и Покупателем. Все претензии по Товару/Услуге Покупатель предъявляет непосредственно Продавцу.</p>
|
||||
|
||||
<p>2.7. Агентское вознаграждение Маркетплейса удерживается из суммы, уплаченной Покупателем, и не увеличивает цену для Покупателя сверх установленной Продавцом.</p>
|
||||
</section>
|
||||
|
||||
<section class="legal-section">
|
||||
@@ -509,7 +517,7 @@
|
||||
<p>Бесплатные услуги, предоставляемые в рамках соглашения, не предполагают применение норм законодательства о защите прав потребителей к взаимоотношениям между Пользователем и Продавцом.</p>
|
||||
|
||||
<p><strong>16.6. Отсутствующие отношения</strong></p>
|
||||
<p>Никакие положения соглашения не могут трактоваться как установление агентских связей, партнёрства, совместного предприятия, трудовых отношений или иных видов взаимоотношений, прямо не предусмотренных соглашением.</p>
|
||||
<p>Никакие положения соглашения, кроме прямо предусмотренной пунктом 2.4 агентской модели взаимодействия с Продавцами, не могут трактоваться как установление партнёрства, совместного предприятия, трудовых отношений или иных видов взаимоотношений, прямо не предусмотренных соглашением.</p>
|
||||
|
||||
<p><strong>16.7. Признание недействительности пунктов</strong></p>
|
||||
<p>Признание одного или нескольких положений соглашения недействительным не влияет на законную силу и применимость оставшихся пунктов.</p>
|
||||
|
||||
@@ -1,19 +1,76 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||
import { Observable, timer } from 'rxjs';
|
||||
import { map, retry } from 'rxjs/operators';
|
||||
import { Category, Item, Subcategory } from '../models';
|
||||
import { Category, DeliveryOption, Item, Subcategory } from '../models';
|
||||
import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
export interface QrCreateRequest {
|
||||
qrtype: 'QRDynamic';
|
||||
amount: number;
|
||||
currency: 'RUB';
|
||||
partnerqrID?: string;
|
||||
qrDescription?: string;
|
||||
Userid?: string;
|
||||
Reference?: string;
|
||||
RedirectUrl?: string;
|
||||
}
|
||||
|
||||
export interface QrCreateResponse {
|
||||
qrId?: string;
|
||||
qrID?: string;
|
||||
nspkID?: string;
|
||||
nspkId?: string;
|
||||
nspkurl?: string;
|
||||
status?: string;
|
||||
qrStatus?: string;
|
||||
qrExpirationDate?: string;
|
||||
payload?: string;
|
||||
Payload?: string;
|
||||
qrUrl?: string;
|
||||
partnerqrID?: string | number;
|
||||
partnerID?: string | number;
|
||||
partnerId?: string | number;
|
||||
PartnerID?: string | number;
|
||||
}
|
||||
|
||||
export interface CartPaymentRequest {
|
||||
amount: number;
|
||||
currency: 'RUB';
|
||||
siteuserID: string;
|
||||
siteorderID: string;
|
||||
redirectUrl: string;
|
||||
telegramUsername: string;
|
||||
items: Array<{ itemID: number; price: number; name: string; quantity?: number; delivery?: DeliveryOption }>;
|
||||
}
|
||||
|
||||
export interface QrDynamicStatusResponse {
|
||||
additionalInfo: string;
|
||||
paymentPurpose: string;
|
||||
amount: number;
|
||||
code: string;
|
||||
createDate: string;
|
||||
currency: string;
|
||||
order: string;
|
||||
status: string;
|
||||
qrId: string;
|
||||
transactionDate: string;
|
||||
transactionId: number;
|
||||
qrExpirationDate: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApiService {
|
||||
private readonly baseUrl = environment.apiUrl;
|
||||
private readonly qrBaseUrl = (environment as any).qrApiUrl as string;
|
||||
private readonly cartPaymentPartnerId = 'web-97ec-9c57-4dde-9037-3a68f7f83750';
|
||||
|
||||
private readonly retryConfig = {
|
||||
count: 2,
|
||||
delay: (error: unknown, retryCount: number) => timer(Math.pow(2, retryCount) * 500)
|
||||
delay: (_error: unknown, retryCount: number) => timer(Math.pow(2, retryCount) * 500)
|
||||
};
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
@@ -30,6 +87,83 @@ export class ApiService {
|
||||
return c.startsWith('0x') ? '#' + c.slice(2) : c;
|
||||
}
|
||||
|
||||
private normalizeDeliveryData(
|
||||
raw: any,
|
||||
legacyDeliveryPrice?: number
|
||||
): { options: DeliveryOption[]; isDigital: boolean; requiresSelection: boolean } {
|
||||
const rawDelivery = raw.delivery ?? raw.deliveries;
|
||||
|
||||
if (typeof rawDelivery === 'string' && rawDelivery.trim().toLowerCase() === 'digital') {
|
||||
return { options: [], isDigital: true, requiresSelection: false };
|
||||
}
|
||||
|
||||
const deliveryCandidates = Array.isArray(rawDelivery)
|
||||
? rawDelivery
|
||||
: rawDelivery != null
|
||||
? [rawDelivery]
|
||||
: [];
|
||||
const options = deliveryCandidates
|
||||
.map(candidate => normalizeDeliveryOption(candidate))
|
||||
.filter((option): option is DeliveryOption => option !== null);
|
||||
|
||||
if (options.length > 0) {
|
||||
return { options, isDigital: false, requiresSelection: rawDelivery != null };
|
||||
}
|
||||
|
||||
if (legacyDeliveryPrice !== undefined) {
|
||||
return {
|
||||
options: [{ deliveryPrice: legacyDeliveryPrice, deliveryPlace: '', deliveryTime: '' }],
|
||||
isDigital: false,
|
||||
requiresSelection: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (rawDelivery != null) {
|
||||
return { options: [], isDigital: true, requiresSelection: false };
|
||||
}
|
||||
|
||||
return { options: [], isDigital: false, requiresSelection: false };
|
||||
}
|
||||
|
||||
private normalizeSubcategory(raw: any): Subcategory {
|
||||
const subcategory: Subcategory = {
|
||||
id: String(raw.id ?? raw.categoryId ?? raw.categoryID ?? ''),
|
||||
name: typeof raw.name === 'string' ? raw.name : '',
|
||||
visible: raw.visible ?? true,
|
||||
priority: raw.priority ?? 0,
|
||||
img: raw.img ? this.resolveImageUrl(raw.img) : undefined,
|
||||
categoryId: String(raw.categoryId ?? raw.categoryID ?? raw.id ?? ''),
|
||||
parentId: String(raw.parentId ?? raw.parentID ?? ''),
|
||||
itemCount: raw.itemCount ?? raw.ItemsCount ?? 0,
|
||||
hasItems: raw.hasItems,
|
||||
subcategories: Array.isArray(raw.subcategories)
|
||||
? raw.subcategories
|
||||
.map((sub: any) => this.normalizeSubcategory(sub))
|
||||
.filter((sub: Subcategory) => this.isDisplayableSubcategory(sub))
|
||||
: [],
|
||||
};
|
||||
|
||||
return subcategory;
|
||||
}
|
||||
|
||||
private isDisplayableSubcategory(subcategory: Subcategory): boolean {
|
||||
if (subcategory.visible === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (subcategory.itemCount ?? 0) > 0
|
||||
|| subcategory.hasItems === true
|
||||
|| (subcategory.subcategories?.length ?? 0) > 0;
|
||||
}
|
||||
|
||||
private isDisplayableCategory(category: Category): boolean {
|
||||
return category.visible !== false;
|
||||
}
|
||||
|
||||
private isDisplayableItem(item: Item): boolean {
|
||||
return item.visible !== false;
|
||||
}
|
||||
|
||||
/** Resolve relative image URLs (e.g. ./images/x.webp) against site origin */
|
||||
private resolveImageUrl(url: string): string {
|
||||
if (!url) return '';
|
||||
@@ -46,6 +180,9 @@ export class ApiService {
|
||||
private normalizeItem(raw: any): Item {
|
||||
const { partnerID, ...rest } = raw;
|
||||
const item: Item = { ...rest };
|
||||
let legacyDeliveryPrice = normalizeOptionalNumber(
|
||||
raw.deliveryPrice ?? raw.delivery_price ?? raw.deliveryprice
|
||||
);
|
||||
|
||||
// Extract price/currency/remaining/colour/size from itemDetails[]
|
||||
// Note: Go struct tag is "itemdetails" but actual API may send "itemDetails"
|
||||
@@ -56,16 +193,33 @@ export class ApiService {
|
||||
...d,
|
||||
colour: this.normalizeColor(d.colour || d.color || ''),
|
||||
color: undefined,
|
||||
deliveryPrice: normalizeOptionalNumber(
|
||||
d.deliveryPrice ?? d.delivery_price ?? d.deliveryprice
|
||||
),
|
||||
}));
|
||||
if (item.price == null || item.price === 0) item.price = detail.price;
|
||||
if (!item.currency) item.currency = detail.currency;
|
||||
if (!item.colour) item.colour = this.normalizeColor(detail.colour || detail.color || '');
|
||||
if (!item.size) item.size = detail.size || '';
|
||||
if (legacyDeliveryPrice === undefined) {
|
||||
legacyDeliveryPrice = normalizeOptionalNumber(
|
||||
detail.deliveryPrice ?? detail.delivery_price ?? detail.deliveryprice
|
||||
);
|
||||
}
|
||||
// Use remaining from detail for stock level
|
||||
if (raw.remaining == null && detail.remaining != null) {
|
||||
(raw as any).remaining = detail.remaining;
|
||||
}
|
||||
}
|
||||
const deliveryData = this.normalizeDeliveryData(raw, legacyDeliveryPrice);
|
||||
if (deliveryData.options.length > 0) {
|
||||
item.deliveryOptions = deliveryData.options;
|
||||
item.deliveryMode = 'selectable';
|
||||
item.deliverySelectionRequired = deliveryData.requiresSelection;
|
||||
} else if (deliveryData.isDigital) {
|
||||
item.deliveryMode = 'digital';
|
||||
item.deliverySelectionRequired = false;
|
||||
}
|
||||
|
||||
// Map backOffice string id → legacy numeric itemID
|
||||
if (raw.id != null && raw.itemID == null) {
|
||||
@@ -186,7 +340,9 @@ export class ApiService {
|
||||
if (!items || !Array.isArray(items)) {
|
||||
return [];
|
||||
}
|
||||
return items.map(item => this.normalizeItem(item));
|
||||
return items
|
||||
.map(item => this.normalizeItem(item))
|
||||
.filter(item => this.isDisplayableItem(item));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,7 +400,9 @@ export class ApiService {
|
||||
cat.name = cat.name || '';
|
||||
|
||||
if (raw.subcategories && Array.isArray(raw.subcategories)) {
|
||||
cat.subcategories = raw.subcategories;
|
||||
cat.subcategories = raw.subcategories
|
||||
.map((sub: any) => this.normalizeSubcategory(sub))
|
||||
.filter((sub: Subcategory) => this.isDisplayableSubcategory(sub));
|
||||
}
|
||||
|
||||
return cat;
|
||||
@@ -252,7 +410,9 @@ export class ApiService {
|
||||
|
||||
private normalizeCategories(cats: any[] | null | undefined): Category[] {
|
||||
if (!cats || !Array.isArray(cats)) return [];
|
||||
return cats.map(c => this.normalizeCategory(c));
|
||||
return cats
|
||||
.map(c => this.normalizeCategory(c))
|
||||
.filter(category => this.isDisplayableCategory(category));
|
||||
}
|
||||
|
||||
// ─── Core Marketplace Endpoints ───────────────────────────
|
||||
@@ -348,57 +508,55 @@ export class ApiService {
|
||||
return this.http.post<{ message: string }>(`${this.baseUrl}/items/${itemID}/questiion`, body);
|
||||
}
|
||||
|
||||
// Payment - SBP Integration via websession QR
|
||||
createPayment(sessionId: string): Observable<{
|
||||
qrId: string;
|
||||
qrStatus: string;
|
||||
qrExpirationDate: string;
|
||||
Payload: string;
|
||||
qrUrl: string;
|
||||
}> {
|
||||
return this.http.post<{
|
||||
qrId: string;
|
||||
qrStatus: string;
|
||||
qrExpirationDate: string;
|
||||
Payload: string;
|
||||
qrUrl: string;
|
||||
}>(`${this.baseUrl}/websession/${sessionId}/qr`, {});
|
||||
createPayment(payload: QrCreateRequest, headers?: { authorizationKey?: string; userIdValue?: string }): Observable<QrCreateResponse> {
|
||||
let httpHeaders = new HttpHeaders();
|
||||
if (headers?.authorizationKey) {
|
||||
httpHeaders = httpHeaders.set('authorization-key', headers.authorizationKey);
|
||||
}
|
||||
if (headers?.userIdValue) {
|
||||
httpHeaders = httpHeaders.set('userid-value', headers.userIdValue);
|
||||
}
|
||||
return this.http.post<QrCreateResponse>(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders });
|
||||
}
|
||||
|
||||
checkPaymentStatus(sessionId: string, qrId: string): Observable<{
|
||||
additionalInfo: string;
|
||||
paymentPurpose: string;
|
||||
amount: number;
|
||||
code: string;
|
||||
createDate: string;
|
||||
currency: string;
|
||||
order: string;
|
||||
paymentStatus: string;
|
||||
qrId: string;
|
||||
transactionDate: string;
|
||||
transactionId: number;
|
||||
qrExpirationDate: string;
|
||||
}> {
|
||||
return this.http.get<{
|
||||
additionalInfo: string;
|
||||
paymentPurpose: string;
|
||||
amount: number;
|
||||
code: string;
|
||||
createDate: string;
|
||||
currency: string;
|
||||
order: string;
|
||||
paymentStatus: string;
|
||||
qrId: string;
|
||||
transactionDate: string;
|
||||
transactionId: number;
|
||||
qrExpirationDate: string;
|
||||
}>(`${this.baseUrl}/websession/${sessionId}/${qrId}`);
|
||||
createCartPayment(payload: CartPaymentRequest): Observable<QrCreateResponse> {
|
||||
return this.http.post<QrCreateResponse>(`${this.baseUrl}/cart`, payload);
|
||||
}
|
||||
|
||||
checkCartPaymentStatus(qrId: string): Observable<QrDynamicStatusResponse> {
|
||||
return this.http.get<QrDynamicStatusResponse>(
|
||||
`${this.qrBaseUrl}/qr/dynamic/${this.cartPaymentPartnerId}/${encodeURIComponent(qrId)}`
|
||||
);
|
||||
}
|
||||
|
||||
checkPaymentStatus(partnerQrId: string, qrId: string): Observable<QrDynamicStatusResponse> {
|
||||
return this.http.get<QrDynamicStatusResponse>(
|
||||
`${this.qrBaseUrl}/qr/dynamic/${encodeURIComponent(partnerQrId)}/${encodeURIComponent(qrId)}`
|
||||
);
|
||||
}
|
||||
|
||||
resolvePaymentQrId(response: QrCreateResponse): string {
|
||||
return response.qrId ?? response.qrID ?? response.nspkID ?? response.nspkId ?? '';
|
||||
}
|
||||
|
||||
resolvePaymentLink(response: QrCreateResponse): string {
|
||||
return response.nspkurl ?? response.Payload ?? response.payload ?? response.qrUrl ?? '';
|
||||
}
|
||||
|
||||
resolvePaymentQrUrl(response: QrCreateResponse): string {
|
||||
const paymentLink = this.resolvePaymentLink(response);
|
||||
if (paymentLink) {
|
||||
return `https://api.qrserver.com/v1/create-qr-code/?size=256x256&margin=8&data=${encodeURIComponent(paymentLink)}`;
|
||||
}
|
||||
|
||||
return response.qrUrl ?? '';
|
||||
}
|
||||
|
||||
submitPurchaseEmail(emailData: {
|
||||
email: string;
|
||||
phone?: string;
|
||||
telegramUserId: string | null;
|
||||
items: Array<{ itemID: number; name: string; price: number; currency: string }>;
|
||||
items: Array<{ itemID: number; name: string; price: number; currency: string; quantity?: number; delivery?: DeliveryOption }>;
|
||||
}): Observable<{ message: string }> {
|
||||
return this.http.post<{ message: string }>(`${this.baseUrl}/purchase-email`, emailData);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ export class AuthService {
|
||||
/** Display name of authenticated user */
|
||||
readonly displayName = computed(() => this.sessionSignal()?.displayName ?? null);
|
||||
|
||||
private readonly apiUrl = environment.apiUrl;
|
||||
private readonly authApiUrl = environment.authApiUrl;
|
||||
private sessionCheckTimer?: ReturnType<typeof setTimeout>;
|
||||
|
||||
@@ -85,10 +84,16 @@ export class AuthService {
|
||||
|
||||
/** Generate the Telegram login URL for bot-based auth */
|
||||
getTelegramLoginUrl(webSessionID = this.generateGuid()): string {
|
||||
const botUsername = (environment as Record<string, unknown>)['telegramBot'] as string || 'DexarSupport_bot';
|
||||
const botUsername = this.getTelegramBotUsername();
|
||||
return `https://t.me/${botUsername}?start=${encodeURIComponent(webSessionID)}`;
|
||||
}
|
||||
|
||||
/** Generate a Telegram app deep link for mobile login without opening a browser tab. */
|
||||
getTelegramAppLoginUrl(webSessionID: string): string {
|
||||
const botUsername = this.getTelegramBotUsername();
|
||||
return `tg://resolve?domain=${encodeURIComponent(botUsername)}&start=${encodeURIComponent(webSessionID)}`;
|
||||
}
|
||||
|
||||
/** Get QR code data URL for Telegram login */
|
||||
getTelegramQrUrl(): string {
|
||||
return this.getTelegramLoginUrl();
|
||||
@@ -113,14 +118,6 @@ export class AuthService {
|
||||
);
|
||||
}
|
||||
|
||||
/** Sync local cart to the backend session after login */
|
||||
syncCart(sessionId: string, items: Array<{ itemID: number; quantity: number; colour?: string; size?: string; price?: number }>): Observable<unknown> {
|
||||
if (!items.length) return of(null);
|
||||
return this.http.post(`${this.apiUrl}/websession/${sessionId}`, items, {
|
||||
withCredentials: true,
|
||||
}).pipe(catchError(() => of(null)));
|
||||
}
|
||||
|
||||
/** Show login dialog (called when user tries to pay without being logged in) */
|
||||
requestLogin(): void {
|
||||
this.showLoginSignal.set(true);
|
||||
@@ -153,7 +150,7 @@ export class AuthService {
|
||||
this.sessionSignal.set(session);
|
||||
this.statusSignal.set('authenticated');
|
||||
this.setStoredWebSessionID(session.sessionId);
|
||||
this.scheduleSessionRefresh(session.expiresAt);
|
||||
this.scheduleSessionRefresh(session.expires);
|
||||
}
|
||||
|
||||
private clearAuthState(status: AuthStatus): void {
|
||||
@@ -214,19 +211,19 @@ export class AuthService {
|
||||
const explicitDisplayName = this.readString(this.readFirst(response, ['displayName', 'DisplayName', 'name', 'Name']))
|
||||
?? this.readString(this.readFirst(user, ['displayName', 'DisplayName', 'name', 'Name']))
|
||||
const displayName = explicitDisplayName ?? username ?? (fullName || 'Telegram User');
|
||||
const telegramUserId = this.readNumber(this.readFirst(user, ['telegramUserId', 'telegramUserID', 'TelegramUserID', 'id', 'ID']))
|
||||
?? this.readNumber(this.readFirst(response, ['telegramUserId', 'telegramUserID', 'TelegramUserID', 'userID', 'UserID']))
|
||||
?? 0;
|
||||
const telegramUserId = this.readNumber(this.readFirst(user, ['userId','telegramUserId', 'telegramUserID', 'TelegramUserID', 'id', 'ID']))
|
||||
?? this.readNumber(this.readFirst(response, ['userId', 'telegramUserId', 'telegramUserID', 'TelegramUserID', 'userID', 'UserID', 'UserId']))
|
||||
?? null;
|
||||
const expiresAt = this.readString(this.readFirst(response, ['expiresAt', 'ExpiresAt', 'expires', 'Expires']))
|
||||
?? new Date(Date.now() + WEB_SESSION_COOKIE_MAX_AGE_SECONDS * 1000).toISOString();
|
||||
|
||||
return {
|
||||
sessionId,
|
||||
telegramUserId,
|
||||
userId: telegramUserId,
|
||||
username,
|
||||
displayName,
|
||||
active,
|
||||
expiresAt,
|
||||
expires: expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -357,4 +354,8 @@ export class AuthService {
|
||||
|
||||
document.cookie = `${WEB_SESSION_COOKIE}=; Max-Age=0; Path=/; SameSite=Lax`;
|
||||
}
|
||||
|
||||
private getTelegramBotUsername(): string {
|
||||
return (environment as Record<string, unknown>)['telegramBot'] as string || 'DexarSupport_bot';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, signal, computed, effect } from '@angular/core';
|
||||
import { ApiService } from './api.service';
|
||||
import { Item, CartItem } from '../models';
|
||||
import { Injectable, signal, computed, effect, Injector } from '@angular/core';
|
||||
import { DeliveryOption, CartItem } from '../models';
|
||||
import { getDiscountedPrice } from '../utils/item.utils';
|
||||
import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils';
|
||||
import { environment } from '../../environments/environment';
|
||||
import type { } from '../types/telegram.types';
|
||||
|
||||
@@ -28,8 +28,27 @@ export class CartService {
|
||||
return total + (getDiscountedPrice(item) * item.quantity);
|
||||
}, 0);
|
||||
});
|
||||
totalDeliveryPrice = computed(() => {
|
||||
const items = this.cartItems();
|
||||
if (!Array.isArray(items)) return 0;
|
||||
return items.reduce((total, item) => {
|
||||
return total + ((item.selectedDelivery?.deliveryPrice ?? 0) * item.quantity);
|
||||
}, 0);
|
||||
});
|
||||
totalWithDelivery = computed(() => this.totalPrice() + this.totalDeliveryPrice());
|
||||
allRequiredDeliveriesSelected = computed(() => {
|
||||
const items = this.cartItems();
|
||||
if (!Array.isArray(items)) return true;
|
||||
return items.every(item => !this.itemRequiresDeliverySelection(item) || item.selectedDelivery != null);
|
||||
});
|
||||
hasDeliveryPrice = computed(() => {
|
||||
const items = this.cartItems();
|
||||
if (!Array.isArray(items)) return false;
|
||||
return this.allRequiredDeliveriesSelected()
|
||||
&& items.some(item => (item.deliveryOptions?.length ?? 0) > 0 || item.selectedDelivery != null);
|
||||
});
|
||||
|
||||
constructor(private apiService: ApiService) {
|
||||
constructor(private injector: Injector) {
|
||||
this.loadCart();
|
||||
|
||||
// Auto-save whenever cart changes (skip the initial empty state)
|
||||
@@ -41,6 +60,74 @@ export class CartService {
|
||||
});
|
||||
}
|
||||
|
||||
private sameDeliveryOption(left: DeliveryOption, right: DeliveryOption): boolean {
|
||||
return left.deliveryPrice === right.deliveryPrice
|
||||
&& left.deliveryPlace === right.deliveryPlace
|
||||
&& left.deliveryTime === right.deliveryTime;
|
||||
}
|
||||
|
||||
private itemRequiresDeliverySelection(item: CartItem): boolean {
|
||||
return item.deliveryMode !== 'digital'
|
||||
&& (item.deliveryOptions?.length ?? 0) > 0
|
||||
&& item.deliverySelectionRequired !== false;
|
||||
}
|
||||
|
||||
private normalizeDeliveryState(item: CartItem): {
|
||||
deliveryMode?: CartItem['deliveryMode'];
|
||||
deliveryOptions: DeliveryOption[];
|
||||
deliverySelectionRequired: boolean;
|
||||
selectedDelivery: DeliveryOption | null;
|
||||
} {
|
||||
const normalizedOptions = Array.isArray(item.deliveryOptions)
|
||||
? item.deliveryOptions
|
||||
.map(option => normalizeDeliveryOption(option))
|
||||
.filter((option): option is DeliveryOption => option !== null)
|
||||
: [];
|
||||
const legacyDeliveryPrice = normalizeOptionalNumber(item.deliveryPrice);
|
||||
const deliveryOptions = normalizedOptions.length > 0
|
||||
? normalizedOptions
|
||||
: legacyDeliveryPrice !== undefined
|
||||
? [{ deliveryPrice: legacyDeliveryPrice, deliveryPlace: '', deliveryTime: '' }]
|
||||
: [];
|
||||
const deliveryMode = item.deliveryMode === 'digital'
|
||||
? 'digital'
|
||||
: deliveryOptions.length > 0
|
||||
? 'selectable'
|
||||
: undefined;
|
||||
const deliverySelectionRequired = deliveryOptions.length > 0
|
||||
? item.deliverySelectionRequired !== false && normalizedOptions.length > 0
|
||||
: false;
|
||||
const selectedDelivery = normalizeDeliveryOption(item.selectedDelivery)
|
||||
?? (deliveryOptions.length === 1 && !deliverySelectionRequired ? deliveryOptions[0] : null);
|
||||
const matchedSelection = selectedDelivery && deliveryOptions.length > 0
|
||||
? deliveryOptions.find(option => this.sameDeliveryOption(option, selectedDelivery)) ?? selectedDelivery
|
||||
: selectedDelivery;
|
||||
|
||||
return {
|
||||
deliveryMode,
|
||||
deliveryOptions,
|
||||
deliverySelectionRequired,
|
||||
selectedDelivery: matchedSelection,
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeCartItem(item: CartItem): CartItem {
|
||||
const { deliveryPrice, ...rest } = item;
|
||||
const deliveryState = this.normalizeDeliveryState(item);
|
||||
const hasDeliveryState = !!deliveryState.deliveryMode
|
||||
|| deliveryState.deliveryOptions.length > 0
|
||||
|| deliveryState.selectedDelivery != null;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
quantity: item.quantity || 1,
|
||||
...(hasDeliveryState ? { deliverySelectionRequired: deliveryState.deliverySelectionRequired } : {}),
|
||||
...(deliveryState.deliveryMode ? { deliveryMode: deliveryState.deliveryMode } : {}),
|
||||
...(deliveryState.deliveryOptions.length > 0 ? { deliveryOptions: deliveryState.deliveryOptions } : {}),
|
||||
...(deliveryState.selectedDelivery ? { selectedDelivery: deliveryState.selectedDelivery } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
private saveToStorage(items: CartItem[]): void {
|
||||
const data = JSON.stringify(items);
|
||||
|
||||
@@ -90,10 +177,7 @@ export class CartService {
|
||||
try {
|
||||
const items = JSON.parse(json);
|
||||
if (Array.isArray(items)) {
|
||||
this.cartItems.set(items.map(item => ({
|
||||
...item,
|
||||
quantity: item.quantity || 1
|
||||
})));
|
||||
this.cartItems.set(items.map(item => this.normalizeCartItem(item)));
|
||||
return true;
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -116,23 +200,28 @@ export class CartService {
|
||||
} else {
|
||||
// Get item details from API and add to cart
|
||||
this.addingItems.add(itemID);
|
||||
this.apiService.getItem(itemID).subscribe({
|
||||
next: (item) => {
|
||||
const cartItem: CartItem = {
|
||||
...item,
|
||||
quantity,
|
||||
...(variant?.colour != null && { colour: variant.colour }),
|
||||
...(variant?.size != null && { size: variant.size }),
|
||||
...(variant?.price != null && { price: variant.price }),
|
||||
...(variant?.currency != null && { currency: variant.currency }),
|
||||
};
|
||||
this.cartItems.set([...this.cartItems(), cartItem]);
|
||||
this.addingItems.delete(itemID);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error adding to cart:', err);
|
||||
this.addingItems.delete(itemID);
|
||||
}
|
||||
import('./api.service').then(({ ApiService }) => {
|
||||
this.injector.get(ApiService).getItem(itemID).subscribe({
|
||||
next: (item) => {
|
||||
const cartItem = this.normalizeCartItem({
|
||||
...item,
|
||||
quantity,
|
||||
...(variant?.colour != null && { colour: variant.colour }),
|
||||
...(variant?.size != null && { size: variant.size }),
|
||||
...(variant?.price != null && { price: variant.price }),
|
||||
...(variant?.currency != null && { currency: variant.currency }),
|
||||
});
|
||||
this.cartItems.set([...this.cartItems(), cartItem]);
|
||||
this.addingItems.delete(itemID);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error adding to cart:', err);
|
||||
this.addingItems.delete(itemID);
|
||||
}
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error('Error loading API service:', err);
|
||||
this.addingItems.delete(itemID);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -150,6 +239,22 @@ export class CartService {
|
||||
this.cartItems.set(updatedItems);
|
||||
}
|
||||
|
||||
setSelectedDelivery(itemID: number, selectedDelivery: DeliveryOption | null): void {
|
||||
const normalizedSelection = normalizeDeliveryOption(selectedDelivery);
|
||||
const updatedItems = this.cartItems().map(item => {
|
||||
if (item.itemID !== itemID) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return this.normalizeCartItem({
|
||||
...item,
|
||||
selectedDelivery: normalizedSelection,
|
||||
});
|
||||
});
|
||||
|
||||
this.cartItems.set(updatedItems);
|
||||
}
|
||||
|
||||
removeItems(itemIDs: number[]): void {
|
||||
const currentItems = this.cartItems();
|
||||
const updatedItems = currentItems.filter(item => !itemIDs.includes(item.itemID));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface TelegramUser {
|
||||
interface TelegramUser {
|
||||
id: number;
|
||||
first_name: string;
|
||||
last_name?: string;
|
||||
@@ -21,3 +21,5 @@ declare global {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
@@ -13,25 +13,10 @@ export function getMainImage(item: Item): string {
|
||||
return item.photos?.[0]?.url || '/assets/images/placeholder.svg';
|
||||
}
|
||||
|
||||
export function getAllImages(item: Item): string[] {
|
||||
if (item.imgs && item.imgs.length > 0) {
|
||||
return item.imgs;
|
||||
}
|
||||
return item.photos?.map(p => p.url) || [];
|
||||
}
|
||||
|
||||
export function trackByItemId(index: number, item: Item): number | string {
|
||||
export function trackByItemId(_index: number, item: Item): number | string {
|
||||
return item.id || item.itemID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display description — supports both legacy HTML string
|
||||
* and structured key-value pairs from backOffice API.
|
||||
*/
|
||||
export function hasStructuredDescription(item: Item): boolean {
|
||||
return Array.isArray(item.descriptionFields) && item.descriptionFields.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute stock status from quantity if the legacy `remainings` field is absent.
|
||||
*/
|
||||
|
||||
58
src/app/utils/normalization.utils.ts
Normal file
58
src/app/utils/normalization.utils.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { DeliveryOption } from '../models';
|
||||
|
||||
export function normalizeOptionalNumber(value: unknown): number | undefined {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalized = typeof value === 'number'
|
||||
? value
|
||||
: Number(String(value).replace(',', '.'));
|
||||
|
||||
return Number.isFinite(normalized) ? normalized : undefined;
|
||||
}
|
||||
|
||||
function normalizeOptionalString(value: unknown): string {
|
||||
if (typeof value === 'string') {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
if (typeof value === 'number' || typeof value === 'bigint') {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> | null {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
||||
? value as Record<string, unknown>
|
||||
: null;
|
||||
}
|
||||
|
||||
export function normalizeDeliveryOption(value: unknown): DeliveryOption | null {
|
||||
const source = asRecord(value);
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const deliveryPlace = normalizeOptionalString(
|
||||
source['deliveryPlace'] ?? source['delivery_place'] ?? source['deliveryplace']
|
||||
);
|
||||
const deliveryTime = normalizeOptionalString(
|
||||
source['deliveryTime'] ?? source['delivery_time'] ?? source['deliverytime']
|
||||
);
|
||||
const deliveryPrice = normalizeOptionalNumber(
|
||||
source['deliveryPrice'] ?? source['delivery_price'] ?? source['deliveryprice']
|
||||
);
|
||||
|
||||
if (deliveryPrice === undefined && !deliveryPlace && !deliveryTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
deliveryPrice: deliveryPrice ?? 0,
|
||||
deliveryPlace,
|
||||
deliveryTime,
|
||||
};
|
||||
}
|
||||
@@ -6,6 +6,7 @@ export const environment = {
|
||||
theme: 'lavero',
|
||||
apiUrl: '/api',
|
||||
authApiUrl: 'https://users.vitanova.network:456',
|
||||
qrApiUrl: 'https://qr.vitanova.network/api',
|
||||
logo: '/assets/images/lavero/lavero-logo.png',
|
||||
contactEmail: 'info@lavero.store',
|
||||
supportEmail: 'info@lavero.store',
|
||||
|
||||
@@ -6,6 +6,7 @@ export const environment = {
|
||||
theme: 'lavero',
|
||||
apiUrl: '/api',
|
||||
authApiUrl: 'https://users.vitanova.network:456',
|
||||
qrApiUrl: 'https://qr.vitanova.network/api',
|
||||
logo: '/assets/images/lavero/lavero-logo.png',
|
||||
contactEmail: 'info@lavero.store',
|
||||
supportEmail: 'info@lavero.store',
|
||||
|
||||
@@ -6,6 +6,7 @@ export const environment = {
|
||||
theme: 'novo',
|
||||
apiUrl: '/api',
|
||||
authApiUrl: 'https://users.vitanova.network:456',
|
||||
qrApiUrl: 'https://qr.vitanova.network/api',
|
||||
logo: '/assets/images/novo-logo.svg',
|
||||
contactEmail: 'info@novo.market',
|
||||
supportEmail: 'info@novo.market',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user