style: apply prettier formatting
All checks were successful
PR Checks / prettier-autofix (pull_request) Successful in 8s
PR Checks / security-sast (pull_request) Successful in 29s
PR Checks / test-backend (pull_request) Successful in 27s
PR Checks / test-frontend (pull_request) Successful in 1m2s

This commit is contained in:
printcalc-ci
2026-03-24 12:19:19 +00:00
parent c8913af660
commit cb86137730
4 changed files with 86 additions and 66 deletions

View File

@@ -97,7 +97,9 @@ describe('ProductDetailComponent', () => {
const languageService = { const languageService = {
currentLang, currentLang,
selectedLang: () => currentLang(), selectedLang: () => currentLang(),
setLocalizedRouteOverrides: jasmine.createSpy('setLocalizedRouteOverrides'), setLocalizedRouteOverrides: jasmine.createSpy(
'setLocalizedRouteOverrides',
),
clearLocalizedRouteOverrides: jasmine.createSpy( clearLocalizedRouteOverrides: jasmine.createSpy(
'clearLocalizedRouteOverrides', 'clearLocalizedRouteOverrides',
), ),
@@ -113,7 +115,9 @@ describe('ProductDetailComponent', () => {
.createSpy('quantityForVariant') .createSpy('quantityForVariant')
.and.returnValue(0), .and.returnValue(0),
loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)), loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)),
resolveMediaUrl: jasmine.createSpy('resolveMediaUrl').and.returnValue(null), resolveMediaUrl: jasmine
.createSpy('resolveMediaUrl')
.and.returnValue(null),
}; };
const router = { const router = {
@@ -126,9 +130,13 @@ describe('ProductDetailComponent', () => {
} as unknown as Router; } as unknown as Router;
const activatedRoute = { const activatedRoute = {
paramMap: of(convertToParamMap({ productSlug: '91823f84-bike-wall-hanger' })), paramMap: of(
convertToParamMap({ productSlug: '91823f84-bike-wall-hanger' }),
),
snapshot: { snapshot: {
paramMap: convertToParamMap({ productSlug: '91823f84-bike-wall-hanger' }), paramMap: convertToParamMap({
productSlug: '91823f84-bike-wall-hanger',
}),
}, },
} as unknown as ActivatedRoute; } as unknown as ActivatedRoute;
@@ -200,7 +208,9 @@ describe('ProductDetailComponent', () => {
it('builds a soft SSR fallback with 200 + index follow', () => { it('builds a soft SSR fallback with 200 + index follow', () => {
const { component, seoService, responseInit } = createComponent(); const { component, seoService, responseInit } = createComponent();
expect((component as any).shouldUseSoftSeoFallback({ status: 500 })).toBeTrue(); expect(
(component as any).shouldUseSoftSeoFallback({ status: 500 }),
).toBeTrue();
(component as any).setResponseStatus(200); (component as any).setResponseStatus(200);
(component as any).applySoftFallbackSeo('91823f84-bike-wall-hanger'); (component as any).applySoftFallbackSeo('91823f84-bike-wall-hanger');
@@ -221,7 +231,9 @@ describe('ProductDetailComponent', () => {
it('keeps hard fallback noindex for missing products', () => { it('keeps hard fallback noindex for missing products', () => {
const { component, seoService, responseInit } = createComponent(); const { component, seoService, responseInit } = createComponent();
expect((component as any).shouldUseSoftSeoFallback({ status: 404 })).toBeFalse(); expect(
(component as any).shouldUseSoftSeoFallback({ status: 404 }),
).toBeFalse();
(component as any).setResponseStatus(404); (component as any).setResponseStatus(404);
(component as any).applyHardFallbackSeo(); (component as any).applyHardFallbackSeo();

View File

@@ -254,37 +254,35 @@ export class ProductDetailComponent {
} }
const productSlug = routeParams.productSlug as string; const productSlug = routeParams.productSlug as string;
return this.shopService return this.shopService.getProductByPublicPath(productSlug).pipe(
.getProductByPublicPath(productSlug) catchError((error) => {
.pipe( this.languageService.clearLocalizedRouteOverrides();
catchError((error) => { this.product.set(null);
this.languageService.clearLocalizedRouteOverrides(); this.selectedVariantId.set(null);
this.product.set(null); this.setSelectedImageAssetId(null);
this.selectedVariantId.set(null); this.modelFile.set(null);
this.setSelectedImageAssetId(null); const isNotFound = error?.status === 404;
this.modelFile.set(null); if (isNotFound) {
const isNotFound = error?.status === 404; this.error.set('SHOP.NOT_FOUND');
if (isNotFound) { this.setResponseStatus(404);
this.error.set('SHOP.NOT_FOUND'); this.applyHardFallbackSeo();
this.setResponseStatus(404);
this.applyHardFallbackSeo();
return of(null);
}
if (this.shouldUseSoftSeoFallback(error)) {
this.error.set(null);
this.softFallbackActive.set(true);
this.setResponseStatus(200);
this.applySoftFallbackSeo(productSlug);
return of(null);
}
this.error.set('SHOP.LOAD_ERROR');
this.setResponseStatus(503);
return of(null); return of(null);
}), }
finalize(() => this.loading.set(false)),
); if (this.shouldUseSoftSeoFallback(error)) {
this.error.set(null);
this.softFallbackActive.set(true);
this.setResponseStatus(200);
this.applySoftFallbackSeo(productSlug);
return of(null);
}
this.error.set('SHOP.LOAD_ERROR');
this.setResponseStatus(503);
return of(null);
}),
finalize(() => this.loading.set(false)),
);
}), }),
takeUntilDestroyed(this.destroyRef), takeUntilDestroyed(this.destroyRef),
) )
@@ -904,9 +902,7 @@ export class ProductDetailComponent {
return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name)); return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name));
} }
private normalizeRouteParam( private normalizeRouteParam(value: string | null | undefined): string | null {
value: string | null | undefined,
): string | null {
const normalized = String(value ?? '').trim(); const normalized = String(value ?? '').trim();
return normalized || null; return normalized || null;
} }

View File

@@ -60,24 +60,26 @@ describe('ShopPageComponent', () => {
'TranslateService', 'TranslateService',
['instant'], ['instant'],
); );
translate.instant.and.callFake((key: string, params?: { count?: number }) => { translate.instant.and.callFake(
const translations: Record<string, string> = { (key: string, params?: { count?: number }) => {
'SHOP.TITLE': 'Technische Lösungen', const translations: Record<string, string> = {
'SHOP.SUBTITLE': 'Fertige Produkte, die praktische Probleme lösen', 'SHOP.TITLE': 'Technische Lösungen',
'SHOP.CATALOG_TITLE': 'Alle Produkte', 'SHOP.SUBTITLE': 'Fertige Produkte, die praktische Probleme lösen',
'SHOP.CATALOG_LABEL': 'Katalog', 'SHOP.CATALOG_TITLE': 'Alle Produkte',
'SHOP.SELECTED_CATEGORY': 'Ausgewählte Kategorie', 'SHOP.CATALOG_LABEL': 'Katalog',
'SHOP.CATALOG_META_DESCRIPTION': 'SHOP.SELECTED_CATEGORY': 'Ausgewählte Kategorie',
'Entdecken Sie 3D-gedruckte Produkte und technisches Zubehör.', 'SHOP.CATALOG_META_DESCRIPTION':
'SEO.ROUTES.SHOP.CATEGORY_TITLE': 'Shop-Kategorie | 3D fab', 'Entdecken Sie 3D-gedruckte Produkte und technisches Zubehör.',
'SEO.ROUTES.SHOP.CATEGORY_DESCRIPTION': 'SEO.ROUTES.SHOP.CATEGORY_TITLE': 'Shop-Kategorie | 3D fab',
'Entdecken Sie Produkte dieser Kategorie und technische Lösungen.', 'SEO.ROUTES.SHOP.CATEGORY_DESCRIPTION':
}; 'Entdecken Sie Produkte dieser Kategorie und technische Lösungen.',
if (key === 'SHOP.CATEGORY_META') { };
return `${params?.count ?? 0} Produkte in dieser Kategorie verfügbar`; if (key === 'SHOP.CATEGORY_META') {
} return `${params?.count ?? 0} Produkte in dieser Kategorie verfügbar`;
return translations[key] ?? key; }
}); return translations[key] ?? key;
},
);
const currentLang = signal<'it' | 'en' | 'de' | 'fr'>('de'); const currentLang = signal<'it' | 'en' | 'de' | 'fr'>('de');
const languageService = { const languageService = {
@@ -100,11 +102,17 @@ describe('ShopPageComponent', () => {
flattenCategoryTree: jasmine flattenCategoryTree: jasmine
.createSpy('flattenCategoryTree') .createSpy('flattenCategoryTree')
.and.returnValue([]), .and.returnValue([]),
quantityForProduct: jasmine.createSpy('quantityForProduct').and.returnValue(0), quantityForProduct: jasmine
.createSpy('quantityForProduct')
.and.returnValue(0),
loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)), loadCart: jasmine.createSpy('loadCart').and.returnValue(of(null)),
clearCart: jasmine.createSpy('clearCart').and.returnValue(of(null)), clearCart: jasmine.createSpy('clearCart').and.returnValue(of(null)),
removeCartItem: jasmine.createSpy('removeCartItem').and.returnValue(of(null)), removeCartItem: jasmine
updateCartItem: jasmine.createSpy('updateCartItem').and.returnValue(of(null)), .createSpy('removeCartItem')
.and.returnValue(of(null)),
updateCartItem: jasmine
.createSpy('updateCartItem')
.and.returnValue(of(null)),
}; };
const router = { const router = {
@@ -164,7 +172,9 @@ describe('ShopPageComponent', () => {
}); });
it('keeps noindex for categories explicitly marked as non-indexable', () => { it('keeps noindex for categories explicitly marked as non-indexable', () => {
const { component, seoService } = createComponent('/de/shop/compatible-with-garmin'); const { component, seoService } = createComponent(
'/de/shop/compatible-with-garmin',
);
(component as any).applySeo(buildCategory({ indexable: false })); (component as any).applySeo(buildCategory({ indexable: false }));
@@ -180,7 +190,9 @@ describe('ShopPageComponent', () => {
'/de/shop/compatible-with-garmin', '/de/shop/compatible-with-garmin',
); );
expect((component as any).shouldUseSoftSeoFallback({ status: 500 })).toBeTrue(); expect(
(component as any).shouldUseSoftSeoFallback({ status: 500 }),
).toBeTrue();
(component as any).setResponseStatus(200); (component as any).setResponseStatus(200);
(component as any).applySoftFallbackSeo('compatible-with-garmin'); (component as any).applySoftFallbackSeo('compatible-with-garmin');
@@ -203,7 +215,9 @@ describe('ShopPageComponent', () => {
'/de/shop/compatible-with-garmin', '/de/shop/compatible-with-garmin',
); );
expect((component as any).shouldUseSoftSeoFallback({ status: 404 })).toBeFalse(); expect(
(component as any).shouldUseSoftSeoFallback({ status: 404 }),
).toBeFalse();
(component as any).setResponseStatus(404); (component as any).setResponseStatus(404);
(component as any).applyHardErrorSeo(); (component as any).applyHardErrorSeo();

View File

@@ -528,9 +528,7 @@ export class ShopPageComponent {
return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name)); return this.normalizeRouteParam(this.route.snapshot.paramMap.get(name));
} }
private normalizeRouteParam( private normalizeRouteParam(value: string | null | undefined): string | null {
value: string | null | undefined,
): string | null {
const normalized = String(value ?? '').trim(); const normalized = String(value ?? '').trim();
return normalized || null; return normalized || null;
} }