fix(builder): composition audit fixes for project editor sections

- Reset-section button: raw <button> with hardcoded colors -> app-button variant="danger"
- section.shared.scss: raw button/input/select colors switched to CSS theme vars (--primary-color, --bg-primary, --bg-secondary, --error-color) instead of bare hex
- save-bar scss referenced nonexistent CSS vars (--surface, --border, --warning, --danger, --muted-foreground, --info-bg, --info) that always fell back to hardcoded hex; renamed to the real theme vars (--bg-primary, --border-color, --warning-color, --error-color, --text-secondary, --info-color) so the save bar is actually theme-aware
- footer/homepage/widgets section scss: nonexistent --danger-color var renamed to --error-color
- static-pages-editor: replaced dead `.editor-section-card` wrapper class (removed from shared stylesheet in the Sprint 30 redesign, never migrated here) with app-section-card, restoring the card chrome every sibling editor section has
- widgets-section: empty state (no widgets) rendered nothing; added app-empty-state
- homepage-section: empty state (no homepage page) rendered nothing; added app-empty-state
- navigation-section: header/footer nav move-up/move-down buttons had no accessible name (bare uarr/darr glyphs); added aria-label
- Added builder.widgetsEmptyTitle/Desc and builder.homepageEmptyTitle/Desc i18n keys (en/ru/hy)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-23 10:47:37 +04:00
parent 2e31e80e28
commit 4ebc15fff8
19 changed files with 65 additions and 61 deletions

View File

@@ -1,4 +1,4 @@
<section class="editor-section-card">
<app-section-card>
@if (fieldError('staticPages'); as msg) {
<p class="editor-error">{{ msg }}</p>
}
@@ -94,4 +94,4 @@
}
<app-media-picker [open]="mediaPickerOpen" (selected)="onImagePicked($event)" (closed)="mediaPickerOpen = false" />
</section>
</app-section-card>

View File

@@ -11,6 +11,7 @@ import { ButtonComponent } from '../../../shared/ui/button/button.component';
import { InputComponent } from '../../../shared/ui/input/input.component';
import { FormFieldComponent } from '../../../shared/ui/form-field/form-field.component';
import { EmptyStateComponent } from '../../../shared/ui/empty-state/empty-state.component';
import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component';
import { SelectComponent, SelectOption } from '../../../shared/ui/select/select.component';
import { ToggleComponent } from '../../../shared/ui/toggle/toggle.component';
import { MediaPickerComponent } from '../../../shared/media/media-picker/media-picker.component';
@@ -33,6 +34,7 @@ const ALL_LOCALES_FILTER = 'all';
InputComponent,
FormFieldComponent,
EmptyStateComponent,
SectionCardComponent,
SelectComponent,
ToggleComponent,
MediaPickerComponent,

View File

