From a8ad459506cac8277e537257a3f9c7656ea29dea Mon Sep 17 00:00:00 2001 From: master <> Date: Sun, 29 Mar 2026 16:03:44 +0300 Subject: [PATCH] Remove redundant headers from pack detail tabs, fix button theming All 7 pack detail components (dashboard, editor, rule-builder, yaml, simulation, approvals, explain) had their own eyebrow + h1 + lede headers that duplicated the parent pack-shell's dynamic title+subtitle. Changes per component: - Dashboard: removed header, added empty state with guidance, themed btns - Editor: removed header, kept metadata as right-aligned flex, themed btns - YAML: removed header, fixed label colors and button theming - Rule builder: removed header, fixed label colors - Simulation: removed header, kept status pill, themed btns from gradient to btn-primary-bg, fixed hardcoded rgba colors - Approvals: removed header, kept workflow status badges, themed btns - Explain: removed header, kept action links, added btn/btn--secondary CSS Button theming: replaced var(--color-status-info-text) backgrounds and hardcoded rgba() with var(--color-btn-primary-bg/border/text) and var(--color-btn-secondary-bg/border/text) across all components. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../approvals/policy-approvals.component.ts | 82 ++++++----------- .../dashboard/policy-dashboard.component.ts | 42 +++++---- .../editor/policy-editor.component.ts | 90 ++++++------------- .../explain/policy-explain.component.ts | 43 ++++----- .../policy-rule-builder.component.ts | 20 +---- .../simulation/policy-simulation.component.ts | 73 +++++---------- .../yaml/policy-yaml-editor.component.ts | 30 +------ 7 files changed, 122 insertions(+), 258 deletions(-) diff --git a/src/Web/StellaOps.Web/src/app/features/policy-studio/approvals/policy-approvals.component.ts b/src/Web/StellaOps.Web/src/app/features/policy-studio/approvals/policy-approvals.component.ts index 6b50a9c4d..0f1777fe0 100644 --- a/src/Web/StellaOps.Web/src/app/features/policy-studio/approvals/policy-approvals.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/policy-studio/approvals/policy-approvals.component.ts @@ -22,33 +22,24 @@ import { PolicyApiService } from '../services/policy-api.service'; changeDetection: ChangeDetectionStrategy.OnPush, template: `
-
-
-

Policy Studio · Approvals

-

Submit, review, approve

-

- Two-person approval with deterministic audit trail and scoped activation windows. -

+ @if (workflow) { +
+ + {{ workflow.status | titlecase }} + + Required approvers: {{ workflow.requiredApprovers }} + Current: {{ workflow.currentApprovers }} + + Two-person rule: {{ isReadyToApprove ? 'Satisfied' : 'Missing second approver' }} + + + Checklist: {{ pendingChecklist === 0 ? 'Ready' : pendingChecklist + ' open item' + (pendingChecklist === 1 ? '' : 's') }} + + + Schedule: {{ scheduleSummary }} +
- @if (workflow) { -
- - {{ workflow.status | titlecase }} - - Required approvers: {{ workflow.requiredApprovers }} - Current: {{ workflow.currentApprovers }} - - Two-person rule: {{ isReadyToApprove ? 'Satisfied' : 'Missing second approver' }} - - - Checklist: {{ pendingChecklist === 0 ? 'Ready' : pendingChecklist + ' open item' + (pendingChecklist === 1 ? '' : 's') }} - - - Schedule: {{ scheduleSummary }} - -
- } -
+ } @if (workflow) {
@@ -225,30 +216,13 @@ import { PolicyApiService } from '../services/policy-api.service'; gap: 1rem; } - .approvals__header { + .approvals__actions { display: flex; - justify-content: space-between; - gap: 1rem; - align-items: flex-start; - } - - .approvals__eyebrow { - margin: 0; - color: var(--color-brand-muted); - text-transform: uppercase; - letter-spacing: 0.05em; - font-size: 0.8rem; - } - - .approvals__lede { - margin: 0.2rem 0 0; - color: var(--color-text-secondary); - } - - .approvals__meta { - display: grid; - justify-items: end; - gap: 0.3rem; + justify-content: flex-end; + gap: 0.5rem; + margin-bottom: 1rem; + flex-wrap: wrap; + align-items: center; } .pill { @@ -334,16 +308,16 @@ import { PolicyApiService } from '../services/policy-api.service'; } .btn { - background: var(--color-status-info-text); - border: 1px solid var(--color-status-info-text); - color: var(--color-text-primary); + background: var(--color-btn-primary-bg); + border: 1px solid var(--color-btn-primary-border); + color: var(--color-btn-primary-text); border-radius: var(--radius-xl); padding: 0.55rem 0.9rem; font-weight: var(--font-weight-bold); cursor: pointer; } - .btn:hover { background: var(--color-status-info-text); } + .btn:hover { opacity: 0.9; } .btn:disabled { opacity: 0.6; cursor: not-allowed; } @@ -469,7 +443,7 @@ import { PolicyApiService } from '../services/policy-api.service'; .schedule__summary { display: flex; flex-direction: column; gap: 0.15rem; color: var(--color-text-secondary); } - @media (max-width: 960px) { .approvals__header { flex-direction: column; } } + @media (max-width: 960px) { .approvals__actions { justify-content: flex-start; } } `, ] }) diff --git a/src/Web/StellaOps.Web/src/app/features/policy-studio/dashboard/policy-dashboard.component.ts b/src/Web/StellaOps.Web/src/app/features/policy-studio/dashboard/policy-dashboard.component.ts index daa817a12..2a9ec18be 100644 --- a/src/Web/StellaOps.Web/src/app/features/policy-studio/dashboard/policy-dashboard.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/policy-studio/dashboard/policy-dashboard.component.ts @@ -18,20 +18,20 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, template: `
-
-
-

Policy Studio · Runs

-

Run dashboards

-

Heatmap, VEX wins, and suppressions; deterministic ordering.

+ @if (dashboard) { +
+ Runs: {{ dashboard.runs.length }} + Rules: {{ dashboard.ruleHeatmap.length }}
- @if (dashboard) { -
- Runs: {{ dashboard.runs.length }} - Rules: {{ dashboard.ruleHeatmap.length }} -
- } -
- + } + + @if (!loading && !dashboard) { +
+

No run data available

+

Run a policy evaluation to see dashboards, heatmaps, and daily deltas here.

+
+ } + @if (dashboard) {
@@ -105,12 +105,12 @@ import { `, styles: [ ` - :host { display: block; background: var(--color-surface-primary); color: var(--color-text-primary); ; } + :host { display: block; background: var(--color-surface-primary); color: var(--color-text-primary); } .dash { max-width: 1200px; margin: 0 auto; padding: 1.5rem; } - .dash__header { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; } - .dash__eyebrow { margin: 0; color: var(--color-status-info); text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.8rem; } - .dash__lede { margin: 0.2rem 0 0; color: var(--color-text-muted); } - .dash__meta { display: flex; gap: 0.5rem; flex-wrap: wrap; } + .dash__actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; } + .dash__empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 3rem 1.5rem; border: 1px dashed var(--color-border-primary); border-radius: var(--radius-xl); background: var(--color-surface-elevated); text-align: center; } + .dash__empty-title { margin: 0; font-size: 1.1rem; font-weight: var(--font-weight-semibold); color: var(--color-text-primary); } + .dash__empty-hint { margin: 0.5rem 0 0; color: var(--color-text-muted); font-size: 0.9rem; max-width: 420px; } .pill { border: 1px solid var(--color-border-primary); padding: 0.35rem 0.7rem; border-radius: var(--radius-full); } .dash__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1rem; margin-top: 1rem; } .card { background: var(--color-surface-elevated); border: 1px solid var(--color-border-primary); border-radius: var(--radius-xl); padding: 1rem; box-shadow: 0 10px 30px rgba(0,0,0,0.25); } @@ -124,11 +124,15 @@ import { .heatmap__name { color: var(--color-text-primary); font-weight: var(--font-weight-semibold); } .heatmap__bar { display: flex; align-items: center; gap: 0.4rem; background: var(--color-surface-elevated); border-radius: var(--radius-full); overflow: hidden; padding: 0.2rem; } .heatmap__fill { display: block; height: 10px; background: linear-gradient(90deg, var(--color-status-info), var(--color-status-info-text)); border-radius: var(--radius-full); } - .heatmap__value { color: rgba(212, 201, 168, 0.5); font-size: 0.85rem; } + .heatmap__value { color: var(--color-text-muted); font-size: 0.85rem; } .heatmap__latency { color: var(--color-text-muted); font-size: 0.85rem; } .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.5rem; } .chip { border: 1px solid var(--color-border-primary); border-radius: var(--radius-full); padding: 0.25rem 0.6rem; background: var(--color-surface-primary); } .chip--muted { opacity: 0.8; } + .btn { background: var(--color-btn-primary-bg); border: 1px solid var(--color-btn-primary-border); color: var(--color-btn-primary-text); border-radius: var(--radius-lg); padding: 0.4rem 0.8rem; cursor: pointer; font-weight: var(--font-weight-semibold); } + .btn:hover { opacity: 0.9; } + .btn:disabled { opacity: 0.6; cursor: not-allowed; } + .btn--ghost { background: transparent; color: var(--color-text-secondary); border: 1px solid var(--color-border-primary); } `, ] }) diff --git a/src/Web/StellaOps.Web/src/app/features/policy-studio/editor/policy-editor.component.ts b/src/Web/StellaOps.Web/src/app/features/policy-studio/editor/policy-editor.component.ts index f20e2fa19..dd694ae43 100644 --- a/src/Web/StellaOps.Web/src/app/features/policy-studio/editor/policy-editor.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/policy-studio/editor/policy-editor.component.ts @@ -45,35 +45,23 @@ interface ChecklistItem { changeDetection: ChangeDetectionStrategy.OnPush, template: `
-
-
-

