// DSO Portal product mockups — reusable React components rendered as inline SVG/HTML // Styled to match ImmiHub DS. All are static visual mockups, not interactive. const mockupCSS = ` .pm { font-family: var(--font-sans); color: var(--fg-1); font-size: 12px; } .pm-app { display: grid; grid-template-columns: 200px 1fr; background: var(--bg-page); min-height: 560px; } .pm-side { background: #fff; border-right: 1px solid var(--border-subtle); padding: 18px 14px; } .pm-brand { display:flex; align-items:center; gap:8px; padding: 4px 8px 18px; border-bottom: 1px solid var(--border-subtle); margin-bottom: 14px; } .pm-brand-plane { width: 22px; height:22px; background: var(--im-blue); border-radius: 6px; display:flex; align-items:center; justify-content:center; color:#fff; } .pm-brand-wordmark { font-weight:600; font-size:14px; color: var(--fg-1); letter-spacing:-0.01em; } .pm-brand-lockup { font-family: var(--font-serif); font-style: italic; font-size: 10.5px; color: var(--fg-3); padding-left: 6px; border-left: 1px solid var(--border-subtle); margin-left: 6px; } .pm-nav { display:grid; gap:2px; } .pm-nav-item { padding: 7px 10px; font-size: 12px; color: var(--fg-2); border-radius: 6px; display:flex; align-items:center; gap:8px; cursor: default; } .pm-nav-item.on { background: var(--im-blue-ice); color: var(--im-blue-deep); font-weight: 500; } .pm-nav-ico { width:14px; height: 14px; opacity: 0.9; } .pm-nav-section { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-3); padding: 14px 10px 6px; font-weight: 500; } .pm-main { padding: 20px 24px; overflow: hidden; } .pm-topbar { display:flex; align-items:center; justify-content: space-between; margin-bottom: 20px; } .pm-title { font-family: var(--font-serif); font-size: 20px; font-weight: 500; color: var(--fg-1); letter-spacing: -0.01em; } .pm-crumb { font-size: 11px; color: var(--fg-3); margin-bottom: 4px; } .pm-search { background: var(--bg-alt); border-radius: 6px; padding: 6px 12px; font-size: 11px; color: var(--fg-3); width: 220px; } .pm-avatar { width: 26px; height: 26px; border-radius: 50%; background: var(--im-blue); color:#fff; font-size: 10px; font-weight:500; display:flex; align-items:center; justify-content:center; } .pm-kpis { display:grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 18px; } .pm-kpi { background: #fff; border:1px solid var(--border-subtle); border-radius: 10px; padding: 14px; } .pm-kpi-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-3); font-weight:500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pm-kpi-num { font-family: var(--font-serif); font-size: 28px; font-weight: 500; color: var(--fg-1); letter-spacing: -0.02em; margin-top: 4px; } .pm-kpi-delta { font-size: 10px; color: var(--fg-3); margin-top: 4px; } .pm-kpi-delta.up { color: var(--im-status-danger-fg); } .pm-card { background: #fff; border: 1px solid var(--border-subtle); border-radius: 10px; padding: 16px; } .pm-card h4 { margin: 0 0 10px; font-family: var(--font-serif); font-size: 14px; font-weight: 500; color: var(--fg-1); letter-spacing: -0.01em; } .pm-two-col { display: grid; grid-template-columns: 1.3fr 1fr; gap: 12px; } .pm-pill { display:inline-flex; align-items:center; gap:4px; padding: 2px 8px; border-radius: 999px; font-size: 10px; font-weight:500; } .pm-pill.crit { background: var(--im-status-danger-bg); color: var(--im-status-danger-fg); } .pm-pill.warn { background: var(--im-status-warn-bg); color: var(--im-status-warn-fg); } .pm-pill.ok { background: var(--im-status-valid-bg); color: var(--im-status-valid-fg); } .pm-pill.muted { background: var(--bg-alt); color: var(--fg-2); } .pm-pill.info { background: var(--im-blue-ice); color: var(--im-blue-deep); } .pm-row { display:flex; align-items:center; padding: 8px 0; gap: 10px; border-bottom: 1px solid var(--border-subtle); font-size: 11.5px; } .pm-row:last-child { border-bottom: 0; } .pm-days { font-family: var(--font-mono); font-size: 11px; color: var(--fg-2); min-width: 52px; } .pm-days.red { color: var(--im-status-danger-fg); } .pm-days.amber { color: var(--im-status-warn-fg); } .pm-stu { display:flex; align-items:center; gap:8px; flex: 1; min-width: 0; } .pm-stu-avatar { width:22px; height:22px; border-radius: 50%; font-size: 9px; font-weight:500; color:#fff; display:flex; align-items:center; justify-content:center; flex-shrink: 0; } .pm-stu-name { font-weight: 500; color: var(--fg-1); font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pm-stu-sub { color: var(--fg-3); font-size: 10.5px; } .pm-tbl { width: 100%; border-collapse: collapse; font-size: 11.5px; } .pm-tbl th { text-align:left; padding: 6px 8px 10px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-3); border-bottom: 1px solid var(--border-subtle); font-weight: 500; } .pm-tbl td { padding: 9px 8px; border-bottom: 1px solid var(--border-subtle); color: var(--fg-2); } .pm-cal { display:grid; grid-template-columns: repeat(30, 1fr); gap: 2px; margin-top: 10px; } .pm-cal-day { aspect-ratio: 1; border-radius: 2px; background: var(--bg-alt); position: relative; } .pm-cal-day.e1 { background: #B8D5EB; } .pm-cal-day.e2 { background: var(--im-blue-sky); } .pm-cal-day.e3 { background: var(--im-blue); } .pm-cal-day.crit { background: var(--im-status-danger-fg); } .pm-cal-day.warn { background: var(--im-status-warn-fg); } .pm-cal-leg { display:flex; gap: 14px; margin-top: 10px; font-size: 10px; color: var(--fg-3); } .pm-cal-leg span { display:inline-flex; align-items:center; gap:5px; } .pm-cal-leg i { width:8px; height:8px; border-radius: 2px; background: var(--im-blue); display:inline-block; } .pm-btn { display:inline-flex; align-items:center; gap:5px; padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight:500; border:1px solid var(--border-default); background:#fff; color: var(--fg-1); cursor: default; } .pm-btn.primary { background: var(--im-blue); color: #fff; border-color: transparent; } .pm-btn.ghost { background: transparent; border-color: var(--border-default); } @media (max-width: 900px) { .pm-app { grid-template-columns: 1fr; min-height: auto; } .pm-side { display: none; } .pm-main { padding: 14px 16px; } .pm-topbar { flex-wrap: wrap; gap: 10px; align-items: flex-start; } .pm-topbar > div:last-child { flex: 1 1 100%; justify-content: flex-end; } .pm-search { width: 100%; max-width: none; flex: 1; min-width: 0; } .pm-title { font-size: 17px; } .pm-kpis { grid-template-columns: repeat(2, 1fr); } .pm-two-col { grid-template-columns: 1fr; } } @media (max-width: 480px) { .pm-kpis { grid-template-columns: 1fr; } .pm-kpi-num { font-size: 22px; } .pm-tbl { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; } } `; // Deterministic avatar colors const avatarColors = ['#4F9ED6', '#2B7AB8', '#7BB8E2', '#34B87C', '#E5A318', '#8896A6']; const avatarColor = (name) => { let h = 0; for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0; return avatarColors[h % avatarColors.length]; }; const initials = (name) => name.split(' ').map(s => s[0]).slice(0,2).join('').toUpperCase(); function SideNav({ active }) { const items = [ ['Overview', 'Calendar', 'overview'], ['SEVIS Queue', 'AlertTriangle', 'sevis'], ['New Arrivals', 'Users', 'arrivals'], ['Students', 'User', 'students'], ['Requests', 'Inbox', 'requests'], ['Batch I-20', 'FileText', 'batch'], ['What-If Simulator', 'GitBranch', 'whatif'], ['Reports', 'BarChart', 'reports'], ['Audit Trail', 'Shield', 'audit'], ]; return (
ImmiHub
DSO Portal
{items.map(([label, icon, id]) => { const Ic = Ico[icon]; return (
{label}
); })}
Administration
DSO team
Settings
); } function TopBar({ crumb, title, user = 'Priya Raman' }) { return (
{crumb}
{title}
Search students, I-20 numbers, SEVIS IDs…
{initials(user)}
); } // ---------- OVERVIEW ---------- function OverviewMockup() { const students = [ ['Yuki Tanaka', 'PhD · Mech Eng · Program ends 17d', 'crit', 'i20-expiring', 17], ['Aisha Patel', 'STEM OPT · E-Verify 19d overdue', 'crit', 'everify-overdue', -19], ['Jian Li', 'F-1 · Termination report due', 'crit', 'termination', 3], ['Naledi Mokoena', 'OPT ending · STEM OPT eligible', 'warn', 'ead-expiring', 89], ['Rohan Mehta', 'CPT request · Summer 2026', 'warn', 'cpt-pending', 6], ['Ming Chen', 'Travel signature stale', 'warn', 'travel-sig-stale', 11], ['Sara Okonkwo', 'New arrival · Registration due', 'info', 'registration-due', 8], ]; return (
Active F-1 / J-1
2,847
+12 this week
SEVIS events · 30d
142
11 due this week
Open requests
38
8 awaiting DSO
At risk
9
3 critical