@@ -6,20 +6,20 @@
align-items: flex-start;
gap: 1rem;
padding: 0.75rem 1rem;
background: var(--surface, #fff);
border-top: 1px solid var(--border, #ddd);
background: var(--bg-primary, #fff);
border-top: 1px solid var(--border-color, #ddd);
z-index: 5;
}
.project-editor-save-bar-dirty {
color: var(--warning, #b45309);
color: var(--warning-color, #b45309);
margin-left: 0.5rem;
}
.project-editor-save-bar-issues {
margin: 0.25rem 0 0;
padding-left: 1.25rem;
color: var(--danger, #b91c1c);
color: var(--error-color, #b91c1c);
li.is-warning {
color: var(--warning-color, #b45309);
@@ -37,7 +37,7 @@
}
.project-editor-save-bar-saved-at {
color: var(--muted-foreground, #6b7280);
color: var(--text-secondary, #6b7280);
margin-left: 0.5rem;
font-size: 0.85em;
}
@@ -51,7 +51,7 @@
justify-content: space-between;
align-items: center;
padding: 0.5rem 1rem;
background: var(--info-bg, #eff6ff);
color: var(--info, #1d4ed8);
border-bottom: 1px solid var(--border, #ddd);
background: color-mix(in srgb, var(--info-color, #1d4ed8) 12%, var(--bg-primary, #eff6ff));
color: var(--info-color, #1d4ed8);
border-bottom: 1px solid var(--border-color, #ddd);
}

View File

@@ -84,7 +84,7 @@
<section class="project-editor-stack">
@if (canResetActiveSection()) {
<div class="project-editor-section-actions">
<button type="button" (click)="resetActiveSection()">{{ 'builder.resetSection' | translate }}</button>
<app-button variant="danger" size="sm" (click)="resetActiveSection()">{{ 'builder.resetSection' | translate }}</app-button>
</div>
}
@switch (activeSection()) {

View File

@@ -277,32 +277,6 @@
justify-content: flex-end;
}
.project-editor-section-actions button {
color: #b91c1c;
background: transparent;
border: 1px solid #d3dad9;
border-radius: var(--radius-sm, 8px);
padding: 6px 12px;
cursor: pointer;
transition: background-color var(--transition-fast, 120ms ease), border-color var(--transition-fast, 120ms ease);
&:hover {
background: rgba(185, 28, 28, 0.08);
border-color: #b91c1c;
}
&:focus-visible {
outline: 2px solid #b91c1c;
outline-offset: 2px;
}
}
@media (prefers-reduced-motion: reduce) {
.project-editor-section-actions button {
transition: none;
}
}
.project-editor-empty {
min-height: 240px;
display: grid;

View File

@@ -19,6 +19,7 @@ import { TranslatePipe } from '../../../i18n/translate.pipe';
import { TranslateService } from '../../../i18n/translate.service';
import { LanguageService } from '../../../services/language.service';
import { IconComponent } from '../../../shared/ui/icon/icon.component';
import { ButtonComponent } from '../../../shared/ui/button/button.component';
import { StaticPagesEditorComponent } from '../../content-management/components/static-pages-editor.component';
import { ProjectEditorSaveBarComponent } from '../components/save-bar/project-editor-save-bar.component';
import { EDITOR_SECTION_BOOTSTRAP_KEYS, ProjectEditorSectionId } from '../models/project-editor.model';
@@ -64,6 +65,7 @@ const SECTION_LABEL_KEYS: Record<ProjectEditorSectionId, string> = {
ProjectEditorPreviewSectionComponent,
ProjectEditorSaveBarComponent,
IconComponent,
ButtonComponent,
],
templateUrl: './project-editor-page.component.html',
styleUrls: ['./project-editor-page.component.scss'],

View File

@@ -14,7 +14,7 @@
color: var(--text-secondary, #6b7280);
font-size: var(--font-size-lg, 1rem);
&:hover { color: var(--danger-color, #c0392b); }
&:hover { color: var(--error-color, #c0392b); }
}
.footer-columns {
@@ -54,7 +54,7 @@
color: var(--text-secondary, #6b7280);
font-size: var(--font-size-lg, 1rem);
&:hover { color: var(--danger-color, #c0392b); }
&:hover { color: var(--error-color, #c0392b); }
}
.footer-column__links {

View File

@@ -1,4 +1,8 @@
@if (homePage()) {
@if (!homePage()) {
<app-section-card [title]="'builder.homepage' | translate">
<app-empty-state [title]="'builder.homepageEmptyTitle' | translate" [description]="'builder.homepageEmptyDesc' | translate" />
</app-section-card>
} @else {
<app-homepage-overview />
<app-section-card [title]="'builder.homepage' | translate">
@if (fieldError('pages'); as msg) {

View File

@@ -63,7 +63,7 @@
}
.block-card__visible-toggle { display: flex; align-items: center; gap: 6px; font-size: var(--font-size-sm, 0.8125rem); }
.block-card__remove:hover { color: var(--danger-color, #c0392b); }
.block-card__remove:hover { color: var(--error-color, #c0392b); }
.block-catalog { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border-color, #d3dad9); }

View File

@@ -7,6 +7,7 @@ import { TranslateService } from '../../../i18n/translate.service';
import { InputComponent } from '../../../shared/ui/input/input.component';
import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component';
import { ToggleComponent } from '../../../shared/ui/toggle/toggle.component';
import { EmptyStateComponent } from '../../../shared/ui/empty-state/empty-state.component';
import { HomepageOverviewComponent } from './homepage/homepage-overview.component';
import { SectionConfig } from '../../../shared/models/config';
import { IconComponent } from '../../../shared/ui/icon/icon.component';
@@ -43,7 +44,7 @@ const BLOCK_BY_TYPE = new Map(BLOCK_CATALOG.map(entry => [entry.type, entry]));
@Component({
selector: 'app-project-editor-homepage-section',
standalone: true,
imports: [DragDropModule, FormsModule, TranslatePipe, InputComponent, SectionCardComponent, ToggleComponent, HomepageOverviewComponent, IconComponent],
imports: [DragDropModule, FormsModule, TranslatePipe, InputComponent, SectionCardComponent, ToggleComponent, EmptyStateComponent, HomepageOverviewComponent, IconComponent],
templateUrl: './homepage-section.component.html',
styleUrls: ['./section.shared.scss', './homepage-section.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush

View File

@@ -16,8 +16,8 @@
<div class="editor-actions">
<h3>{{ labelOf(item) }}</h3>
<div class="editor-actions">
<app-button variant="secondary" size="sm" (click)="move('header', item.id, -1)">&uarr;</app-button>
<app-button variant="secondary" size="sm" (click)="move('header', item.id, 1)">&darr;</app-button>
<app-button variant="secondary" size="sm" [attr.aria-label]="'builder.widgetMoveUp' | translate" (click)="move('header', item.id, -1)">&uarr;</app-button>
<app-button variant="secondary" size="sm" [attr.aria-label]="'builder.widgetMoveDown' | translate" (click)="move('header', item.id, 1)">&darr;</app-button>
<app-button variant="secondary" size="sm" (click)="removeLink('header', item.id)">{{ 'builder.removeLink' | translate }}</app-button>
</div>
</div>
@@ -50,8 +50,8 @@
<div class="editor-actions">
<h3>{{ labelOf(item) }}</h3>
<div class="editor-actions">
<app-button variant="secondary" size="sm" (click)="move('footer', item.id, -1)">&uarr;</app-button>
<app-button variant="secondary" size="sm" (click)="move('footer', item.id, 1)">&darr;</app-button>
<app-button variant="secondary" size="sm" [attr.aria-label]="'builder.widgetMoveUp' | translate" (click)="move('footer', item.id, -1)">&uarr;</app-button>
<app-button variant="secondary" size="sm" [attr.aria-label]="'builder.widgetMoveDown' | translate" (click)="move('footer', item.id, 1)">&darr;</app-button>
<app-button variant="secondary" size="sm" (click)="removeLink('footer', item.id)">{{ 'builder.removeLink' | translate }}</app-button>
</div>
</div>

View File

@@ -59,7 +59,7 @@ select {
border: 1px solid var(--border-color, #d3dad9);
border-radius: 10px;
font: inherit;
background: #fff;
background: var(--bg-primary, #fff);
}
input[type='checkbox'] {
@@ -93,7 +93,7 @@ input[type='color'] {
border: 1px solid var(--border-color, #d3dad9);
border-radius: var(--radius-md, 12px);
padding: 12px;
background: #fbfcfc;
background: var(--bg-secondary, #fbfcfc);
}
.editor-actions {
@@ -105,8 +105,8 @@ input[type='color'] {
button {
min-height: 42px;
border-radius: 10px;
border: 1px solid #497671;
background: #497671;
border: 1px solid var(--primary-color, #497671);
background: var(--primary-color, #497671);
color: #fff;
padding: 0 14px;
font-weight: var(--font-weight-bold, 700);
@@ -128,7 +128,7 @@ button {
}
&:focus-visible {
outline: 2px solid #497671;
outline: 2px solid var(--primary-color, #497671);
outline-offset: 2px;
}
@@ -141,17 +141,17 @@ button {
}
button.secondary {
background: #fff;
color: #1e3c38;
background: var(--bg-primary, #fff);
color: var(--text-primary, #1e3c38);
border-color: var(--border-color, #d3dad9);
&:hover {
background: var(--bg-secondary, #f5f5f5);
border-color: #497671;
border-color: var(--primary-color, #497671);
}
&:focus-visible {
outline-color: #497671;
outline-color: var(--primary-color, #497671);
}
}
@@ -167,7 +167,7 @@ button.secondary {
.editor-error {
margin: 0;
color: #991b1b;
color: var(--error-color, #991b1b);
}
.preview-validation {

View File

@@ -1,4 +1,8 @@
@if (widgets().length > 0) {
@if (widgets().length === 0) {
<app-section-card [title]="'builder.widgets' | translate">
<app-empty-state [title]="'builder.widgetsEmptyTitle' | translate" [description]="'builder.widgetsEmptyDesc' | translate" />
</app-section-card>
} @else {
<app-section-card [title]="'builder.widgets' | translate">
@if (fieldError('pages'); as msg) {
<p class="editor-error">{{ msg }}</p>

View File

@@ -72,8 +72,8 @@
}
.widget-card__remove:hover:not(:disabled) {
border-color: var(--error-color) !important;
color: var(--error-color) !important;
border-color: var(--error-color);
color: var(--error-color);
}
.widget-card__visible-toggle {
@@ -113,5 +113,5 @@
color: var(--text-secondary, #6b7280);
font-size: var(--font-size-lg, 1rem);
&:hover { color: var(--danger-color, #c0392b); }
&:hover { color: var(--error-color, #c0392b); }
}

View File

@@ -7,6 +7,7 @@ import { InputComponent } from '../../../shared/ui/input/input.component';
import { ButtonComponent } from '../../../shared/ui/button/button.component';
import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component';
import { ToggleComponent } from '../../../shared/ui/toggle/toggle.component';
import { EmptyStateComponent } from '../../../shared/ui/empty-state/empty-state.component';
import { WidgetConfig } from '../../../shared/models/config';
import { IconComponent } from '../../../shared/ui/icon/icon.component';
import { AppIconName } from '../../../shared/ui/icon/icon-registry';
@@ -31,7 +32,7 @@ const WIDGET_LABEL_KEYS: Record<string, string> = {
@Component({
selector: 'app-project-editor-widgets-section',
standalone: true,
imports: [FormsModule, TranslatePipe, InputComponent, ButtonComponent, SectionCardComponent, ToggleComponent, IconComponent],
imports: [FormsModule, TranslatePipe, InputComponent, ButtonComponent, SectionCardComponent, ToggleComponent, EmptyStateComponent, IconComponent],
templateUrl: './widgets-section.component.html',
styleUrls: ['./section.shared.scss', './widgets-section.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush