diff --git a/README.md b/README.md index ec26e75..1d374ad 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,6 @@ npm run lint ``` ## Current scope -- Minimal frontend shell only. -- No live API wiring in the baseline commit. -- `environment summary` stays mock-only until a worker-visible route exists in the current backend runtime. - -## Next development slice -1. Add app shell sections for `timeline`, `audit feed`, and `environment summary`. -2. Introduce typed DTO/fixture layer from the approved first-slice contracts. -3. Keep `environment summary` behind mock data until the live route is available. +- App shell slice for `timeline`, `audit feed`, and `environment summary`. +- Local typed fixtures only. No live API wiring. +- `environment summary` stays explicitly mock-only until a worker-visible route exists in the current backend runtime. diff --git a/src/App.css b/src/App.css index 6f16b54..b7c82f0 100644 --- a/src/App.css +++ b/src/App.css @@ -1,7 +1,7 @@ :root { - color: #e5eef7; - background: #0b1220; - font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + color: #ebf1ea; + background: #101611; + font-family: 'IBM Plex Sans', 'Segoe UI', sans-serif; } body { @@ -9,8 +9,9 @@ body { min-width: 320px; min-height: 100vh; background: - radial-gradient(circle at top, rgba(56, 189, 248, 0.18), transparent 35%), - linear-gradient(180deg, #020617 0%, #0f172a 100%); + radial-gradient(circle at top left, rgba(201, 255, 148, 0.14), transparent 32%), + radial-gradient(circle at right, rgba(102, 163, 255, 0.1), transparent 28%), + linear-gradient(180deg, #0b100c 0%, #161d17 100%); } #root { @@ -18,23 +19,38 @@ body { } .app-shell { - max-width: 1120px; + max-width: 1180px; margin: 0 auto; - padding: 48px 24px 64px; + padding: 40px 20px 56px; } .hero-card, -.next-slice-card, -.status-grid article { - background: rgba(15, 23, 42, 0.8); - border: 1px solid rgba(148, 163, 184, 0.18); - border-radius: 24px; - box-shadow: 0 20px 45px rgba(2, 6, 23, 0.35); +.overview-strip article, +.panel, +.summary-card, +.timeline-item, +.audit-item { + background: rgba(18, 25, 20, 0.84); + border: 1px solid rgba(207, 234, 212, 0.1); + box-shadow: 0 18px 40px rgba(5, 8, 6, 0.3); } .hero-card, -.next-slice-card { +.panel, +.overview-strip article, +.summary-card, +.timeline-item, +.audit-item { + border-radius: 22px; +} + +.hero-card { + display: grid; + grid-template-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr); + gap: 24px; + align-items: end; padding: 28px; + margin-bottom: 20px; } .eyebrow { @@ -42,52 +58,177 @@ body { text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.8rem; - color: #38bdf8; + color: #b8f36b; } h1, h2, -p, -ul { +h3, +p { margin-top: 0; } h1 { - font-size: clamp(2.4rem, 5vw, 4rem); - margin-bottom: 16px; + font-size: clamp(2.4rem, 4vw, 3.5rem); + margin-bottom: 0; + line-height: 0.95; + letter-spacing: -0.04em; +} + +h2 { + margin-bottom: 0; + font-size: 1.2rem; +} + +h3 { + margin-bottom: 8px; + font-size: 1rem; } .lede { - font-size: 1.1rem; + margin-bottom: 0; + align-self: stretch; + font-size: 1rem; line-height: 1.7; - max-width: 68ch; - color: #cbd5e1; + color: #c8d4c8; } -.status-grid { +.overview-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 20px; - margin: 28px 0; + gap: 16px; + margin-bottom: 20px; } -.status-grid article { +.overview-strip article { + padding: 18px 20px; +} + +.kicker, +.section-label, +.item-meta { + display: inline-block; + margin: 0 0 8px; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: #93a393; +} + +.overview-strip strong { + display: block; + font-size: 1rem; + color: #f5f8f4; +} + +.content-grid { + display: grid; + grid-template-columns: repeat(12, minmax(0, 1fr)); + gap: 20px; +} + +.panel { + grid-column: span 4; padding: 22px; } -.status-grid h2, -.next-slice-card h2 { - margin-bottom: 12px; - font-size: 1.1rem; +.panel-wide { + grid-column: span 8; } -.status-grid p, -.next-slice-card li { - color: #cbd5e1; - line-height: 1.6; +.panel-heading { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin-bottom: 18px; } -.next-slice-card ul { - padding-left: 20px; +.panel-badge { + border-radius: 999px; + padding: 7px 12px; + font-size: 0.8rem; + color: #d8f4af; + background: rgba(184, 243, 107, 0.12); + border: 1px solid rgba(184, 243, 107, 0.18); +} + +.panel-badge.muted { + color: #c6d0c6; + background: rgba(198, 208, 198, 0.08); + border-color: rgba(198, 208, 198, 0.12); +} + +.timeline-list, +.audit-list, +.summary-grid { + display: grid; + gap: 14px; +} + +.timeline-list { + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); +} + +.timeline-item, +.audit-item, +.summary-card { + padding: 18px; +} + +.timeline-item h3, +.audit-item h3, +.summary-card h3 { + color: #f5f8f4; +} + +.timeline-item p, +.audit-item p, +.summary-card p { margin-bottom: 0; + line-height: 1.55; + color: #c8d4c8; +} + +.timeline-stable { + border-color: rgba(157, 255, 172, 0.16); +} + +.timeline-active { + border-color: rgba(255, 211, 122, 0.18); +} + +.timeline-attention { + border-color: rgba(255, 143, 143, 0.18); +} + +.audit-target { + color: #b8f36b; +} + +.summary-grid { + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); +} + +@media (max-width: 900px) { + .hero-card, + .content-grid { + grid-template-columns: 1fr; + } + + .panel, + .panel-wide { + grid-column: auto; + } +} + +@media (max-width: 640px) { + .app-shell { + padding-inline: 16px; + } + + .hero-card, + .panel { + padding: 20px; + } } diff --git a/src/App.tsx b/src/App.tsx index 1458746..2d2e82d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,45 +1,101 @@ import './App.css' - -const nextSlice = [ - 'Timeline block wired to approved DTO fixtures', - 'Audit feed block wired to approved DTO fixtures', - 'Environment summary placeholder kept mock-only', -] +import { + auditEntries, + environmentSummaryItems, + timelineEntries, +} from './fixtures/dashboard' function App() { return (
-
-

Mission Control / devopspanel

-

DevOps orchestration admin panel

+
+
+

Mission Control / devopspanel

+

Operations shell

+

- Baseline frontend scaffold for the first implementation slice. This commit - intentionally sets the stack and shell contract before live integrations. + Local mock slice for shell structure only. Timeline, audit feed, and + environment summary are rendered from typed fixtures with no live API wiring.

-
+ -
+
-

Baseline stack

-

React + TypeScript + Vite

+ Stack + React + TypeScript + Vite
-

Current state

-

Scaffold only, no runtime data wiring yet

+ Data mode + Local typed fixtures
-

Guardrail

-

Environment summary remains mock-only until live worker-visible route exists

+ Guardrail + Environment summary is mock-only
-
-

Next development slice

-
    - {nextSlice.map((item) => ( -
  • {item}
  • - ))} -
+
+
+
+
+

Timeline

+

Planned operator moments

+
+ {timelineEntries.length} items +
+ +
+ {timelineEntries.map((entry) => ( +
+

{entry.windowLabel}

+

{entry.title}

+

{entry.detail}

+
+ ))} +
+
+ +
+
+
+

Audit Feed

+

Recent shell events

+
+
+ +
+ {auditEntries.map((entry) => ( +
+

{entry.occurredAt}

+

+ {entry.actor} {entry.action} +

+

{entry.target}

+

{entry.summary}

+
+ ))} +
+
+ +
+
+
+

Environment Summary

+

Mock-only environment snapshot

+
+ mock only +
+ +
+ {environmentSummaryItems.map((item) => ( +
+

{item.label}

+

{item.value}

+

{item.note}

+
+ ))} +
+
) diff --git a/src/fixtures/dashboard.ts b/src/fixtures/dashboard.ts new file mode 100644 index 0000000..c6fa143 --- /dev/null +++ b/src/fixtures/dashboard.ts @@ -0,0 +1,77 @@ +import type { + AuditEntry, + EnvironmentSummaryItem, + TimelineEntry, +} from '../types/dashboard' + +export const timelineEntries: TimelineEntry[] = [ + { + id: 'tl-1', + title: 'Morning maintenance window', + windowLabel: '08:30-09:00 UTC', + detail: 'Patch validation and worker drift review staged for operator sign-off.', + tone: 'stable', + }, + { + id: 'tl-2', + title: 'Release rehearsal', + windowLabel: '11:15-12:00 UTC', + detail: 'Dry-run checklist for deployment sequencing and rollback notes.', + tone: 'active', + }, + { + id: 'tl-3', + title: 'Credential rotation follow-up', + windowLabel: '15:00-15:30 UTC', + detail: 'Pending verification items remain isolated to the mock shell.', + tone: 'attention', + }, +] + +export const auditEntries: AuditEntry[] = [ + { + id: 'af-1', + actor: 'ops.release', + action: 'approved', + target: 'frontend-shell-slice', + occurredAt: '10:42 UTC', + summary: 'Scope held to local fixtures and presentational shell changes only.', + }, + { + id: 'af-2', + actor: 'runtime.guardrail', + action: 'flagged', + target: 'environment-summary', + occurredAt: '09:18 UTC', + summary: 'Section copy must stay explicitly mock-only until backend visibility exists.', + }, + { + id: 'af-3', + actor: 'design.review', + action: 'recorded', + target: 'layout-baseline', + occurredAt: '08:05 UTC', + summary: 'Three-panel shell accepted as the next UI slice for iteration.', + }, +] + +export const environmentSummaryItems: EnvironmentSummaryItem[] = [ + { + id: 'env-1', + label: 'Mock worker coverage', + value: '12 workers', + note: 'Mock dataset only. Not sourced from runtime inventory.', + }, + { + id: 'env-2', + label: 'Mock alert posture', + value: '2 advisory notices', + note: 'Mock status text for layout validation only.', + }, + { + id: 'env-3', + label: 'Mock change queue', + value: '5 queued actions', + note: 'Mock placeholder until live API contracts are introduced.', + }, +] diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts new file mode 100644 index 0000000..29dfb27 --- /dev/null +++ b/src/types/dashboard.ts @@ -0,0 +1,25 @@ +export type TimelineTone = 'stable' | 'active' | 'attention' + +export interface TimelineEntry { + id: string + title: string + windowLabel: string + detail: string + tone: TimelineTone +} + +export interface AuditEntry { + id: string + actor: string + action: string + target: string + occurredAt: string + summary: string +} + +export interface EnvironmentSummaryItem { + id: string + label: string + value: string + note: string +}