Apply Stitch media operations redesign
This commit is contained in:
@@ -7,6 +7,7 @@ import HeaderActions from './ui/HeaderActions'
|
|||||||
import HeaderIdentity from './ui/HeaderIdentity'
|
import HeaderIdentity from './ui/HeaderIdentity'
|
||||||
import SiteStatus from './ui/SiteStatus'
|
import SiteStatus from './ui/SiteStatus'
|
||||||
import UserViewBanner from './ui/UserViewBanner'
|
import UserViewBanner from './ui/UserViewBanner'
|
||||||
|
import WorkspaceNavigation from './ui/WorkspaceNavigation'
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Magent',
|
title: 'Magent',
|
||||||
@@ -37,6 +38,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
|||||||
<HeaderActions />
|
<HeaderActions />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<WorkspaceNavigation />
|
||||||
<UserViewBanner />
|
<UserViewBanner />
|
||||||
<SiteStatus />
|
<SiteStatus />
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -306,6 +306,15 @@ export default function NewRequestClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectedPoster = artworkUrl(selected?.posterPath, 'w185')
|
const selectedPoster = artworkUrl(selected?.posterPath, 'w185')
|
||||||
|
const currentFlowStep = success
|
||||||
|
? 5
|
||||||
|
: selected
|
||||||
|
? 4
|
||||||
|
: searchAttempted
|
||||||
|
? 3
|
||||||
|
: mediaType
|
||||||
|
? 2
|
||||||
|
: 1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="card request-portal-page">
|
<main className="card request-portal-page">
|
||||||
@@ -322,6 +331,18 @@ export default function NewRequestClient() {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</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>}
|
{error && <div className="error-banner request-flow-alert">{error}</div>}
|
||||||
{success && <div className="status-banner request-flow-alert">{success}</div>}
|
{success && <div className="status-banner request-flow-alert">{success}</div>}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
:root,
|
||||||
[data-theme='dark'],
|
[data-theme='dark'],
|
||||||
@@ -46,6 +46,42 @@
|
|||||||
--error-ink: #ffd6d6;
|
--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;
|
letter-spacing: 0 !important;
|
||||||
}
|
}
|
||||||
@@ -2727,6 +2763,772 @@ button:disabled,
|
|||||||
.request-complete-actions { display: grid; }
|
.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 */
|
/* Guided issue reporting */
|
||||||
.issue-portal-page {
|
.issue-portal-page {
|
||||||
display: grid;
|
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 { 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; }
|
.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
@@ -16,6 +16,7 @@ const normalizeRecentResults = (items: any[]) =>
|
|||||||
id,
|
id,
|
||||||
title: !rawTitle || placeholder ? `Request #${id}` : rawTitle,
|
title: !rawTitle || placeholder ? `Request #${id}` : rawTitle,
|
||||||
year: item.year,
|
year: item.year,
|
||||||
|
type: item.type,
|
||||||
statusLabel: item.statusLabel,
|
statusLabel: item.statusLabel,
|
||||||
artwork: item.artwork,
|
artwork: item.artwork,
|
||||||
createdAt: item.createdAt ?? null,
|
createdAt: item.createdAt ?? null,
|
||||||
@@ -41,8 +42,9 @@ export default function HomePage() {
|
|||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
year?: number
|
year?: number
|
||||||
|
type?: string
|
||||||
statusLabel?: string
|
statusLabel?: string
|
||||||
artwork?: { poster_url?: string }
|
artwork?: { poster_url?: string; backdrop_url?: string }
|
||||||
createdAt?: string | null
|
createdAt?: string | null
|
||||||
}[]
|
}[]
|
||||||
>([])
|
>([])
|
||||||
@@ -248,15 +250,22 @@ export default function HomePage() {
|
|||||||
return label.includes('ready') || label.includes('available')
|
return label.includes('ready') || label.includes('available')
|
||||||
}).length
|
}).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 (
|
return (
|
||||||
<main className="card home-page">
|
<main className="card home-page">
|
||||||
<section className="home-command">
|
<section className="home-command">
|
||||||
<div className="home-command-copy">
|
<div className="home-command-copy">
|
||||||
<span className="section-kicker">Request lookup</span>
|
<span className="section-kicker">Media operations</span>
|
||||||
<h1>My requests</h1>
|
<h1>My requests</h1>
|
||||||
<p>
|
<p>Manage and track your media processing queue.</p>
|
||||||
Enter a title and year, or jump straight to a request using its request number.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={submit} className="home-search">
|
<form onSubmit={submit} className="home-search">
|
||||||
<label htmlFor="request-search">Title, year, or request number</label>
|
<label htmlFor="request-search">Title, year, or request number</label>
|
||||||
@@ -334,17 +343,24 @@ export default function HomePage() {
|
|||||||
<option value={180}>180 days</option>
|
<option value={180}>180 days</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</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>
|
||||||
)}
|
)}
|
||||||
</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">
|
<div className="recent-grid home-recent-grid">
|
||||||
{recentLoading ? (
|
{recentLoading ? (
|
||||||
<div className="loading-center">
|
<div className="loading-center">
|
||||||
@@ -364,7 +380,7 @@ export default function HomePage() {
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.push(`/requests/${item.id}`)}
|
onClick={() => router.push(`/requests/${item.id}`)}
|
||||||
className="recent-card"
|
className={`recent-card is-${requestCardState(item.statusLabel).key}`}
|
||||||
>
|
>
|
||||||
{item.artwork?.poster_url ? (
|
{item.artwork?.poster_url ? (
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -56,7 +56,11 @@ export default function AdminSidebar() {
|
|||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
return (
|
return (
|
||||||
<nav className="admin-sidebar">
|
<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) => (
|
{NAV_GROUPS.map((group) => (
|
||||||
<div key={group.title} className="admin-nav-group">
|
<div key={group.title} className="admin-nav-group">
|
||||||
<span className="admin-nav-title">{group.title}</span>
|
<span className="admin-nav-title">{group.title}</span>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user