30-day SEVIS reporting calendar

{Array.from({length: 30}).map((_, i) => { const map = { 2:'e1', 3:'crit', 5:'e2', 7:'warn', 9:'e1', 10:'e3', 12:'e2', 14:'warn', 17:'crit', 19:'e1', 21:'e2', 24:'warn', 27:'e1', 28:'e2', 29:'e3' }; return
; })}
Overdue Due this week Reportable Clear
Next 7 days: 3 termination reports, 2 program extensions, 6 travel signature reissues, 2 STEM OPT validations.

Risk-first · students

{students.map((s, i) => (
{s[4] < 0 ? `${-s[4]}d over` : `${s[4]}d left`}
{initials(s[0])}
{s[0]}
{s[1]}
{s[3]}
))}
); } // ---------- SEVIS QUEUE ---------- function SevisQueueMockup() { const rows = [ ['3d', 'red', 'Termination report', 'Jian Li', 'N0034567890', 'Unauthorized employment', 'Registrar confirm pending'], ['5d', 'amber', 'Program extension', 'Yuki Tanaka', 'N0045612388', 'New end date 2027-09-01', 'Advisor form pending'], ['6d', 'amber', 'CPT authorization', 'Rohan Mehta', 'N0038821104', 'Grafton Capital · ECON 498', 'Ready to file'], ['9d', '', 'Initial registration', 'Sara Okonkwo', 'N0049110023', '—', 'Arrived · check-in complete'], ['11d', '', 'Travel signature', 'Ming Chen', 'N0031290045', '12-month window', 'Ready to sign'], ['14d', '', 'STEM OPT validation', 'Naledi Mokoena', 'N0029887712', '6-month E-Verify check', 'Employer verified'], ['19d', '', 'Program extension', 'Aditya Singh', 'N0045921667', 'Dept approval on file', 'Ready to file'], ['23d', '', 'CPT authorization', 'Hana Kim', 'N0046110885', 'Boston Sci · BIO 590', 'Course map pending'], ]; return (
All · 142 Overdue · 2 Due this week · 11 Next 30 days · 129
{rows.map((r, i) => ( ))}
Due Event Student SEVIS ID Detail Blocker
{r[0]} {r[2]}
{initials(r[3])}
{r[3]}
{r[4]} {r[5]} {r[6]} File in SEVIS →
); } // ---------- STUDENT DRAWER ---------- function StudentDrawerMockup() { const timeline = [ ['2023-08-21', 'I-20 issued', 'Active · Initial'], ['2023-09-04', 'Program start · check-in', 'Active'], ['2024-05-12', 'Travel signature', '12-mo window'], ['2025-04-03', 'Travel signature (reissue)', 'Current'], ['2026-04-02', 'Program extension requested', 'In review'], ]; return (
Students · PhD candidates · Active
YT
Yuki Tanaka
F-1 · PhD Mechanical Engineering · SEVIS N0045612388 · Cohort AY23
17 days to program end
Approve extension Sign travel endorsement Send message ···
Overview
I-20 lifecycle
Requests
Employment
Messages
Audit

