changes and optimisations
This commit is contained in:
@@ -15,21 +15,22 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (item() && !loading()) {
|
||||
@if (item(); as item) {
|
||||
@if (!loading()) {
|
||||
<div class="novo-item-content">
|
||||
<div class="novo-gallery">
|
||||
@if (item()?.photos && item()!.photos!.length > 0) {
|
||||
@if (item.photos && item.photos.length > 0) {
|
||||
<div class="novo-main-photo">
|
||||
@if (item()!.photos![selectedPhotoIndex()]?.video) {
|
||||
<video [src]="item()!.photos![selectedPhotoIndex()].url" controls></video>
|
||||
@if (item.photos[selectedPhotoIndex()]?.video) {
|
||||
<video [src]="item.photos[selectedPhotoIndex()].url" controls></video>
|
||||
} @else {
|
||||
<img [src]="item()!.photos![selectedPhotoIndex()].url" [alt]="item()!.name" />
|
||||
<img [src]="item.photos[selectedPhotoIndex()].url" [alt]="item.name" />
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (item()!.photos!.length > 1) {
|
||||
@if (item.photos.length > 1) {
|
||||
<div class="novo-thumbnails">
|
||||
@for (photo of item()!.photos!; track $index) {
|
||||
@for (photo of item.photos; track $index) {
|
||||
<div
|
||||
class="novo-thumb"
|
||||
[class.active]="selectedPhotoIndex() === $index"
|
||||
@@ -55,31 +56,31 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-info">
|
||||
<h1 class="novo-title">{{ item()!.name }}</h1>
|
||||
<h1 class="novo-title">{{ item.name }}</h1>
|
||||
|
||||
<div class="novo-rating">
|
||||
<span class="stars">{{ getRatingStars(item()!.rating) }}</span>
|
||||
<span class="value">{{ item()!.rating }}</span>
|
||||
<span class="reviews">({{ item()!.callbacks?.length || 0 }})</span>
|
||||
<span class="stars">{{ getRatingStars(item.rating) }}</span>
|
||||
<span class="value">{{ item.rating }}</span>
|
||||
<span class="reviews">({{ item.callbacks?.length || 0 }})</span>
|
||||
</div>
|
||||
|
||||
<div class="novo-price-block">
|
||||
@if (item()!.discount > 0) {
|
||||
@if (item.discount > 0) {
|
||||
<div class="price-row">
|
||||
<span class="old-price">{{ item()!.price }} {{ item()!.currency }}</span>
|
||||
<span class="discount-badge">-{{ item()!.discount }}%</span>
|
||||
<span class="old-price">{{ item.price }} {{ item.currency }}</span>
|
||||
<span class="discount-badge">-{{ item.discount }}%</span>
|
||||
</div>
|
||||
<div class="current-price">{{ getDiscountedPrice() | number:'1.2-2' }} {{ item()!.currency }}</div>
|
||||
<div class="current-price">{{ getDiscountedPrice() | number:'1.2-2' }} {{ item.currency }}</div>
|
||||
} @else {
|
||||
<div class="current-price">{{ item()!.price }} {{ item()!.currency }}</div>
|
||||
<div class="current-price">{{ item.price }} {{ item.currency }}</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="novo-stock">
|
||||
<span class="stock-label">{{ 'itemDetail.stock' | translate }}</span>
|
||||
<div class="stock-indicator" [class.high]="item()!.remainings === 'high'" [class.medium]="item()!.remainings === 'medium'" [class.low]="item()!.remainings === 'low'">
|
||||
<div class="stock-indicator" [class.high]="item.remainings === 'high'" [class.medium]="item.remainings === 'medium'" [class.low]="item.remainings === 'low'">
|
||||
<span class="dot"></span>
|
||||
{{ item()!.remainings === 'high' ? ('itemDetail.inStock' | translate) : item()!.remainings === 'medium' ? ('itemDetail.mediumStock' | translate) : ('itemDetail.lowStock' | translate) }}
|
||||
{{ item.remainings === 'high' ? ('itemDetail.inStock' | translate) : item.remainings === 'medium' ? ('itemDetail.mediumStock' | translate) : ('itemDetail.lowStock' | translate) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -94,13 +95,13 @@
|
||||
|
||||
<div class="novo-description">
|
||||
<h3>{{ 'itemDetail.description' | translate }}</h3>
|
||||
<div [innerHTML]="getSafeHtml(item()!.description)"></div>
|
||||
<div [innerHTML]="getSafeHtml(item.description)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="novo-reviews">
|
||||
<h2>{{ 'itemDetail.reviews' | translate }} ({{ item()!.callbacks?.length || 0 }})</h2>
|
||||
<h2>{{ 'itemDetail.reviews' | translate }} ({{ item.callbacks?.length || 0 }})</h2>
|
||||
|
||||
<!-- novo Review Form -->
|
||||
<div class="novo-review-form">
|
||||
@@ -169,8 +170,8 @@
|
||||
</div>
|
||||
|
||||
<div class="novo-reviews-list">
|
||||
@if (item()!.callbacks && item()!.callbacks!.length > 0) {
|
||||
@for (review of item()!.callbacks!; track review.userID) {
|
||||
@if (item.callbacks && item.callbacks.length > 0) {
|
||||
@for (review of item.callbacks; track $index) {
|
||||
<div class="novo-review-card">
|
||||
<div class="review-header">
|
||||
<div class="reviewer-info">
|
||||
@@ -189,6 +190,7 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
} @else {
|
||||
@@ -208,13 +210,14 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (item() && !loading()) {
|
||||
@if (item(); as item) {
|
||||
@if (!loading()) {
|
||||
<div class="dx-item-content">
|
||||
<!-- Gallery: thumbnails left + main photo -->
|
||||
<div class="dx-gallery">
|
||||
@if (item()?.photos && item()!.photos!.length > 0) {
|
||||
@if (item.photos && item.photos.length > 0) {
|
||||
<div class="dx-thumbnails">
|
||||
@for (photo of item()!.photos!; track $index) {
|
||||
@for (photo of item.photos; track $index) {
|
||||
<div
|
||||
class="dx-thumb"
|
||||
[class.active]="selectedPhotoIndex() === $index"
|
||||
@@ -228,11 +231,11 @@
|
||||
</div>
|
||||
}
|
||||
<div class="dx-main-photo">
|
||||
@if (item()?.photos && item()!.photos!.length > 0) {
|
||||
@if (item()!.photos![selectedPhotoIndex()]?.video) {
|
||||
<video [src]="item()!.photos![selectedPhotoIndex()].url" controls></video>
|
||||
@if (item.photos && item.photos.length > 0) {
|
||||
@if (item.photos[selectedPhotoIndex()]?.video) {
|
||||
<video [src]="item.photos[selectedPhotoIndex()].url" controls></video>
|
||||
} @else {
|
||||
<img [src]="item()!.photos![selectedPhotoIndex()].url" [alt]="item()!.name" fetchpriority="high" decoding="async" />
|
||||
<img [src]="item.photos[selectedPhotoIndex()].url" [alt]="item.name" fetchpriority="high" decoding="async" />
|
||||
}
|
||||
} @else {
|
||||
<div class="dx-no-image">
|
||||
@@ -249,40 +252,40 @@
|
||||
|
||||
<!-- Item Info -->
|
||||
<div class="dx-info">
|
||||
<h1 class="dx-title">{{ item()!.name }}</h1>
|
||||
<h1 class="dx-title">{{ item.name }}</h1>
|
||||
|
||||
<div class="dx-rating">
|
||||
<div class="dx-stars">
|
||||
@for (star of [1, 2, 3, 4, 5]; track star) {
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" [attr.fill]="star <= item()!.rating ? '#497671' : 'none'" [attr.stroke]="star <= item()!.rating ? '#497671' : '#a1b4b5'" stroke-width="2">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" [attr.fill]="star <= item.rating ? '#497671' : 'none'" [attr.stroke]="star <= item.rating ? '#497671' : '#a1b4b5'" stroke-width="2">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||||
</svg>
|
||||
}
|
||||
</div>
|
||||
<span class="dx-rating-value">{{ item()!.rating }}</span>
|
||||
<span class="dx-rating-count">({{ item()!.callbacks?.length || 0 }} {{ 'itemDetail.reviewsCount' | translate }})</span>
|
||||
<span class="dx-rating-value">{{ item.rating }}</span>
|
||||
<span class="dx-rating-count">({{ item.callbacks?.length || 0 }} {{ 'itemDetail.reviewsCount' | translate }})</span>
|
||||
</div>
|
||||
|
||||
<div class="dx-price-block">
|
||||
@if (item()!.discount > 0) {
|
||||
@if (item.discount > 0) {
|
||||
<div class="dx-price-row">
|
||||
<span class="dx-old-price">{{ item()!.price }} {{ item()!.currency }}</span>
|
||||
<span class="dx-discount-tag">-{{ item()!.discount }}%</span>
|
||||
<span class="dx-old-price">{{ item.price }} {{ item.currency }}</span>
|
||||
<span class="dx-discount-tag">-{{ item.discount }}%</span>
|
||||
</div>
|
||||
}
|
||||
<div class="dx-current-price">
|
||||
{{ item()!.discount > 0 ? (getDiscountedPrice() | number:'1.2-2') : item()!.price }} {{ item()!.currency }}
|
||||
{{ item.discount > 0 ? (getDiscountedPrice() | number:'1.2-2') : item.price }} {{ item.currency }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dx-stock">
|
||||
<span class="dx-stock-label">{{ 'itemDetail.stock' | translate }}</span>
|
||||
<span class="dx-stock-status"
|
||||
[class.high]="item()!.remainings === 'high'"
|
||||
[class.medium]="item()!.remainings === 'medium'"
|
||||
[class.low]="item()!.remainings === 'low'">
|
||||
[class.high]="item.remainings === 'high'"
|
||||
[class.medium]="item.remainings === 'medium'"
|
||||
[class.low]="item.remainings === 'low'">
|
||||
<span class="dx-stock-dot"></span>
|
||||
{{ item()!.remainings === 'high' ? ('itemDetail.inStock' | translate) : item()!.remainings === 'medium' ? ('itemDetail.mediumStock' | translate) : ('itemDetail.lastItems' | translate) }}
|
||||
{{ item.remainings === 'high' ? ('itemDetail.inStock' | translate) : item.remainings === 'medium' ? ('itemDetail.mediumStock' | translate) : ('itemDetail.lastItems' | translate) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -297,14 +300,14 @@
|
||||
|
||||
<div class="dx-description">
|
||||
<h2>{{ 'itemDetail.description' | translate }}</h2>
|
||||
<div class="dx-description-text" [innerHTML]="getSafeHtml(item()!.description)"></div>
|
||||
<div class="dx-description-text" [innerHTML]="getSafeHtml(item.description)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reviews Section -->
|
||||
<div class="dx-reviews-section">
|
||||
<h2>{{ 'itemDetail.reviews' | translate }} ({{ item()!.callbacks?.length || 0 }})</h2>
|
||||
<h2>{{ 'itemDetail.reviews' | translate }} ({{ item.callbacks?.length || 0 }})</h2>
|
||||
|
||||
<div class="dx-review-form">
|
||||
<h3>{{ 'itemDetail.leaveReview' | translate }}</h3>
|
||||
@@ -365,8 +368,8 @@
|
||||
</div>
|
||||
|
||||
<div class="dx-reviews-list">
|
||||
@if (item()?.callbacks && item()!.callbacks!.length > 0) {
|
||||
@for (callback of item()!.callbacks; track $index) {
|
||||
@if (item.callbacks && item.callbacks.length > 0) {
|
||||
@for (callback of item.callbacks; track $index) {
|
||||
<div class="dx-review-card">
|
||||
<div class="dx-review-header">
|
||||
<div class="dx-reviewer">
|
||||
@@ -397,11 +400,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Q&A Section -->
|
||||
@if (item()!.questions && item()!.questions!.length > 0) {
|
||||
@if (item.questions && item.questions.length > 0) {
|
||||
<div class="dx-qa-section">
|
||||
<h2>{{ 'itemDetail.qna' | translate }} ({{ item()!.questions!.length }})</h2>
|
||||
<h2>{{ 'itemDetail.qna' | translate }} ({{ item.questions.length }})</h2>
|
||||
<div class="dx-qa-list">
|
||||
@for (question of item()!.questions!; track $index) {
|
||||
@for (question of item.questions; track $index) {
|
||||
<div class="dx-qa-card">
|
||||
<div class="dx-question">
|
||||
<span class="dx-qa-label q">В</span>
|
||||
@@ -426,6 +429,7 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
// ========== DEXAR ITEM DETAIL - Redesigned 2026 ==========
|
||||
@use 'sass:color';
|
||||
|
||||
// ========== DEXAR ITEM DETAIL - Redesigned 2026 ==========
|
||||
$dx-font: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
$dx-dark: #1e3c38;
|
||||
$dx-primary: #497671;
|
||||
@@ -50,7 +52,7 @@ $dx-card-bg: #f5f3f9;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: darken($dx-primary, 8%);
|
||||
background: color.adjust($dx-primary, $lightness: -8%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
@@ -281,7 +283,7 @@ $dx-card-bg: #f5f3f9;
|
||||
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15);
|
||||
|
||||
&:hover {
|
||||
background: darken($dx-primary, 8%);
|
||||
background: color.adjust($dx-primary, $lightness: -8%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(73, 118, 113, 0.3);
|
||||
}
|
||||
@@ -434,7 +436,7 @@ $dx-card-bg: #f5f3f9;
|
||||
justify-content: center;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: darken($dx-primary, 8%);
|
||||
background: color.adjust($dx-primary, $lightness: -8%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Component, OnInit, OnDestroy, signal, ChangeDetectionStrategy, inject }
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { ApiService, CartService, TelegramService, SeoService } from '../../services';
|
||||
import { ApiService, CartService, TelegramService, SeoService, LanguageService } from '../../services';
|
||||
import { Item } from '../../models';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { SecurityContext } from '@angular/core';
|
||||
@@ -42,6 +42,7 @@ export class ItemDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
private seoService = inject(SeoService);
|
||||
private i18n = inject(TranslateService);
|
||||
private langService = inject(LanguageService);
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@@ -100,7 +101,7 @@ export class ItemDetailComponent implements OnInit, OnDestroy {
|
||||
return getDiscountedPrice(currentItem);
|
||||
}
|
||||
|
||||
getSafeHtml(html: string): SafeHtml {
|
||||
getSafeHtml(html: string): string {
|
||||
return this.sanitizer.sanitize(SecurityContext.HTML, html) || '';
|
||||
}
|
||||
|
||||
@@ -123,7 +124,9 @@ export class ItemDetailComponent implements OnInit, OnDestroy {
|
||||
if (diffDays < 7) return `${diffDays} ${this.i18n.t('itemDetail.daysAgo')}`;
|
||||
if (diffDays < 30) return `${Math.floor(diffDays / 7)} ${this.i18n.t('itemDetail.weeksAgo')}`;
|
||||
|
||||
return date.toLocaleDateString('ru-RU', {
|
||||
const localeMap: Record<string, string> = { ru: 'ru-RU', en: 'en-US', hy: 'hy-AM' };
|
||||
const locale = localeMap[this.langService.currentLanguage()] || 'ru-RU';
|
||||
return date.toLocaleDateString(locale, {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
|
||||
Reference in New Issue
Block a user