added language routing system
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { brandInfoRoutes, brandLegalRoutes } from './brands/brand-routes';
|
||||
import { languageGuard } from './guards/language.guard';
|
||||
|
||||
// Core routes (same across all brands)
|
||||
const coreRoutes: Routes = [
|
||||
@@ -29,13 +30,18 @@ const coreRoutes: Routes = [
|
||||
}
|
||||
];
|
||||
|
||||
// Combine core routes with brand-specific routes
|
||||
// All routes sit under a :lang prefix (e.g. /ru/cart, /en/item/5)
|
||||
export const routes: Routes = [
|
||||
...coreRoutes,
|
||||
...brandInfoRoutes,
|
||||
...brandLegalRoutes,
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: ''
|
||||
}
|
||||
path: ':lang',
|
||||
canActivate: [languageGuard],
|
||||
children: [
|
||||
...coreRoutes,
|
||||
...brandInfoRoutes,
|
||||
...brandLegalRoutes,
|
||||
{ path: '**', redirectTo: '' }
|
||||
]
|
||||
},
|
||||
// URLs without a language prefix → redirect to default language
|
||||
{ path: '**', redirectTo: 'ru' }
|
||||
];
|
||||
@@ -45,7 +45,8 @@ export class App implements OnInit {
|
||||
.subscribe((event) => {
|
||||
const navEnd = event as NavigationEnd;
|
||||
const url = navEnd.urlAfterRedirects || navEnd.url;
|
||||
this.isHomePage.set(url === '/' || url === '/home' || url === '');
|
||||
// Home pages: /ru, /en, /hy (with or without trailing slash)
|
||||
this.isHomePage.set(/^\/[a-z]{2}\/?$/.test(url) || url === '/' || url === '');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h2>6. Возврат средств</h2>
|
||||
<p>6.1. Порядок возврата денежных средств регулируется <a routerLink="/return-policy">Политикой возврата</a> и зависит от типа приобретенного Товара/Услуги.</p>
|
||||
<p>6.1. Порядок возврата денежных средств регулируется <a [routerLink]="'/return-policy' | langRoute">Политикой возврата</a> и зависит от типа приобретенного Товара/Услуги.</p>
|
||||
<p>6.2. Возврат средств производится на тот же платежный инструмент, с которого была произведена оплата.</p>
|
||||
<p>6.3. Срок возврата денежных средств составляет:</p>
|
||||
<div class="refund-times">
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { LangRoutePipe } from '../../../../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-payment-terms-novo',
|
||||
imports: [],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './payment-terms.component.html',
|
||||
styleUrls: ['../../../../../pages/legal/payment-terms/payment-terms.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<p>1.4. Акцепт происходит автоматически при любом действии: визите, регистрации, оформлении покупки.</p>
|
||||
<p>1.5. Подписание бумажного договора не требуется — электронная форма юридически действительна.</p>
|
||||
<p>1.6. Несогласие с условиями означает обязанность покинуть сайт.</p>
|
||||
<p>1.7. Также применяется <a routerLink="/privacy-policy">Политика конфиденциальности</a>.</p>
|
||||
<p>1.7. Также применяется <a [routerLink]="'/privacy-policy' | langRoute">Политика конфиденциальности</a>.</p>
|
||||
<p>1.8. Мы можем обновлять условия в одностороннем порядке.</p>
|
||||
<p>1.9. Промо-кампании могут иметь специальные правила.</p>
|
||||
</section>
|
||||
@@ -204,7 +204,7 @@
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h2>13. Возврат и обмен товара</h2>
|
||||
<p><strong>13.1. Общие правила:</strong> Цифровые товары не подлежат возврату. Физические товары — согласно <a routerLink="/return-policy">Политике возврата</a> и законам о правах потребителей.</p>
|
||||
<p><strong>13.1. Общие правила:</strong> Цифровые товары не подлежат возврату. Физические товары — согласно <a [routerLink]="'/return-policy' | langRoute">Политике возврата</a> и законам о правах потребителей.</p>
|
||||
<p><strong>13.2. Процедура возврата:</strong> В соответствии с соглашением и законодательством РФ.</p>
|
||||
<p><strong>13.3. Акционные наборы:</strong> Возврат только в комплексе, отдельные товары вернуть нельзя.</p>
|
||||
<p><strong>13.4. Затраты на доставку:</strong> При возврате качественного товара продавец может взыскать затраты на доставку.</p>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { LangRoutePipe } from '../../../../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-public-offer-novo',
|
||||
imports: [],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './public-offer.component.html',
|
||||
styleUrls: ['../../../../../pages/legal/public-offer/public-offer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -11,27 +11,27 @@
|
||||
<div class="novo-footer-col">
|
||||
<h4>Компания</h4>
|
||||
<ul class="novo-footer-links">
|
||||
<li><a routerLink="/about">О нас</a></li>
|
||||
<li><a routerLink="/contacts">Контакты</a></li>
|
||||
<li><a routerLink="/company-details">Реквизиты</a></li>
|
||||
<li><a [routerLink]="'/about' | langRoute">О нас</a></li>
|
||||
<li><a [routerLink]="'/contacts' | langRoute">Контакты</a></li>
|
||||
<li><a [routerLink]="'/company-details' | langRoute">Реквизиты</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="novo-footer-col">
|
||||
<h4>Поддержка</h4>
|
||||
<ul class="novo-footer-links">
|
||||
<li><a routerLink="/faq">FAQ</a></li>
|
||||
<li><a routerLink="/delivery">Доставка</a></li>
|
||||
<li><a routerLink="/guarantee">Гарантия</a></li>
|
||||
<li><a [routerLink]="'/faq' | langRoute">FAQ</a></li>
|
||||
<li><a [routerLink]="'/delivery' | langRoute">Доставка</a></li>
|
||||
<li><a [routerLink]="'/guarantee' | langRoute">Гарантия</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="novo-footer-col">
|
||||
<h4>Правовая информация</h4>
|
||||
<ul class="novo-footer-links">
|
||||
<li><a routerLink="/public-offer">Оферта</a></li>
|
||||
<li><a routerLink="/privacy-policy">Конфиденциальность</a></li>
|
||||
<li><a routerLink="/return-policy">Возврат</a></li>
|
||||
<li><a [routerLink]="'/public-offer' | langRoute">Оферта</a></li>
|
||||
<li><a [routerLink]="'/privacy-policy' | langRoute">Конфиденциальность</a></li>
|
||||
<li><a [routerLink]="'/return-policy' | langRoute">Возврат</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,28 +63,28 @@
|
||||
<div class="dexar-footer-col">
|
||||
<h4>Информация</h4>
|
||||
<ul>
|
||||
<li><a routerLink="/about">О компании</a></li>
|
||||
<li><a routerLink="/contacts">Контакты</a></li>
|
||||
<li><a routerLink="/company-details">Реквизиты</a></li>
|
||||
<li><a [routerLink]="'/about' | langRoute">О компании</a></li>
|
||||
<li><a [routerLink]="'/contacts' | langRoute">Контакты</a></li>
|
||||
<li><a [routerLink]="'/company-details' | langRoute">Реквизиты</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dexar-footer-col">
|
||||
<h4>Документы</h4>
|
||||
<ul>
|
||||
<li><a routerLink="/payment-terms">Правила оплаты</a></li>
|
||||
<li><a routerLink="/return-policy">Политика возврата</a></li>
|
||||
<li><a routerLink="/public-offer">Публичная оферта</a></li>
|
||||
<li><a routerLink="/privacy-policy">Конфиденциальность</a></li>
|
||||
<li><a [routerLink]="'/payment-terms' | langRoute">Правила оплаты</a></li>
|
||||
<li><a [routerLink]="'/return-policy' | langRoute">Политика возврата</a></li>
|
||||
<li><a [routerLink]="'/public-offer' | langRoute">Публичная оферта</a></li>
|
||||
<li><a [routerLink]="'/privacy-policy' | langRoute">Конфиденциальность</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dexar-footer-col">
|
||||
<h4>Помощь</h4>
|
||||
<ul>
|
||||
<li><a routerLink="/faq">FAQ</a></li>
|
||||
<li><a routerLink="/delivery">Доставка</a></li>
|
||||
<li><a routerLink="/guarantee">Гарантия</a></li>
|
||||
<li><a [routerLink]="'/faq' | langRoute">FAQ</a></li>
|
||||
<li><a [routerLink]="'/delivery' | langRoute">Доставка</a></li>
|
||||
<li><a [routerLink]="'/guarantee' | langRoute">Гарантия</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
imports: [RouterLink],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<header class="novo-header">
|
||||
<div class="novo-header-container">
|
||||
<div class="novo-left">
|
||||
<a routerLink="/" class="novo-logo" (click)="closeMenu()">
|
||||
<a [routerLink]="'/' | langRoute" class="novo-logo" (click)="closeMenu()">
|
||||
<app-logo />
|
||||
<!-- <span class="novo-brand">{{ brandName }}</span> -->
|
||||
</a>
|
||||
@@ -11,16 +11,16 @@
|
||||
|
||||
<nav class="novo-nav" [class.novo-nav-open]="menuOpen">
|
||||
<div class="novo-nav-links">
|
||||
<a routerLink="/" routerLinkActive="novo-active" [routerLinkActiveOptions]="{exact: true}" (click)="closeMenu()" class="novo-link">
|
||||
<a [routerLink]="'/' | langRoute" routerLinkActive="novo-active" [routerLinkActiveOptions]="{exact: true}" (click)="closeMenu()" class="novo-link">
|
||||
Главная
|
||||
</a>
|
||||
<a routerLink="/search" routerLinkActive="novo-active" (click)="closeMenu()" class="novo-link">
|
||||
<a [routerLink]="'/search' | langRoute" routerLinkActive="novo-active" (click)="closeMenu()" class="novo-link">
|
||||
Поиск
|
||||
</a>
|
||||
<a routerLink="/about" routerLinkActive="novo-active" (click)="closeMenu()" class="novo-link">
|
||||
<a [routerLink]="'/about' | langRoute" routerLinkActive="novo-active" (click)="closeMenu()" class="novo-link">
|
||||
О нас
|
||||
</a>
|
||||
<a routerLink="/contacts" routerLinkActive="novo-active" (click)="closeMenu()" class="novo-link">
|
||||
<a [routerLink]="'/contacts' | langRoute" routerLinkActive="novo-active" (click)="closeMenu()" class="novo-link">
|
||||
Контакты
|
||||
</a>
|
||||
</div>
|
||||
@@ -29,7 +29,7 @@
|
||||
<div class="novo-right">
|
||||
<app-language-selector />
|
||||
|
||||
<a routerLink="/cart" routerLinkActive="novo-cart-active" class="novo-cart" (click)="closeMenu()">
|
||||
<a [routerLink]="'/cart' | langRoute" routerLinkActive="novo-cart-active" class="novo-cart" (click)="closeMenu()">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="9" cy="21" r="1"></circle>
|
||||
<circle cx="20" cy="21" r="1"></circle>
|
||||
@@ -53,21 +53,21 @@
|
||||
<header class="dexar-header">
|
||||
<div class="dexar-header-container">
|
||||
<!-- Logo -->
|
||||
<a routerLink="/" class="dexar-logo" (click)="closeMenu()">
|
||||
<a [routerLink]="'/' | langRoute" class="dexar-logo" (click)="closeMenu()">
|
||||
<app-logo />
|
||||
</a>
|
||||
|
||||
<!-- Navigation Buttons (desktop) -->
|
||||
<nav class="dexar-nav">
|
||||
<div class="dexar-nav-group">
|
||||
<a routerLink="/" routerLinkActive="dexar-active" [routerLinkActiveOptions]="{exact: true}"
|
||||
<a [routerLink]="'/' | langRoute" routerLinkActive="dexar-active" [routerLinkActiveOptions]="{exact: true}"
|
||||
(click)="closeMenu()" class="dexar-nav-btn dexar-nav-btn-left">
|
||||
Главная
|
||||
</a>
|
||||
<a routerLink="/about" routerLinkActive="dexar-active" (click)="closeMenu()" class="dexar-nav-btn dexar-nav-btn-middle">
|
||||
<a [routerLink]="'/about' | langRoute" routerLinkActive="dexar-active" (click)="closeMenu()" class="dexar-nav-btn dexar-nav-btn-middle">
|
||||
О нас
|
||||
</a>
|
||||
<a routerLink="/contacts" routerLinkActive="dexar-active" (click)="closeMenu()" class="dexar-nav-btn dexar-nav-btn-right">
|
||||
<a [routerLink]="'/contacts' | langRoute" routerLinkActive="dexar-active" (click)="closeMenu()" class="dexar-nav-btn dexar-nav-btn-right">
|
||||
Контакты
|
||||
</a>
|
||||
</div>
|
||||
@@ -95,7 +95,7 @@
|
||||
<!-- Right Actions -->
|
||||
<div class="dexar-actions">
|
||||
<!-- Cart Button -->
|
||||
<a routerLink="/cart" routerLinkActive="dexar-cart-active" class="dexar-cart-btn" (click)="closeMenu()">
|
||||
<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" />
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
<!-- Mobile Menu Panel (outside header for correct stacking) -->
|
||||
<div class="dexar-mobile-menu" [class.dexar-mobile-menu-open]="menuOpen">
|
||||
<a routerLink="/" routerLinkActive="dexar-mobile-active" [routerLinkActiveOptions]="{exact: true}"
|
||||
<a [routerLink]="'/' | langRoute" routerLinkActive="dexar-mobile-active" [routerLinkActiveOptions]="{exact: true}"
|
||||
(click)="closeMenu()" class="dexar-mobile-item">
|
||||
<svg width="24" height="24" viewBox="0 0 31 31" fill="none">
|
||||
<path d="M16.185 2.22124C15.8067 1.84292 15.1933 1.84292 14.815 2.22124L3.18999 13.8462C3.00831 14.0279 2.90625 14.2743 2.90625 14.5312V28.0938C2.90625 28.6288 3.33997 29.0625 3.875 29.0625H12.5938C13.1288 29.0625 13.5625 28.6288 13.5625 28.0938V20.3438H17.4375V28.0938C17.4375 28.6288 17.8712 29.0625 18.4062 29.0625H27.125C27.66 29.0625 28.0938 28.6288 28.0938 28.0938V14.5312C28.0938 14.2743 27.9917 14.0279 27.81 13.8462L25.1875 11.2237V4.84375C25.1875 4.30872 24.7538 3.875 24.2188 3.875H22.2812C21.7462 3.875 21.3125 4.30872 21.3125 4.84375V7.34873L16.185 2.22124ZM4.84375 27.125V14.9325L15.5 4.27627L26.1562 14.9325V27.125H19.375V19.375C19.375 18.84 18.9413 18.4062 18.4062 18.4062H12.5938C12.0587 18.4062 11.625 18.84 11.625 19.375V27.125H4.84375Z" fill="#497671" />
|
||||
@@ -149,7 +149,7 @@
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a routerLink="/about" routerLinkActive="dexar-mobile-active" (click)="closeMenu()" class="dexar-mobile-item">
|
||||
<a [routerLink]="'/about' | langRoute" routerLinkActive="dexar-mobile-active" (click)="closeMenu()" class="dexar-mobile-item">
|
||||
<svg width="24" height="24" viewBox="0 0 31 31" fill="none">
|
||||
<path d="M27.125 1.9375C28.1951 1.9375 29.0625 2.80495 29.0625 3.875V27.125C29.0625 28.1951 28.1951 29.0625 27.125 29.0625H3.875C2.80495 29.0625 1.9375 28.1951 1.9375 27.125V3.875C1.9375 2.80495 2.80495 1.9375 3.875 1.9375H27.125ZM3.875 0C1.7349 0 0 1.7349 0 3.875V27.125C0 29.2651 1.7349 31 3.875 31H27.125C29.2651 31 31 29.2651 31 27.125V3.875C31 1.7349 29.2651 0 27.125 0H3.875Z" fill="#497671" />
|
||||
<path d="M17.3032 12.764L12.8644 13.3203L12.7055 14.0582L13.5796 14.2172C14.1472 14.3534 14.2608 14.5577 14.1359 15.1254L12.7055 21.8461C12.3308 23.583 12.9098 24.4004 14.2721 24.4004C15.3279 24.4004 16.554 23.9122 17.1102 23.2424L17.2805 22.4364C16.8945 22.777 16.3269 22.9132 15.9523 22.9132C15.4187 22.9132 15.2257 22.5386 15.362 21.8801L17.3032 12.764Z" fill="#497671" />
|
||||
@@ -161,7 +161,7 @@
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a routerLink="/contacts" routerLinkActive="dexar-mobile-active" (click)="closeMenu()" class="dexar-mobile-item">
|
||||
<a [routerLink]="'/contacts' | langRoute" routerLinkActive="dexar-mobile-active" (click)="closeMenu()" class="dexar-mobile-item">
|
||||
<svg width="24" height="24" viewBox="0 0 31 31" fill="none">
|
||||
<path d="M0 7.75C0 5.6099 1.7349 3.875 3.875 3.875H27.125C29.2651 3.875 31 5.6099 31 7.75V23.25C31 25.3901 29.2651 27.125 27.125 27.125H3.875C1.7349 27.125 0 25.3901 0 23.25V7.75ZM3.875 5.8125C2.80495 5.8125 1.9375 6.67995 1.9375 7.75V8.17025L15.5 16.3078L29.0625 8.17025V7.75C29.0625 6.67995 28.1951 5.8125 27.125 5.8125H3.875ZM29.0625 10.4297L19.9406 15.9029L29.0625 21.5164V10.4297ZM28.9971 23.7511L18.0688 17.026L15.5 18.5672L12.9312 17.026L2.00292 23.7511C2.22375 24.5782 2.97823 25.1875 3.875 25.1875H27.125C28.0218 25.1875 28.7762 24.5782 28.9971 23.7511ZM1.9375 21.5164L11.0594 15.9029L1.9375 10.4297V21.5164Z" fill="#497671" />
|
||||
</svg>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Component, ChangeDetectionStrategy, Renderer2, inject, DOCUMENT } from '@angular/core';
|
||||
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { CartService } from '../../services';
|
||||
import { CartService, LanguageService } from '../../services';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { LogoComponent } from '../logo/logo.component';
|
||||
import { LanguageSelectorComponent } from '../language-selector/language-selector.component';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
imports: [RouterLink, RouterLinkActive, LogoComponent, LanguageSelectorComponent],
|
||||
imports: [RouterLink, RouterLinkActive, LogoComponent, LanguageSelectorComponent, LangRoutePipe],
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -21,6 +22,7 @@ export class HeaderComponent {
|
||||
|
||||
private renderer = inject(Renderer2);
|
||||
private document = inject(DOCUMENT);
|
||||
private langService = inject(LanguageService);
|
||||
|
||||
constructor(private cartService: CartService, private router: Router) {
|
||||
this.cartItemCount = this.cartService.itemCount;
|
||||
@@ -41,12 +43,14 @@ export class HeaderComponent {
|
||||
}
|
||||
|
||||
navigateToSearch(): void {
|
||||
this.router.navigate(['/search']);
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}/search`]);
|
||||
}
|
||||
|
||||
navigateToCatalog(): void {
|
||||
this.closeMenu();
|
||||
this.router.navigate(['/']).then(() => {
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}`]).then(() => {
|
||||
setTimeout(() => {
|
||||
this.document.getElementById('catalog')?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 100);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[showIndicators]="true">
|
||||
<ng-template let-product pTemplate="item">
|
||||
<div class="item-card">
|
||||
<a [routerLink]="['/item', product.itemID]" class="item-link">
|
||||
<a [routerLink]="['/item', product.itemID] | langRoute" class="item-link">
|
||||
<div class="item-image">
|
||||
<img [src]="getItemImage(product)" [alt]="product.name" loading="lazy" />
|
||||
@if (product.discount > 0) {
|
||||
|
||||
@@ -8,11 +8,12 @@ import { ApiService, CartService } from '../../services';
|
||||
import { Item } from '../../models';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { getDiscountedPrice, getMainImage } from '../../utils/item.utils';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-items-carousel',
|
||||
templateUrl: './items-carousel.component.html',
|
||||
imports: [DecimalPipe, RouterLink, CarouselModule, ButtonModule, TagModule],
|
||||
imports: [DecimalPipe, RouterLink, CarouselModule, ButtonModule, TagModule, LangRoutePipe],
|
||||
styleUrls: ['./items-carousel.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ export class LanguageSelectorComponent {
|
||||
|
||||
selectLanguage(lang: Language): void {
|
||||
if (lang.enabled) {
|
||||
this.languageService.setLanguage(lang.code);
|
||||
this.languageService.switchLanguage(lang.code);
|
||||
this.dropdownOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
28
src/app/guards/language.guard.ts
Normal file
28
src/app/guards/language.guard.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { inject } from '@angular/core';
|
||||
import { CanActivateFn, Router } from '@angular/router';
|
||||
import { LanguageService } from '../services/language.service';
|
||||
|
||||
export const languageGuard: CanActivateFn = (route, state) => {
|
||||
const langService = inject(LanguageService);
|
||||
const router = inject(Router);
|
||||
const lang = route.params['lang'];
|
||||
|
||||
const langObj = langService.languages.find(l => l.code === lang);
|
||||
|
||||
if (langObj?.enabled) {
|
||||
// Valid and enabled language — set it and proceed
|
||||
langService.setLanguage(lang);
|
||||
return true;
|
||||
}
|
||||
|
||||
const defaultLang = langService.currentLanguage();
|
||||
|
||||
if (langObj && !langObj.enabled) {
|
||||
// Known but disabled language — redirect to default lang, keep the rest of the path
|
||||
const pathAfterLang = state.url.slice(1 + lang.length);
|
||||
return router.createUrlTree([`/${defaultLang}${pathAfterLang}`]);
|
||||
}
|
||||
|
||||
// Not a recognized language code — treat as a legacy URL without lang prefix
|
||||
return router.createUrlTree([`/${defaultLang}${state.url}`]);
|
||||
};
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
<h2>Корзина пуста</h2>
|
||||
<p>Добавьте товары, чтобы начать покупки</p>
|
||||
<a routerLink="/" class="shop-btn">Перейти к покупкам</a>
|
||||
<a [routerLink]="'/' | langRoute" class="shop-btn">Перейти к покупкам</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
[class.swiped]="swipedItemId() === item.itemID"
|
||||
(touchstart)="onSwipeStart(item.itemID, $event)">
|
||||
<div class="cart-item">
|
||||
<a [routerLink]="['/item', item.itemID]" class="item-image">
|
||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-image">
|
||||
<img [src]="getMainImage(item)" [alt]="item.name" loading="lazy" />
|
||||
</a>
|
||||
|
||||
<div class="item-info">
|
||||
<div class="item-header">
|
||||
<a [routerLink]="['/item', item.itemID]" class="item-name">{{ item.name }}</a>
|
||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-name">{{ item.name }}</a>
|
||||
<button class="remove-btn" (click)="removeItem(item.itemID)" title="Remove">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M18 6L6 18M6 6l12 12"/>
|
||||
@@ -114,10 +114,10 @@
|
||||
<span class="checkmark"></span>
|
||||
<span class="terms-text">
|
||||
Я согласен с
|
||||
<a routerLink="/public-offer" target="_blank">публичной офертой</a>,
|
||||
<a routerLink="/return-policy" target="_blank">политикой возврата</a>,
|
||||
<a routerLink="/guarantee" target="_blank">условиями гарантии</a> и
|
||||
<a routerLink="/privacy-policy" target="_blank">политикой конфиденциальности</a>
|
||||
<a [routerLink]="'/public-offer' | langRoute" target="_blank">публичной офертой</a>,
|
||||
<a [routerLink]="'/return-policy' | langRoute" target="_blank">политикой возврата</a>,
|
||||
<a [routerLink]="'/guarantee' | langRoute" target="_blank">условиями гарантии</a> и
|
||||
<a [routerLink]="'/privacy-policy' | langRoute" target="_blank">политикой конфиденциальности</a>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -2,17 +2,18 @@ import { Component, computed, ChangeDetectionStrategy, signal, OnDestroy } from
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CartService, ApiService } from '../../services';
|
||||
import { CartService, ApiService, LanguageService } from '../../services';
|
||||
import { Item, CartItem } from '../../models';
|
||||
import { interval, Subscription } from 'rxjs';
|
||||
import { switchMap, take } from 'rxjs/operators';
|
||||
import { EmptyCartIconComponent } from '../../components/empty-cart-icon/empty-cart-icon.component';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { getDiscountedPrice, getMainImage, trackByItemId } from '../../utils/item.utils';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cart',
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, EmptyCartIconComponent],
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, EmptyCartIconComponent, LangRoutePipe],
|
||||
templateUrl: './cart.component.html',
|
||||
styleUrls: ['./cart.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -52,7 +53,8 @@ export class CartComponent implements OnDestroy {
|
||||
constructor(
|
||||
private cartService: CartService,
|
||||
private apiService: ApiService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
private langService: LanguageService
|
||||
) {
|
||||
this.items = this.cartService.items;
|
||||
this.itemCount = this.cartService.itemCount;
|
||||
@@ -316,7 +318,8 @@ export class CartComponent implements OnDestroy {
|
||||
// Close popup and redirect to home page
|
||||
setTimeout(() => {
|
||||
this.closePaymentPopup();
|
||||
this.router.navigate(['/']);
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}`]);
|
||||
}, 500);
|
||||
},
|
||||
error: (err) => {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="items-grid">
|
||||
@for (item of items(); track trackByItemId($index, item)) {
|
||||
<div class="item-card">
|
||||
<a [routerLink]="['/item', item.itemID]" class="item-link">
|
||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-link">
|
||||
<div class="item-image">
|
||||
<img [src]="getMainImage(item)" [alt]="item.name" loading="lazy" decoding="async" width="300" height="300" />
|
||||
@if (item.discount > 0) {
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
<h3>Упс! Здесь пока пусто</h3>
|
||||
<p>В этой категории ещё нет товаров, но скоро они появятся</p>
|
||||
<a routerLink="/" class="no-items-btn">На главную</a>
|
||||
<a [routerLink]="'/' | langRoute" class="no-items-btn">На главную</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@ import { ApiService, CartService } from '../../services';
|
||||
import { Item } from '../../models';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { getDiscountedPrice, getMainImage, trackByItemId } from '../../utils/item.utils';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-category',
|
||||
imports: [DecimalPipe, RouterLink],
|
||||
imports: [DecimalPipe, RouterLink, LangRoutePipe],
|
||||
templateUrl: './category.component.html',
|
||||
styleUrls: ['./category.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
@if (subcategories().length > 0) {
|
||||
<div class="categories-grid">
|
||||
@for (cat of subcategories(); track trackByCategoryId($index, cat)) {
|
||||
<a [routerLink]="['/category', cat.categoryID]" class="category-card">
|
||||
<a [routerLink]="['/category', cat.categoryID] | langRoute" class="category-card">
|
||||
<div class="category-image">
|
||||
@if (cat.icon) {
|
||||
<img [src]="cat.icon" [alt]="cat.name" loading="lazy" decoding="async" />
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
<h3>Упс! Подкатегорий пока нет</h3>
|
||||
<p>В этом разделе ещё нет подкатегорий, но скоро они появятся</p>
|
||||
<a routerLink="/" class="no-subcats-btn">На главную</a>
|
||||
<a [routerLink]="'/' | langRoute" class="no-subcats-btn">На главную</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Component, OnInit, OnDestroy, signal, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { ApiService } from '../../services';
|
||||
import { ApiService, LanguageService } from '../../services';
|
||||
import { Category } from '../../models';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-subcategories',
|
||||
imports: [RouterLink],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './subcategories.component.html',
|
||||
styleUrls: ['./subcategories.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -23,7 +24,8 @@ export class SubcategoriesComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private apiService: ApiService
|
||||
private apiService: ApiService,
|
||||
private langService: LanguageService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -48,7 +50,8 @@ export class SubcategoriesComponent implements OnInit, OnDestroy {
|
||||
|
||||
if (!subs || subs.length === 0) {
|
||||
// No subcategories: redirect to items list for this category
|
||||
this.router.navigate(['/category', parentID, 'items'], { replaceUrl: true });
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}/category`, parentID, 'items'], { replaceUrl: true });
|
||||
} else {
|
||||
this.subcategories.set(subs);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="novo-hero-content">
|
||||
<h1 class="novo-hero-title">Добро пожаловать в {{ brandName }}</h1>
|
||||
<p class="novo-hero-subtitle">Найдите всё, что нужно, в одном месте</p>
|
||||
<a routerLink="/search" class="novo-hero-btn">
|
||||
<a [routerLink]="'/search' | langRoute" class="novo-hero-btn">
|
||||
Начать поиск
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
@@ -50,7 +50,7 @@
|
||||
} @else {
|
||||
<div class="novo-categories-grid">
|
||||
@for (category of topLevelCategories(); track category.categoryID) {
|
||||
<a [routerLink]="['/category', category.categoryID]" class="novo-category-card">
|
||||
<a [routerLink]="['/category', category.categoryID] | langRoute" class="novo-category-card">
|
||||
<div class="novo-category-image">
|
||||
@if (category.icon) {
|
||||
<img [src]="category.icon" [alt]="category.name" loading="lazy" />
|
||||
@@ -126,7 +126,7 @@
|
||||
} @else {
|
||||
<div class="dexar-categories-grid">
|
||||
@for (category of topLevelCategories(); track category.categoryID) {
|
||||
<a [routerLink]="['/category', category.categoryID]"
|
||||
<a [routerLink]="['/category', category.categoryID] | langRoute"
|
||||
class="dexar-category-card"
|
||||
[class.dexar-category-card--wide]="isWideCategory(category.categoryID)">
|
||||
<div class="dexar-category-image">
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Component, OnInit, signal, computed, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { ApiService } from '../../services';
|
||||
import { ApiService, LanguageService } from '../../services';
|
||||
import { Category } from '../../models';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { ItemsCarouselComponent } from '../../components/items-carousel/items-carousel.component';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
imports: [RouterLink, ItemsCarouselComponent],
|
||||
imports: [RouterLink, ItemsCarouselComponent, LangRoutePipe],
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -48,7 +49,7 @@ export class HomeComponent implements OnInit {
|
||||
return cache;
|
||||
});
|
||||
|
||||
constructor(private apiService: ApiService, private router: Router) {}
|
||||
constructor(private apiService: ApiService, private router: Router, private langService: LanguageService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadCategories();
|
||||
@@ -103,7 +104,8 @@ export class HomeComponent implements OnInit {
|
||||
}
|
||||
|
||||
navigateToSearch(): void {
|
||||
this.router.navigate(['/search']);
|
||||
const lang = this.langService.currentLanguage();
|
||||
this.router.navigate([`/${lang}/search`]);
|
||||
}
|
||||
|
||||
scrollToCatalog(): void {
|
||||
|
||||
@@ -232,7 +232,7 @@
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>Какие товары вернуть нельзя?</h3>
|
||||
<p>Нельзя вернуть товары, перечисленные в Постановлении Правительства РФ №2463: медикаменты, косметику, бельё, активированные цифровые продукты и индивидуальные заказы. Подробности смотрите в разделе <a routerLink="/return-policy">«Политика возврата»</a>.</p>
|
||||
<p>Нельзя вернуть товары, перечисленные в Постановлении Правительства РФ №2463: медикаменты, косметику, бельё, активированные цифровые продукты и индивидуальные заказы. Подробности смотрите в разделе <a [routerLink]="'/return-policy' | langRoute">«Политика возврата»</a>.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
@@ -274,7 +274,7 @@
|
||||
|
||||
<div class="faq-item">
|
||||
<h3>Как вы защищаете мои личные данные?</h3>
|
||||
<p>Используем современные методы шифрования SSL/TLS, не храним реквизиты карт, выполняем требования закона №152-ФЗ о защите персональной информации. Подробности — в <a routerLink="/privacy-policy">Политике конфиденциальности</a>.</p>
|
||||
<p>Используем современные методы шифрования SSL/TLS, не храним реквизиты карт, выполняем требования закона №152-ФЗ о защите персональной информации. Подробности — в <a [routerLink]="'/privacy-policy' | langRoute">Политике конфиденциальности</a>.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { LangRoutePipe } from '../../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-faq',
|
||||
imports: [],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './faq.component.html',
|
||||
styleUrls: ['./faq.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@if (error()) {
|
||||
<div class="novo-error">
|
||||
<p>{{ error() }}</p>
|
||||
<a routerLink="/" class="novo-back-link">Вернуться</a>
|
||||
<a [routerLink]="'/' | langRoute" class="novo-back-link">Вернуться</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
@if (error()) {
|
||||
<div class="dx-error">
|
||||
<p>{{ error() }}</p>
|
||||
<a routerLink="/" class="dx-back-link">Вернуться на главную</a>
|
||||
<a [routerLink]="'/' | langRoute" class="dx-back-link">Вернуться на главную</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ import { Subscription } from 'rxjs';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { SecurityContext } from '@angular/core';
|
||||
import { getDiscountedPrice } from '../../utils/item.utils';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-item-detail',
|
||||
imports: [DecimalPipe, RouterLink, FormsModule],
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, LangRoutePipe],
|
||||
templateUrl: './item-detail.component.html',
|
||||
styleUrls: ['./item-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h2>6. Возврат средств</h2>
|
||||
<p>6.1. Порядок возврата денежных средств регулируется <a routerLink="/return-policy">Политикой возврата</a> и зависит от типа приобретенного Товара/Услуги.</p>
|
||||
<p>6.1. Порядок возврата денежных средств регулируется <a [routerLink]="'/return-policy' | langRoute">Политикой возврата</a> и зависит от типа приобретенного Товара/Услуги.</p>
|
||||
<p>6.2. Возврат средств производится на тот же платежный инструмент, с которого была произведена оплата.</p>
|
||||
<p>6.3. Срок возврата денежных средств составляет:</p>
|
||||
<div class="refund-times">
|
||||
@@ -232,7 +232,7 @@
|
||||
|
||||
<section class="legal-section">
|
||||
<h2>6. Возврат средств</h2>
|
||||
<p>6.1. Порядок возврата денежных средств регулируется <a routerLink="/return-policy">Политикой возврата</a> и зависит от типа приобретенного Товара/Услуги.</p>
|
||||
<p>6.1. Порядок возврата денежных средств регулируется <a [routerLink]="'/return-policy' | langRoute">Политикой возврата</a> и зависит от типа приобретенного Товара/Услуги.</p>
|
||||
<p>6.2. Возврат средств производится на тот же платежный инструмент, с которого была произведена оплата.</p>
|
||||
<p>6.3. Срок возврата денежных средств составляет:</p>
|
||||
<ul>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { LangRoutePipe } from '../../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-payment-terms',
|
||||
imports: [RouterLink],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './payment-terms.component.html',
|
||||
styleUrls: ['./payment-terms.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<p>1.4. Акцепт происходит автоматически при любом действии: визите, регистрации, оформлении покупки.</p>
|
||||
<p>1.5. Подписание бумажного договора не требуется — электронная форма юридически действительна.</p>
|
||||
<p>1.6. Несогласие с условиями означает обязанность покинуть сайт.</p>
|
||||
<p>1.7. Также применяется <a routerLink="/privacy-policy">Политика конфиденциальности</a>.</p>
|
||||
<p>1.7. Также применяется <a [routerLink]="'/privacy-policy' | langRoute">Политика конфиденциальности</a>.</p>
|
||||
<p>1.8. Мы можем обновлять условия в одностороннем порядке.</p>
|
||||
<p>1.9. Промо-кампании могут иметь специальные правила.</p>
|
||||
</section>
|
||||
@@ -206,7 +206,7 @@
|
||||
<section class="info-card wide">
|
||||
<div class="card-icon">↩️</div>
|
||||
<h2>13. Возврат и обмен товара</h2>
|
||||
<p><strong>13.1. Общие правила:</strong> Цифровые товары не подлежат возврату. Физические товары — согласно <a routerLink="/return-policy">Политике возврата</a> и законам о правах потребителей.</p>
|
||||
<p><strong>13.1. Общие правила:</strong> Цифровые товары не подлежат возврату. Физические товары — согласно <a [routerLink]="'/return-policy' | langRoute">Политике возврата</a> и законам о правах потребителей.</p>
|
||||
<p><strong>13.2. Процедура возврата:</strong> В соответствии с соглашением и законодательством РФ.</p>
|
||||
<p><strong>13.3. Акционные наборы:</strong> Возврат только в комплексе, отдельные товары вернуть нельзя.</p>
|
||||
<p><strong>13.4. Затраты на доставку:</strong> При возврате качественного товара продавец может взыскать затраты на доставку.</p>
|
||||
@@ -316,7 +316,7 @@
|
||||
|
||||
<p>1.7. В случае несогласия с условиями Пользователь обязуется незамедлительно прекратить пользование ресурсом.</p>
|
||||
|
||||
<p>1.8. Дополнительно регулирование использования сайта осуществляется <a routerLink="/privacy-policy">Политикой обработки персональных данных</a>.</p>
|
||||
<p>1.8. Дополнительно регулирование использования сайта осуществляется <a [routerLink]="'/privacy-policy' | langRoute">Политикой обработки персональных данных</a>.</p>
|
||||
|
||||
<p>1.9. Изменения в соглашение могут вноситься Владельцем без предварительного уведомления и становятся обязательными с момента публикации изменений.</p>
|
||||
|
||||
@@ -640,7 +640,7 @@
|
||||
<h2>13. Возврат и обмен товара</h2>
|
||||
|
||||
<p><strong>13.1. Общие правила возврата</strong></p>
|
||||
<p>Цифровые товары (предоставляемые в электронной форме) не подлежат возврату согласно российскому законодательству. Возврат физических товаров осуществляется в соответствии с разделом <a routerLink="/return-policy">«Политика возврата»</a> и действующими законами о правах потребителей.</p>
|
||||
<p>Цифровые товары (предоставляемые в электронной форме) не подлежат возврату согласно российскому законодательству. Возврат физических товаров осуществляется в соответствии с разделом <a [routerLink]="'/return-policy' | langRoute">«Политика возврата»</a> и действующими законами о правах потребителей.</p>
|
||||
|
||||
<p><strong>13.2. Возврат товара</strong></p>
|
||||
<p>Возврат или замена товаров, представленных на сайте и подлежащих возврату, происходят в соответствии с данным соглашением и законодательством Российской Федерации.</p>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { LangRoutePipe } from '../../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-public-offer',
|
||||
imports: [RouterLink],
|
||||
imports: [RouterLink, LangRoutePipe],
|
||||
templateUrl: './public-offer.component.html',
|
||||
styleUrls: ['./public-offer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<div class="items-grid">
|
||||
@for (item of items(); track trackByItemId($index, item)) {
|
||||
<div class="item-card">
|
||||
<a [routerLink]="['/item', item.itemID]" class="item-link">
|
||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-link">
|
||||
<div class="item-image">
|
||||
<img [src]="getMainImage(item)" [alt]="item.name" loading="lazy" decoding="async" width="300" height="300" />
|
||||
@if (item.discount > 0) {
|
||||
|
||||
@@ -7,10 +7,11 @@ import { Item } from '../../models';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
import { getDiscountedPrice, getMainImage, trackByItemId } from '../../utils/item.utils';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search',
|
||||
imports: [DecimalPipe, FormsModule, RouterLink],
|
||||
imports: [DecimalPipe, FormsModule, RouterLink, LangRoutePipe],
|
||||
templateUrl: './search.component.html',
|
||||
styleUrls: ['./search.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
25
src/app/pipes/lang-route.pipe.ts
Normal file
25
src/app/pipes/lang-route.pipe.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Pipe, PipeTransform, inject } from '@angular/core';
|
||||
import { LanguageService } from '../services/language.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'langRoute',
|
||||
pure: false
|
||||
})
|
||||
export class LangRoutePipe implements PipeTransform {
|
||||
private langService = inject(LanguageService);
|
||||
|
||||
transform(value: string | (string | number)[]): string | (string | number)[] {
|
||||
const lang = this.langService.currentLanguage();
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return value === '/' ? `/${lang}` : `/${lang}${value}`;
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
const [first, ...rest] = value;
|
||||
return [`/${lang}${first}`, ...rest];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
export interface Language {
|
||||
code: string;
|
||||
@@ -22,7 +23,7 @@ export class LanguageService {
|
||||
|
||||
currentLanguage = this.currentLanguageSignal.asReadonly();
|
||||
|
||||
constructor() {
|
||||
constructor(private router: Router) {
|
||||
// Load saved language from localStorage
|
||||
const savedLang = localStorage.getItem('selectedLanguage');
|
||||
if (savedLang && this.languages.find(l => l.code === savedLang && l.enabled)) {
|
||||
@@ -38,6 +39,19 @@ export class LanguageService {
|
||||
}
|
||||
}
|
||||
|
||||
/** Change language and navigate to the same page with the new prefix */
|
||||
switchLanguage(langCode: string): void {
|
||||
const lang = this.languages.find(l => l.code === langCode);
|
||||
if (!lang?.enabled) return;
|
||||
|
||||
const currentUrl = this.router.url;
|
||||
const currentLang = this.currentLanguageSignal();
|
||||
const newUrl = currentUrl.replace(new RegExp(`^/${currentLang}`), `/${langCode}`);
|
||||
|
||||
this.setLanguage(langCode);
|
||||
this.router.navigateByUrl(newUrl);
|
||||
}
|
||||
|
||||
getCurrentLanguage(): Language | undefined {
|
||||
return this.languages.find(l => l.code === this.currentLanguageSignal());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user