F-1 journey timeline

{timeline.map((t, i) => (
{t[0]}
{t[1]}
{t[2]}
))}

Risk signals

i20-expiring
Program end 2026-05-03 · extension filed but advisor form pending
advisor-form-pending
Prof. Harding · overdue 4 days · 2 reminders sent
At a glance
StatusActive
Program end2026-05-03
Last travel sig2025-04-03
I-94 admit untilD/S
DSO assignedP. Raman
DeptMech Engineering
FundingRA · NSF GRFP
Next action: Reissue I-20 once advisor form arrives. Estimated SEVIS report in 3 days.
); } // ---------- WHAT-IF SIMULATOR ---------- function WhatIfMockup() { return (

Proposed change

Current program end
2026-05-03
New program end
2026-09-01
+4 months · advisor-approved
I want preview. No change will be filed with SEVIS until you confirm.
Reset Commit change →

Downstream effects

5 derived changes
{[ ['Grace period', '60 days ends 2026-07-02', '60 days ends 2026-10-31', 'ok'], ['OPT earliest start', '2026-02-03 (90d before)', '2026-06-03 (shifted)', 'warn'], ['OPT latest start', '2026-08-02', '2026-11-30', 'ok'], ['SEVIS report', 'Program extension · due by 2026-05-03', 'Extended to 2026-09-01', 'info'], ['CPT eligibility', '4 CPT-eligible courses summer 2026', 'Remains eligible', 'ok'], ].map((r, i) => (
{r[0]}
{r[1]}
{r[2]}
{r[3] === 'ok' ? 'safe' : r[3] === 'warn' ? 'review' : 'report'}
))}