Policy Studio · Authoring

-

{{ pack?.name || 'Loading policy…' }}

- @if (pack) { -

- Version {{ pack.version }} · Status: {{ pack.status | titlecase }} · - Digest: {{ pack.digest || 'pending' }} -

- } -
- @if (pack) { -
-
- Updated - {{ pack.modifiedAt | date: 'medium' }} -
-
- Authors - {{ pack.metadata?.author || pack.createdBy }} -
-
- Tags - {{ pack.tags?.length || 0 }} -
+ @if (pack) { +
+
+ Updated + {{ pack.modifiedAt | date: 'medium' }}
- } -
- +
+ Authors + {{ pack.metadata?.author || pack.createdBy }} +
+
+ Tags + {{ pack.tags?.length || 0 }} +
+
+ } +
@@ -201,40 +189,12 @@ interface ChecklistItem { padding: 1.5rem; } - .policy-editor__header { - display: flex; - justify-content: space-between; - gap: 1.5rem; - align-items: flex-start; - margin-bottom: 1.25rem; - } - - .policy-editor__title h1 { - margin: 0.1rem 0; - font-size: 1.8rem; - font-weight: var(--font-weight-bold); - color: var(--color-text-inverse); - } - - .policy-editor__eyebrow { - margin: 0; - color: var(--color-status-info-border); - letter-spacing: 0.05em; - text-transform: uppercase; - font-size: 0.75rem; - } - - .policy-editor__subtitle { - margin: 0; - color: var(--color-text-muted); - font-size: 0.95rem; - } - .policy-editor__meta { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + display: flex; gap: 0.75rem; - min-width: 320px; + justify-content: flex-end; + flex-wrap: wrap; + margin-bottom: 1rem; } .policy-editor__meta-item { @@ -334,9 +294,9 @@ interface ChecklistItem { } .btn { - border: 1px solid var(--color-status-info-text); - background: var(--color-status-info-text); - color: var(--color-text-primary); + border: 1px solid var(--color-btn-primary-border); + background: var(--color-btn-primary-bg); + color: var(--color-btn-primary-text); border-radius: var(--radius-lg); padding: 0.55rem 0.85rem; font-weight: var(--font-weight-semibold); @@ -345,8 +305,7 @@ interface ChecklistItem { } .btn:hover { - background: var(--color-status-info-text); - border-color: var(--color-status-info-text); + opacity: 0.9; } .btn:disabled { @@ -356,6 +315,7 @@ interface ChecklistItem { .btn--ghost { background: transparent; + color: var(--color-text-secondary); border-color: var(--color-border-primary); } diff --git a/src/Web/StellaOps.Web/src/app/features/policy-studio/explain/policy-explain.component.ts b/src/Web/StellaOps.Web/src/app/features/policy-studio/explain/policy-explain.component.ts index 6419ca5c2..6bdf01cc4 100644 --- a/src/Web/StellaOps.Web/src/app/features/policy-studio/explain/policy-explain.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/policy-studio/explain/policy-explain.component.ts @@ -13,24 +13,17 @@ import jsPDF from './jspdf.stub'; template: `
@if (result) { -
-
-

Policy Studio · Explain

-

Run {{ result.runId }}

-

Policy {{ result.policyId }} · Version {{ result.policyVersion }}

-
-
- - Create immutable audit bundle - - - -
-
+
+ + Create immutable audit bundle + + + +
} @if (result) { @@ -73,14 +66,14 @@ import jsPDF from './jspdf.stub'; `, styles: [ ` - :host { display: block; background: var(--color-surface-elevated); color: var(--color-text-primary); ; } + :host { display: block; background: var(--color-surface-elevated); color: var(--color-text-primary); } .expl { max-width: 1200px; margin: 0 auto; padding: 1.5rem; } - .expl__header { display: flex; justify-content: space-between; align-items: center; } - .expl__eyebrow { margin: 0; color: var(--color-status-info); text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.8rem; } - .expl__lede { margin: 0.2rem 0 0; color: var(--color-text-muted); } - .expl__meta { display: flex; gap: 0.5rem; align-items: center; } - .expl__btn { display: inline-flex; align-items: center; border: 1px solid var(--color-border-primary); border-radius: var(--radius-lg); padding: 0.35rem 0.65rem; color: var(--color-text-primary); text-decoration: none; background: var(--color-surface-elevated); } - .expl__btn:hover { border-color: var(--color-status-info); } + .expl__actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-bottom: 1rem; align-items: center; } + .expl__btn { display: inline-flex; align-items: center; border: 1px solid var(--color-btn-secondary-border); border-radius: var(--radius-lg); padding: 0.35rem 0.65rem; color: var(--color-btn-secondary-text); text-decoration: none; background: var(--color-btn-secondary-bg); } + .expl__btn:hover { opacity: 0.9; } + .btn { background: var(--color-btn-primary-bg); border: 1px solid var(--color-btn-primary-border); color: var(--color-btn-primary-text); border-radius: var(--radius-lg); padding: 0.35rem 0.65rem; cursor: pointer; font-weight: var(--font-weight-semibold); } + .btn--secondary { background: var(--color-btn-secondary-bg); border-color: var(--color-btn-secondary-border); color: var(--color-btn-secondary-text); } + .btn:hover { opacity: 0.9; } .expl__grid { display: grid; grid-template-columns: 2fr 1fr; gap: 1rem; margin-top: 1rem; } .card { background: var(--color-surface-elevated); border: 1px solid var(--color-border-primary); border-radius: var(--radius-xl); padding: 1rem; } ol { margin: 0.5rem 0 0; padding-left: 1.25rem; } diff --git a/src/Web/StellaOps.Web/src/app/features/policy-studio/rule-builder/policy-rule-builder.component.ts b/src/Web/StellaOps.Web/src/app/features/policy-studio/rule-builder/policy-rule-builder.component.ts index da5241cba..52da8203f 100644 --- a/src/Web/StellaOps.Web/src/app/features/policy-studio/rule-builder/policy-rule-builder.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/policy-studio/rule-builder/policy-rule-builder.component.ts @@ -9,17 +9,6 @@ import { ActivatedRoute } from '@angular/router'; changeDetection: ChangeDetectionStrategy.OnPush, template: `
-
-
-

Policy Studio · Rule Builder

-

Guided rule construction

-

Deterministic preview JSON updates as you edit.

-
-
- Pack: {{ packId }} -
-
-