dev #37

Merged
JoeKung merged 47 commits from dev into main 2026-03-10 17:43:46 +01:00
16 changed files with 177 additions and 85 deletions
Showing only changes of commit 4342e9b1b1 - Show all commits

View File

@@ -57,7 +57,8 @@ export class SeoService {
this.asString(mergedData['seoDescription']) ?? this.defaultDescription; this.asString(mergedData['seoDescription']) ?? this.defaultDescription;
const robots = this.asString(mergedData['seoRobots']) ?? 'index, follow'; const robots = this.asString(mergedData['seoRobots']) ?? 'index, follow';
const ogTitle = this.asString(mergedData['ogTitle']) ?? title; const ogTitle = this.asString(mergedData['ogTitle']) ?? title;
const ogDescription = this.asString(mergedData['ogDescription']) ?? description; const ogDescription =
this.asString(mergedData['ogDescription']) ?? description;
this.applySeoValues(title, description, robots, ogTitle, ogDescription); this.applySeoValues(title, description, robots, ogTitle, ogDescription);
} }

View File

@@ -75,7 +75,10 @@
[ngModel]="orderTypeFilter" [ngModel]="orderTypeFilter"
(ngModelChange)="onOrderTypeFilterChange($event)" (ngModelChange)="onOrderTypeFilterChange($event)"
> >
<option *ngFor="let option of orderTypeFilterOptions" [ngValue]="option"> <option
*ngFor="let option of orderTypeFilterOptions"
[ngValue]="option"
>
{{ option }} {{ option }}
</option> </option>
</select> </select>
@@ -133,7 +136,9 @@
<span <span
class="order-type-badge" class="order-type-badge"
[class.order-type-badge--shop]="orderKind(selectedOrder) === 'SHOP'" [class.order-type-badge--shop]="orderKind(selectedOrder) === 'SHOP'"
[class.order-type-badge--mixed]="orderKind(selectedOrder) === 'MIXED'" [class.order-type-badge--mixed]="
orderKind(selectedOrder) === 'MIXED'
"
> >
{{ orderKindLabel(selectedOrder) }} {{ orderKindLabel(selectedOrder) }}
</span> </span>
@@ -162,7 +167,8 @@
<strong>Stato ordine</strong><span>{{ selectedOrder.status }}</span> <strong>Stato ordine</strong><span>{{ selectedOrder.status }}</span>
</div> </div>
<div class="ui-meta-item"> <div class="ui-meta-item">
<strong>Tipo ordine</strong><span>{{ orderKindLabel(selectedOrder) }}</span> <strong>Tipo ordine</strong
><span>{{ orderKindLabel(selectedOrder) }}</span>
</div> </div>
<div class="ui-meta-item"> <div class="ui-meta-item">
<strong>Totale</strong <strong>Totale</strong
@@ -279,7 +285,9 @@
></span> ></span>
<span> <span>
{{ getItemColorLabel(item) }} {{ getItemColorLabel(item) }}
<ng-container *ngIf="getItemColorCodeSuffix(item) as colorCode"> <ng-container
*ngIf="getItemColorCodeSuffix(item) as colorCode"
>
({{ colorCode }}) ({{ colorCode }})
</ng-container> </ng-container>
</span> </span>
@@ -300,7 +308,12 @@
<button <button
type="button" type="button"
class="ui-button ui-button--ghost" class="ui-button ui-button--ghost"
(click)="downloadItemFile(item.id, item.originalFilename || itemDisplayName(item))" (click)="
downloadItemFile(
item.id,
item.originalFilename || itemDisplayName(item)
)
"
> >
{{ downloadItemLabel(item) }} {{ downloadItemLabel(item) }}
</button> </button>
@@ -373,7 +386,10 @@
<h4>Parametri per file</h4> <h4>Parametri per file</h4>
<div class="file-color-list"> <div class="file-color-list">
<div class="file-color-row" *ngFor="let item of printItems(selectedOrder)"> <div
class="file-color-row"
*ngFor="let item of printItems(selectedOrder)"
>
<span class="filename">{{ item.originalFilename }}</span> <span class="filename">{{ item.originalFilename }}</span>
<span class="file-color"> <span class="file-color">
{{ getItemMaterialLabel(item) }} | Colore: {{ getItemMaterialLabel(item) }} | Colore:

View File

@@ -131,7 +131,8 @@ export class AdminDashboardComponent implements OnInit {
this.selectedOrder = order; this.selectedOrder = order;
this.selectedStatus = order.status; this.selectedStatus = order.status;
this.selectedPaymentMethod = order.paymentMethod || 'OTHER'; this.selectedPaymentMethod = order.paymentMethod || 'OTHER';
this.showPrintDetails = this.showPrintDetails && this.hasPrintItems(order); this.showPrintDetails =
this.showPrintDetails && this.hasPrintItems(order);
this.detailLoading = false; this.detailLoading = false;
}, },
error: () => { error: () => {
@@ -446,7 +447,8 @@ export class AdminDashboardComponent implements OnInit {
this.selectedStatus = updatedOrder.status; this.selectedStatus = updatedOrder.status;
this.selectedPaymentMethod = this.selectedPaymentMethod =
updatedOrder.paymentMethod || this.selectedPaymentMethod; updatedOrder.paymentMethod || this.selectedPaymentMethod;
this.showPrintDetails = this.showPrintDetails && this.hasPrintItems(updatedOrder); this.showPrintDetails =
this.showPrintDetails && this.hasPrintItems(updatedOrder);
} }
private applyListFiltersAndSelection(): void { private applyListFiltersAndSelection(): void {

View File

@@ -393,10 +393,7 @@ export class CheckoutComponent implements OnInit {
} }
private loadStlPreviews(session: any): void { private loadStlPreviews(session: any): void {
if ( if (!this.sessionId || !Array.isArray(session?.items)) {
!this.sessionId ||
!Array.isArray(session?.items)
) {
return; return;
} }

View File

@@ -161,7 +161,9 @@
<app-button <app-button
variant="outline" variant="outline"
(click)="completeOrder()" (click)="completeOrder()"
[disabled]="!selectedPaymentMethod || o.paymentStatus === 'REPORTED'" [disabled]="
!selectedPaymentMethod || o.paymentStatus === 'REPORTED'
"
[fullWidth]="true" [fullWidth]="true"
> >
{{ {{
@@ -201,10 +203,14 @@
</div> </div>
<div class="order-item-meta"> <div class="order-item-meta">
<span>{{ "CHECKOUT.QTY" | translate }}: {{ item.quantity }}</span> <span
>{{ "CHECKOUT.QTY" | translate }}: {{ item.quantity }}</span
>
<span *ngIf="showItemMaterial(item)"> <span *ngIf="showItemMaterial(item)">
{{ "CHECKOUT.MATERIAL" | translate }}: {{ "CHECKOUT.MATERIAL" | translate }}:
{{ item.materialCode || ("ORDER.NOT_AVAILABLE" | translate) }} {{
item.materialCode || ("ORDER.NOT_AVAILABLE" | translate)
}}
</span> </span>
<span *ngIf="itemVariantLabel(item) as variantLabel"> <span *ngIf="itemVariantLabel(item) as variantLabel">
{{ "SHOP.VARIANT" | translate }}: {{ variantLabel }} {{ "SHOP.VARIANT" | translate }}: {{ variantLabel }}
@@ -252,7 +258,9 @@
<strong>{{ orderKindLabel(o) }}</strong> <strong>{{ orderKindLabel(o) }}</strong>
</div> </div>
<div> <div>
<span class="summary-label">{{ "ORDER.ITEM_COUNT" | translate }}</span> <span class="summary-label">{{
"ORDER.ITEM_COUNT" | translate
}}</span>
<strong>{{ (o.items || []).length }}</strong> <strong>{{ (o.items || []).length }}</strong>
</div> </div>
</div> </div>

View File

@@ -263,7 +263,9 @@ export class OrderComponent implements OnInit {
return shopName; return shopName;
} }
return String(item?.originalFilename ?? this.translate.instant('ORDER.NOT_AVAILABLE')); return String(
item?.originalFilename ?? this.translate.instant('ORDER.NOT_AVAILABLE'),
);
} }
itemVariantLabel(item: PublicOrderItem): string | null { itemVariantLabel(item: PublicOrderItem): string | null {

View File

@@ -46,9 +46,7 @@
<div class="pricing"> <div class="pricing">
<span class="price">{{ priceLabel() | currency: "CHF" }}</span> <span class="price">{{ priceLabel() | currency: "CHF" }}</span>
@if (hasPriceRange()) { @if (hasPriceRange()) {
<small class="price-note">{{ <small class="price-note">{{ "SHOP.PRICE_FROM" | translate }}</small>
"SHOP.PRICE_FROM" | translate
}}</small>
} }
</div> </div>

View File

@@ -4,8 +4,11 @@
border: 1px solid rgba(16, 24, 32, 0.08); border: 1px solid rgba(16, 24, 32, 0.08);
border-radius: 1.1rem; border-radius: 1.1rem;
overflow: hidden; overflow: hidden;
background: background: linear-gradient(
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 246, 241, 1)); 180deg,
rgba(255, 255, 255, 0.98),
rgba(248, 246, 241, 1)
);
box-shadow: 0 18px 40px rgba(16, 24, 32, 0.08); box-shadow: 0 18px 40px rgba(16, 24, 32, 0.08);
transition: transition:
transform 0.2s ease, transform 0.2s ease,
@@ -24,7 +27,11 @@
display: block; display: block;
min-height: 244px; min-height: 244px;
background: background:
radial-gradient(circle at top right, rgba(250, 207, 10, 0.28), transparent 42%), radial-gradient(
circle at top right,
rgba(250, 207, 10, 0.28),
transparent 42%
),
linear-gradient(160deg, #f7f4ed 0%, #ece7db 100%); linear-gradient(160deg, #f7f4ed 0%, #ece7db 100%);
} }

View File

@@ -44,7 +44,9 @@
<button <button
type="button" type="button"
class="thumb" class="thumb"
[class.active]="selectedImage().mediaAssetId === image.mediaAssetId" [class.active]="
selectedImage().mediaAssetId === image.mediaAssetId
"
(click)="selectImage(image.mediaAssetId)" (click)="selectImage(image.mediaAssetId)"
> >
@if (imageUrl(image); as imageUrl) { @if (imageUrl(image); as imageUrl) {
@@ -68,13 +70,16 @@
</div> </div>
<div class="dimensions"> <div class="dimensions">
<span> <span>
X {{ p.model3d.boundingBoxXMm || 0 | number: "1.0-1" }} mm X
{{ p.model3d.boundingBoxXMm || 0 | number: "1.0-1" }} mm
</span> </span>
<span> <span>
Y {{ p.model3d.boundingBoxYMm || 0 | number: "1.0-1" }} mm Y
{{ p.model3d.boundingBoxYMm || 0 | number: "1.0-1" }} mm
</span> </span>
<span> <span>
Z {{ p.model3d.boundingBoxZMm || 0 | number: "1.0-1" }} mm Z
{{ p.model3d.boundingBoxZMm || 0 | number: "1.0-1" }} mm
</span> </span>
</div> </div>
</div> </div>
@@ -133,7 +138,8 @@
<span class="cart-pill"> <span class="cart-pill">
{{ {{
"SHOP.IN_CART_LONG" "SHOP.IN_CART_LONG"
| translate: { count: selectedVariantCartQuantity() } | translate
: { count: selectedVariantCartQuantity() }
}} }}
</span> </span>
} }
@@ -157,7 +163,9 @@
<small>{{ variant.variantLabel }}</small> <small>{{ variant.variantLabel }}</small>
} }
</span> </span>
<strong>{{ variant.priceChf | currency: "CHF" }}</strong> <strong>{{
variant.priceChf | currency: "CHF"
}}</strong>
</button> </button>
} }
</div> </div>
@@ -165,9 +173,13 @@
<div class="quantity-row"> <div class="quantity-row">
<span>{{ "SHOP.QUANTITY" | translate }}</span> <span>{{ "SHOP.QUANTITY" | translate }}</span>
<div class="qty-control"> <div class="qty-control">
<button type="button" (click)="decreaseQuantity()">-</button> <button type="button" (click)="decreaseQuantity()">
-
</button>
<span>{{ quantity() }}</span> <span>{{ quantity() }}</span>
<button type="button" (click)="increaseQuantity()">+</button> <button type="button" (click)="increaseQuantity()">
+
</button>
</div> </div>
</div> </div>
@@ -178,9 +190,8 @@
(click)="addToCart()" (click)="addToCart()"
> >
{{ {{
(isAddingToCart() (isAddingToCart() ? "SHOP.ADDING" : "SHOP.ADD_CART")
? "SHOP.ADDING" | translate
: "SHOP.ADD_CART") | translate
}} }}
</app-button> </app-button>

View File

@@ -1,7 +1,11 @@
.product-page { .product-page {
padding: var(--space-8) 0 var(--space-12); padding: var(--space-8) 0 var(--space-12);
background: background:
radial-gradient(circle at top left, rgba(250, 207, 10, 0.18), transparent 20%), radial-gradient(
circle at top left,
rgba(250, 207, 10, 0.18),
transparent 20%
),
linear-gradient(180deg, #faf7ee 0%, var(--color-bg) 25%); linear-gradient(180deg, #faf7ee 0%, var(--color-bg) 25%);
} }
@@ -40,7 +44,11 @@
border-radius: 1.25rem; border-radius: 1.25rem;
border: 1px solid rgba(16, 24, 32, 0.08); border: 1px solid rgba(16, 24, 32, 0.08);
background: background:
radial-gradient(circle at top right, rgba(250, 207, 10, 0.3), transparent 30%), radial-gradient(
circle at top right,
rgba(250, 207, 10, 0.3),
transparent 30%
),
linear-gradient(160deg, #f8f4ea 0%, #eee8db 100%); linear-gradient(160deg, #f8f4ea 0%, #eee8db 100%);
box-shadow: 0 18px 42px rgba(16, 24, 32, 0.08); box-shadow: 0 18px 42px rgba(16, 24, 32, 0.08);
} }
@@ -325,13 +333,12 @@ h1 {
} }
.skeleton-block { .skeleton-block {
background: background: linear-gradient(
linear-gradient( 110deg,
110deg, rgba(255, 255, 255, 0.7) 8%,
rgba(255, 255, 255, 0.7) 8%, rgba(238, 235, 226, 0.95) 18%,
rgba(238, 235, 226, 0.95) 18%, rgba(255, 255, 255, 0.7) 33%
rgba(255, 255, 255, 0.7) 33% );
);
background-size: 220% 100%; background-size: 220% 100%;
animation: skeleton 1.35s linear infinite; animation: skeleton 1.35s linear infinite;
} }

