From 7e9bc4b925247973a3aec523d1d7751ac65468bf Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Mon, 19 Jan 2026 11:37:59 -0500 Subject: [PATCH 1/2] fix: Correct AdminNav to AdminSidebar for 401 redirect handling When users accessed admin pages while logged out, the API returned 401 but the JavaScript tried to call window.AdminNav.redirectToLogin() which doesn't exist. The correct object is window.AdminSidebar. This caused the catch block to show "Failed to load admin data" instead of properly redirecting to the login page. Fixed in 14 admin HTML files. Also removed dead code in admin-org-detail that referenced AdminNav.user which never existed. Co-Authored-By: Claude Opus 4.5 --- .changeset/tiny-groups-share.md | 2 ++ server/public/admin-account-detail.html | 6 ++-- server/public/admin-agreements.html | 2 +- server/public/admin-domain-discovery.html | 6 ++-- server/public/admin-email.html | 2 +- server/public/admin-feeds.html | 2 +- server/public/admin-meetings.html | 2 +- server/public/admin-members.html | 10 +++--- .../public/admin-notification-channels.html | 2 +- server/public/admin-org-detail.html | 35 ++++++++----------- server/public/admin-products.html | 6 ++-- server/public/admin-prospects.html | 16 ++++----- server/public/admin-users.html | 2 +- server/public/admin-working-groups.html | 2 +- server/public/admin.html | 2 +- 15 files changed, 47 insertions(+), 50 deletions(-) create mode 100644 .changeset/tiny-groups-share.md diff --git a/.changeset/tiny-groups-share.md b/.changeset/tiny-groups-share.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/tiny-groups-share.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/server/public/admin-account-detail.html b/server/public/admin-account-detail.html index d37d867e91..67611c866d 100644 --- a/server/public/admin-account-detail.html +++ b/server/public/admin-account-detail.html @@ -2338,7 +2338,7 @@

({})); @@ -2402,7 +2402,7 @@

Edit Agreement

const response = await fetch('/api/admin/agreements'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load agreements'); diff --git a/server/public/admin-domain-discovery.html b/server/public/admin-domain-discovery.html index beba880490..b7d1d3f686 100644 --- a/server/public/admin-domain-discovery.html +++ b/server/public/admin-domain-discovery.html @@ -507,7 +507,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); @@ -548,7 +548,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Bulk enrichment failed'); @@ -627,7 +627,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); diff --git a/server/public/admin-email.html b/server/public/admin-email.html index 912ec9cb60..cc92292404 100644 --- a/server/public/admin-email.html +++ b/server/public/admin-email.html @@ -430,7 +430,7 @@

Recent Email Sends