Risk signal impact

i20-expiring cleared
+ new: opt-window-shift
); } // ---------- BATCH I-20 ---------- function BatchI20Mockup() { const steps = ['Select', 'Review & sign', 'Generate PDFs', 'Ready for pickup']; return (
{steps.map((s, i) => (
{i+1}
{s}
))}
45 students selected · pre-departure window · spring break 2026
Filter Sign all 45 travel endorsements
{[ ['Ming Chen', 'MS Computer Science', '2025-03-11', 'Mar 14 – Apr 4', '12-mo · OK', 'Signed · Ready'], ['Aditya Singh', 'PhD Chemistry', '2024-12-02', 'Mar 14 – Mar 28', '12-mo · OK', 'Signed · Ready'], ['Priya Iyer', 'MBA', '2025-04-18', 'Mar 15 – Mar 29', '12-mo · OK', 'Signed · Ready'], ['Jin Park', 'PhD Economics', '2025-02-07', 'Mar 16 – Apr 12', '12-mo · OK', 'Signed · Ready'], ['Liam O\'Brien', 'MS Data Science', '2024-09-14', 'Mar 14 – Mar 23', 'Expires mid-trip', 'Needs review'], ['Fatima Al-Sayed', 'PhD Biology', '2025-03-02', 'Mar 14 – Apr 3', '12-mo · OK', 'Signed · Ready'], ].map((r, i) => ( ))}
Student Program Last signature Travel window Page 2 text Status
{i !== 4 && }
{r[0]} {r[1]} {r[2]} {r[3]} {r[4]} {r[5]}
Showing 6 of 45 Generated PDFs will be available in ~90s · audit-logged
); } // ---------- AUDIT TRAIL ---------- function AuditMockup() { const rows = [ ['2026-04-16 08:14:02', '10.24.19.88', 'P. Raman', 'Signed travel endorsement', 'Ming Chen · N0031290045'], ['2026-04-16 08:11:47', '10.24.19.88', 'P. Raman', 'Approved CPT', 'Rohan Mehta · N0038821104 · ECON 498'], ['2026-04-16 08:04:21', '10.24.19.88', 'P. Raman', 'Viewed student record', 'Yuki Tanaka · N0045612388'], ['2026-04-15 17:42:09', '10.24.19.44', 'M. Delacroix', 'Filed termination report', 'Jian Li · N0034567890 · unauthorized-employment'], ['2026-04-15 15:22:36', '10.24.19.44', 'M. Delacroix', 'Issued I-20', 'Sara Okonkwo · N0049110023 · Initial'], ['2026-04-15 14:18:02', '10.24.19.88', 'P. Raman', 'Batch signed 42 endorsements', 'Pre-departure spring 2026'], ['2026-04-15 11:03:58', '10.24.19.12', 'D. Watanabe', 'Updated program dates', 'Yuki Tanaka · N0045612388 · +4mo'], ['2026-04-14 16:31:14', '10.24.19.44', 'M. Delacroix', 'Auto-assigned to DSO queue', '12 new arrivals'], ]; return (
Filter · all DSOs Range · last 30 days Action · all
Export PDF Export CSV
{rows.map((r, i) => ( ))}
Timestamp IP Actor Action Target
{r[0]} {r[1]} {r[2]} {r[3]} {r[4]}
Showing 8 of 1,284 entries · AY 2025–26 · SHA-256 integrity verified
); } // ---------- NEW ARRIVALS ---------- function NewArrivalsMockup() { return (
{[ ['Issued', '214', 'I-20s'], ['In transit', '57', 'not yet arrived'], ['Arrived', '128', 'awaiting check-in'], ['Registered', '89', 'active in SEVIS'], ['Window open', '23', 'days remaining'], ].map(([l, n, s], i) => (
{l}
{n}
{s}
))}
Initial registration · 30-day federal window
Day 7 of 30
{[ ['Sara Okonkwo', 'PhD Public Health', '2026-04-09', 'Complete', '—', 8, 'warn'], ['Takeshi Yamamoto', 'MS Civil Eng', '2026-04-10', 'Complete', 'Active', 0, 'ok'], ['Diya Chatterjee', 'PhD Physics', '2026-04-11', 'Complete', '—', 9, 'warn'], ['Lee Min-jun', 'MBA', '2026-04-12', 'Scheduled 04-17', '—', 11, ''], ['Ngozi Adebayo', 'MS Biotech', '2026-04-13', 'Complete', 'Active', 0, 'ok'], ['Raúl Vázquez', 'PhD History', '2026-04-14', 'No-show', '—', 4, 'crit'], ].map((r, i) => ( ))}
StudentProgramArrivalCheck-inSEVIS registeredDays left
{r[0]} {r[1]} {r[2]} {r[3]} {r[4]} {r[5] === 0 ? 'Registered' : `${r[5]}d`}
); } // ---------- REQUESTS INBOX ---------- function RequestsMockup() { return (
All requests · 388 awaiting DSO
{[ ['Rohan Mehta', 'CPT · Summer 2026', 'high', 'on', '2h'], ['Naledi Mokoena', 'STEM OPT extension', 'high', '', '4h'], ['Ming Chen', 'Travel signature', 'med', '', '1d'], ['Fatima Al-Sayed', 'Program extension', 'high', '', '1d'], ['Liam O\'Brien', 'Reduced course load', 'med', '', '2d'], ['Jin Park', 'Travel signature', 'low', '', '3d'], ].map((r, i) => (
{r[0]}
{r[2]}
{r[1]}
Submitted {r[4]} ago
))}
RM
Rohan Mehta · CPT request
MS Computer Science · submitted 2h ago · 3 documents attached
Priority
Employer · Grafton Capital, New York NY
Role · Quantitative Analyst Intern (Summer 2026)
Dates · 2026-05-19 → 2026-08-15 · 20hrs/wk academic year, 40hrs/wk summer
Course link · ECON 498 · Directed Research · Department confirmed
Summary: First CPT · eligible (completed 1 academic year) · offer letter, course syllabus, faculty advisor approval on file. Course map validates directly with ECON 498 spring-26 enrollment. Ready to authorize.
Approve and issue I-20 Request more info Decline
); } function Mockup({ kind }) { switch (kind) { case 'overview': return ; case 'sevis': return ; case 'drawer': return ; case 'whatif': return ; case 'batch': return ; case 'audit': return ; case 'arrivals': return ; case 'requests': return ; default: return ; } } const mockupStyleTag = () => ; Object.assign(window, { Mockup, mockupStyleTag });