From 3465343a698f10cc3107ea460f14819a5a1054c8 Mon Sep 17 00:00:00 2001 From: Zak Bearman Date: Tue, 15 Sep 2026 15:46:09 +1200 Subject: [PATCH] feat: route ready-title issues through guided workflow --- frontend/app/ops-redesign.css | 56 ++++----- frontend/app/portal/PortalClient.tsx | 45 ++++++- .../app/requests/[id]/RequestIssueDialog.tsx | 119 ------------------ frontend/app/requests/[id]/page.tsx | 10 +- scripts/review_ready_request_ui.cjs | 61 +++++++-- 5 files changed, 116 insertions(+), 175 deletions(-) delete mode 100644 frontend/app/requests/[id]/RequestIssueDialog.tsx diff --git a/frontend/app/ops-redesign.css b/frontend/app/ops-redesign.css index 9c3a79c..9aef465 100644 --- a/frontend/app/ops-redesign.css +++ b/frontend/app/ops-redesign.css @@ -3656,48 +3656,35 @@ textarea:focus { margin-top: 5px; } .request-problem-button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 9px; border: 1px solid rgba(126, 215, 255, 0.36); border-radius: var(--ops-radius); background: rgba(79, 70, 229, 0.18); color: var(--ops-text); font-weight: 750; + text-decoration: none; } .request-problem-button:hover { border-color: rgba(126, 215, 255, 0.7); background: rgba(79, 70, 229, 0.28); } .request-ready-unavailable { margin-top: 5px; padding: 12px; border: 1px solid var(--ops-line); border-radius: var(--ops-radius); color: var(--ops-muted); font-size: 0.75rem; } -.request-issue-dialog { - width: min(620px, calc(100vw - 28px)); - max-height: min(760px, calc(100vh - 28px)); - margin: auto; - padding: 0; - overflow: auto; - border: 1px solid var(--ops-line); - border-radius: 12px; - background: #201f21; - color: var(--ops-text); - box-shadow: 0 28px 80px rgba(0, 0, 0, 0.58); +.issue-prefilled-request { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + margin-bottom: 16px; + padding: 14px; + border: 1px solid rgba(126, 215, 255, 0.3); + border-radius: 9px; + background: rgba(14, 165, 233, 0.07); } -.request-issue-dialog::backdrop { background: rgba(5, 5, 8, 0.76); backdrop-filter: blur(5px); } -.request-issue-dialog form { display: grid; gap: 18px; padding: 22px; } -.request-issue-dialog form > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; } -.request-issue-dialog h2 { margin: 3px 0 0; font-size: 1.55rem; } -.request-issue-media { display: grid; gap: 4px; padding: 14px; border: 1px solid var(--ops-line); border-radius: 9px; background: var(--ops-panel-2); } -.request-issue-media > span, -.request-issue-media > small { color: var(--ops-muted); font-size: 0.7rem; } -.request-issue-media > strong { font-size: 1rem; } -.request-issue-dialog label { display: grid; gap: 8px; color: var(--ops-text); font-size: 0.8rem; font-weight: 750; } -.request-issue-dialog textarea { width: 100%; resize: vertical; border-color: var(--ops-line); background: var(--ops-panel-2); color: var(--ops-text); line-height: 1.5; } -.request-issue-help { margin: -10px 0 0; color: var(--ops-muted); font-size: 0.72rem; } -.request-issue-dialog footer { display: flex; justify-content: flex-end; } -.request-issue-dialog footer > button, -.request-issue-success a { min-height: 42px; padding: 10px 18px; } -.request-issue-success { display: grid; justify-items: center; gap: 12px; padding: 34px 24px; text-align: center; } -.request-issue-success > span { display: grid; width: 52px; height: 52px; place-items: center; border: 1px solid rgba(72, 224, 178, 0.5); border-radius: 50%; color: var(--request-green); font-size: 1.5rem; } -.request-issue-success h2, -.request-issue-success p { margin: 0; } -.request-issue-success p { max-width: 50ch; color: var(--ops-muted); line-height: 1.55; } -.request-issue-success > div { display: flex; gap: 10px; margin-top: 8px; } -.request-issue-success a { display: inline-flex; align-items: center; border-radius: var(--ops-radius); background: var(--ops-primary); color: var(--ops-primary-2); font-weight: 750; text-decoration: none; } +.issue-prefilled-request > div { display: grid; gap: 4px; } +.issue-prefilled-request strong { color: var(--ops-text); font-size: 1rem; } +.issue-prefilled-request small { color: var(--ops-muted); } +.issue-prefilled-request > a { flex: 0 0 auto; text-decoration: none; } @media (max-width: 1250px) { .global-search { flex-basis: 190px; width: 190px; } @@ -3726,9 +3713,8 @@ textarea:focus { .request-ready-actions { grid-template-columns: 1fr; } .request-ready-actions > section + section { border-top: 1px solid var(--ops-line-soft); border-left: 0; } .request-ready-actions > section > p { min-height: 0; } - .request-issue-dialog form { padding: 18px; } - .request-issue-success > div { width: 100%; flex-direction: column; } - .request-issue-success a { justify-content: center; } + .issue-prefilled-request { align-items: stretch; flex-direction: column; } + .issue-prefilled-request > a { text-align: center; } } /* Guided issue reporting */ diff --git a/frontend/app/portal/PortalClient.tsx b/frontend/app/portal/PortalClient.tsx index 86b8f02..f4ee31f 100644 --- a/frontend/app/portal/PortalClient.tsx +++ b/frontend/app/portal/PortalClient.tsx @@ -586,13 +586,30 @@ export default function PortalClient({ workspace }: PortalClientProps) { useEffect(() => { if (typeof window === 'undefined') return - const raw = new URLSearchParams(window.location.search).get('item') - if (!raw) { - setPreselectedItemId(null) - return - } - const parsed = Number.parseInt(raw, 10) + const params = new URLSearchParams(window.location.search) + const raw = params.get('item') + const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN setPreselectedItemId(Number.isNaN(parsed) || parsed <= 0 ? null : parsed) + + const requestId = Number.parseInt(params.get('reportRequest') ?? '', 10) + const title = params.get('title')?.trim() + const type = params.get('type') + const rawYear = Number.parseInt(params.get('year') ?? '', 10) + if (requestId > 0 && title && (type === 'movie' || type === 'tv')) { + const media: DiscoveryResult = { + title, + year: rawYear >= 1870 && rawYear <= 2200 ? rawYear : null, + type, + requestId, + statusLabel: 'Ready to watch', + accessible: true, + } + setIssueSelectedMedia(media) + setIssueMediaTitle(media.title) + setIssueMediaType(type) + setIssueMediaQuery(`${media.title}${media.year ? ` (${media.year})` : ''}`) + setIssueStep('problem') + } }, []) const loadMe = async () => { @@ -860,6 +877,14 @@ export default function PortalClient({ workspace }: PortalClientProps) { } const payload = await response.json() as IssueTargetOptions if (version !== issueOptionsVersion.current) return + const verifiedMedia: DiscoveryResult = { + ...media, + title: payload.title, + type: payload.request_type, + } + setIssueSelectedMedia(verifiedMedia) + setIssueMediaTitle(verifiedMedia.title) + setIssueMediaType(payload.request_type) setIssueOptions(payload) setIssueOptionsMessage(payload.message ?? null) const firstSeason = payload.seasons.find((season) => season.season_number > 0) ?? payload.seasons[0] @@ -981,12 +1006,14 @@ export default function PortalClient({ workspace }: PortalClientProps) { setMediaServerError(null) setError(null) setStatus(null) + const shouldLoadSelectedRequest = Boolean(issueSelectedMedia?.requestId && !issueOptions) setIssueStep(issueSelectedMedia && issueOptions ? 'symptoms' : 'media') setSelectedSeasonNumbers([]) setSelectedEpisodeIds([]) if (category === 'playback' || category === 'service_unavailable') { void checkMediaServer() } + if (shouldLoadSelectedRequest && issueSelectedMedia) void loadIssueOptions(issueSelectedMedia) } const toggleStringChoice = ( @@ -1607,6 +1634,12 @@ export default function PortalClient({ workspace }: PortalClientProps) {
+ {issueSelectedMedia?.requestId ? ( +
+
Reporting a problem with{issueSelectedMedia.title}{issueSelectedMedia.year ? ` (${issueSelectedMedia.year})` : ''}Request #{issueSelectedMedia.requestId} is already selected.
+ Back to request +
+ ) : null}
{ISSUE_CATEGORIES.map((category) => ( - { event.preventDefault(); close() }}> - {issueId ? ( -
- -

Thanks, we've got it

-

Your report is now issue #{issueId}. You can follow its progress from the Issues page.

- -
- ) : ( - -
-
Report a problem

What's wrong?

- -
-
{requestType === 'tv' ? 'TV show' : 'Movie'}{title}{year ? ` (${year})` : ''}Request #{requestId} · Ready to watch
-