const response = await fetch('/api/admin/email/stats'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load stats'); diff --git a/server/public/admin-feeds.html b/server/public/admin-feeds.html index 0ccfd7e690..21cb704ecf 100644 --- a/server/public/admin-feeds.html +++ b/server/public/admin-feeds.html @@ -678,7 +678,7 @@

Delete Feed

const response = await fetch('/api/admin/feeds'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load feeds'); diff --git a/server/public/admin-meetings.html b/server/public/admin-meetings.html index 00e93c303b..c61c6f57e4 100644 --- a/server/public/admin-meetings.html +++ b/server/public/admin-meetings.html @@ -426,7 +426,7 @@

Meeting Details

const response = await fetch('/api/admin/working-groups'); if (!response.ok) { if (response.status === 401) { - window.AdminNav?.redirectToLogin(); + window.AdminSidebar?.redirectToLogin(); return; } throw new Error('Failed to load working groups'); diff --git a/server/public/admin-members.html b/server/public/admin-members.html index da41e33aec..e5a3e3f7bd 100644 --- a/server/public/admin-members.html +++ b/server/public/admin-members.html @@ -462,7 +462,7 @@

This action cannot be undone

console.log('[admin-members] Response status:', response.status); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load members'); @@ -709,7 +709,7 @@

This action cannot be undone

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const errorData = await response.json().catch(() => ({ message: response.statusText })); @@ -749,7 +749,7 @@

This action cannot be undone

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to sync member data'); @@ -818,7 +818,7 @@

This action cannot be undone

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load payment history'); @@ -944,7 +944,7 @@

This action cannot be undone

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } diff --git a/server/public/admin-notification-channels.html b/server/public/admin-notification-channels.html index ad6e3d4d7f..a912d25b74 100644 --- a/server/public/admin-notification-channels.html +++ b/server/public/admin-notification-channels.html @@ -418,7 +418,7 @@

Delete Channel

const response = await fetch('/api/admin/notification-channels'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load channels'); diff --git a/server/public/admin-org-detail.html b/server/public/admin-org-detail.html index 0676d408ae..f3e33ebac7 100644 --- a/server/public/admin-org-detail.html +++ b/server/public/admin-org-detail.html @@ -1489,7 +1489,7 @@

Set Discount

if (!orgResponse.ok) { if (orgResponse.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } if (orgResponse.status === 404) { @@ -1505,11 +1505,6 @@

Set Discount

domainsData = domainsResponse.ok ? await domainsResponse.json() : { domains: [], workos_domains: [] }; teamMembers = teamResponse.ok ? await teamResponse.json() : []; - // Get current user ID from AdminNav if available - if (window.AdminNav && window.AdminNav.user) { - currentUserId = window.AdminNav.user.id; - } - renderOrganization(); renderOwnerSelector(); populateOwnerDropdowns(); @@ -1799,7 +1794,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const data = await response.json(); @@ -2231,7 +2226,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to save changes'); @@ -2301,7 +2296,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to save activity'); @@ -2355,7 +2350,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to save follow-up'); @@ -2383,7 +2378,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to complete step'); @@ -2531,7 +2526,7 @@

Set Discount

if (response && !response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to update owner'); @@ -2570,7 +2565,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to update watch status'); @@ -2730,7 +2725,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error(await getErrorMessage(response, 'Failed to add domain')); @@ -2760,7 +2755,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error(await getErrorMessage(response, 'Failed to set primary domain')); @@ -2790,7 +2785,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error(await getErrorMessage(response, 'Failed to remove domain')); @@ -2822,7 +2817,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error(await getErrorMessage(response, 'Failed to verify domain')); @@ -2954,7 +2949,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to save interest level'); @@ -3186,7 +3181,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error(await getErrorMessage(response, 'Failed to link domain')); @@ -4260,7 +4255,7 @@

Set Discount

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } if (response.status === 503) { diff --git a/server/public/admin-products.html b/server/public/admin-products.html index 71a2ce3ee5..a50a0ed04b 100644 --- a/server/public/admin-products.html +++ b/server/public/admin-products.html @@ -557,7 +557,7 @@

Archive Product

const response = await fetch('/api/admin/products'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load products'); @@ -915,7 +915,7 @@

Archive Product

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const result = await response.json(); @@ -960,7 +960,7 @@

Archive Product

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to archive product'); diff --git a/server/public/admin-prospects.html b/server/public/admin-prospects.html index 94aa32d19d..fb6762c8a1 100644 --- a/server/public/admin-prospects.html +++ b/server/public/admin-prospects.html @@ -1390,7 +1390,7 @@

Bulk Import Prospects

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load prospects'); @@ -1772,7 +1772,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); @@ -1814,7 +1814,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Bulk enrichment failed'); @@ -1852,7 +1852,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); @@ -1944,7 +1944,7 @@

No unmapped domains found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); @@ -2161,7 +2161,7 @@

Search for Companies

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); @@ -2262,7 +2262,7 @@

No Results Found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } if (response.status === 409) { @@ -3283,7 +3283,7 @@

No accounts found

if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } const error = await response.json(); diff --git a/server/public/admin-users.html b/server/public/admin-users.html index ba1b46f2da..1831b93363 100644 --- a/server/public/admin-users.html +++ b/server/public/admin-users.html @@ -996,7 +996,7 @@

Different Approach

if (!groupsResponse.ok) { if (groupsResponse.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load working groups'); diff --git a/server/public/admin-working-groups.html b/server/public/admin-working-groups.html index c94490c3e3..900fd38023 100644 --- a/server/public/admin-working-groups.html +++ b/server/public/admin-working-groups.html @@ -633,7 +633,7 @@

Delete Committee

const response = await fetch('/api/admin/working-groups'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load working groups'); diff --git a/server/public/admin.html b/server/public/admin.html index e430365383..d6491da123 100644 --- a/server/public/admin.html +++ b/server/public/admin.html @@ -792,7 +792,7 @@

Dashboard

const response = await fetch('/api/admin/stats'); if (!response.ok) { if (response.status === 401) { - window.AdminNav.redirectToLogin(); + window.AdminSidebar.redirectToLogin(); return; } throw new Error('Failed to load stats'); From a6e65a8bb547ca17214689c3c7606defba42d085 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Mon, 19 Jan 2026 11:39:59 -0500 Subject: [PATCH 2/2] feat: Add AdminSidebar.fetch() for shared 401 handling Added a shared fetch wrapper that automatically handles 401 redirects, so pages don't need to duplicate the 401 check. Usage: // Before (repeated in every page): const response = await fetch('/api/admin/...'); if (!response.ok) { if (response.status === 401) { window.AdminSidebar.redirectToLogin(); return; } throw new Error('...'); } // After (401 handled automatically): const response = await AdminSidebar.fetch('/api/admin/...'); if (!response.ok) { throw new Error('...'); } Updated admin.html as an example. Other pages can be migrated gradually. Co-Authored-By: Claude Opus 4.5 --- server/public/admin-sidebar.js | 14 +++++++++++++- server/public/admin.html | 6 +----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/server/public/admin-sidebar.js b/server/public/admin-sidebar.js index 9f650fe630..8035b13066 100644 --- a/server/public/admin-sidebar.js +++ b/server/public/admin-sidebar.js @@ -386,6 +386,17 @@ window.location.href = `/auth/login?return_to=${returnUrl}`; } + // Shared fetch wrapper that handles 401 redirects automatically + async function adminFetch(url, options = {}) { + const response = await fetch(url, options); + if (response.status === 401) { + redirectToLogin(); + // Return a never-resolving promise to prevent further processing + return new Promise(() => {}); + } + return response; + } + // Auto-initialize only on admin pages function shouldInitialize() { return window.location.pathname.startsWith('/admin'); @@ -405,6 +416,7 @@ init, toggleSidebar, closeSidebar, - redirectToLogin + redirectToLogin, + fetch: adminFetch }; })(); diff --git a/server/public/admin.html b/server/public/admin.html index d6491da123..ed7b21e245 100644 --- a/server/public/admin.html +++ b/server/public/admin.html @@ -789,12 +789,8 @@

Dashboard

async function loadStats() { try { - const response = await fetch('/api/admin/stats'); + const response = await AdminSidebar.fetch('/api/admin/stats'); if (!response.ok) { - if (response.status === 401) { - window.AdminSidebar.redirectToLogin(); - return; - } throw new Error('Failed to load stats'); }