changes and optimisations

This commit is contained in:
sdarbinyan
2026-03-06 17:45:34 +04:00
parent c112aded47
commit c3e4e695eb
9 changed files with 129 additions and 69 deletions

View File

@@ -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>
}

View File

@@ -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);
}

View File

@@ -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'