From 582c20170de195b0414284928fc5d130a45592df Mon Sep 17 00:00:00 2001 From: Dean Wenstrand Date: Sat, 11 Jul 2026 10:36:47 -0700 Subject: [PATCH 1/3] Add getUsageStats admin query + operation (schema regen on shot-lab tip) Co-Authored-By: Claude Fable 5 --- src/index.tsx | 170 +++++++++++++++++++++++++++++++++ src/operations/usage_stats.gql | 26 +++++ src/schema.gql | 32 +++++++ 3 files changed, 228 insertions(+) create mode 100644 src/operations/usage_stats.gql diff --git a/src/index.tsx b/src/index.tsx index cb56791..483f275 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3018,6 +3018,12 @@ export type ProcessingFailedErr = { processing: VideoProcessingGql; }; +export type ProcessingStatusCountGql = { + __typename?: "ProcessingStatusCountGQL"; + count: Scalars["Int"]["output"]; + status: Scalars["String"]["output"]; +}; + export enum ProcessingStatusEnum { Created = "CREATED", Failed = "FAILED", @@ -3065,6 +3071,7 @@ export type Query = { getShotsWithMetadata: GetShotsResult; getStorageStatus?: Maybe; getTableState: TableStateGql; + getUsageStats: UsageStatsGql; getUser?: Maybe; getUserRelationshipsMatching: UserRelationshipsResult; getUserSubscriptionStatus: UserSubscriptionStatusGql; @@ -3229,6 +3236,10 @@ export type QueryGetTableStateArgs = { useHomography?: InputMaybe; }; +export type QueryGetUsageStatsArgs = { + days?: Scalars["Int"]["input"]; +}; + export type QueryGetUserArgs = { userId: Scalars["Int"]["input"]; }; @@ -3944,6 +3955,35 @@ export type UploadStreamGql = { uploadsCompleted: Scalars["Int"]["output"]; }; +export type UsageStatsDailyGql = { + __typename?: "UsageStatsDailyGQL"; + activeUsers: Scalars["Int"]["output"]; + day: Scalars["Date"]["output"]; + newUsers: Scalars["Int"]["output"]; + sessions: Scalars["Int"]["output"]; + shots: Scalars["Int"]["output"]; +}; + +export type UsageStatsGql = { + __typename?: "UsageStatsGQL"; + daily: Array; + processingLast24h: Array; + totalRuns: Scalars["Int"]["output"]; + totalShots: Scalars["Int"]["output"]; + totalUsers: Scalars["Int"]["output"]; + totalVideos: Scalars["Int"]["output"]; + windows: Array; +}; + +export type UsageStatsWindowGql = { + __typename?: "UsageStatsWindowGQL"; + activeUsers: Scalars["Int"]["output"]; + label: Scalars["String"]["output"]; + newUsers: Scalars["Int"]["output"]; + sessions: Scalars["Int"]["output"]; + shots: Scalars["Int"]["output"]; +}; + export type UserGql = { __typename?: "UserGQL"; activeVideoId?: Maybe; @@ -7395,6 +7435,42 @@ export type DeleteTagsMutation = { deleteTags: boolean; }; +export type GetUsageStatsQueryVariables = Exact<{ + days?: Scalars["Int"]["input"]; +}>; + +export type GetUsageStatsQuery = { + __typename?: "Query"; + getUsageStats: { + __typename?: "UsageStatsGQL"; + totalUsers: number; + totalVideos: number; + totalShots: number; + totalRuns: number; + windows: Array<{ + __typename?: "UsageStatsWindowGQL"; + label: string; + newUsers: number; + activeUsers: number; + sessions: number; + shots: number; + }>; + daily: Array<{ + __typename?: "UsageStatsDailyGQL"; + day: any; + newUsers: number; + activeUsers: number; + sessions: number; + shots: number; + }>; + processingLast24h: Array<{ + __typename?: "ProcessingStatusCountGQL"; + status: string; + count: number; + }>; + }; +}; + export type GetProfileImageUploadLinkMutationVariables = Exact<{ fileExt?: InputMaybe; }>; @@ -15669,6 +15745,100 @@ export type DeleteTagsMutationOptions = Apollo.BaseMutationOptions< DeleteTagsMutation, DeleteTagsMutationVariables >; +export const GetUsageStatsDocument = gql` + query getUsageStats($days: Int! = 30) { + getUsageStats(days: $days) { + totalUsers + totalVideos + totalShots + totalRuns + windows { + label + newUsers + activeUsers + sessions + shots + } + daily { + day + newUsers + activeUsers + sessions + shots + } + processingLast24h { + status + count + } + } + } +`; + +/** + * __useGetUsageStatsQuery__ + * + * To run a query within a React component, call `useGetUsageStatsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetUsageStatsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetUsageStatsQuery({ + * variables: { + * days: // value for 'days' + * }, + * }); + */ +export function useGetUsageStatsQuery( + baseOptions?: Apollo.QueryHookOptions< + GetUsageStatsQuery, + GetUsageStatsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + GetUsageStatsDocument, + options, + ); +} +export function useGetUsageStatsLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + GetUsageStatsQuery, + GetUsageStatsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + GetUsageStatsDocument, + options, + ); +} +export function useGetUsageStatsSuspenseQuery( + baseOptions?: Apollo.SuspenseQueryHookOptions< + GetUsageStatsQuery, + GetUsageStatsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useSuspenseQuery< + GetUsageStatsQuery, + GetUsageStatsQueryVariables + >(GetUsageStatsDocument, options); +} +export type GetUsageStatsQueryHookResult = ReturnType< + typeof useGetUsageStatsQuery +>; +export type GetUsageStatsLazyQueryHookResult = ReturnType< + typeof useGetUsageStatsLazyQuery +>; +export type GetUsageStatsSuspenseQueryHookResult = ReturnType< + typeof useGetUsageStatsSuspenseQuery +>; +export type GetUsageStatsQueryResult = Apollo.QueryResult< + GetUsageStatsQuery, + GetUsageStatsQueryVariables +>; export const GetProfileImageUploadLinkDocument = gql` mutation getProfileImageUploadLink($fileExt: String = ".png") { getProfileImageUploadLink(fileExt: $fileExt) { diff --git a/src/operations/usage_stats.gql b/src/operations/usage_stats.gql new file mode 100644 index 0000000..c6090d9 --- /dev/null +++ b/src/operations/usage_stats.gql @@ -0,0 +1,26 @@ +query getUsageStats($days: Int! = 30) { + getUsageStats(days: $days) { + totalUsers + totalVideos + totalShots + totalRuns + windows { + label + newUsers + activeUsers + sessions + shots + } + daily { + day + newUsers + activeUsers + sessions + shots + } + processingLast24h { + status + count + } + } +} diff --git a/src/schema.gql b/src/schema.gql index 844aefd..465f9be 100644 --- a/src/schema.gql +++ b/src/schema.gql @@ -100,6 +100,7 @@ type Query { countRespectsLimit: Boolean! = false ): [ShotGQL!]! getShotsByIds(ids: [Int!]!): [ShotGQL!]! + getUsageStats(days: Int! = 30): UsageStatsGQL! getUser(userId: Int!): UserGQL doesUsernameExist(candidateUsername: String!): Boolean! getLoggedInUser: UserGQL @@ -1168,6 +1169,37 @@ input CreatedAfter @oneOf { createdAt: DateTime } +type UsageStatsGQL { + totalUsers: Int! + totalVideos: Int! + totalShots: Int! + totalRuns: Int! + windows: [UsageStatsWindowGQL!]! + daily: [UsageStatsDailyGQL!]! + processingLast24h: [ProcessingStatusCountGQL!]! +} + +type UsageStatsWindowGQL { + label: String! + newUsers: Int! + activeUsers: Int! + sessions: Int! + shots: Int! +} + +type UsageStatsDailyGQL { + day: Date! + newUsers: Int! + activeUsers: Int! + sessions: Int! + shots: Int! +} + +type ProcessingStatusCountGQL { + status: String! + count: Int! +} + type UserRelationshipsResult { inquiringUser: UserGQL! relationships: [UserRelationship!]! -- 2.49.1 From 5c0ff8c31b26befc193371f62c2ad37c71b5cba8 Mon Sep 17 00:00:00 2001 From: Dean Wenstrand Date: Sat, 11 Jul 2026 10:48:25 -0700 Subject: [PATCH 2/3] Add freemium funnel to getUsageStats Co-Authored-By: Claude Fable 5 --- src/index.tsx | 31 +++++++++++++++++++++++++++++++ src/operations/usage_stats.gql | 9 +++++++++ src/schema.gql | 11 +++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/index.tsx b/src/index.tsx index 483f275..62df1f9 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2299,6 +2299,17 @@ export type FloatRangeFilter = { lessThanInclusive?: Scalars["Boolean"]["input"]; }; +export type FunnelStatsGql = { + __typename?: "FunnelStatsGQL"; + activationActivated: Scalars["Int"]["output"]; + activationCohort: Scalars["Int"]["output"]; + newPaid7d: Scalars["Int"]["output"]; + newPaid30d: Scalars["Int"]["output"]; + payingUsers: Scalars["Int"]["output"]; + retentionPrevActive: Scalars["Int"]["output"]; + retentionReturned: Scalars["Int"]["output"]; +}; + export type GameTypeTagMetric = { __typename?: "GameTypeTagMetric"; madeShots: Scalars["Int"]["output"]; @@ -3967,6 +3978,7 @@ export type UsageStatsDailyGql = { export type UsageStatsGql = { __typename?: "UsageStatsGQL"; daily: Array; + funnel: FunnelStatsGql; processingLast24h: Array; totalRuns: Scalars["Int"]["output"]; totalShots: Scalars["Int"]["output"]; @@ -7447,6 +7459,16 @@ export type GetUsageStatsQuery = { totalVideos: number; totalShots: number; totalRuns: number; + funnel: { + __typename?: "FunnelStatsGQL"; + activationCohort: number; + activationActivated: number; + payingUsers: number; + newPaid7d: number; + newPaid30d: number; + retentionPrevActive: number; + retentionReturned: number; + }; windows: Array<{ __typename?: "UsageStatsWindowGQL"; label: string; @@ -15748,6 +15770,15 @@ export type DeleteTagsMutationOptions = Apollo.BaseMutationOptions< export const GetUsageStatsDocument = gql` query getUsageStats($days: Int! = 30) { getUsageStats(days: $days) { + funnel { + activationCohort + activationActivated + payingUsers + newPaid7d + newPaid30d + retentionPrevActive + retentionReturned + } totalUsers totalVideos totalShots diff --git a/src/operations/usage_stats.gql b/src/operations/usage_stats.gql index c6090d9..d919c46 100644 --- a/src/operations/usage_stats.gql +++ b/src/operations/usage_stats.gql @@ -1,5 +1,14 @@ query getUsageStats($days: Int! = 30) { getUsageStats(days: $days) { + funnel { + activationCohort + activationActivated + payingUsers + newPaid7d + newPaid30d + retentionPrevActive + retentionReturned + } totalUsers totalVideos totalShots diff --git a/src/schema.gql b/src/schema.gql index 465f9be..183b604 100644 --- a/src/schema.gql +++ b/src/schema.gql @@ -1170,6 +1170,7 @@ input CreatedAfter @oneOf { } type UsageStatsGQL { + funnel: FunnelStatsGQL! totalUsers: Int! totalVideos: Int! totalShots: Int! @@ -1179,6 +1180,16 @@ type UsageStatsGQL { processingLast24h: [ProcessingStatusCountGQL!]! } +type FunnelStatsGQL { + activationCohort: Int! + activationActivated: Int! + payingUsers: Int! + newPaid7d: Int! + newPaid30d: Int! + retentionPrevActive: Int! + retentionReturned: Int! +} + type UsageStatsWindowGQL { label: String! newUsers: Int! -- 2.49.1 From 6f356f9535bbdd13be915cc7ce7c69b454c9fab1 Mon Sep 17 00:00:00 2001 From: Dean Wenstrand Date: Sat, 11 Jul 2026 11:38:20 -0700 Subject: [PATCH 3/3] Add freemium epoch + era conversion to funnel Co-Authored-By: Claude Fable 5 --- src/index.tsx | 9 +++++++++ src/operations/usage_stats.gql | 3 +++ src/schema.gql | 3 +++ 3 files changed, 15 insertions(+) diff --git a/src/index.tsx b/src/index.tsx index 62df1f9..cac063d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2303,6 +2303,9 @@ export type FunnelStatsGql = { __typename?: "FunnelStatsGQL"; activationActivated: Scalars["Int"]["output"]; activationCohort: Scalars["Int"]["output"]; + eraPaying: Scalars["Int"]["output"]; + eraSignups: Scalars["Int"]["output"]; + freemiumEpoch: Scalars["Date"]["output"]; newPaid7d: Scalars["Int"]["output"]; newPaid30d: Scalars["Int"]["output"]; payingUsers: Scalars["Int"]["output"]; @@ -7461,6 +7464,9 @@ export type GetUsageStatsQuery = { totalRuns: number; funnel: { __typename?: "FunnelStatsGQL"; + freemiumEpoch: any; + eraSignups: number; + eraPaying: number; activationCohort: number; activationActivated: number; payingUsers: number; @@ -15771,6 +15777,9 @@ export const GetUsageStatsDocument = gql` query getUsageStats($days: Int! = 30) { getUsageStats(days: $days) { funnel { + freemiumEpoch + eraSignups + eraPaying activationCohort activationActivated payingUsers diff --git a/src/operations/usage_stats.gql b/src/operations/usage_stats.gql index d919c46..8d6960d 100644 --- a/src/operations/usage_stats.gql +++ b/src/operations/usage_stats.gql @@ -1,6 +1,9 @@ query getUsageStats($days: Int! = 30) { getUsageStats(days: $days) { funnel { + freemiumEpoch + eraSignups + eraPaying activationCohort activationActivated payingUsers diff --git a/src/schema.gql b/src/schema.gql index 183b604..5c0b37f 100644 --- a/src/schema.gql +++ b/src/schema.gql @@ -1181,6 +1181,9 @@ type UsageStatsGQL { } type FunnelStatsGQL { + freemiumEpoch: Date! + eraSignups: Int! + eraPaying: Int! activationCohort: Int! activationActivated: Int! payingUsers: Int! -- 2.49.1