diff --git a/apps/remix/app/components/tables/admin-organisation-stats-table.tsx b/apps/remix/app/components/tables/admin-organisation-stats-table.tsx index d3233bacf..17d46ec43 100644 --- a/apps/remix/app/components/tables/admin-organisation-stats-table.tsx +++ b/apps/remix/app/components/tables/admin-organisation-stats-table.tsx @@ -12,7 +12,7 @@ import { ChevronDownIcon, ChevronsUpDownIcon, ChevronUpIcon } from 'lucide-react import { useMemo } from 'react'; import { Link, useSearchParams } from 'react-router'; -type OrderByColumn = 'documentCount' | 'emailCount' | 'apiCount' | 'emailReports' | 'totalCount'; +type OrderByColumn = 'documentCount' | 'emailCount' | 'apiCount' | 'emailReports' | 'teamCount' | 'totalCount'; type OrderByDirection = 'asc' | 'desc'; const parseOrderByColumn = (value: string | undefined): OrderByColumn | undefined => { @@ -21,6 +21,7 @@ const parseOrderByColumn = (value: string | undefined): OrderByColumn | undefine value === 'emailCount' || value === 'apiCount' || value === 'emailReports' || + value === 'teamCount' || value === 'totalCount' ) { return value; @@ -178,6 +179,11 @@ export const AdminOrganisationStatsTable = ({ displayMode = 'usage' }: AdminOrga accessorKey: 'originalClaimId', cell: ({ row }) => {row.original.originalClaimId ?? '—'}, }, + { + header: () => sortableHeader(t`Teams`, 'teamCount'), + accessorKey: 'teamCount', + cell: ({ row }) => row.original.teamCount, + }, { header: t`Period`, accessorKey: 'period', @@ -240,6 +246,9 @@ export const AdminOrganisationStatsTable = ({ displayMode = 'usage' }: AdminOrga + + + diff --git a/packages/trpc/server/admin-router/find-organisation-stats.ts b/packages/trpc/server/admin-router/find-organisation-stats.ts index 98838ed1a..ad7030021 100644 --- a/packages/trpc/server/admin-router/find-organisation-stats.ts +++ b/packages/trpc/server/admin-router/find-organisation-stats.ts @@ -32,7 +32,7 @@ type FindOrganisationStatsOptions = { claimId?: string; page?: number; perPage?: number; - orderByColumn?: 'documentCount' | 'emailCount' | 'apiCount' | 'emailReports' | 'totalCount'; + orderByColumn?: 'documentCount' | 'emailCount' | 'apiCount' | 'emailReports' | 'teamCount' | 'totalCount'; orderByDirection?: 'asc' | 'desc'; }; @@ -95,6 +95,12 @@ export const findOrganisationStats = async ({ 'OrganisationClaim.documentQuota as documentQuota', 'OrganisationClaim.emailQuota as emailQuota', 'OrganisationClaim.apiQuota as apiQuota', + // Teams have no monthly history, so this is the current count for every period. + eb + .selectFrom('Team') + .whereRef('Team.organisationId', '=', 'Organisation.id') + .select(sql`count("Team"."id")`.as('count')) + .as('teamCount'), totalCountExpression.as('totalCount'), eb.fn.countAll().over().as('totalRows'), ]) @@ -104,6 +110,7 @@ export const findOrganisationStats = async ({ .with('emailCount', () => qb.orderBy('OrganisationMonthlyStat.emailCount', orderByDirection)) .with('apiCount', () => qb.orderBy('OrganisationMonthlyStat.apiCount', orderByDirection)) .with('emailReports', () => qb.orderBy('OrganisationMonthlyStat.emailReports', orderByDirection)) + .with('teamCount', () => qb.orderBy('teamCount', orderByDirection)) .with('totalCount', () => qb.orderBy(totalCountExpression, orderByDirection)) .with(undefined, () => // Default ordering mirrors the desired SQL: email, api, document descending. @@ -132,6 +139,7 @@ export const findOrganisationStats = async ({ emailCount: Number(row.emailCount), apiCount: Number(row.apiCount), emailReports: Number(row.emailReports), + teamCount: Number(row.teamCount), documentQuota: row.documentQuota === null ? null : Number(row.documentQuota), emailQuota: row.emailQuota === null ? null : Number(row.emailQuota), apiQuota: row.apiQuota === null ? null : Number(row.apiQuota), diff --git a/packages/trpc/server/admin-router/find-organisation-stats.types.ts b/packages/trpc/server/admin-router/find-organisation-stats.types.ts index ef8d0e36d..d6021ccd1 100644 --- a/packages/trpc/server/admin-router/find-organisation-stats.types.ts +++ b/packages/trpc/server/admin-router/find-organisation-stats.types.ts @@ -9,7 +9,7 @@ export const ZFindOrganisationStatsRequestSchema = ZFindSearchParamsSchema.exten .optional(), claimId: z.string().describe('Filter stats by the original subscription claim ID.').optional(), orderByColumn: z - .enum(['documentCount', 'emailCount', 'apiCount', 'emailReports', 'totalCount']) + .enum(['documentCount', 'emailCount', 'apiCount', 'emailReports', 'teamCount', 'totalCount']) .describe('The column to sort by.') .optional(), orderByDirection: z.enum(['asc', 'desc']).describe('Sort direction.').default('desc'), @@ -27,6 +27,7 @@ export const ZFindOrganisationStatsResponseSchema = ZFindResultResponse.extend({ emailCount: z.number(), apiCount: z.number(), emailReports: z.number(), + teamCount: z.number(), documentQuota: z.number().nullable(), emailQuota: z.number().nullable(), apiQuota: z.number().nullable(),