fix(storefront): composition audit fixes for cart, catalog, product, compare, wishlist, static pages
- Replace hand-rolled loading/error/empty markup with shared app-skeleton, app-empty-state, and app-button across catalog, product details, cart, compare, wishlist, and the public static-page renderer - Fix hardcoded hex colors that bypassed theme CSS variables (catalog, product details), restoring multi-tenant theme correctness - Remove ~1100 lines of dead "alt" cart theme CSS (never applied by the template) from cart.component.scss, bringing it back under the 40kB build budget (89.49kB -> 59.39kB cart-component chunk) - Swap legacy global .btn/.btn-ghost/.btn-primary classes for app-button in compare and wishlist empty/toolbar actions
This commit is contained in:
@@ -1,24 +1,36 @@
|
||||
<main class="product-details-page">
|
||||
@if (loading()) {
|
||||
<section class="product-details-loading" role="status" aria-live="polite">
|
||||
<div class="product-details-spinner"></div>
|
||||
<p>{{ 'productDetails.loading' | translate }}</p>
|
||||
<section class="product-details-loading" role="status" aria-live="polite" aria-busy="true">
|
||||
<div class="product-details-loading-layout">
|
||||
<app-skeleton shape="rect" class="product-details-loading-gallery" />
|
||||
<div class="product-details-loading-main">
|
||||
<app-skeleton shape="text" width="60%" height="28px" />
|
||||
<app-skeleton shape="text" width="30%" height="20px" />
|
||||
<app-skeleton shape="rect" height="120px" />
|
||||
<app-skeleton shape="rect" height="44px" />
|
||||
</div>
|
||||
</div>
|
||||
<span class="sr-only">{{ 'productDetails.loading' | translate }}</span>
|
||||
</section>
|
||||
}
|
||||
|
||||
@if (error()) {
|
||||
<section class="product-details-message error" role="alert">
|
||||
<h1>{{ 'productDetails.errorTitle' | translate }}</h1>
|
||||
<p>{{ error()! | translate }}</p>
|
||||
<button type="button" (click)="retry()">{{ 'productDetails.retry' | translate }}</button>
|
||||
<app-empty-state [title]="'productDetails.errorTitle' | translate" [description]="error()! | translate">
|
||||
<div slot="actions">
|
||||
<app-button variant="primary" (click)="retry()">{{ 'productDetails.retry' | translate }}</app-button>
|
||||
</div>
|
||||
</app-empty-state>
|
||||
</section>
|
||||
}
|
||||
|
||||
@if (missing() && !loading() && !error()) {
|
||||
<section class="product-details-message">
|
||||
<h1>{{ 'productDetails.missingTitle' | translate }}</h1>
|
||||
<p>{{ 'productDetails.missingDescription' | translate }}</p>
|
||||
<a [routerLink]="'/catalog' | langRoute">{{ 'productDetails.backToCatalog' | translate }}</a>
|
||||
<app-empty-state [title]="'productDetails.missingTitle' | translate" [description]="'productDetails.missingDescription' | translate">
|
||||
<div slot="actions">
|
||||
<app-button variant="primary" [routerLink]="'/catalog' | langRoute">{{ 'productDetails.backToCatalog' | translate }}</app-button>
|
||||
</div>
|
||||
</app-empty-state>
|
||||
</section>
|
||||
}
|
||||
|
||||
|
||||
@@ -35,58 +35,51 @@
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.product-details-loading,
|
||||
.product-details-loading {
|
||||
min-height: 420px;
|
||||
}
|
||||
|
||||
.product-details-message {
|
||||
min-height: 420px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
text-align: center;
|
||||
color: #697777;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: #1e3c38;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
button,
|
||||
a {
|
||||
min-height: 42px;
|
||||
padding: 0 22px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-lg, 13px);
|
||||
background: #497671;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.product-details-message.error {
|
||||
color: #991b1b;
|
||||
::ng-deep .app-empty-state__title {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
|
||||
.product-details-spinner {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border: 4px solid #d3dad9;
|
||||
border-top-color: #497671;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
.product-details-loading-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 0.9fr) minmax(340px, 1fr);
|
||||
gap: 28px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
.product-details-loading-gallery {
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
.product-details-loading-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
@@ -95,7 +88,8 @@
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.product-details-layout {
|
||||
.product-details-layout,
|
||||
.product-details-loading-layout {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import { ProductVariantSelectorComponent, VariantOptionGroup } from '../componen
|
||||
import { ProductTabItem, ProductTabKey, ProductTabsComponent } from '../engagement/components/product-tabs/product-tabs.component';
|
||||
import { QuestionListComponent } from '../engagement/components/question-list/question-list.component';
|
||||
import { ReviewListComponent } from '../engagement/components/review-list/review-list.component';
|
||||
import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component';
|
||||
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
||||
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-details-container',
|
||||
@@ -48,6 +51,9 @@ import { ReviewListComponent } from '../engagement/components/review-list/review
|
||||
ReviewListComponent,
|
||||
QuestionListComponent,
|
||||
RelatedProductsComponent,
|
||||
EmptyStateComponent,
|
||||
SkeletonComponent,
|
||||
ButtonComponent,
|
||||
],
|
||||
templateUrl: './product-details-container.component.html',
|
||||
styleUrls: ['./product-details-container.component.scss'],
|
||||
|
||||
Reference in New Issue
Block a user