From ea713d17309dc6803d6e35b89d113a4fed276682 Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 13:08:42 -0700 Subject: [PATCH 01/10] ACHOO-111: Add UUID column to DataTable --- components/DataTable.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/DataTable.tsx b/components/DataTable.tsx index 8c768b7..a4e7429 100644 --- a/components/DataTable.tsx +++ b/components/DataTable.tsx @@ -1,7 +1,7 @@ import React from 'react'; interface DataItem { - rank?: number; // Rank is now an optional property + rank?: number; name: string; value: number; uuid: string; @@ -29,6 +29,9 @@ const DataTable: React.FC = ({ title, data, valueLabel }) => { Name + + UUID + {valueLabel} @@ -43,6 +46,9 @@ const DataTable: React.FC = ({ title, data, valueLabel }) => { {item.name} + + {item.uuid} + {item.value.toLocaleString()} @@ -51,7 +57,7 @@ const DataTable: React.FC = ({ title, data, valueLabel }) => { - + Total From e074d2a6a093ef8f9ab56aa2d4acbf9cf0449a8b Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 13:12:14 -0700 Subject: [PATCH 02/10] ACHOO-111: Add percentages to DataTable --- components/Dashboard.tsx | 2 ++ components/DataTable.tsx | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/components/Dashboard.tsx b/components/Dashboard.tsx index 96c1944..c6268dd 100644 --- a/components/Dashboard.tsx +++ b/components/Dashboard.tsx @@ -346,6 +346,7 @@ const Dashboard: React.FC = () => { uuid: app.uuid, }))} valueLabel="Views" + total={viewsSummary.reduce((sum, app) => sum + app.views, 0)} /> { uuid: app.uuid, }))} valueLabel="Visits" + total={visitsSummary.reduce((sum, app) => sum + app.visits, 0)} /> diff --git a/components/DataTable.tsx b/components/DataTable.tsx index a4e7429..38a2978 100644 --- a/components/DataTable.tsx +++ b/components/DataTable.tsx @@ -11,9 +11,10 @@ interface DataTableProps { title: string; data: DataItem[]; valueLabel: string; + total: number; } -const DataTable: React.FC = ({ title, data, valueLabel }) => { +const DataTable: React.FC = ({ title, data, valueLabel, total }) => { return (

@@ -35,6 +36,9 @@ const DataTable: React.FC = ({ title, data, valueLabel }) => { {valueLabel} + + % of Total + @@ -52,6 +56,9 @@ const DataTable: React.FC = ({ title, data, valueLabel }) => { {item.value.toLocaleString()} + + {total > 0 ? ((item.value / total) * 100).toFixed(1) + '%' : '—'} + ))} @@ -61,7 +68,10 @@ const DataTable: React.FC = ({ title, data, valueLabel }) => { Total - {data.reduce((sum, item) => sum + item.value, 0).toLocaleString()} + {total.toLocaleString()} + + + 100% From adea7b1ba0af234a579058022cf6d7b023d7a006 Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 13:33:01 -0700 Subject: [PATCH 03/10] ACHOO-111: Incomplete implementation of /applications. Missing passing date range to API routes --- app/applications/page.tsx | 85 +++++++++++++++++++++++++++++++++++++++ middleware.ts | 18 +++++++-- 2 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 app/applications/page.tsx diff --git a/app/applications/page.tsx b/app/applications/page.tsx new file mode 100644 index 0000000..d6838ad --- /dev/null +++ b/app/applications/page.tsx @@ -0,0 +1,85 @@ +import React from 'react'; + +const BASE_URL = process.env.VERCEL_URL + ? `https://${process.env.VERCEL_URL}` + : process.env.NEXT_PUBLIC_BASE_URL + ? process.env.NEXT_PUBLIC_BASE_URL + : 'http://localhost:3000'; + +async function fetchData() { + const [appsRes, viewsRes, visitsRes] = await Promise.all([ + fetch(`${BASE_URL}/api/acquia/applications`), + fetch(`${BASE_URL}/api/acquia/views`), + fetch(`${BASE_URL}/api/acquia/visits`), + ]); + const [apps, viewsRaw, visitsRaw] = await Promise.all([ + appsRes.ok ? appsRes.json() : [], + viewsRes.ok ? viewsRes.json() : [], + visitsRes.ok ? visitsRes.json() : [], + ]); +// console.log('viewsRaw:', viewsRaw); +// console.log('visitsRaw:', visitsRaw); + // Defensive: ensure arrays + const views = Array.isArray(viewsRaw) + ? viewsRaw + : viewsRaw && Array.isArray(viewsRaw.data) + ? viewsRaw.data + : []; + const visits = Array.isArray(visitsRaw) + ? visitsRaw + : visitsRaw && Array.isArray(visitsRaw.data) + ? visitsRaw.data + : []; + return { apps, views, visits }; +} + +function getAppStats(apps: any[], views: { map: (arg0: (v: any) => any[]) => Iterable; reduce: (arg0: (sum: any, v: any) => any, arg1: number) => any; }, visits: { map: (arg0: (v: any) => any[]) => Iterable; reduce: (arg0: (sum: any, v: any) => any, arg1: number) => any; }) { + // Map views/visits by app uuid + const viewsByApp = Object.fromEntries(views.map(v => [v.uuid, v.views])); + const visitsByApp = Object.fromEntries(visits.map(v => [v.uuid, v.visits])); + // Calculate totals + const totalViews = views.reduce((sum, v) => sum + v.views, 0); + const totalVisits = visits.reduce((sum, v) => sum + v.visits, 0); + + // Merge stats + return apps.map(app => ({ + ...app, + views: viewsByApp[app.uuid] || 0, + visits: visitsByApp[app.uuid] || 0, + viewsPct: totalViews ? ((viewsByApp[app.uuid] || 0) / totalViews) * 100 : 0, + visitsPct: totalVisits ? ((visitsByApp[app.uuid] || 0) / totalVisits) * 100 : 0, + })); +} + +export default async function ApplicationsPage() { + const { apps, views, visits } = await fetchData(); + const stats = getAppStats(apps, views, visits); + + return ( +
+

Application Views & Visits

+ + + + + + + + + + + + {stats.map(app => ( + + + + + + + + ))} + +
ApplicationViews% of ViewsVisits% of Visits
{app.name}{app.views.toLocaleString()}{app.viewsPct.toFixed(1)}%{app.visits.toLocaleString()}{app.visitsPct.toFixed(1)}%
+
+ ); +} \ No newline at end of file diff --git a/middleware.ts b/middleware.ts index 383c9f8..92a20e1 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,13 +1,13 @@ import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; -import auth from 'basic-auth'; const USERNAME = 'sws'; const PASSWORD = 'sws'; export function middleware(request: NextRequest) { - // Only protect paths you want (here, all except /_next, /api/public, etc.) const { pathname } = request.nextUrl; + + // Allow static assets and public API routes if ( pathname.startsWith('/_next') || pathname.startsWith('/api/public') || @@ -16,6 +16,19 @@ export function middleware(request: NextRequest) { return NextResponse.next(); } + // Allow requests from localhost (IPv4 and IPv6) + const ip = + request.headers.get('x-forwarded-for') || + request.headers.get('x-real-ip') || + ''; + if ( + ip === '127.0.0.1' || + ip === '::1' || + ip.startsWith('::ffff:127.0.0.1') + ) { + return NextResponse.next(); + } + // Get the Authorization header const authHeader = request.headers.get('authorization'); if (!authHeader) { @@ -43,7 +56,6 @@ export function middleware(request: NextRequest) { }); } -// Optionally, define which paths to match export const config = { matcher: ['/((?!_next|api/public|favicon.ico).*)'], }; \ No newline at end of file From f85cdf7a98f45cc502848bbbc3a8425af48dc879 Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 13:35:12 -0700 Subject: [PATCH 04/10] fixup! Add UUID --- app/applications/page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/applications/page.tsx b/app/applications/page.tsx index d6838ad..832ac75 100644 --- a/app/applications/page.tsx +++ b/app/applications/page.tsx @@ -62,6 +62,7 @@ export default async function ApplicationsPage() { Application + UUID Views % of Views Visits @@ -72,6 +73,7 @@ export default async function ApplicationsPage() { {stats.map(app => ( {app.name} + {app.uuid} {app.views.toLocaleString()} {app.viewsPct.toFixed(1)}% {app.visits.toLocaleString()} From 9d793f41c08860333def425f03df7c03f78bd778 Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 13:41:07 -0700 Subject: [PATCH 05/10] ACHOO-111 WIP: initial implementation of UUID dynamic application-per-page --- app/applications/[uuid]/page.tsx | 83 ++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 app/applications/[uuid]/page.tsx diff --git a/app/applications/[uuid]/page.tsx b/app/applications/[uuid]/page.tsx new file mode 100644 index 0000000..1fae723 --- /dev/null +++ b/app/applications/[uuid]/page.tsx @@ -0,0 +1,83 @@ +import React from 'react'; + +const BASE_URL = process.env.VERCEL_URL + ? `https://${process.env.VERCEL_URL}` + : process.env.NEXT_PUBLIC_BASE_URL + ? process.env.NEXT_PUBLIC_BASE_URL + : 'http://localhost:3000'; + +interface PageProps { + params: { uuid: string }; +} + +async function fetchAppDetail(uuid: string, from: string, to: string) { + // Fetch all apps (to get the name), and this app's views/visits + const [appsRes, viewsRes, visitsRes] = await Promise.all([ + fetch(`${BASE_URL}/api/acquia/applications`), + fetch(`${BASE_URL}/api/acquia/views?from=${from}&to=${to}`), + fetch(`${BASE_URL}/api/acquia/visits?from=${from}&to=${to}`), + ]); + const [apps, viewsRaw, visitsRaw] = await Promise.all([ + appsRes.ok ? appsRes.json() : [], + viewsRes.ok ? viewsRes.json() : [], + visitsRes.ok ? visitsRes.json() : [], + ]); + const app = Array.isArray(apps) ? apps.find((a: any) => a.uuid === uuid) : null; + const views = Array.isArray(viewsRaw) + ? viewsRaw + : viewsRaw && Array.isArray(viewsRaw.data) + ? viewsRaw.data + : []; + const visits = Array.isArray(visitsRaw) + ? visitsRaw + : visitsRaw && Array.isArray(visitsRaw.data) + ? visitsRaw.data + : []; + const appViews = views.find((v: any) => v.uuid === uuid); + const appVisits = visits.find((v: any) => v.uuid === uuid); + const totalViews = views.reduce((sum: number, v: any) => sum + (v.views || 0), 0); + const totalVisits = visits.reduce((sum: number, v: any) => sum + (v.visits || 0), 0); + + return { + app, + views: appViews ? appViews.views : 0, + visits: appVisits ? appVisits.visits : 0, + viewsPct: totalViews ? ((appViews ? appViews.views : 0) / totalViews) * 100 : 0, + visitsPct: totalVisits ? ((appVisits ? appVisits.visits : 0) / totalVisits) * 100 : 0, + from, + to, + }; +} + +export default async function ApplicationDetailPage({ params }: PageProps) { + // You can make these dynamic or user-selectable + const from = '2025-08-01'; + const to = '2025-08-31'; + + const { app, views, visits, viewsPct, visitsPct } = await fetchAppDetail(params.uuid, from, to); + + if (!app) { + return ( +
+

Application Not Found

+

No application found with UUID: {params.uuid}

+
+ ); + } + + return ( +
+

{app.name}

+
+ UUID: {app.uuid} +
+
+ Views ({from} to {to}): {views.toLocaleString()} ({viewsPct.toFixed(1)}%) +
+
+ Visits ({from} to {to}): {visits.toLocaleString()} ({visitsPct.toFixed(1)}%) +
+ {/* Add more details or charts here if desired */} +
+ ); +} \ No newline at end of file From 2b869b896669d75efa99807fa98059f8fd4c8d1e Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 14:29:03 -0700 Subject: [PATCH 06/10] ACHOO-111: Working implementation of per-application reporting --- app/applications/[uuid]/page.tsx | 286 +++++++++++++++++++++++-------- 1 file changed, 218 insertions(+), 68 deletions(-) diff --git a/app/applications/[uuid]/page.tsx b/app/applications/[uuid]/page.tsx index 1fae723..0d04d86 100644 --- a/app/applications/[uuid]/page.tsx +++ b/app/applications/[uuid]/page.tsx @@ -1,83 +1,233 @@ -import React from 'react'; +'use client'; -const BASE_URL = process.env.VERCEL_URL - ? `https://${process.env.VERCEL_URL}` - : process.env.NEXT_PUBLIC_BASE_URL - ? process.env.NEXT_PUBLIC_BASE_URL - : 'http://localhost:3000'; +import React, { useState, useEffect } from 'react'; +import CountUpTimer from '@/components/CountUpTimer'; + +const DEFAULT_SUBSCRIPTION_UUID = process.env.NEXT_PUBLIC_ACQUIA_SUBSCRIPTION_UUID || ''; +const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; interface PageProps { params: { uuid: string }; } -async function fetchAppDetail(uuid: string, from: string, to: string) { - // Fetch all apps (to get the name), and this app's views/visits - const [appsRes, viewsRes, visitsRes] = await Promise.all([ - fetch(`${BASE_URL}/api/acquia/applications`), - fetch(`${BASE_URL}/api/acquia/views?from=${from}&to=${to}`), - fetch(`${BASE_URL}/api/acquia/visits?from=${from}&to=${to}`), - ]); - const [apps, viewsRaw, visitsRaw] = await Promise.all([ - appsRes.ok ? appsRes.json() : [], - viewsRes.ok ? viewsRes.json() : [], - visitsRes.ok ? visitsRes.json() : [], - ]); - const app = Array.isArray(apps) ? apps.find((a: any) => a.uuid === uuid) : null; - const views = Array.isArray(viewsRaw) - ? viewsRaw - : viewsRaw && Array.isArray(viewsRaw.data) - ? viewsRaw.data - : []; - const visits = Array.isArray(visitsRaw) - ? visitsRaw - : visitsRaw && Array.isArray(visitsRaw.data) - ? visitsRaw.data - : []; - const appViews = views.find((v: any) => v.uuid === uuid); - const appVisits = visits.find((v: any) => v.uuid === uuid); - const totalViews = views.reduce((sum: number, v: any) => sum + (v.views || 0), 0); - const totalVisits = visits.reduce((sum: number, v: any) => sum + (v.visits || 0), 0); +export default function ApplicationDetailPage({ params }: PageProps) { + const [subscriptionUuid, setSubscriptionUuid] = useState(DEFAULT_SUBSCRIPTION_UUID); + const [from, setFrom] = useState(''); + const [to, setTo] = useState(''); + const [loading, setLoading] = useState(false); + const [loadingStep, setLoadingStep] = useState(''); + const [elapsedTime, setElapsedTime] = useState(null); + const [appName, setAppName] = useState(''); + const [views, setViews] = useState(0); + const [visits, setVisits] = useState(0); + const [viewsPct, setViewsPct] = useState(0); + const [visitsPct, setVisitsPct] = useState(0); + const [error, setError] = useState(null); - return { - app, - views: appViews ? appViews.views : 0, - visits: appVisits ? appVisits.visits : 0, - viewsPct: totalViews ? ((appViews ? appViews.views : 0) / totalViews) * 100 : 0, - visitsPct: totalVisits ? ((appVisits ? appVisits.visits : 0) / totalVisits) * 100 : 0, - from, - to, - }; -} + // Fetch application name on mount or when subscriptionUuid changes + useEffect(() => { + const fetchAppName = async () => { + try { + setLoadingStep('Fetching application info...'); + const res = await fetch(`${BASE_URL}/api/acquia/applications?subscriptionUuid=${subscriptionUuid}`); + const apps = await res.json(); + const app = Array.isArray(apps) ? apps.find((a: any) => a.uuid === params.uuid) : null; + setAppName(app ? app.name : ''); + } catch { + setAppName(''); + } finally { + setLoadingStep(''); + } + }; + if (subscriptionUuid) fetchAppName(); + }, [subscriptionUuid, params.uuid]); -export default async function ApplicationDetailPage({ params }: PageProps) { - // You can make these dynamic or user-selectable - const from = '2025-08-01'; - const to = '2025-08-31'; + const fetchAppDetail = async () => { + setLoading(true); + setLoadingStep('Fetching analytics data...'); + setError(null); + setElapsedTime(null); + const startTime = Date.now(); + try { + const paramsObj: Record = {}; + if (subscriptionUuid) paramsObj.subscriptionUuid = subscriptionUuid; + if (from) paramsObj.from = from; + if (to) paramsObj.to = to; + const query = new URLSearchParams(paramsObj).toString(); - const { app, views, visits, viewsPct, visitsPct } = await fetchAppDetail(params.uuid, from, to); + setLoadingStep('Fetching views and visits...'); + const [viewsRes, visitsRes] = await Promise.all([ + fetch(`${BASE_URL}/api/acquia/views?${query}`), + fetch(`${BASE_URL}/api/acquia/visits?${query}`), + ]); + const [viewsRaw, visitsRaw] = await Promise.all([ + viewsRes.ok ? viewsRes.json() : [], + visitsRes.ok ? visitsRes.json() : [], + ]); + const viewsArr = Array.isArray(viewsRaw) + ? viewsRaw + : viewsRaw && Array.isArray(viewsRaw.data) + ? viewsRaw.data + : []; + const visitsArr = Array.isArray(visitsRaw) + ? visitsRaw + : visitsRaw && Array.isArray(visitsRaw.data) + ? visitsRaw.data + : []; + const appViewsTotal = viewsArr + .filter((v: any) => v.uuid === params.uuid || v.applicationUuid === params.uuid) + .reduce((sum: number, v: any) => sum + (v.views || 0), 0); - if (!app) { - return ( -
-

Application Not Found

-

No application found with UUID: {params.uuid}

-
- ); - } + const appVisitsTotal = visitsArr + .filter((v: any) => v.uuid === params.uuid || v.applicationUuid === params.uuid) + .reduce((sum: number, v: any) => sum + (v.visits || 0), 0); + + const totalViews = viewsArr.reduce((sum: number, v: any) => sum + (v.views || 0), 0); + const totalVisits = visitsArr.reduce((sum: number, v: any) => sum + (v.visits || 0), 0); + + setViews(appViewsTotal); + setVisits(appVisitsTotal); + setViewsPct(totalViews ? (appViewsTotal / totalViews) * 100 : 0); + setVisitsPct(totalVisits ? (appVisitsTotal / totalVisits) * 100 : 0); + setLoadingStep('Complete!'); + } catch (err) { + setError('Failed to fetch application details.'); + } finally { + const endTime = Date.now(); + setElapsedTime((endTime - startTime) / 1000); + setLoading(false); + setLoadingStep(''); + } + }; return ( -
-

{app.name}

-
- UUID: {app.uuid} -
-
- Views ({from} to {to}): {views.toLocaleString()} ({viewsPct.toFixed(1)}%) -
-
- Visits ({from} to {to}): {visits.toLocaleString()} ({visitsPct.toFixed(1)}%) -
- {/* Add more details or charts here if desired */} +
+

+ Views and Visits Data for {appName ? appName : {params.uuid}} +

+
+
+ + setSubscriptionUuid(e.target.value)} + className="w-full p-2 border rounded mb-4" + style={{ + borderColor: 'var(--stanford-gray)', + color: 'var(--stanford-black)', + fontFamily: 'Source Sans Pro, Arial, sans-serif', + }} + required + /> +
+
+ + setFrom(e.target.value)} + className="w-full px-3 py-2 border rounded-md focus:outline-none" + style={{ + borderColor: 'var(--stanford-gray)', + color: 'var(--stanford-black)', + fontFamily: 'Source Sans Pro, Arial, sans-serif', + }} + disabled={loading} + /> +
+
+ + setTo(e.target.value)} + className="w-full px-3 py-2 border rounded-md focus:outline-none" + style={{ + borderColor: 'var(--stanford-gray)', + color: 'var(--stanford-black)', + fontFamily: 'Source Sans Pro, Arial, sans-serif', + }} + disabled={loading} + /> +
+
+ +
+ + {loading && ( +
+ +
{loadingStep}
+
+ )} + + {!loading && elapsedTime !== null && ( +
+ +
+ Data loaded in {elapsedTime.toFixed(1)} seconds +
+
+ )} +
+ +