View File

@@ -121,7 +121,9 @@ export class ProductDetailComponent {
combineLatest([ combineLatest([
toObservable(this.productSlug, { injector: this.injector }), toObservable(this.productSlug, { injector: this.injector }),
toObservable(this.languageService.currentLang, { injector: this.injector }), toObservable(this.languageService.currentLang, {
injector: this.injector,
}),
]) ])
.pipe( .pipe(
tap(() => { tap(() => {
@@ -160,13 +162,22 @@ export class ProductDetailComponent {
} }
this.product.set(product); this.product.set(product);
this.selectedVariantId.set(product.defaultVariant?.id ?? product.variants[0]?.id ?? null); this.selectedVariantId.set(
this.selectedImageAssetId.set(product.primaryImage?.mediaAssetId ?? product.images[0]?.mediaAssetId ?? null); product.defaultVariant?.id ?? product.variants[0]?.id ?? null,
);
this.selectedImageAssetId.set(
product.primaryImage?.mediaAssetId ??
product.images[0]?.mediaAssetId ??
null,
);
this.quantity.set(1); this.quantity.set(1);
this.applySeo(product); this.applySeo(product);
if (product.model3d?.url && product.model3d.originalFilename) { if (product.model3d?.url && product.model3d.originalFilename) {
this.loadModelPreview(product.model3d.url, product.model3d.originalFilename); this.loadModelPreview(
product.model3d.url,
product.model3d.originalFilename,
);
} else { } else {
this.modelFile.set(null); this.modelFile.set(null);
this.modelLoading.set(false); this.modelLoading.set(false);
@@ -239,7 +250,9 @@ export class ProductDetailComponent {
} }
priceLabel(): number { priceLabel(): number {
return this.selectedVariant()?.priceChf ?? this.product()?.priceFromChf ?? 0; return (
this.selectedVariant()?.priceChf ?? this.product()?.priceFromChf ?? 0
);
} }
colorLabel(variant: ShopProductVariantOption): string { colorLabel(variant: ShopProductVariantOption): string {
@@ -282,7 +295,8 @@ export class ProductDetailComponent {
product.seoDescription || product.seoDescription ||
product.excerpt || product.excerpt ||
this.translate.instant('SHOP.CATALOG_META_DESCRIPTION'); this.translate.instant('SHOP.CATALOG_META_DESCRIPTION');
const robots = product.indexable === false ? 'noindex, nofollow' : 'index, follow'; const robots =
product.indexable === false ? 'noindex, nofollow' : 'index, follow';
this.seoService.applyPageSeo({ this.seoService.applyPageSeo({
title, title,

View File

@@ -129,7 +129,9 @@ describe('ShopService', () => {
it('posts add-to-cart with credentials and replaces local cart state', () => { it('posts add-to-cart with credentials and replaces local cart state', () => {
service.addToCart('variant-red', 2).subscribe(); service.addToCart('variant-red', 2).subscribe();
const request = httpMock.expectOne('http://localhost:8000/api/shop/cart/items'); const request = httpMock.expectOne(
'http://localhost:8000/api/shop/cart/items',
);
expect(request.request.method).toBe('POST'); expect(request.request.method).toBe('POST');
expect(request.request.withCredentials).toBeTrue(); expect(request.request.withCredentials).toBeTrue();
expect(request.request.body).toEqual({ expect(request.request.body).toEqual({

View File

@@ -251,9 +251,12 @@ export class ShopService {
params = params.set('featured', String(featured)); params = params.set('featured', String(featured));
} }
return this.http.get<ShopProductCatalogResponse>(`${this.apiUrl}/products`, { return this.http.get<ShopProductCatalogResponse>(
params, `${this.apiUrl}/products`,
}); {
params,
},
);
} }
getProduct(slug: string): Observable<ShopProductDetail> { getProduct(slug: string): Observable<ShopProductDetail> {
@@ -337,10 +340,7 @@ export class ShopService {
.pipe(tap((cart) => this.setCart(cart))); .pipe(tap((cart) => this.setCart(cart)));
} }
getProductModelFile( getProductModelFile(urlOrPath: string, filename: string): Observable<File> {
urlOrPath: string,
filename: string,
): Observable<File> {
return this.http return this.http
.get(this.resolveApiUrl(urlOrPath), { .get(this.resolveApiUrl(urlOrPath), {
responseType: 'blob', responseType: 'blob',

View File

@@ -4,20 +4,16 @@
<div class="hero-copy"> <div class="hero-copy">
<p class="ui-eyebrow">{{ "SHOP.HERO_EYEBROW" | translate }}</p> <p class="ui-eyebrow">{{ "SHOP.HERO_EYEBROW" | translate }}</p>
<h1 class="ui-hero-display"> <h1 class="ui-hero-display">
{{ {{ selectedCategory()?.name || ("SHOP.TITLE" | translate) }}
selectedCategory()?.name || ("SHOP.TITLE" | translate)
}}
</h1> </h1>
<p class="ui-copy-lead"> <p class="ui-copy-lead">
{{ {{ selectedCategory()?.description || ("SHOP.SUBTITLE" | translate) }}
selectedCategory()?.description ||
("SHOP.SUBTITLE" | translate)
}}
</p> </p>
<p class="ui-copy-subtitle"> <p class="ui-copy-subtitle">
{{ {{
selectedCategory() selectedCategory()
? ("SHOP.CATEGORY_META" | translate: { count: selectedCategory()?.productCount || 0 }) ? ("SHOP.CATEGORY_META"
| translate: { count: selectedCategory()?.productCount || 0 })
: ("SHOP.CATALOG_META_DESCRIPTION" | translate) : ("SHOP.CATALOG_META_DESCRIPTION" | translate)
}} }}
</p> </p>
@@ -75,7 +71,9 @@
<app-card> <app-card>
<div class="panel-head"> <div class="panel-head">
<div> <div>
<p class="panel-kicker">{{ "SHOP.CATEGORY_PANEL_KICKER" | translate }}</p> <p class="panel-kicker">
{{ "SHOP.CATEGORY_PANEL_KICKER" | translate }}
</p>
<h2 class="panel-title"> <h2 class="panel-title">
{{ "SHOP.CATEGORY_PANEL_TITLE" | translate }} {{ "SHOP.CATEGORY_PANEL_TITLE" | translate }}
</h2> </h2>
@@ -155,7 +153,9 @@
<div class="qty-control"> <div class="qty-control">
<button <button
type="button" type="button"
[disabled]="cartMutating() && busyLineItemId() === item.id" [disabled]="
cartMutating() && busyLineItemId() === item.id
"
(click)="decreaseQuantity(item)" (click)="decreaseQuantity(item)"
> >
- -
@@ -163,7 +163,9 @@
<span>{{ item.quantity }}</span> <span>{{ item.quantity }}</span>
<button <button
type="button" type="button"
[disabled]="cartMutating() && busyLineItemId() === item.id" [disabled]="
cartMutating() && busyLineItemId() === item.id
"
(click)="increaseQuantity(item)" (click)="increaseQuantity(item)"
> >
+ +
@@ -189,7 +191,9 @@
<div class="cart-totals"> <div class="cart-totals">
<div class="cart-total-row"> <div class="cart-total-row">
<span>{{ "SHOP.CART_SUBTOTAL" | translate }}</span> <span>{{ "SHOP.CART_SUBTOTAL" | translate }}</span>
<strong>{{ cart()?.itemsTotalChf || 0 | currency: "CHF" }}</strong> <strong>{{
cart()?.itemsTotalChf || 0 | currency: "CHF"
}}</strong>
</div> </div>
<div class="cart-total-row"> <div class="cart-total-row">
<span>{{ "SHOP.CART_SHIPPING" | translate }}</span> <span>{{ "SHOP.CART_SHIPPING" | translate }}</span>
@@ -284,7 +288,10 @@
</div> </div>
} @else { } @else {
<div class="product-grid"> <div class="product-grid">
@for (product of products(); track trackByProduct($index, product)) { @for (
product of products();
track trackByProduct($index, product)
) {
<app-product-card <app-product-card
[product]="product" [product]="product"
[cartQuantity]="productCartQuantity(product.id)" [cartQuantity]="productCartQuantity(product.id)"

View File

@@ -1,7 +1,11 @@
.shop-page { .shop-page {
--shop-hero-bg: #f8f3e5; --shop-hero-bg: #f8f3e5;
background: background:
radial-gradient(circle at top right, rgba(250, 207, 10, 0.24), transparent 22%), radial-gradient(
circle at top right,
rgba(250, 207, 10, 0.24),
transparent 22%
),
linear-gradient(180deg, #faf7ef 0%, #f6f2e8 24%, var(--color-bg) 24%); linear-gradient(180deg, #faf7ef 0%, #f6f2e8 24%, var(--color-bg) 24%);
} }
@@ -296,13 +300,12 @@
.skeleton-card { .skeleton-card {
min-height: 400px; min-height: 400px;
border-radius: 1.1rem; border-radius: 1.1rem;
background: background: linear-gradient(
linear-gradient( 110deg,
110deg, rgba(255, 255, 255, 0.7) 8%,
rgba(255, 255, 255, 0.7) 8%, rgba(238, 235, 226, 0.95) 18%,
rgba(238, 235, 226, 0.95) 18%, rgba(255, 255, 255, 0.7) 33%
rgba(255, 255, 255, 0.7) 33% );
);
background-size: 220% 100%; background-size: 220% 100%;
animation: skeleton 1.35s linear infinite; animation: skeleton 1.35s linear infinite;
} }

View File

@@ -11,7 +11,16 @@ import {
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { Router, RouterLink } from '@angular/router'; import { Router, RouterLink } from '@angular/router';
import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { catchError, combineLatest, finalize, forkJoin, map, of, switchMap, tap } from 'rxjs'; import {
catchError,
combineLatest,
finalize,
forkJoin,
map,
of,
switchMap,
tap,
} from 'rxjs';
import { SeoService } from '../../core/services/seo.service'; import { SeoService } from '../../core/services/seo.service';
import { LanguageService } from '../../core/services/language.service'; import { LanguageService } from '../../core/services/language.service';
import { AppButtonComponent } from '../../shared/components/app-button/app-button.component'; import { AppButtonComponent } from '../../shared/components/app-button/app-button.component';
@@ -69,7 +78,9 @@ export class ShopPageComponent {
() => this.selectedCategory()?.slug ?? this.categorySlug() ?? null, () => this.selectedCategory()?.slug ?? this.categorySlug() ?? null,
); );
readonly cartItems = computed(() => readonly cartItems = computed(() =>
(this.cart()?.items ?? []).filter((item) => item.lineItemType === 'SHOP_PRODUCT'), (this.cart()?.items ?? []).filter(
(item) => item.lineItemType === 'SHOP_PRODUCT',
),
); );
readonly cartHasItems = computed(() => this.cartItems().length > 0); readonly cartHasItems = computed(() => this.cartItems().length > 0);
@@ -87,7 +98,9 @@ export class ShopPageComponent {
combineLatest([ combineLatest([
toObservable(this.categorySlug, { injector: this.injector }), toObservable(this.categorySlug, { injector: this.injector }),
toObservable(this.languageService.currentLang, { injector: this.injector }), toObservable(this.languageService.currentLang, {
injector: this.injector,
}),
]) ])
.pipe( .pipe(
tap(() => { tap(() => {
@@ -145,7 +158,9 @@ export class ShopPageComponent {
} }
cartItemName(item: ShopCartItem): string { cartItemName(item: ShopCartItem): string {
return item.displayName || item.shopProductName || item.originalFilename || '-'; return (
item.displayName || item.shopProductName || item.originalFilename || '-'
);
} }
cartItemVariant(item: ShopCartItem): string | null { cartItemVariant(item: ShopCartItem): string | null {
@@ -261,12 +276,14 @@ export class ShopPageComponent {
} }
const title = const title =
category.seoTitle || `${category.name} | ${this.translate.instant('SHOP.TITLE')} | 3D fab`; category.seoTitle ||
`${category.name} | ${this.translate.instant('SHOP.TITLE')} | 3D fab`;
const description = const description =
category.seoDescription || category.seoDescription ||
category.description || category.description ||
this.translate.instant('SHOP.CATALOG_META_DESCRIPTION'); this.translate.instant('SHOP.CATALOG_META_DESCRIPTION');
const robots = category.indexable === false ? 'noindex, nofollow' : 'index, follow'; const robots =
category.indexable === false ? 'noindex, nofollow' : 'index, follow';
this.seoService.applyPageSeo({ this.seoService.applyPageSeo({
title, title,