Apply Stitch media operations redesign
Magent CI/CD / verify (push) Successful in 10m46s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Successful in 14s

This commit is contained in:
2026-09-01 22:58:48 +12:00
parent 32bfa20ab7
commit f65e1b114c
6 changed files with 1003 additions and 16 deletions
+2
View File
@@ -7,6 +7,7 @@ import HeaderActions from './ui/HeaderActions'
import HeaderIdentity from './ui/HeaderIdentity'
import SiteStatus from './ui/SiteStatus'
import UserViewBanner from './ui/UserViewBanner'
import WorkspaceNavigation from './ui/WorkspaceNavigation'
export const metadata = {
title: 'Magent',
@@ -37,6 +38,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
<HeaderActions />
</div>
</header>
<WorkspaceNavigation />
<UserViewBanner />
<SiteStatus />
{children}
@@ -306,6 +306,15 @@ export default function NewRequestClient() {
}
const selectedPoster = artworkUrl(selected?.posterPath, 'w185')
const currentFlowStep = success
? 5
: selected
? 4
: searchAttempted
? 3
: mediaType
? 2
: 1
return (
<main className="card request-portal-page">
@@ -322,6 +331,18 @@ export default function NewRequestClient() {
</div>
</header>
<ol className="request-master-stepper" aria-label="New request progress">
{['Type', 'Search', 'Select', 'Config', 'Submit'].map((label, index) => {
const step = index + 1
return (
<li key={label} className={step === currentFlowStep ? 'is-active' : step < currentFlowStep ? 'is-complete' : ''}>
<span>{step < currentFlowStep ? '✓' : step}</span>
<strong>{label}</strong>
</li>
)
})}
</ol>
{error && <div className="error-banner request-flow-alert">{error}</div>}
{success && <div className="status-banner request-flow-alert">{success}</div>}
+853 -1
View File
@@ -1,4 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600;700&display=swap');
:root,
[data-theme='dark'],
@@ -46,6 +46,42 @@
--error-ink: #ffd6d6;
}
/* Stitch production handoff: Media-Ops master system */
:root,
[data-theme='dark'],
[data-theme='light'] {
--ops-bg: #131315;
--ops-bg-2: #0e0e10;
--ops-panel: #1c1b1d;
--ops-panel-2: #201f21;
--ops-panel-3: #2a2a2c;
--ops-line: #46464d;
--ops-line-soft: rgba(145, 144, 152, 0.24);
--ops-text: #e5e1e4;
--ops-muted: #c7c5ce;
--ops-faint: #919098;
--ops-primary: #090d25;
--ops-primary-2: #c2c4e5;
--ops-cyan: #22d3ee;
--ops-cyan-2: #3b82f6;
--ops-coral: #ffb5a0;
--ops-green: #14b8a6;
--ops-red: #ef4444;
--ops-warn: #f59e0b;
--ops-radius-sm: 4px;
--ops-radius: 8px;
--ops-radius-lg: 12px;
--ink: var(--ops-text);
--ink-muted: var(--ops-muted);
--paper: var(--ops-bg);
--paper-strong: var(--ops-panel);
--border: var(--ops-line-soft);
--panel: var(--ops-panel);
--panel-soft: rgba(255, 255, 255, 0.035);
--input-bg: #0e0e10;
--input-ink: var(--ops-text);
}
* {
letter-spacing: 0 !important;
}
@@ -2727,6 +2763,772 @@ button:disabled,
.request-complete-actions { display: grid; }
}
/* ========================================================================== */
/* Stitch master layout and component alignment */
/* ========================================================================== */
html,
body {
background: var(--ops-bg);
}
body {
background-image: none;
font-family: Inter, "Segoe UI", Arial, sans-serif;
font-size: 14px;
line-height: 1.45;
}
h1,
h2,
h3,
h4,
.brand {
font-family: "DM Sans", "Segoe UI", sans-serif;
}
button,
input,
select,
textarea {
font-family: Inter, "Segoe UI", sans-serif;
}
.page {
display: block;
width: 100%;
max-width: none;
min-height: 100vh;
margin: 0;
padding: 64px 0 88px;
}
.header {
position: fixed;
inset: 0 0 auto;
z-index: 1000;
display: grid;
grid-template-columns: minmax(220px, 1fr) minmax(480px, auto) minmax(220px, 1fr);
grid-template-rows: 64px;
align-items: center;
width: 100%;
height: 64px;
margin: 0;
padding: 0 24px;
border: 0;
border-bottom: 1px solid var(--ops-line);
background: rgba(14, 14, 16, 0.96);
backdrop-filter: blur(16px);
}
.header-left,
.header-right,
.header-nav {
grid-row: 1;
margin: 0;
}
.header-left { grid-column: 1; }
.header-nav { grid-column: 2; }
.header-right { grid-column: 3; justify-self: end; }
.brand-link { gap: 10px; }
.brand-logo--header { width: 32px; height: 32px; }
.brand {
color: var(--ops-primary-2);
font-size: 1.7rem;
font-weight: 700;
text-transform: none;
text-shadow: none;
}
.tagline { display: none; }
.beta-chip {
min-height: 28px;
padding: 4px 10px;
border-color: var(--ops-line);
background: var(--ops-panel-2);
color: var(--ops-primary-2);
font-size: 0.65rem;
}
.header-actions { gap: 4px; flex-wrap: nowrap; }
.header-actions a {
min-height: 64px;
padding: 0 12px;
border: 0;
border-bottom: 2px solid transparent;
border-radius: 0;
background: transparent;
color: var(--ops-muted);
font-family: "JetBrains Mono", Consolas, monospace;
font-size: 0.76rem;
}
.header-actions a span { color: var(--ops-faint); font-size: 0.68rem; }
.header-actions a:hover,
.header-actions a.is-active {
border-color: var(--ops-primary-2);
background: transparent;
color: var(--ops-primary-2);
}
.header-actions a.is-active span { color: var(--ops-primary-2); }
.user-view-toggle {
min-height: 32px;
border-color: var(--ops-line);
background: var(--ops-primary);
color: var(--ops-primary-2);
}
.avatar-button {
width: 34px;
height: 34px;
border-radius: 50%;
background: var(--ops-panel-3);
}
.signed-in-dropdown { background: #1c1b1d; }
.workspace-sidebar {
position: fixed;
inset: 64px auto 0 0;
z-index: 90;
display: flex;
width: 280px;
padding: 24px 16px 18px;
flex-direction: column;
gap: 22px;
border-right: 1px solid var(--ops-line);
background: #201f21;
}
.workspace-sidebar-identity {
display: grid;
grid-template-columns: 42px minmax(0, 1fr);
align-items: center;
gap: 12px;
}
.workspace-sidebar-logo {
width: 42px;
height: 42px;
border-radius: 8px;
}
.workspace-sidebar-identity > div { display: grid; gap: 2px; min-width: 0; }
.workspace-sidebar-identity strong { font-family: "DM Sans", sans-serif; font-size: 1.05rem; }
.workspace-sidebar-identity span {
overflow: hidden;
color: var(--ops-muted);
font-family: "JetBrains Mono", monospace;
font-size: 0.64rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.workspace-new-request,
.admin-new-request {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
min-height: 48px;
border: 1px solid #2f3455;
border-radius: 8px;
background: var(--ops-primary);
color: var(--ops-primary-2);
font-family: "DM Sans", sans-serif;
font-size: 1rem;
font-weight: 600;
text-decoration: none;
}
.workspace-new-request span,
.admin-new-request span { font-size: 1.4rem; font-weight: 400; }
.workspace-sidebar > nav { display: grid; gap: 5px; }
.workspace-sidebar > nav a {
display: flex;
align-items: center;
gap: 13px;
min-height: 45px;
padding: 9px 13px;
border: 1px solid transparent;
border-radius: 8px;
color: var(--ops-muted);
font-family: "JetBrains Mono", monospace;
font-size: 0.78rem;
text-decoration: none;
}
.workspace-sidebar svg,
.workspace-mobile-nav svg {
width: 21px;
height: 21px;
fill: none;
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 1.8;
}
.workspace-sidebar > nav a:hover,
.workspace-sidebar > nav a.is-active {
border-color: rgba(194, 196, 229, 0.12);
background: #454652;
color: #e5e1e4;
}
.workspace-sidebar-footer {
display: grid;
gap: 4px;
margin-top: auto;
padding-top: 16px;
border-top: 1px solid var(--ops-line);
}
.workspace-sidebar-footer a {
padding: 9px 13px;
color: var(--ops-muted);
font-family: "JetBrains Mono", monospace;
font-size: 0.75rem;
text-decoration: none;
}
.workspace-mobile-nav { display: none; }
.page:has(.workspace-sidebar) > main,
.page:has(.workspace-sidebar) > .user-view-banner,
.page:has(.workspace-sidebar) > .site-banner {
width: auto;
margin-right: 24px;
margin-left: 304px;
}
.page:has(.workspace-sidebar) > main {
max-width: 1600px;
margin-top: 0;
padding: 32px 0;
}
.page > .user-view-banner,
.page > .site-banner {
margin-top: 16px;
}
.card,
.admin-card,
.admin-panel,
.main-panel,
.summary-card,
.portal-overview-card,
.status-box,
.timeline-card,
.modal-card,
.admin-zone,
.admin-rail-card,
.users-summary-card,
.stat-card {
border-color: var(--ops-line) !important;
background: var(--ops-panel) !important;
}
.page:has(.workspace-sidebar) > main.card,
.admin-card {
border: 0 !important;
background: transparent !important;
}
button:not(.avatar-button),
.button {
border-radius: 8px;
}
button:not(.ghost-button):not(.avatar-button):not(.issue-category-card):not(.request-type-card):not(.request-result-card):not(.recent-card),
.settings-action-button {
border-color: #30365d;
background: var(--ops-primary);
color: var(--ops-primary-2);
box-shadow: none;
}
.ghost-button,
button.ghost-button {
border-color: var(--ops-line);
background: var(--ops-panel-3);
color: var(--ops-text);
}
input,
select,
textarea {
border-color: var(--ops-line) !important;
background: var(--ops-bg-2) !important;
color: var(--ops-text) !important;
}
input:focus,
select:focus,
textarea:focus {
border-color: var(--ops-primary-2) !important;
box-shadow: 0 0 0 1px rgba(194, 196, 229, 0.28) !important;
}
.section-kicker,
.eyebrow,
.request-overview-label,
.request-stage-number,
.request-stage-state,
.request-flow-heading > div > span,
.invite-flow-number,
.admin-nav-title {
font-family: "JetBrains Mono", monospace !important;
}
/* My Requests masters */
.home-page { max-width: 1400px !important; gap: 22px; }
.home-command {
grid-template-columns: minmax(0, 1fr) minmax(380px, 0.8fr);
padding: 0 0 22px;
border: 0;
border-bottom: 1px solid var(--ops-line);
border-radius: 0;
background: transparent;
}
.home-command-copy h1 { font-size: 2.25rem; letter-spacing: -0.02em !important; }
.home-command-copy p { font-size: 0.9rem; }
.home-search-row input,
.home-search-row button { min-height: 44px; }
.home-metric-strip { display: none; }
.home-recent {
padding: 0;
border: 0;
background: transparent;
}
.home-recent-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.home-recent-grid .recent-card {
display: grid;
grid-template-columns: 82px minmax(0, 1fr);
grid-template-rows: minmax(118px, 1fr) auto;
align-items: stretch;
min-height: 250px;
padding: 12px;
overflow: hidden;
border-color: var(--ops-line) !important;
border-radius: 12px !important;
background: var(--ops-panel-2) !important;
}
.home-recent-grid .recent-poster {
grid-column: 1;
grid-row: 1;
width: 72px;
height: 108px;
align-self: start;
border-radius: 7px !important;
object-fit: cover;
}
.home-recent-grid .recent-info {
display: flex;
grid-column: 2;
grid-row: 1;
flex-direction: column;
gap: 9px;
min-width: 0;
padding: 7px 3px;
}
.home-recent-grid .recent-title { font-family: "DM Sans", sans-serif; font-size: 1.08rem; }
.home-recent-grid .recent-meta {
display: flex;
align-items: center;
gap: 7px;
color: var(--ops-muted) !important;
font-family: "JetBrains Mono", monospace;
font-size: 0.68rem;
}
.home-recent-grid .recent-meta::before {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ops-faint);
content: "";
}
.home-recent-grid .is-processing .recent-meta::before { background: var(--ops-primary-2); }
.home-recent-grid .is-attention .recent-meta::before { background: var(--ops-coral); }
.home-recent-grid .is-ready .recent-meta::before { background: var(--ops-green); }
.home-recent-grid .recent-open-cue {
grid-column: 1 / -1;
grid-row: 2;
align-self: end;
padding: 9px;
border: 1px solid var(--ops-line);
border-radius: 6px;
background: var(--ops-primary);
color: var(--ops-primary-2);
text-align: center;
}
.request-filter-chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 18px 0;
}
.request-filter-chips button {
min-height: 32px;
padding: 5px 15px;
border: 1px solid var(--ops-line) !important;
border-radius: 999px !important;
background: var(--ops-panel-3) !important;
color: var(--ops-muted) !important;
font-size: 0.74rem;
}
.request-filter-chips button.is-active {
border-color: #343b71 !important;
background: var(--ops-primary) !important;
color: var(--ops-primary-2) !important;
}
.request-filter-chips i {
display: inline-block;
width: 6px;
height: 6px;
margin-right: 6px;
border-radius: 50%;
background: var(--ops-coral);
}
/* New Request master */
.request-portal-page { max-width: 1180px !important; }
.request-portal-hero {
min-height: 0;
padding: 0 0 18px;
border-bottom: 1px solid var(--ops-line);
background: transparent;
}
.request-portal-hero h1 { font-size: 2rem; }
.request-portal-route { background: var(--ops-panel-2); }
.request-master-stepper {
position: relative;
display: grid;
grid-template-columns: repeat(5, 1fr);
margin: 4px 0 16px;
padding: 0;
list-style: none;
}
.request-master-stepper::before {
position: absolute;
top: 15px;
right: 10%;
left: 10%;
height: 1px;
background: var(--ops-line);
content: "";
}
.request-master-stepper li {
position: relative;
z-index: 1;
display: grid;
justify-items: center;
gap: 6px;
color: var(--ops-faint);
font-family: "JetBrains Mono", monospace;
font-size: 0.68rem;
}
.request-master-stepper li > span {
display: grid;
place-items: center;
width: 30px;
height: 30px;
border: 4px solid var(--ops-bg);
border-radius: 50%;
background: var(--ops-panel-3);
}
.request-master-stepper li.is-active,
.request-master-stepper li.is-complete { color: var(--ops-primary-2); }
.request-master-stepper li.is-active > span,
.request-master-stepper li.is-complete > span {
background: var(--ops-primary-2);
color: #2b2f48;
box-shadow: 0 0 12px rgba(194, 196, 229, 0.22);
}
.request-flow-stage {
border-color: var(--ops-line);
background: var(--ops-panel);
}
.request-type-card,
.request-result-card,
.request-selection-summary,
.request-existing-state,
.request-submit-bar {
border-color: var(--ops-line) !important;
background: var(--ops-panel-2) !important;
}
.request-type-card { min-height: 220px; }
.request-type-card.is-selected,
.request-result-card.is-selected {
border-color: var(--ops-primary-2) !important;
background: rgba(194, 196, 229, 0.055) !important;
box-shadow: 0 0 22px rgba(194, 196, 229, 0.07);
}
/* Request Details master */
.request-detail-page { max-width: 1500px !important; gap: 24px; }
.request-detail-page .request-header {
min-height: 180px;
padding: 24px;
border: 1px solid var(--ops-line);
border-radius: 12px;
background: var(--ops-panel);
}
.request-detail-page .request-poster { width: 92px; height: 138px; object-fit: cover; }
.request-detail-page .request-header h1 { font-size: clamp(2rem, 4vw, 3rem); }
.request-overview,
.request-journey,
.request-repair-activity,
.request-advanced {
border-color: var(--ops-line) !important;
background: var(--ops-panel) !important;
}
.request-overview { border-radius: 12px; overflow: hidden; }
.request-stage-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; }
.request-stage {
min-height: 180px;
border-color: var(--ops-line) !important;
border-radius: 10px;
background: var(--ops-panel-2) !important;
}
.request-stage.stage-complete {
border-top: 3px solid var(--ops-green) !important;
background: linear-gradient(180deg, rgba(20, 184, 166, 0.07), var(--ops-panel-2) 38%) !important;
}
.request-stage.stage-active {
border-color: rgba(194, 196, 229, 0.58) !important;
border-top: 3px solid var(--ops-primary-2) !important;
background: linear-gradient(180deg, rgba(194, 196, 229, 0.08), var(--ops-panel-2) 40%) !important;
box-shadow: 0 0 18px rgba(194, 196, 229, 0.08) !important;
}
.request-stage.stage-attention { border-top: 3px solid var(--ops-warn) !important; }
.request-live-indicator { border-color: rgba(20, 184, 166, 0.4); color: #5eead4; }
.request-meter-track { background: #353437; }
.request-meter-track > span { background: var(--ops-primary-2); }
.request-release-modal { background: #201f21; }
/* Issues and Invites masters */
.portal-page,
.profile-invites-section { max-width: 1500px !important; }
.issue-portal-page > .issue-portal-hero {
min-height: 0;
padding: 0 0 20px;
border: 0;
border-bottom: 1px solid var(--ops-line);
border-radius: 0;
background: transparent;
}
.issue-flow,
.issue-reports-column,
.profile-section,
.invite-flow-step,
.profile-invites-list {
border-color: var(--ops-line) !important;
background: var(--ops-panel) !important;
}
.issue-category-card {
border-color: var(--ops-line-soft);
background: var(--ops-panel-2);
}
.issue-category-card.is-selected {
border-color: var(--ops-primary-2);
background: rgba(194, 196, 229, 0.055);
box-shadow: 0 0 16px rgba(194, 196, 229, 0.06);
}
.issue-category-marker { border-color: var(--ops-line); color: var(--ops-primary-2); }
.issue-detail-modal { background: #201f21; }
.issue-modal-backdrop { background: rgba(5, 5, 7, 0.76); backdrop-filter: blur(6px); }
.invite-flow-route { border-color: var(--ops-line); background: var(--ops-bg-2); }
.invite-flow-route li.is-active,
.invite-flow-route li.is-complete { color: var(--ops-primary-2); }
.invite-flow-step.is-active {
border-color: rgba(194, 196, 229, 0.55) !important;
box-shadow: 0 0 18px rgba(194, 196, 229, 0.06);
}
.invite-delivery-grid > button,
.invite-policy-note,
.invite-delivery-summary,
.invite-created-card,
.admin-list-item {
border-color: var(--ops-line) !important;
background: var(--ops-panel-2) !important;
}
/* Configuration master: each concern is its own service/operational region */
.admin-shell {
width: 100%;
max-width: none;
grid-template-columns: 280px minmax(0, 1fr);
gap: 0;
align-items: stretch;
}
.admin-shell.admin-shell--with-rail {
grid-template-columns: 280px minmax(0, 1fr) 320px;
gap: 0;
}
.admin-shell-nav {
position: sticky;
top: 64px;
align-self: start;
height: calc(100vh - 64px);
}
.admin-sidebar {
height: 100%;
padding: 24px 16px;
overflow-y: auto;
border: 0;
border-right: 1px solid var(--ops-line);
border-radius: 0;
background: #201f21;
}
.admin-sidebar-identity { display: grid; gap: 4px; }
.admin-sidebar-identity strong { color: var(--ops-primary-2); font-family: "DM Sans", sans-serif; font-size: 1.35rem; }
.admin-sidebar-identity span { color: var(--ops-muted); font-family: "JetBrains Mono", monospace; font-size: 0.65rem; }
.admin-sidebar-identity i {
display: inline-block;
width: 7px;
height: 7px;
margin-right: 7px;
border-radius: 50%;
background: var(--ops-green);
}
.admin-new-request { margin: 12px 0 4px; }
.admin-nav-title {
color: var(--ops-faint);
font-size: 0.62rem;
letter-spacing: 0.08em !important;
}
.admin-nav-links a { min-height: 34px; padding: 7px 10px; font-family: "JetBrains Mono", monospace; font-size: 0.7rem; }
.admin-nav-links a:hover,
.admin-nav-links a.is-active { border-color: transparent; background: #454652; color: #e5e1e4; }
.admin-card { padding: 32px 40px 60px; }
.admin-header { align-items: end; }
.admin-header h1 { color: var(--ops-primary-2); font-size: 2rem; }
.admin-shell-rail {
top: 80px;
align-self: start;
padding: 24px 20px 24px 0;
}
.service-status-panel {
border-color: var(--ops-line) !important;
background: var(--ops-panel-3) !important;
}
.service-status-grid > div,
.config-subsection-nav,
.config-subsection {
border-color: var(--ops-line) !important;
background: var(--ops-panel) !important;
}
.config-subsection-nav {
position: sticky;
top: 72px;
z-index: 8;
backdrop-filter: blur(12px);
}
.config-subsection-nav a { border-color: var(--ops-line); background: var(--ops-panel-2); }
.config-subsection-nav a small { color: var(--ops-primary-2); }
.config-subsection {
padding: 24px !important;
border-radius: 12px !important;
}
.config-subsection .section-header {
padding-bottom: 14px;
border-bottom: 1px solid var(--ops-line);
}
.config-subsection .section-header h2 { font-size: 1.2rem; }
.admin-form .admin-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.admin-form .admin-grid > label {
min-height: 142px;
padding: 16px;
border-color: var(--ops-line-soft);
background: var(--ops-panel-2);
}
.admin-form .admin-grid > label.field-span-full { grid-column: 1 / -1; }
.admin-form .label-row > span:first-child { font-family: "JetBrains Mono", monospace; font-size: 0.72rem; }
.admin-form .admin-grid label[data-helper]::after { color: var(--ops-muted); font-family: Inter, sans-serif; }
.settings-section-actions { margin-top: 4px; padding-top: 18px; }
@media (max-width: 1250px) {
.header { grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr); }
.header-actions a { padding-inline: 8px; font-size: 0.7rem; }
.workspace-sidebar { width: 240px; }
.page:has(.workspace-sidebar) > main,
.page:has(.workspace-sidebar) > .user-view-banner,
.page:has(.workspace-sidebar) > .site-banner { margin-left: 264px; }
.request-stage-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.admin-shell,
.admin-shell.admin-shell--with-rail { grid-template-columns: 240px minmax(0, 1fr); }
.admin-shell-rail { display: none; }
}
@media (max-width: 980px) {
.header { grid-template-columns: minmax(0, 1fr) auto; padding-inline: 16px; }
.header-left { grid-column: 1; }
.header-right { grid-column: 2; }
.header-nav { display: none; }
.workspace-sidebar { display: none; }
.workspace-mobile-nav {
position: fixed;
inset: auto 0 0;
z-index: 1000;
display: flex;
min-height: 70px;
align-items: center;
justify-content: space-around;
padding: 7px 10px max(7px, env(safe-area-inset-bottom));
border-top: 1px solid var(--ops-line);
border-radius: 12px 12px 0 0;
background: #353437;
}
.workspace-mobile-nav a {
display: grid;
min-width: 58px;
justify-items: center;
gap: 3px;
padding: 7px 8px;
border-radius: 999px;
color: var(--ops-muted);
font-family: "JetBrains Mono", monospace;
font-size: 0.58rem;
text-decoration: none;
}
.workspace-mobile-nav a.is-active { background: var(--ops-primary); color: var(--ops-primary-2); }
.workspace-mobile-nav svg { width: 22px; height: 22px; }
.page:has(.workspace-sidebar) > main,
.page:has(.workspace-sidebar) > .user-view-banner,
.page:has(.workspace-sidebar) > .site-banner {
width: auto;
margin-right: 14px;
margin-left: 14px;
}
.page:has(.workspace-sidebar) > main { padding-top: 18px; padding-bottom: 28px; }
.home-command { grid-template-columns: 1fr; }
.home-recent-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.admin-shell,
.admin-shell.admin-shell--with-rail { display: block; }
.admin-shell-nav { position: static; height: auto; }
.admin-sidebar { height: auto; border-right: 0; border-bottom: 1px solid var(--ops-line); }
.admin-sidebar-identity,
.admin-new-request { display: none; }
.admin-nav-group { min-width: 200px; }
.admin-sidebar { display: flex; overflow-x: auto; }
.admin-card { padding: 24px 18px 90px; }
}
@media (max-width: 680px) {
.page { padding-bottom: 78px; }
.header { height: 58px; grid-template-rows: 58px; }
.page { padding-top: 58px; }
.brand-logo--header { width: 28px; height: 28px; }
.brand { font-size: 1.35rem; }
.beta-chip { display: none; }
.user-view-toggle { max-width: 96px; overflow: hidden; padding-inline: 7px; font-size: 0.56rem; white-space: nowrap; }
.home-command { gap: 16px; }
.home-command-copy h1 { font-size: 1.75rem; }
.home-search-row { grid-template-columns: 1fr auto; }
.home-search-row button { padding-inline: 12px; }
.home-section-heading { align-items: flex-start; }
.home-recent-grid { grid-template-columns: 1fr; }
.home-recent-grid .recent-card {
grid-template-columns: 78px minmax(0, 1fr);
grid-template-rows: auto;
min-height: 128px;
}
.home-recent-grid .recent-open-cue { display: none; }
.request-filter-chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; }
.request-filter-chips button { flex: 0 0 auto; }
.request-master-stepper li strong { font-size: 0.58rem; }
.request-master-stepper li > span { width: 27px; height: 27px; }
.request-stage-grid { grid-template-columns: 1fr; }
.request-stage { min-height: 0; }
.admin-form .admin-grid { grid-template-columns: 1fr; }
.admin-form .admin-grid > label.field-span-full { grid-column: auto; }
.config-subsection { padding: 18px !important; }
.config-subsection-nav { top: 62px; }
}
/* Guided issue reporting */
.issue-portal-page {
display: grid;
@@ -4010,3 +4812,53 @@ button:disabled,
.issue-pipeline-card li { grid-template-columns: 25px minmax(0, 1fr); align-items: center; justify-items: start; text-align: left; }
.issue-pipeline-card li::before { top: -9px; right: auto; left: 12px; width: 2px; height: 10px; }
}
/* Final screen-specific alignment: these rules intentionally follow the
legacy Issues styles so the Stitch tokens remain authoritative. */
.issue-flow,
.issue-reports-column,
.issue-detail-modal.is-open,
.issue-pipeline-card,
.issue-resolution-card,
.issue-media-search,
.media-status-card {
border-color: var(--ops-line) !important;
background: var(--ops-panel) !important;
}
.issue-category-card,
.issue-choice-card,
.issue-season-card,
.issue-episode-card {
border-color: var(--ops-line-soft);
background: var(--ops-panel-2);
}
.issue-category-card:hover,
.issue-choice-card:hover,
.issue-season-card:hover,
.issue-episode-card:hover {
border-color: rgba(194, 196, 229, 0.48);
}
.issue-category-card.is-selected,
.issue-choice-card.is-selected,
.issue-season-card.is-selected,
.issue-episode-card.is-selected {
border-color: rgba(194, 196, 229, 0.7);
background: rgba(194, 196, 229, 0.07);
box-shadow: 0 0 18px rgba(194, 196, 229, 0.06);
}
.issue-detail-modal.is-open {
top: max(80px, 5vh);
bottom: 4vh;
max-height: calc(96vh - 80px);
}
@media (max-width: 640px) {
.issue-detail-modal.is-open {
inset: 74px 10px 10px;
max-height: calc(100vh - 84px);
}
}
+30 -14
View File
@@ -16,6 +16,7 @@ const normalizeRecentResults = (items: any[]) =>
id,
title: !rawTitle || placeholder ? `Request #${id}` : rawTitle,
year: item.year,
type: item.type,
statusLabel: item.statusLabel,
artwork: item.artwork,
createdAt: item.createdAt ?? null,
@@ -41,8 +42,9 @@ export default function HomePage() {
id: number
title: string
year?: number
type?: string
statusLabel?: string
artwork?: { poster_url?: string }
artwork?: { poster_url?: string; backdrop_url?: string }
createdAt?: string | null
}[]
>([])
@@ -248,15 +250,22 @@ export default function HomePage() {
return label.includes('ready') || label.includes('available')
}).length
const requestCardState = (value?: string) => {
const label = String(value ?? '').toLowerCase()
if (label.includes('ready') || label.includes('available')) return { key: 'ready', label: value || 'Ready', progress: 100 }
if (label.includes('declined') || label.includes('failed') || label.includes('error')) return { key: 'attention', label: value || 'Needs attention', progress: 12 }
if (label.includes('partial')) return { key: 'attention', label: value || 'Partially ready', progress: 65 }
if (label.includes('working') || label.includes('progress') || label.includes('download')) return { key: 'processing', label: value || 'In progress', progress: 58 }
return { key: 'waiting', label: value || 'Waiting', progress: 4 }
}
return (
<main className="card home-page">
<section className="home-command">
<div className="home-command-copy">
<span className="section-kicker">Request lookup</span>
<span className="section-kicker">Media operations</span>
<h1>My requests</h1>
<p>
Enter a title and year, or jump straight to a request using its request number.
</p>
<p>Manage and track your media processing queue.</p>
</div>
<form onSubmit={submit} className="home-search">
<label htmlFor="request-search">Title, year, or request number</label>
@@ -334,17 +343,24 @@ export default function HomePage() {
<option value={180}>180 days</option>
</select>
</label>
<label className="recent-filter">
<span>Stage</span>
<select value={recentStage} onChange={(event) => setRecentStage(event.target.value)}>
{REQUEST_STAGE_OPTIONS.map((option) => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</select>
</label>
</div>
)}
</div>
{authReady && (
<div className="request-filter-chips" aria-label="Filter requests by stage">
{REQUEST_STAGE_OPTIONS.filter((option) => ['all', 'pending', 'in_progress', 'working', 'ready'].includes(option.value)).map((option) => (
<button
type="button"
key={option.value}
className={recentStage === option.value ? 'is-active' : undefined}
onClick={() => setRecentStage(option.value)}
>
{option.value === 'working' ? <i aria-hidden="true" /> : null}
{option.value === 'all' ? 'All' : option.label}
</button>
))}
</div>
)}
<div className="recent-grid home-recent-grid">
{recentLoading ? (
<div className="loading-center">
@@ -364,7 +380,7 @@ export default function HomePage() {
key={item.id}
type="button"
onClick={() => router.push(`/requests/${item.id}`)}
className="recent-card"
className={`recent-card is-${requestCardState(item.statusLabel).key}`}
>
{item.artwork?.poster_url ? (
<img
+5 -1
View File
@@ -56,7 +56,11 @@ export default function AdminSidebar() {
const pathname = usePathname()
return (
<nav className="admin-sidebar">
<div className="admin-sidebar-title">Settings</div>
<div className="admin-sidebar-identity">
<strong>Magent Admin</strong>
<span><i aria-hidden="true" />Configuration workspace</span>
</div>
<a className="admin-new-request" href="/new-requests"><span>+</span> New request</a>
{NAV_GROUPS.map((group) => (
<div key={group.title} className="admin-nav-group">
<span className="admin-nav-title">{group.title}</span>
+92
View File
@@ -0,0 +1,92 @@
'use client'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'
import { authFetch, getApiBase, getToken } from '../lib/auth'
import BrandingLogo from './BrandingLogo'
type NavigationItem = {
href: string
label: string
shortLabel: string
icon: 'dashboard' | 'media' | 'issues' | 'invites' | 'settings'
adminOnly?: boolean
match: (path: string) => boolean
}
const NAVIGATION: NavigationItem[] = [
{ href: '/', label: 'My Requests', shortLabel: 'Requests', icon: 'dashboard', match: (path) => path === '/' || path.startsWith('/requests/') },
{ href: '/new-requests', label: 'New Request', shortLabel: 'New', icon: 'media', match: (path) => path === '/new-requests' },
{ href: '/portal/issues', label: 'Issues', shortLabel: 'Issues', icon: 'issues', match: (path) => path.startsWith('/portal/issues') },
{ href: '/profile/invites', label: 'Invites', shortLabel: 'Invites', icon: 'invites', match: (path) => path.startsWith('/profile/invites') },
{ href: '/admin', label: 'Configuration', shortLabel: 'Config', icon: 'settings', adminOnly: true, match: (path) => path.startsWith('/admin') },
]
const HIDDEN_ROUTES = ['/login', '/signup', '/forgot-password', '/reset-password', '/how-it-works']
function NavigationIcon({ name }: { name: NavigationItem['icon'] }) {
const paths: Record<NavigationItem['icon'], React.ReactNode> = {
dashboard: <><rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" /></>,
media: <><rect x="3" y="5" width="18" height="15" rx="2" /><path d="m8 3 2 4m4-4 2 4M3 10h18" /><path d="m10 13 5 3-5 3z" /></>,
issues: <><path d="M12 3 2.8 19h18.4L12 3Z" /><path d="M12 9v4m0 3h.01" /></>,
invites: <><circle cx="9" cy="8" r="3" /><path d="M3.5 20v-2.2A4.8 4.8 0 0 1 8.3 13h1.4a4.8 4.8 0 0 1 3.8 1.9M17 8v6m-3-3h6" /></>,
settings: <><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1a1.7 1.7 0 0 0 1.9.3A1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z" /></>,
}
return <svg viewBox="0 0 24 24" aria-hidden="true">{paths[name]}</svg>
}
export default function WorkspaceNavigation() {
const pathname = usePathname()
const [role, setRole] = useState<string | null>(null)
const [ready, setReady] = useState(false)
useEffect(() => {
const token = getToken()
if (!token) {
setReady(true)
return
}
authFetch(`${getApiBase()}/auth/me`)
.then(async (response) => {
if (response.ok) setRole((await response.json())?.role ?? 'user')
})
.catch(() => undefined)
.finally(() => setReady(true))
}, [])
if (!ready || !getToken() || HIDDEN_ROUTES.some((route) => pathname.startsWith(route)) || pathname.startsWith('/admin')) {
return null
}
const items = NAVIGATION.filter((item) => !item.adminOnly || role === 'admin')
return (
<>
<aside className="workspace-sidebar" aria-label="Workspace navigation">
<div className="workspace-sidebar-identity">
<BrandingLogo className="workspace-sidebar-logo" />
<div><strong>{role === 'admin' ? 'Magent Admin' : 'Magent'}</strong><span>Media operations workspace</span></div>
</div>
<a className="workspace-new-request" href="/new-requests"><span>+</span> New request</a>
<nav>
{items.map((item) => (
<a key={item.href} href={item.href} className={item.match(pathname) ? 'is-active' : undefined}>
<NavigationIcon name={item.icon} /><span>{item.label}</span>
</a>
))}
</nav>
<div className="workspace-sidebar-footer">
<a href="/feedback">Support</a>
<a href={role === 'admin' ? '/admin' : '/profile'}>Settings</a>
</div>
</aside>
<nav className="workspace-mobile-nav" aria-label="Mobile navigation">
{items.slice(0, 5).map((item) => (
<a key={item.href} href={item.href} className={item.match(pathname) ? 'is-active' : undefined}>
<NavigationIcon name={item.icon} /><span>{item.shortLabel}</span>
</a>
))}
</nav>
</>
)
}