+ (Note that it can take several minutes to fetch data from the Acquia API.) +

+
+
+ {error && ( +
{error}
+ )} + {!appName ? ( +
No application found with UUID: {params.uuid}
+ ) : ( +
+
+ Name: {appName} +
+
+ UUID: {params.uuid} +
+
+ Views{from && to ? ` (${from} to ${to})` : ''}: {views.toLocaleString()} ({viewsPct.toFixed(1)}%) +
+
+ Visits{from && to ? ` (${from} to ${to})` : ''}: {visits.toLocaleString()} ({visitsPct.toFixed(1)}%) +
+
+ )}
); } \ No newline at end of file From b080b38980cd38439f0f4736a24cd5e7b834baba Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 14:35:26 -0700 Subject: [PATCH 07/10] fixup! Vercel build fix? --- app/applications/[uuid]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/applications/[uuid]/page.tsx b/app/applications/[uuid]/page.tsx index 0d04d86..e6891a9 100644 --- a/app/applications/[uuid]/page.tsx +++ b/app/applications/[uuid]/page.tsx @@ -10,7 +10,7 @@ interface PageProps { params: { uuid: string }; } -export default function ApplicationDetailPage({ params }: PageProps) { +export default function ApplicationDetailPage({ params }: any) { const [subscriptionUuid, setSubscriptionUuid] = useState(DEFAULT_SUBSCRIPTION_UUID); const [from, setFrom] = useState(''); const [to, setTo] = useState(''); From 17f2c2ad960cfe7dba882197339066b1ddba900e Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 14:40:29 -0700 Subject: [PATCH 08/10] fixup! Vercel CORS --- app/applications/[uuid]/page.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/applications/[uuid]/page.tsx b/app/applications/[uuid]/page.tsx index e6891a9..4dbdcdd 100644 --- a/app/applications/[uuid]/page.tsx +++ b/app/applications/[uuid]/page.tsx @@ -4,7 +4,11 @@ import React, { useState, useEffect } from 'react'; import CountUpTimer from '@/components/CountUpTimer'; const DEFAULT_SUBSCRIPTION_UUID = process.env.NEXT_PUBLIC_ACQUIA_SUBSCRIPTION_UUID || ''; -const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; +const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL + || (typeof window === 'undefined' + ? `https://${process.env.VERCEL_URL}` // On server, use Vercel URL + : '') // On client, use relative URLs + || 'http://localhost:3000'; interface PageProps { params: { uuid: string }; From 7a6f1d2baffab5d181a31cf38c1842329dec8979 Mon Sep 17 00:00:00 2001 From: John Bickar Date: Thu, 11 Sep 2025 14:51:25 -0700 Subject: [PATCH 09/10] WIP: Vercel CORS. Should be working on local and Vercel --- app/applications/[uuid]/page.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/applications/[uuid]/page.tsx b/app/applications/[uuid]/page.tsx index 4dbdcdd..4fb3669 100644 --- a/app/applications/[uuid]/page.tsx +++ b/app/applications/[uuid]/page.tsx @@ -4,12 +4,14 @@ import React, { useState, useEffect } from 'react'; import CountUpTimer from '@/components/CountUpTimer'; const DEFAULT_SUBSCRIPTION_UUID = process.env.NEXT_PUBLIC_ACQUIA_SUBSCRIPTION_UUID || ''; +/** const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || (typeof window === 'undefined' ? `https://${process.env.VERCEL_URL}` // On server, use Vercel URL : '') // On client, use relative URLs || 'http://localhost:3000'; - +*/ +const BASE_URL = ''; interface PageProps { params: { uuid: string }; } @@ -33,7 +35,8 @@ export default function ApplicationDetailPage({ params }: any) { const fetchAppName = async () => { try { setLoadingStep('Fetching application info...'); - const res = await fetch(`${BASE_URL}/api/acquia/applications?subscriptionUuid=${subscriptionUuid}`); +// const res = await fetch(`${BASE_URL}/api/acquia/applications?subscriptionUuid=${subscriptionUuid}`); + const res = await fetch(`/api/acquia/applications?subscriptionUuid=${subscriptionUuid}`); const apps = await res.json(); const app = Array.isArray(apps) ? apps.find((a: any) => a.uuid === params.uuid) : null; setAppName(app ? app.name : ''); @@ -61,8 +64,10 @@ export default function ApplicationDetailPage({ params }: any) { setLoadingStep('Fetching views and visits...'); const [viewsRes, visitsRes] = await Promise.all([ - fetch(`${BASE_URL}/api/acquia/views?${query}`), - fetch(`${BASE_URL}/api/acquia/visits?${query}`), +// fetch(`${BASE_URL}/api/acquia/views?${query}`), +// fetch(`${BASE_URL}/api/acquia/visits?${query}`), + fetch(`/api/acquia/views?${query}`), + fetch(`/api/acquia/visits?${query}`), ]); const [viewsRaw, visitsRaw] = await Promise.all([ viewsRes.ok ? viewsRes.json() : [], From b793b9f444f37225c7ff7445011d66ccfdea991e Mon Sep 17 00:00:00 2001 From: John Bickar Date: Fri, 12 Sep 2025 12:31:38 -0700 Subject: [PATCH 10/10] fixup! relative URLs for fetches --- app/applications/[uuid]/page.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/applications/[uuid]/page.tsx b/app/applications/[uuid]/page.tsx index 4fb3669..bdbd87b 100644 --- a/app/applications/[uuid]/page.tsx +++ b/app/applications/[uuid]/page.tsx @@ -4,14 +4,6 @@ import React, { useState, useEffect } from 'react'; import CountUpTimer from '@/components/CountUpTimer'; const DEFAULT_SUBSCRIPTION_UUID = process.env.NEXT_PUBLIC_ACQUIA_SUBSCRIPTION_UUID || ''; -/** -const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL - || (typeof window === 'undefined' - ? `https://${process.env.VERCEL_URL}` // On server, use Vercel URL - : '') // On client, use relative URLs - || 'http://localhost:3000'; -*/ -const BASE_URL = ''; interface PageProps { params: { uuid: string }; } @@ -35,7 +27,6 @@ export default function ApplicationDetailPage({ params }: any) { const fetchAppName = async () => { try { setLoadingStep('Fetching application info...'); -// const res = await fetch(`${BASE_URL}/api/acquia/applications?subscriptionUuid=${subscriptionUuid}`); const res = await fetch(`/api/acquia/applications?subscriptionUuid=${subscriptionUuid}`); const apps = await res.json(); const app = Array.isArray(apps) ? apps.find((a: any) => a.uuid === params.uuid) : null; @@ -64,8 +55,6 @@ export default function ApplicationDetailPage({ params }: any) { setLoadingStep('Fetching views and visits...'); const [viewsRes, visitsRes] = await Promise.all([ -// fetch(`${BASE_URL}/api/acquia/views?${query}`), -// fetch(`${BASE_URL}/api/acquia/visits?${query}`), fetch(`/api/acquia/views?${query}`), fetch(`/api/acquia/visits?${query}`), ]);