From 6dca4548844f845ebf5f82ea0327c0095307ff4e Mon Sep 17 00:00:00 2001 From: stophecom Date: Wed, 1 Jul 2026 22:14:12 +0200 Subject: [PATCH] feat(admin): add org subscription tiers and secret request counts Extends the admin dashboard with an org subscription-tier breakdown table and totalSecretRequests columns/stats, mirroring the existing per-user tier and secrets views. Co-Authored-By: Claude Sonnet 5 --- src/lib/server/analytics.ts | 14 ++++++++- .../(default)/account/admin/+page.server.ts | 4 +++ .../(default)/account/admin/+page.svelte | 30 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/lib/server/analytics.ts b/src/lib/server/analytics.ts index 9bb46f9e..4bb371ab 100644 --- a/src/lib/server/analytics.ts +++ b/src/lib/server/analytics.ts @@ -146,7 +146,8 @@ export const getOrganizationSizes = async () => { orgId: organization.id, name: organization.name, memberCount: count(membership.userId), - totalSecrets: sql`coalesce(sum(${stats.totalSecrets}), 0)` + totalSecrets: sql`coalesce(sum(${stats.totalSecrets}), 0)`, + totalSecretRequests: sql`coalesce(sum(${stats.totalSecretRequests}), 0)` }) .from(organization) .innerJoin(membership, eq(membership.organizationId, organization.id)) @@ -156,6 +157,17 @@ export const getOrganizationSizes = async () => { return result; }; +export const getOrganizationsByTier = async () => { + const result = await db + .select({ + tier: organization.subscriptionTier, + count: count() + }) + .from(organization) + .groupBy(organization.subscriptionTier); + return result; +}; + // ── Subscriptions & Revenue ──────────────────────────── export const getActiveSubscriptions = async () => { diff --git a/src/routes/(app)/(default)/account/admin/+page.server.ts b/src/routes/(app)/(default)/account/admin/+page.server.ts index 452a19bb..f71d0d64 100644 --- a/src/routes/(app)/(default)/account/admin/+page.server.ts +++ b/src/routes/(app)/(default)/account/admin/+page.server.ts @@ -3,6 +3,7 @@ import { getAdoptionRates, getApiKeyStats, getGlobalStats, + getOrganizationsByTier, getOrganizationSizes, getRecentSignups, getSecretCounts, @@ -30,6 +31,7 @@ export const load: PageServerLoad = async () => { secretRequestStats, totalOrganizations, organizationSizes, + organizationsByTier, activeSubscriptions, apiKeyStats, whiteLabelStats @@ -45,6 +47,7 @@ export const load: PageServerLoad = async () => { getSecretRequestStats(), getTotalOrganizations(), getOrganizationSizes(), + getOrganizationsByTier(), getActiveSubscriptions(), getApiKeyStats(), getWhiteLabelStats() @@ -63,6 +66,7 @@ export const load: PageServerLoad = async () => { secretRequestStats, totalOrganizations, organizationSizes, + organizationsByTier, activeSubscriptions, apiKeyStats, whiteLabelStats diff --git a/src/routes/(app)/(default)/account/admin/+page.svelte b/src/routes/(app)/(default)/account/admin/+page.svelte index 93007d85..ac204979 100644 --- a/src/routes/(app)/(default)/account/admin/+page.svelte +++ b/src/routes/(app)/(default)/account/admin/+page.svelte @@ -184,6 +184,10 @@ Expired: {data.secretRequestStats.expired} +
+ Total (all-time): + {data.globalStats?.totalSecretRequests ?? 0} +
{#if data.secretRequestStats.total > 0}
Response rate: @@ -219,6 +223,30 @@ +

Subscription Tiers

+ + + + Tier + Organizations + % + + + + {#each data.organizationsByTier as tier, i (i)} + + {tier.tier} + {tier.count} + {formatPercent(tier.count / data.totalOrganizations)} + + {/each} + + + + + {#if data.organizationSizes.length === 0}

No organizations yet.

{:else} @@ -228,6 +256,7 @@ Name Members Secrets + Secret Requests @@ -236,6 +265,7 @@ {org.name} {org.memberCount} {org.totalSecrets} + {org.totalSecretRequests} {/each}