diff --git a/apps/landing/components/landing/LandingPage.tsx b/apps/landing/components/landing/LandingPage.tsx
index 41be1f73d..36350c836 100644
--- a/apps/landing/components/landing/LandingPage.tsx
+++ b/apps/landing/components/landing/LandingPage.tsx
@@ -1,3 +1,5 @@
+import { AnalyticStat } from '@jetstream/types';
+import AnalyticsSummary from './AnalyticsSummary';
import ConnectWithTeam from './ConnectWithTeam';
import FeatureGrid from './FeatureGrid';
import FeatureScreenshot from './FeatureScreenshot';
@@ -7,13 +9,12 @@ import Learn from './Learn';
import PersonaFeatures from './PersonaFeatures';
import Testimonial from './Testimonial';
-export const LandingPage = () => (
+export const LandingPage = ({ stats }: { stats?: AnalyticStat[] | null }) => (
- {/* Analytics tracking has been broken for some time - these numbers are not accurate */}
- {/* */}
+ {stats && stats.length > 0 && }
diff --git a/apps/landing/pages/index.tsx b/apps/landing/pages/index.tsx
index a97888218..026700c07 100644
--- a/apps/landing/pages/index.tsx
+++ b/apps/landing/pages/index.tsx
@@ -1,15 +1,18 @@
+import { AnalyticStat } from '@jetstream/types';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import LandingPage from '../components/landing/LandingPage';
-import { fetchBlogPosts } from '../utils/data';
+import { fetchAnalyticsSummary, fetchBlogPosts } from '../utils/data';
-export default function Page({ omitBlogPosts }: InferGetStaticPropsType) {
- return ;
+export default function Page({ stats }: InferGetStaticPropsType) {
+ return ;
}
// This also gets called at build time
export const getStaticProps: GetStaticProps<{
- omitBlogPosts: boolean;
+ stats: AnalyticStat[] | null;
}> = async () => {
- const blogPostsWithRelated = await fetchBlogPosts();
- return { props: { omitBlogPosts: Object.values(blogPostsWithRelated || {}).length === 0 } };
+ const [blogPostsWithRelated, stats] = await Promise.all([fetchBlogPosts(), fetchAnalyticsSummary()]);
+ // omitBlogPosts was unused by LandingPage, so we drop it here
+ void blogPostsWithRelated;
+ return { props: { stats } };
};
diff --git a/apps/landing/utils/data.ts b/apps/landing/utils/data.ts
index 58af81271..c8f6fc601 100644
--- a/apps/landing/utils/data.ts
+++ b/apps/landing/utils/data.ts
@@ -1,3 +1,4 @@
+import { AnalyticStat } from '@jetstream/types';
import { createClient } from 'contentful';
import { AuthorsById, BlogPost, BlogPostsBySlug, ContentfulBlogPostField, ContentfulIncludes } from './types';
@@ -68,3 +69,103 @@ export async function fetchBlogPosts() {
return blogPostsBySlug;
}
+
+const AMPLITUDE_CHART_IDS = {
+ LOAD: { YEAR: 'jgshgwcl', MONTH: 'iyt2blcf' },
+ QUERY: { YEAR: '4lacgp5q', MONTH: 'icruamqk' },
+ FIELD_CREATION: { YEAR: 'tyu5pjug', MONTH: 'adzowzyc' },
+ APEX_EXECUTED: { YEAR: 'afxl6h2d' },
+ DEPLOYMENTS: { YEAR: 'rz9tpgjy', MONTH: '262an8ek' },
+};
+
+function formatStatValue(value: number): string {
+ if (value >= 1_000_000_000) {
+ return `${(value / 1_000_000_000).toFixed(1)}B+`;
+ }
+ if (value >= 1_000_000) {
+ return `${(value / 1_000_000).toFixed(1)}M+`;
+ }
+ if (value >= 1_000) {
+ return `${Math.round(value / 1_000)}K+`;
+ }
+ return value.toLocaleString();
+}
+
+async function fetchAmplitudeChart(chartId: string, authHeader: string): Promise {
+ const response = await fetch(`https://amplitude.com/api/3/chart/${chartId}/query`, {
+ headers: { Authorization: authHeader },
+ });
+ if (!response.ok) {
+ throw new Error(`Amplitude API error for chart ${chartId}: ${response.status}`);
+ }
+ const data = await response.json();
+ return data.data.seriesCollapsed[0][0].value;
+}
+
+export async function fetchAnalyticsSummary(): Promise {
+ if (!process.env.AMPLITUDE_API_KEY || !process.env.AMPLITUDE_SECRET_KEY) {
+ return null;
+ }
+
+ try {
+ const authHeader = `Basic ${Buffer.from(`${process.env.AMPLITUDE_API_KEY}:${process.env.AMPLITUDE_SECRET_KEY}`).toString('base64')}`;
+
+ const loadYear = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.LOAD.YEAR, authHeader);
+ const loadMonth = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.LOAD.MONTH, authHeader);
+
+ const queryYear = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.QUERY.YEAR, authHeader);
+ const queryMonth = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.QUERY.MONTH, authHeader);
+
+ const fieldCreationYear = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.FIELD_CREATION.YEAR, authHeader);
+ const fieldCreationMonth = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.FIELD_CREATION.MONTH, authHeader);
+
+ const deploymentsYear = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.DEPLOYMENTS.YEAR, authHeader);
+ const deploymentsMonth = await fetchAmplitudeChart(AMPLITUDE_CHART_IDS.DEPLOYMENTS.MONTH, authHeader);
+
+ const lastUpdated = new Date().toISOString();
+
+ return [
+ { id: 'load-year', name: 'Records loaded in the past year', value: formatStatValue(loadYear), valueRaw: loadYear, lastUpdated },
+ { id: 'load-month', name: 'Records loaded in the past month', value: formatStatValue(loadMonth), valueRaw: loadMonth, lastUpdated },
+ { id: 'query-year', name: 'SOQL queries run in the past year', value: formatStatValue(queryYear), valueRaw: queryYear, lastUpdated },
+ {
+ id: 'query-month',
+ name: 'SOQL queries run in the past month',
+ value: formatStatValue(queryMonth),
+ valueRaw: queryMonth,
+ lastUpdated,
+ },
+ {
+ id: 'field-creation-year',
+ name: 'Fields created in the past year',
+ value: formatStatValue(fieldCreationYear),
+ valueRaw: fieldCreationYear,
+ lastUpdated,
+ },
+ {
+ id: 'field-creation-month',
+ name: 'Fields created in the past month',
+ value: formatStatValue(fieldCreationMonth),
+ valueRaw: fieldCreationMonth,
+ lastUpdated,
+ },
+ {
+ id: 'deployments-year',
+ name: 'Deployments in the past year',
+ value: formatStatValue(deploymentsYear),
+ valueRaw: deploymentsYear,
+ lastUpdated,
+ },
+ {
+ id: 'deployments-month',
+ name: 'Deployments in the past month',
+ value: formatStatValue(deploymentsMonth),
+ valueRaw: deploymentsMonth,
+ lastUpdated,
+ },
+ ];
+ } catch (error) {
+ console.error('[ANALYTICS SUMMARY] Error fetching analytics from Amplitude:', error);
+ return null;
+ }
+}