From a6eac3009d9f7500147f12822f6ce3e1baab3e8b Mon Sep 17 00:00:00 2001 From: blaisadmin Date: Tue, 21 Jul 2026 22:14:27 -0400 Subject: [PATCH] changed portrait shape --- backend/src/app.ts | 6 +++ .../repositories/workspaceRepository.test.ts | 7 +++ .../src/repositories/workspaceRepository.ts | 10 +++- frontend/src/App.tsx | 47 +++++++++++-------- 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/backend/src/app.ts b/backend/src/app.ts index e2c23e9..989c6ab 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -2992,6 +2992,12 @@ app.get('/api/admin/summary', requireAuth, requireSessionAuth, requireAdmin, asy rescueBirds: Number(summary?.rescue_birds ?? 0), pendingRescues: Number(summary?.pending_rescues ?? 0), dailyUsers: Number(summary?.daily_users ?? 0), + subscriptionsByPlan: { + household_basic: Number(summary?.household_basic_subscriptions ?? 0), + household_plus: Number(summary?.household_plus_subscriptions ?? 0), + household_macaw: Number(summary?.household_macaw_subscriptions ?? 0), + household_hyacinth_macaw: Number(summary?.household_hyacinth_macaw_subscriptions ?? 0), + }, }, }); } catch (error) { diff --git a/backend/src/repositories/workspaceRepository.test.ts b/backend/src/repositories/workspaceRepository.test.ts index 645c0be..020bb48 100644 --- a/backend/src/repositories/workspaceRepository.test.ts +++ b/backend/src/repositories/workspaceRepository.test.ts @@ -531,6 +531,10 @@ test('getPlatformAdminSummary counts memorialized birds separately', async () => rescue_birds: 5, pending_rescues: 1, daily_users: 2, + household_basic_subscriptions: 2, + household_plus_subscriptions: 1, + household_macaw_subscriptions: 0, + household_hyacinth_macaw_subscriptions: 1, }, ], }); @@ -543,4 +547,7 @@ test('getPlatformAdminSummary counts memorialized birds separately', async () => assert.match(calls[0].text, /memorialized_at IS NOT NULL/); assert.match(calls[0].text, /rescue_birds/); assert.match(calls[0].text, /workspaces\.workspace_type = 'rescue'/); + assert.equal(summary?.household_basic_subscriptions, 2); + assert.match(calls[0].text, /billing_plan = 'household_basic'/); + assert.match(calls[0].text, /subscription_status IN \('active', 'trialing'\)/); }); diff --git a/backend/src/repositories/workspaceRepository.ts b/backend/src/repositories/workspaceRepository.ts index e817262..89415b8 100644 --- a/backend/src/repositories/workspaceRepository.ts +++ b/backend/src/repositories/workspaceRepository.ts @@ -605,6 +605,10 @@ export const getPlatformAdminSummary = async () => { rescue_birds: number; pending_rescues: number; daily_users: number; + household_basic_subscriptions: number; + household_plus_subscriptions: number; + household_macaw_subscriptions: number; + household_hyacinth_macaw_subscriptions: number; }>( `SELECT (SELECT COUNT(*)::int FROM birds) AS total_birds, @@ -614,7 +618,11 @@ export const getPlatformAdminSummary = async () => { (SELECT COUNT(*)::int FROM workspaces WHERE workspace_type = 'rescue') AS rescue_workspaces, (SELECT COUNT(*)::int FROM birds INNER JOIN workspaces ON workspaces.id = birds.workspace_id WHERE workspaces.workspace_type = 'rescue') AS rescue_birds, (SELECT COUNT(*)::int FROM workspaces WHERE workspace_type = 'rescue' AND rescue_verification_status = 'pending') AS pending_rescues, - (SELECT COUNT(DISTINCT user_id)::int FROM auth_sessions WHERE created_at >= CURRENT_DATE) AS daily_users`, + (SELECT COUNT(DISTINCT user_id)::int FROM auth_sessions WHERE created_at >= CURRENT_DATE) AS daily_users, + (SELECT COUNT(*)::int FROM workspaces WHERE billing_plan = 'household_basic' AND subscription_status IN ('active', 'trialing')) AS household_basic_subscriptions, + (SELECT COUNT(*)::int FROM workspaces WHERE billing_plan = 'household_plus' AND subscription_status IN ('active', 'trialing')) AS household_plus_subscriptions, + (SELECT COUNT(*)::int FROM workspaces WHERE billing_plan = 'household_macaw' AND subscription_status IN ('active', 'trialing')) AS household_macaw_subscriptions, + (SELECT COUNT(*)::int FROM workspaces WHERE billing_plan = 'household_hyacinth_macaw' AND subscription_status IN ('active', 'trialing')) AS household_hyacinth_macaw_subscriptions`, ); return result.rows[0]; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ba9127d..193e3c5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -184,6 +184,7 @@ type AdminSummary = { rescueBirds: number; pendingRescues: number; dailyUsers: number; + subscriptionsByPlan: Record; }; type AdminRescueWorkspace = { @@ -1540,7 +1541,7 @@ const OVERVIEW_WINDOW_DAYS = 30; const OVERVIEW_HISTORY_DAYS = 425; const OVERVIEW_WIDTH = 520; const OVERVIEW_HEIGHT = 220; -const OVERVIEW_PADDING = { top: 20, right: 18, bottom: 36, left: 52 }; +const OVERVIEW_PADDING = { top: 48, right: 18, bottom: 36, left: 52 }; const PHOTO_MAX_BYTES = 900_000; const PHOTO_EXPORT_SIZES = [720, 600, 480]; const PHOTO_EXPORT_QUALITIES = [0.9, 0.82, 0.74, 0.66]; @@ -6341,42 +6342,42 @@ function App() { ))} {points.length > 0 ? (() => { const latestPoint = points[points.length - 1]; - const portraitSize = 24; - const portraitGap = 8; - const portraitX = latestPoint.x + portraitGap + portraitSize <= OVERVIEW_WIDTH - 2 - ? latestPoint.x + portraitGap - : latestPoint.x - portraitGap - portraitSize; - const portraitY = Math.min( - Math.max(latestPoint.y - portraitSize / 2, 2), - OVERVIEW_HEIGHT - OVERVIEW_PADDING.bottom - portraitSize - 2, - ); + const pointerRadius = 13; + const pointerCenterY = latestPoint.y - 21; + const pointerTop = pointerCenterY - pointerRadius; + const pointerPath = [ + `M ${latestPoint.x} ${latestPoint.y}`, + `C ${latestPoint.x - 2.5} ${latestPoint.y - 5}, ${latestPoint.x - pointerRadius} ${latestPoint.y - 13}, ${latestPoint.x - pointerRadius} ${pointerCenterY}`, + `A ${pointerRadius} ${pointerRadius} 0 1 1 ${latestPoint.x + pointerRadius} ${pointerCenterY}`, + `C ${latestPoint.x + pointerRadius} ${latestPoint.y - 13}, ${latestPoint.x + 2.5} ${latestPoint.y - 5}, ${latestPoint.x} ${latestPoint.y}`, + 'Z', + ].join(' '); const clipPathId = `overview-bird-portrait-${bird.id}`; return ( - + {`${bird.name}'s latest weight: ${latestPoint.label}`} - @@ -6589,6 +6590,12 @@ function App() { Pending rescues {adminSummary?.pendingRescues ?? '-'} + {(['household_basic', 'household_plus', 'household_macaw', 'household_hyacinth_macaw'] as const).map((billingPlan) => ( +
+ {formatBillingPlanName(billingPlan)} subscriptions + {adminSummary?.subscriptionsByPlan?.[billingPlan] ?? '-'} +
+ ))}