diff --git a/apps/web/scripts/generate-analytics.ts b/apps/web/scripts/generate-analytics.ts
index 0de198f..f26108b 100644
--- a/apps/web/scripts/generate-analytics.ts
+++ b/apps/web/scripts/generate-analytics.ts
@@ -99,7 +99,7 @@ async function generateAnalyticsData() {
.filter((row) => row.trim().length > 0)
.map((row) => row.trim());
- csvText = header + "\n" + dataRows.join("\n");
+ csvText = `${header}\n${dataRows.join("\n")}`;
console.log(`✅ Manually parsed ${dataRows.length} rows`);
}
}
@@ -145,7 +145,7 @@ async function generateAnalyticsData() {
results.data.forEach((row, index) => {
// Skip rows that don't have essential data
- if (!row["*.timestamp"] && !row["timestamp"]) {
+ if (!row["*.timestamp"] && !row.timestamp) {
if (index < 5) {
console.log(
`⚠️ Skipping row ${index} - no timestamp:`,
@@ -156,9 +156,7 @@ async function generateAnalyticsData() {
}
const timestamp =
- row["*.timestamp"] ||
- row["timestamp"] ||
- new Date().toISOString();
+ row["*.timestamp"] || row.timestamp || new Date().toISOString();
const date = timestamp.includes("T")
? timestamp.split("T")[0]
: timestamp.split(" ")[0];
diff --git a/apps/web/src/app/(home)/analytics/_components/addons-examples-charts.tsx b/apps/web/src/app/(home)/analytics/_components/addons-examples-charts.tsx
new file mode 100644
index 0000000..b0e1619
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/addons-examples-charts.tsx
@@ -0,0 +1,114 @@
+import { Bar, BarChart, CartesianGrid, Cell, XAxis, YAxis } from "recharts";
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import { getAddonsData, getExamplesData } from "./data-utils";
+import type { AggregatedAnalyticsData } from "./types";
+import { addonsConfig, examplesConfig } from "./types";
+
+interface AddonsExamplesChartsProps {
+ data: AggregatedAnalyticsData | null;
+}
+
+export function AddonsExamplesCharts({ data }: AddonsExamplesChartsProps) {
+ const addonsData = getAddonsData(data);
+ const examplesData = getExamplesData(data);
+
+ return (
+ <>
+
+
+
+ ▶
+ ADDONS_USAGE.BAR
+
+
+ Additional features and tooling adoption
+
+
+
+
+
+
+
+
+ } />
+
+ {addonsData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ ▶
+ EXAMPLES_USAGE.BAR
+
+
+ Example applications included in projects
+
+
+
+
+
+
+
+
+ } />
+
+ {examplesData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx b/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx
new file mode 100644
index 0000000..9abfa28
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/analytics-header.tsx
@@ -0,0 +1,112 @@
+import { Terminal } from "lucide-react";
+import Image from "next/image";
+import Link from "next/link";
+import discordIcon from "@/public/icon/discord.svg";
+
+interface AnalyticsHeaderProps {
+ totalProjects: number;
+ lastUpdated: string | null;
+ loadingLastUpdated: boolean;
+}
+
+export function AnalyticsHeader({
+ totalProjects,
+ lastUpdated,
+ loadingLastUpdated,
+}: AnalyticsHeaderProps) {
+ return (
+
+
+
+
+
+ ANALYTICS_DASHBOARD.SH
+
+
+
+
+ [{totalProjects} PROJECTS_ANALYZED]
+
+
+
+
+
+ $
+
+ Analytics from Better-T-Stack CLI usage data
+
+
+
+ $
+
+ Uses PostHog - no personal info tracked, runs on each project
+ creation
+
+
+
+ $
+
+ Source:{" "}
+
+ analytics.ts
+
+ {" | "}
+
+ export.csv
+
+
+
+
+ $
+
+ Last updated:{" "}
+ {loadingLastUpdated
+ ? "CHECKING..."
+ : lastUpdated
+ ? `${lastUpdated} UTC`
+ : "UNKNOWN"}
+
+
+
+
+
+
+
+
+
+
+ DISCORD_NOTIFICATIONS.IRC
+
+
+ Join for LIVE project creation alerts
+
+
+
+
+ ▶
+ JOIN
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(home)/analytics/_components/data-utils.ts b/apps/web/src/app/(home)/analytics/_components/data-utils.ts
new file mode 100644
index 0000000..4105f72
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/data-utils.ts
@@ -0,0 +1,119 @@
+import type { AggregatedAnalyticsData } from "./types";
+
+export const getPlatformData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.platformDistribution || [];
+};
+
+export const getPackageManagerData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.packageManagerDistribution || [];
+};
+
+export const getBackendData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.backendDistribution || [];
+};
+
+export const getDatabaseData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.databaseDistribution || [];
+};
+
+export const getORMData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.ormDistribution || [];
+};
+
+export const getDBSetupData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.dbSetupDistribution || [];
+};
+
+export const getAPIData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.apiDistribution || [];
+};
+
+export const getFrontendData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.frontendDistribution || [];
+};
+
+export const getTimeSeriesData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.timeSeries || [];
+};
+
+export const getNodeVersionData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.nodeVersionDistribution || [];
+};
+
+export const getCLIVersionData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.cliVersionDistribution || [];
+};
+
+export const getAuthData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.authDistribution || [];
+};
+
+export const getGitData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.gitDistribution || [];
+};
+
+export const getInstallData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.installDistribution || [];
+};
+
+export const getExamplesData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.examplesDistribution || [];
+};
+
+export const getAddonsData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.addonsDistribution || [];
+};
+
+export const getRuntimeData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.runtimeDistribution || [];
+};
+
+export const getProjectTypeData = (data: AggregatedAnalyticsData | null) => {
+ if (!data) return [];
+ return data.projectTypeDistribution || [];
+};
+
+export const getMonthlyTimeSeriesData = (
+ data: AggregatedAnalyticsData | null,
+) => {
+ if (!data) return [];
+ return data.monthlyTimeSeries || [];
+};
+
+export const getPopularStackCombinations = (
+ data: AggregatedAnalyticsData | null,
+) => {
+ if (!data) return [];
+ return data.popularStackCombinations || [];
+};
+
+export const getDatabaseORMCombinations = (
+ data: AggregatedAnalyticsData | null,
+) => {
+ if (!data) return [];
+ return data.databaseORMCombinations || [];
+};
+
+export const getHourlyDistributionData = (
+ data: AggregatedAnalyticsData | null,
+) => {
+ if (!data) return [];
+ return data.hourlyDistribution || [];
+};
diff --git a/apps/web/src/app/(home)/analytics/_components/dev-environment-charts.tsx b/apps/web/src/app/(home)/analytics/_components/dev-environment-charts.tsx
new file mode 100644
index 0000000..b157540
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/dev-environment-charts.tsx
@@ -0,0 +1,261 @@
+import {
+ Bar,
+ BarChart,
+ CartesianGrid,
+ Cell,
+ Pie,
+ PieChart,
+ XAxis,
+ YAxis,
+} from "recharts";
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import {
+ getCLIVersionData,
+ getGitData,
+ getInstallData,
+ getNodeVersionData,
+ getPackageManagerData,
+} from "./data-utils";
+import type { AggregatedAnalyticsData } from "./types";
+import {
+ cliVersionConfig,
+ gitConfig,
+ installConfig,
+ nodeVersionConfig,
+ packageManagerConfig,
+} from "./types";
+
+interface DevEnvironmentChartsProps {
+ data: AggregatedAnalyticsData | null;
+}
+
+export function DevEnvironmentCharts({ data }: DevEnvironmentChartsProps) {
+ const gitData = getGitData(data);
+ const packageManagerData = getPackageManagerData(data);
+ const installData = getInstallData(data);
+ const nodeVersionData = getNodeVersionData(data);
+ const cliVersionData = getCLIVersionData(data);
+
+ return (
+
+
+
DEV_ENVIRONMENT.CONFIG
+
+
+
+
+
+
+
+ ▶
+
+ GIT_INITIALIZATION.PIE
+
+
+
+ Git repository initialization rate
+
+
+
+
+
+ }
+ />
+
+ `${name} ${(percent * 100).toFixed(0)}%`
+ }
+ >
+ {gitData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+ ▶
+
+ PACKAGE_MANAGER.BAR
+
+
+
+ Package manager usage distribution
+
+
+
+
+
+
+
+
+ } />
+
+ {packageManagerData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ ▶
+
+ INSTALL_PREFERENCE.PIE
+
+
+
+ Automatic dependency installation preference
+
+
+
+
+
+ }
+ />
+
+ `${name} ${(percent * 100).toFixed(0)}%`
+ }
+ >
+ {installData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+ ▶
+ NODE_VERSIONS.BAR
+
+
+ Node.js version distribution (major versions)
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+
+
+ ▶
+ CLI_VERSIONS.BAR
+
+
+ CLI version distribution across project creations
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(home)/analytics/_components/index.ts b/apps/web/src/app/(home)/analytics/_components/index.ts
new file mode 100644
index 0000000..4dea1d8
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/index.ts
@@ -0,0 +1,8 @@
+export { AddonsExamplesCharts } from "./addons-examples-charts";
+export { AnalyticsHeader } from "./analytics-header";
+export * from "./data-utils";
+export { DevEnvironmentCharts } from "./dev-environment-charts";
+export { MetricsCards } from "./metrics-cards";
+export { StackConfigurationCharts } from "./stack-configuration-charts";
+export { TimelineCharts } from "./timeline-charts";
+export * from "./types";
diff --git a/apps/web/src/app/(home)/analytics/_components/metrics-cards.tsx b/apps/web/src/app/(home)/analytics/_components/metrics-cards.tsx
new file mode 100644
index 0000000..521d1c2
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/metrics-cards.tsx
@@ -0,0 +1,170 @@
+import { Cpu, Download, Terminal, TrendingUp, Users } from "lucide-react";
+
+interface MetricsCardsProps {
+ totalProjects: number;
+ avgProjectsPerDay: number;
+ authEnabledPercent: number;
+ mostPopularFrontend: string;
+ mostPopularBackend: string;
+ mostPopularORM: string;
+ mostPopularAPI: string;
+ mostPopularPackageManager: string;
+}
+
+export function MetricsCards({
+ totalProjects,
+ avgProjectsPerDay,
+ authEnabledPercent,
+ mostPopularFrontend,
+ mostPopularBackend,
+ mostPopularORM,
+ mostPopularAPI,
+ mostPopularPackageManager,
+}: MetricsCardsProps) {
+ return (
+
+
+
+
+
+
+
+
+ {totalProjects.toLocaleString()}
+
+
+ $ ./create-better-t-stack executions
+
+
+
+
+
+
+
+
+ {mostPopularFrontend}
+
+
+ $ most_selected_frontend.sh
+
+
+
+
+
+
+
+
+ {mostPopularBackend}
+
+
+ $ most_selected_backend.sh
+
+
+
+
+
+
+
+
+ {mostPopularORM}
+
+
+ $ most_selected_orm.sh
+
+
+
+
+
+
+
+
+ {mostPopularAPI}
+
+
+ $ most_selected_api.sh
+
+
+
+
+
+
+
+
+ {authEnabledPercent}%
+
+
+ $ auth_enabled_percentage.sh
+
+
+
+
+
+
+
+
+ {mostPopularPackageManager}
+
+
+ $ most_used_package_manager.sh
+
+
+
+
+
+
+
+
+ {avgProjectsPerDay.toFixed(1)}
+
+
+ $ average_projects_per_day.sh
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(home)/analytics/_components/stack-configuration-charts.tsx b/apps/web/src/app/(home)/analytics/_components/stack-configuration-charts.tsx
new file mode 100644
index 0000000..2ad1ed3
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/stack-configuration-charts.tsx
@@ -0,0 +1,583 @@
+import {
+ Bar,
+ BarChart,
+ CartesianGrid,
+ Cell,
+ Pie,
+ PieChart,
+ XAxis,
+ YAxis,
+} from "recharts";
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import {
+ getAPIData,
+ getAuthData,
+ getBackendData,
+ getDatabaseData,
+ getDatabaseORMCombinations,
+ getDBSetupData,
+ getFrontendData,
+ getORMData,
+ getPopularStackCombinations,
+ getProjectTypeData,
+ getRuntimeData,
+} from "./data-utils";
+import type { AggregatedAnalyticsData } from "./types";
+import {
+ apiConfig,
+ authConfig,
+ backendConfig,
+ databaseConfig,
+ dbSetupConfig,
+ frontendConfig,
+ ormConfig,
+ projectTypeConfig,
+ runtimeConfig,
+} from "./types";
+
+interface StackConfigurationChartsProps {
+ data: AggregatedAnalyticsData | null;
+}
+
+export function StackConfigurationCharts({
+ data,
+}: StackConfigurationChartsProps) {
+ const backendData = getBackendData(data);
+ const databaseData = getDatabaseData(data);
+ const ormData = getORMData(data);
+ const dbSetupData = getDBSetupData(data);
+ const apiData = getAPIData(data);
+ const frontendData = getFrontendData(data);
+ const authData = getAuthData(data);
+ const runtimeData = getRuntimeData(data);
+ const projectTypeData = getProjectTypeData(data);
+ const popularStackCombinations = getPopularStackCombinations(data);
+ const databaseORMCombinations = getDatabaseORMCombinations(data);
+
+ return (
+
+
+
+
+ STACK_CONFIGURATION.DB
+
+
+
+
+ [CORE_COMPONENTS]
+
+
+
+
+
+
+ ▶
+
+ POPULAR_STACK_COMBINATIONS.BAR
+
+
+
+ Most popular frontend + backend combinations
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+
+ ▶
+
+ FRONTEND_FRAMEWORKS.BAR
+
+
+
+ Frontend framework and meta-framework usage
+
+
+
+
+
+
+
+
+ } />
+
+ {frontendData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+
+ ▶
+
+ BACKEND_FRAMEWORKS.BAR
+
+
+
+ Backend framework distribution
+
+
+
+
+
+
+
+
+ } />
+
+ {backendData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ ▶
+
+ DATABASE_DISTRIBUTION.BAR
+
+
+
+ Database technology distribution
+
+
+
+
+
+
+
+
+ } />
+
+ {databaseData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ ▶
+
+ ORM_DISTRIBUTION.BAR
+
+
+
+ ORM/Database layer distribution
+
+
+
+
+
+
+
+
+ } />
+
+ {ormData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ ▶
+
+ DATABASE_HOSTING.BAR
+
+
+
+ Database hosting service preferences
+
+
+
+
+
+
+
+
+ } />
+
+ {dbSetupData.map((entry) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ ▶
+ API_LAYER.PIE
+
+
+ API layer technology distribution
+
+
+
+
+
+ }
+ />
+
+ {apiData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+ ▶
+ AUTH_ADOPTION.PIE
+
+
+ Authentication implementation rate
+
+
+
+
+
+ }
+ />
+
+ `${name} ${(percent * 100).toFixed(0)}%`
+ }
+ >
+ {authData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+ ▶
+
+ RUNTIME_DISTRIBUTION.PIE
+
+
+
+ JavaScript runtime preference distribution
+
+
+
+
+
+ }
+ />
+
+ `${name} ${(percent * 100).toFixed(0)}%`
+ }
+ >
+ {runtimeData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+ ▶
+ PROJECT_TYPES.PIE
+
+
+ Full-stack vs Frontend-only vs Backend-only projects
+
+
+
+
+
+ }
+ />
+
+ `${name} ${(percent * 100).toFixed(0)}%`
+ }
+ >
+ {projectTypeData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+
+ ▶
+
+ DATABASE_ORM_COMBINATIONS.BAR
+
+
+
+ Popular database + ORM combinations
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(home)/analytics/_components/timeline-charts.tsx b/apps/web/src/app/(home)/analytics/_components/timeline-charts.tsx
new file mode 100644
index 0000000..6535ef7
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/timeline-charts.tsx
@@ -0,0 +1,226 @@
+import { format, parseISO } from "date-fns";
+import {
+ Area,
+ AreaChart,
+ Bar,
+ BarChart,
+ CartesianGrid,
+ Cell,
+ Pie,
+ PieChart,
+ XAxis,
+ YAxis,
+} from "recharts";
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import {
+ getHourlyDistributionData,
+ getMonthlyTimeSeriesData,
+ getPlatformData,
+ getTimeSeriesData,
+} from "./data-utils";
+import type { AggregatedAnalyticsData } from "./types";
+import {
+ hourlyDistributionConfig,
+ platformConfig,
+ timeSeriesConfig,
+} from "./types";
+
+interface TimelineChartsProps {
+ data: AggregatedAnalyticsData | null;
+}
+
+export function TimelineCharts({ data }: TimelineChartsProps) {
+ const timeSeriesData = getTimeSeriesData(data);
+ const monthlyTimeSeriesData = getMonthlyTimeSeriesData(data);
+ const platformData = getPlatformData(data);
+ const hourlyDistributionData = getHourlyDistributionData(data);
+
+ return (
+
+
+
TIMELINE_ANALYSIS.CHARTS
+
+
+
+
+
+
+
+ ▶
+
+ PROJECT_TIMELINE.CHART
+
+
+
+ Daily project creation timeline from actual data
+
+
+
+
+
+
+
+
+ }
+ labelFormatter={(value, payload) => {
+ const date = payload?.[0]?.payload?.date;
+ return date
+ ? format(parseISO(date), "MMM dd, yyyy")
+ : value;
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ ▶
+
+ MONTHLY_TRENDS.CHART
+
+
+
+ Monthly project creation trends
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+
+ ▶
+
+ PLATFORM_DISTRIBUTION.PIE
+
+
+
+ Operating system distribution
+
+
+
+
+
+ }
+ />
+
+ `${name} ${(percent * 100).toFixed(0)}%`
+ }
+ outerRadius={80}
+ fill="hsl(var(--chart-1))"
+ dataKey="value"
+ >
+ {platformData.map((entry) => (
+ |
+ ))}
+
+ } />
+
+
+
+
+
+
+
+
+ ▶
+
+ HOURLY_DISTRIBUTION.BAR
+
+
+
+ Projects created by hour of day (UTC)
+
+
+
+
+
+
+
+
+ }
+ labelFormatter={(value, payload) => {
+ const hour = payload?.[0]?.payload?.displayHour;
+ return hour ? `${hour} UTC` : value;
+ }}
+ />
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(home)/analytics/_components/types.ts b/apps/web/src/app/(home)/analytics/_components/types.ts
new file mode 100644
index 0000000..461a1e9
--- /dev/null
+++ b/apps/web/src/app/(home)/analytics/_components/types.ts
@@ -0,0 +1,404 @@
+import type { ChartConfig } from "@/components/ui/chart";
+
+export interface AggregatedAnalyticsData {
+ lastUpdated: string | null;
+ generatedAt: string;
+ totalRecords: number;
+ timeSeries: Array<{ date: string; displayDate: string; count: number }>;
+ monthlyTimeSeries: Array<{
+ month: string;
+ displayMonth: string;
+ count: number;
+ }>;
+ platformDistribution: Array<{ name: string; value: number }>;
+ packageManagerDistribution: Array<{ name: string; value: number }>;
+ backendDistribution: Array<{ name: string; value: number }>;
+ databaseDistribution: Array<{ name: string; value: number }>;
+ ormDistribution: Array<{ name: string; value: number }>;
+ dbSetupDistribution: Array<{ name: string; value: number }>;
+ apiDistribution: Array<{ name: string; value: number }>;
+ frontendDistribution: Array<{ name: string; value: number }>;
+ nodeVersionDistribution: Array<{ version: string; count: number }>;
+ cliVersionDistribution: Array<{ version: string; count: number }>;
+ authDistribution: Array<{ name: string; value: number }>;
+ gitDistribution: Array<{ name: string; value: number }>;
+ installDistribution: Array<{ name: string; value: number }>;
+ examplesDistribution: Array<{ name: string; value: number }>;
+ addonsDistribution: Array<{ name: string; value: number }>;
+ runtimeDistribution: Array<{ name: string; value: number }>;
+ projectTypeDistribution: Array<{ name: string; value: number }>;
+ popularStackCombinations: Array<{ name: string; value: number }>;
+ databaseORMCombinations: Array<{ name: string; value: number }>;
+ hourlyDistribution: Array<{
+ hour: string;
+ displayHour: string;
+ count: number;
+ }>;
+ summary: {
+ totalProjects: number;
+ avgProjectsPerDay: number;
+ authEnabledPercent: number;
+ mostPopularFrontend: string;
+ mostPopularBackend: string;
+ mostPopularORM: string;
+ mostPopularAPI: string;
+ mostPopularPackageManager: string;
+ };
+}
+
+export const timeSeriesConfig = {
+ projects: {
+ label: "Projects Created",
+ color: "hsl(var(--chart-1))",
+ },
+} satisfies ChartConfig;
+
+export const platformConfig = {
+ darwin: {
+ label: "macOS",
+ color: "hsl(var(--chart-1))",
+ },
+ linux: {
+ label: "Linux",
+ color: "hsl(var(--chart-2))",
+ },
+ win32: {
+ label: "Windows",
+ color: "hsl(var(--chart-3))",
+ },
+ android: {
+ label: "Android",
+ color: "hsl(var(--chart-4))",
+ },
+} satisfies ChartConfig;
+
+export const packageManagerConfig = {
+ npm: {
+ label: "npm",
+ color: "hsl(var(--chart-1))",
+ },
+ pnpm: {
+ label: "pnpm",
+ color: "hsl(var(--chart-2))",
+ },
+ bun: {
+ label: "bun",
+ color: "hsl(var(--chart-3))",
+ },
+} satisfies ChartConfig;
+
+export const backendConfig = {
+ hono: {
+ label: "Hono",
+ color: "hsl(var(--chart-1))",
+ },
+ express: {
+ label: "Express",
+ color: "hsl(var(--chart-2))",
+ },
+ fastify: {
+ label: "Fastify",
+ color: "hsl(var(--chart-3))",
+ },
+ next: {
+ label: "Next.js",
+ color: "hsl(var(--chart-4))",
+ },
+ elysia: {
+ label: "Elysia",
+ color: "hsl(var(--chart-5))",
+ },
+ convex: {
+ label: "Convex",
+ color: "hsl(var(--chart-6))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-7))",
+ },
+} satisfies ChartConfig;
+
+export const databaseConfig = {
+ sqlite: {
+ label: "SQLite",
+ color: "hsl(var(--chart-1))",
+ },
+ postgres: {
+ label: "PostgreSQL",
+ color: "hsl(var(--chart-2))",
+ },
+ mysql: {
+ label: "MySQL",
+ color: "hsl(var(--chart-3))",
+ },
+ mongodb: {
+ label: "MongoDB",
+ color: "hsl(var(--chart-4))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-5))",
+ },
+} satisfies ChartConfig;
+
+export const ormConfig = {
+ drizzle: {
+ label: "Drizzle",
+ color: "hsl(var(--chart-1))",
+ },
+ prisma: {
+ label: "Prisma",
+ color: "hsl(var(--chart-2))",
+ },
+ mongoose: {
+ label: "Mongoose",
+ color: "hsl(var(--chart-3))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-4))",
+ },
+} satisfies ChartConfig;
+
+export const dbSetupConfig = {
+ turso: {
+ label: "Turso",
+ color: "hsl(var(--chart-1))",
+ },
+ "prisma-postgres": {
+ label: "Prisma Postgres",
+ color: "hsl(var(--chart-2))",
+ },
+ "mongodb-atlas": {
+ label: "MongoDB Atlas",
+ color: "hsl(var(--chart-3))",
+ },
+ neon: {
+ label: "Neon",
+ color: "hsl(var(--chart-4))",
+ },
+ supabase: {
+ label: "Supabase",
+ color: "hsl(var(--chart-5))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-6))",
+ },
+} satisfies ChartConfig;
+
+export const apiConfig = {
+ trpc: {
+ label: "tRPC",
+ color: "hsl(var(--chart-1))",
+ },
+ orpc: {
+ label: "oRPC",
+ color: "hsl(var(--chart-2))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-3))",
+ },
+} satisfies ChartConfig;
+
+export const frontendConfig = {
+ "react-router": {
+ label: "React Router",
+ color: "hsl(var(--chart-1))",
+ },
+ "tanstack-router": {
+ label: "TanStack Router",
+ color: "hsl(var(--chart-2))",
+ },
+ "tanstack-start": {
+ label: "TanStack Start",
+ color: "hsl(var(--chart-3))",
+ },
+ next: {
+ label: "Next",
+ color: "hsl(var(--chart-4))",
+ },
+ nuxt: {
+ label: "Nuxt",
+ color: "hsl(var(--chart-5))",
+ },
+ "native-nativewind": {
+ label: "Expo NativeWind",
+ color: "hsl(var(--chart-6))",
+ },
+ "native-unistyles": {
+ label: "Expo Unistyles",
+ color: "hsl(var(--chart-7))",
+ },
+ svelte: {
+ label: "Svelte",
+ color: "hsl(var(--chart-3))",
+ },
+ solid: {
+ label: "Solid",
+ color: "hsl(var(--chart-4))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-7))",
+ },
+} satisfies ChartConfig;
+
+export const nodeVersionConfig = {
+ "18": {
+ label: "Node.js 18",
+ color: "hsl(var(--chart-1))",
+ },
+ "20": {
+ label: "Node.js 20",
+ color: "hsl(var(--chart-2))",
+ },
+ "22": {
+ label: "Node.js 22",
+ color: "hsl(var(--chart-3))",
+ },
+ "16": {
+ label: "Node.js 16",
+ color: "hsl(var(--chart-4))",
+ },
+ other: {
+ label: "Other",
+ color: "hsl(var(--chart-5))",
+ },
+} satisfies ChartConfig;
+
+export const cliVersionConfig = {
+ latest: {
+ label: "Latest",
+ color: "hsl(var(--chart-1))",
+ },
+ outdated: {
+ label: "Outdated",
+ color: "hsl(var(--chart-2))",
+ },
+} satisfies ChartConfig;
+
+export const authConfig = {
+ enabled: {
+ label: "Enabled",
+ color: "hsl(var(--chart-1))",
+ },
+ disabled: {
+ label: "Disabled",
+ color: "hsl(var(--chart-2))",
+ },
+} satisfies ChartConfig;
+
+export const gitConfig = {
+ enabled: {
+ label: "Git Init",
+ color: "hsl(var(--chart-1))",
+ },
+ disabled: {
+ label: "No Git",
+ color: "hsl(var(--chart-2))",
+ },
+} satisfies ChartConfig;
+
+export const installConfig = {
+ enabled: {
+ label: "Auto Install",
+ color: "hsl(var(--chart-1))",
+ },
+ disabled: {
+ label: "Skip Install",
+ color: "hsl(var(--chart-2))",
+ },
+} satisfies ChartConfig;
+
+export const examplesConfig = {
+ todo: {
+ label: "Todo App",
+ color: "hsl(var(--chart-1))",
+ },
+ ai: {
+ label: "AI Example",
+ color: "hsl(var(--chart-2))",
+ },
+ none: {
+ label: "No Examples",
+ color: "hsl(var(--chart-3))",
+ },
+} satisfies ChartConfig;
+
+export const addonsConfig = {
+ pwa: {
+ label: "PWA",
+ color: "hsl(var(--chart-1))",
+ },
+ biome: {
+ label: "Biome",
+ color: "hsl(var(--chart-2))",
+ },
+ tauri: {
+ label: "Tauri",
+ color: "hsl(var(--chart-3))",
+ },
+ husky: {
+ label: "Husky",
+ color: "hsl(var(--chart-4))",
+ },
+ starlight: {
+ label: "Starlight",
+ color: "hsl(var(--chart-5))",
+ },
+ turborepo: {
+ label: "Turborepo",
+ color: "hsl(var(--chart-6))",
+ },
+ none: {
+ label: "No Addons",
+ color: "hsl(var(--chart-7))",
+ },
+} satisfies ChartConfig;
+
+export const runtimeConfig = {
+ node: {
+ label: "Node.js",
+ color: "hsl(var(--chart-1))",
+ },
+ bun: {
+ label: "Bun",
+ color: "hsl(var(--chart-2))",
+ },
+ workers: {
+ label: "Cloudflare Workers",
+ color: "hsl(var(--chart-3))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-4))",
+ },
+} satisfies ChartConfig;
+
+export const projectTypeConfig = {
+ fullstack: {
+ label: "Full-stack",
+ color: "hsl(var(--chart-1))",
+ },
+ "frontend-only": {
+ label: "Frontend-only",
+ color: "hsl(var(--chart-2))",
+ },
+ "backend-only": {
+ label: "Backend-only",
+ color: "hsl(var(--chart-3))",
+ },
+ none: {
+ label: "None",
+ color: "hsl(var(--chart-4))",
+ },
+} satisfies ChartConfig;
+
+export const hourlyDistributionConfig = {
+ count: {
+ label: "Projects Created",
+ color: "hsl(var(--chart-1))",
+ },
+} satisfies ChartConfig;
diff --git a/apps/web/src/app/(home)/analytics/page.tsx b/apps/web/src/app/(home)/analytics/page.tsx
index 0a923ba..43ae1e4 100644
--- a/apps/web/src/app/(home)/analytics/page.tsx
+++ b/apps/web/src/app/(home)/analytics/page.tsx
@@ -1,430 +1,15 @@
"use client";
-import { format, parseISO } from "date-fns";
-import { Cpu, Download, Terminal, TrendingUp, Users } from "lucide-react";
-import Image from "next/image";
-import Link from "next/link";
import { useCallback, useEffect, useState } from "react";
-import {
- Area,
- AreaChart,
- Bar,
- BarChart,
- CartesianGrid,
- Cell,
- Pie,
- PieChart,
- XAxis,
- YAxis,
-} from "recharts";
-import {
- type ChartConfig,
- ChartContainer,
- ChartLegend,
- ChartLegendContent,
- ChartTooltip,
- ChartTooltipContent,
-} from "@/components/ui/chart";
-import discordIcon from "@/public/icon/discord.svg";
import Footer from "../_components/footer";
-
-interface AggregatedAnalyticsData {
- lastUpdated: string | null;
- generatedAt: string;
- totalRecords: number;
- timeSeries: Array<{ date: string; displayDate: string; count: number }>;
- monthlyTimeSeries: Array<{
- month: string;
- displayMonth: string;
- count: number;
- }>;
- platformDistribution: Array<{ name: string; value: number }>;
- packageManagerDistribution: Array<{ name: string; value: number }>;
- backendDistribution: Array<{ name: string; value: number }>;
- databaseDistribution: Array<{ name: string; value: number }>;
- ormDistribution: Array<{ name: string; value: number }>;
- dbSetupDistribution: Array<{ name: string; value: number }>;
- apiDistribution: Array<{ name: string; value: number }>;
- frontendDistribution: Array<{ name: string; value: number }>;
- nodeVersionDistribution: Array<{ version: string; count: number }>;
- cliVersionDistribution: Array<{ version: string; count: number }>;
- authDistribution: Array<{ name: string; value: number }>;
- gitDistribution: Array<{ name: string; value: number }>;
- installDistribution: Array<{ name: string; value: number }>;
- examplesDistribution: Array<{ name: string; value: number }>;
- addonsDistribution: Array<{ name: string; value: number }>;
- runtimeDistribution: Array<{ name: string; value: number }>;
- projectTypeDistribution: Array<{ name: string; value: number }>;
- popularStackCombinations: Array<{ name: string; value: number }>;
- databaseORMCombinations: Array<{ name: string; value: number }>;
- hourlyDistribution: Array<{
- hour: string;
- displayHour: string;
- count: number;
- }>;
- summary: {
- totalProjects: number;
- avgProjectsPerDay: number;
- authEnabledPercent: number;
- mostPopularFrontend: string;
- mostPopularBackend: string;
- mostPopularORM: string;
- mostPopularAPI: string;
- mostPopularPackageManager: string;
- };
-}
-
-const timeSeriesConfig = {
- projects: {
- label: "Projects Created",
- color: "hsl(var(--chart-1))",
- },
-} satisfies ChartConfig;
-
-const platformConfig = {
- darwin: {
- label: "macOS",
- color: "hsl(var(--chart-1))",
- },
- linux: {
- label: "Linux",
- color: "hsl(var(--chart-2))",
- },
- win32: {
- label: "Windows",
- color: "hsl(var(--chart-3))",
- },
- android: {
- label: "Android", // there are 2 records with this platform :)
- color: "hsl(var(--chart-4))",
- },
-} satisfies ChartConfig;
-
-const packageManagerConfig = {
- npm: {
- label: "npm",
- color: "hsl(var(--chart-1))",
- },
- pnpm: {
- label: "pnpm",
- color: "hsl(var(--chart-2))",
- },
- bun: {
- label: "bun",
- color: "hsl(var(--chart-3))",
- },
-} satisfies ChartConfig;
-
-const backendConfig = {
- hono: {
- label: "Hono",
- color: "hsl(var(--chart-1))",
- },
- express: {
- label: "Express",
- color: "hsl(var(--chart-2))",
- },
- fastify: {
- label: "Fastify",
- color: "hsl(var(--chart-3))",
- },
- next: {
- label: "Next.js",
- color: "hsl(var(--chart-4))",
- },
- elysia: {
- label: "Elysia",
- color: "hsl(var(--chart-5))",
- },
- convex: {
- label: "Convex",
- color: "hsl(var(--chart-6))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-7))",
- },
-} satisfies ChartConfig;
-
-const databaseConfig = {
- sqlite: {
- label: "SQLite",
- color: "hsl(var(--chart-1))",
- },
- postgres: {
- label: "PostgreSQL",
- color: "hsl(var(--chart-2))",
- },
- mysql: {
- label: "MySQL",
- color: "hsl(var(--chart-3))",
- },
- mongodb: {
- label: "MongoDB",
- color: "hsl(var(--chart-4))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-5))",
- },
-} satisfies ChartConfig;
-
-const ormConfig = {
- drizzle: {
- label: "Drizzle",
- color: "hsl(var(--chart-1))",
- },
- prisma: {
- label: "Prisma",
- color: "hsl(var(--chart-2))",
- },
- mongoose: {
- label: "Mongoose",
- color: "hsl(var(--chart-3))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-4))",
- },
-} satisfies ChartConfig;
-
-const dbSetupConfig = {
- turso: {
- label: "Turso",
- color: "hsl(var(--chart-1))",
- },
- "prisma-postgres": {
- label: "Prisma Postgres",
- color: "hsl(var(--chart-2))",
- },
- "mongodb-atlas": {
- label: "MongoDB Atlas",
- color: "hsl(var(--chart-3))",
- },
- neon: {
- label: "Neon",
- color: "hsl(var(--chart-4))",
- },
- supabase: {
- label: "Supabase",
- color: "hsl(var(--chart-5))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-6))",
- },
-} satisfies ChartConfig;
-
-const apiConfig = {
- trpc: {
- label: "tRPC",
- color: "hsl(var(--chart-1))",
- },
- orpc: {
- label: "oRPC",
- color: "hsl(var(--chart-2))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-3))",
- },
-} satisfies ChartConfig;
-
-const frontendConfig = {
- "react-router": {
- label: "React Router",
- color: "hsl(var(--chart-1))",
- },
- "tanstack-router": {
- label: "TanStack Router",
- color: "hsl(var(--chart-2))",
- },
- "tanstack-start": {
- label: "TanStack Start",
- color: "hsl(var(--chart-3))",
- },
- next: {
- label: "Next",
- color: "hsl(var(--chart-4))",
- },
- nuxt: {
- label: "Nuxt",
- color: "hsl(var(--chart-5))",
- },
- "native-nativewind": {
- label: "Expo NativeWind",
- color: "hsl(var(--chart-6))",
- },
- "native-unistyles": {
- label: "Expo Unistyles",
- color: "hsl(var(--chart-7))",
- },
- svelte: {
- label: "Svelte",
- color: "hsl(var(--chart-3))",
- },
- solid: {
- label: "Solid",
- color: "hsl(var(--chart-4))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-7))",
- },
-} satisfies ChartConfig;
-
-const nodeVersionConfig = {
- "18": {
- label: "Node.js 18",
- color: "hsl(var(--chart-1))",
- },
- "20": {
- label: "Node.js 20",
- color: "hsl(var(--chart-2))",
- },
- "22": {
- label: "Node.js 22",
- color: "hsl(var(--chart-3))",
- },
- "16": {
- label: "Node.js 16",
- color: "hsl(var(--chart-4))",
- },
- other: {
- label: "Other",
- color: "hsl(var(--chart-5))",
- },
-} satisfies ChartConfig;
-
-const cliVersionConfig = {
- latest: {
- label: "Latest",
- color: "hsl(var(--chart-1))",
- },
- outdated: {
- label: "Outdated",
- color: "hsl(var(--chart-2))",
- },
-} satisfies ChartConfig;
-
-const authConfig = {
- enabled: {
- label: "Enabled",
- color: "hsl(var(--chart-1))",
- },
- disabled: {
- label: "Disabled",
- color: "hsl(var(--chart-2))",
- },
-} satisfies ChartConfig;
-
-const gitConfig = {
- enabled: {
- label: "Git Init",
- color: "hsl(var(--chart-1))",
- },
- disabled: {
- label: "No Git",
- color: "hsl(var(--chart-2))",
- },
-} satisfies ChartConfig;
-
-const installConfig = {
- enabled: {
- label: "Auto Install",
- color: "hsl(var(--chart-1))",
- },
- disabled: {
- label: "Skip Install",
- color: "hsl(var(--chart-2))",
- },
-} satisfies ChartConfig;
-
-const examplesConfig = {
- todo: {
- label: "Todo App",
- color: "hsl(var(--chart-1))",
- },
- ai: {
- label: "AI Example",
- color: "hsl(var(--chart-2))",
- },
- none: {
- label: "No Examples",
- color: "hsl(var(--chart-3))",
- },
-} satisfies ChartConfig;
-
-const addonsConfig = {
- pwa: {
- label: "PWA",
- color: "hsl(var(--chart-1))",
- },
- biome: {
- label: "Biome",
- color: "hsl(var(--chart-2))",
- },
- tauri: {
- label: "Tauri",
- color: "hsl(var(--chart-3))",
- },
- husky: {
- label: "Husky",
- color: "hsl(var(--chart-4))",
- },
- starlight: {
- label: "Starlight",
- color: "hsl(var(--chart-5))",
- },
- turborepo: {
- label: "Turborepo",
- color: "hsl(var(--chart-6))",
- },
- none: {
- label: "No Addons",
- color: "hsl(var(--chart-7))",
- },
-} satisfies ChartConfig;
-
-const runtimeConfig = {
- node: {
- label: "Node.js",
- color: "hsl(var(--chart-1))",
- },
- bun: {
- label: "Bun",
- color: "hsl(var(--chart-2))",
- },
- workers: {
- label: "Cloudflare Workers",
- color: "hsl(var(--chart-3))",
- },
- none: {
- label: "None",
- color: "hsl(var(--chart-4))",
- },
-} satisfies ChartConfig;
-
-const projectTypeConfig = {
- fullstack: {
- label: "Full-stack",
- color: "hsl(var(--chart-1))",
- },
- "frontend-only": {
- label: "Frontend-only",
- color: "hsl(var(--chart-2))",
- },
- "backend-only": {
- label: "Backend-only",
- color: "hsl(var(--chart-3))",
- },
-} satisfies ChartConfig;
-
-const hourlyDistributionConfig = {
- count: {
- label: "Projects Created",
- color: "hsl(var(--chart-1))",
- },
-} satisfies ChartConfig;
+import {
+ AddonsExamplesCharts,
+ type AggregatedAnalyticsData,
+ AnalyticsHeader,
+ DevEnvironmentCharts,
+ MetricsCards,
+ StackConfigurationCharts,
+ TimelineCharts,
+} from "./_components";
export default function AnalyticsPage() {
const [data, setData] = useState(null);
@@ -449,1433 +34,43 @@ export default function AnalyticsPage() {
loadAnalyticsData();
}, [loadAnalyticsData]);
- const getPlatformData = () => {
- if (!data) return [];
- return data.platformDistribution || [];
- };
-
- const getPackageManagerData = () => {
- if (!data) return [];
- return data.packageManagerDistribution || [];
- };
-
- const getBackendData = () => {
- if (!data) return [];
- return data.backendDistribution || [];
- };
-
- const getDatabaseData = () => {
- if (!data) return [];
- return data.databaseDistribution || [];
- };
-
- const getORMData = () => {
- if (!data) return [];
- return data.ormDistribution || [];
- };
-
- const getDBSetupData = () => {
- if (!data) return [];
- return data.dbSetupDistribution || [];
- };
-
- const getAPIData = () => {
- if (!data) return [];
- return data.apiDistribution || [];
- };
-
- const getFrontendData = () => {
- if (!data) return [];
- return data.frontendDistribution || [];
- };
-
- const getTimeSeriesData = () => {
- if (!data) return [];
- return data.timeSeries || [];
- };
-
- const getNodeVersionData = () => {
- if (!data) return [];
- return data.nodeVersionDistribution || [];
- };
-
- const getCLIVersionData = () => {
- if (!data) return [];
- return data.cliVersionDistribution || [];
- };
-
- const getAuthData = () => {
- if (!data) return [];
- return data.authDistribution || [];
- };
-
- const getGitData = () => {
- if (!data) return [];
- return data.gitDistribution || [];
- };
-
- const getInstallData = () => {
- if (!data) return [];
- return data.installDistribution || [];
- };
-
- const getExamplesData = () => {
- if (!data) return [];
- return data.examplesDistribution || [];
- };
-
- const getAddonsData = () => {
- if (!data) return [];
- return data.addonsDistribution || [];
- };
-
- const getRuntimeData = () => {
- if (!data) return [];
- return data.runtimeDistribution || [];
- };
-
- const getProjectTypeData = () => {
- if (!data) return [];
- return data.projectTypeDistribution || [];
- };
-
- const getMonthlyTimeSeriesData = () => {
- if (!data) return [];
- return data.monthlyTimeSeries || [];
- };
-
- const getPopularStackCombinations = () => {
- if (!data) return [];
- return data.popularStackCombinations || [];
- };
-
- const getDatabaseORMCombinations = () => {
- if (!data) return [];
- return data.databaseORMCombinations || [];
- };
-
- const getHourlyDistributionData = () => {
- if (!data) return [];
- return data.hourlyDistribution || [];
- };
-
const totalProjects = data?.summary?.totalProjects || 0;
- const getAvgProjectsPerDay = () => {
- if (!data) return 0;
- return data.summary?.avgProjectsPerDay || 0;
- };
-
- const avgProjectsPerDay = getAvgProjectsPerDay();
+ const avgProjectsPerDay = data?.summary?.avgProjectsPerDay || 0;
const authEnabledPercent = data?.summary?.authEnabledPercent || 0;
-
- const runtimeData = getRuntimeData();
const mostPopularFrontend = data?.summary?.mostPopularFrontend || "None";
const mostPopularBackend = data?.summary?.mostPopularBackend || "None";
-
- const projectTypeData = getProjectTypeData();
- const monthlyTimeSeriesData = getMonthlyTimeSeriesData();
- const popularStackCombinations = getPopularStackCombinations();
- const databaseORMCombinations = getDatabaseORMCombinations();
- const hourlyDistributionData = getHourlyDistributionData();
+ const mostPopularORM = data?.summary?.mostPopularORM || "None";
+ const mostPopularAPI = data?.summary?.mostPopularAPI || "None";
+ const mostPopularPackageManager =
+ data?.summary?.mostPopularPackageManager || "npm";
return (
-
-
-
-
-
- ANALYTICS_DASHBOARD.SH
-
-
-
-
- [{totalProjects} PROJECTS_ANALYZED]
-
-
+
-
-
- $
-
- Analytics from Better-T-Stack CLI usage data
-
-
-
- $
-
- Uses PostHog - no personal info tracked, runs on each project
- creation
-
-
-
- $
-
- Source:{" "}
-
- analytics.ts
-
- {" | "}
-
- export.csv
-
-
-
-
- $
-
- Last updated:{" "}
- {loadingLastUpdated
- ? "CHECKING..."
- : data?.lastUpdated
- ? `${data.lastUpdated} UTC`
- : "UNKNOWN"}
-
-
-
+
-
-
-
-
-
-
- DISCORD_NOTIFICATIONS.IRC
-
-
- Join for LIVE project creation alerts
-
-
-
-
- ▶
-
- JOIN
-
-
-
-
-
+
-
-
+
-
-
-
-
-
- {totalProjects.toLocaleString()}
-
-
- $ ./create-better-t-stack executions
-
-
-
+
-
-
-
-
- {mostPopularFrontend}
-
-
- $ most_selected_frontend.sh
-
-
-
-
-
-
-
-
- {mostPopularBackend}
-
-
- $ most_selected_backend.sh
-
-
-
-
-
-
-
-
- {data?.summary?.mostPopularORM || "None"}
-
-
- $ most_selected_orm.sh
-
-
-
-
-
-
-
-
- {data?.summary?.mostPopularAPI || "None"}
-
-
- $ most_selected_api.sh
-
-
-
-
-
-
-
-
- {authEnabledPercent}%
-
-
- $ auth_enabled_percentage.sh
-
-
-
-
-
-
-
-
- {data?.summary?.mostPopularPackageManager || "npm"}
-
-
- $ most_used_package_manager.sh
-
-
-
-
-
-
-
-
- {avgProjectsPerDay.toFixed(1)}
-
-
- $ average_projects_per_day.sh
-
-
-
-
-
-
-
-
-
TIMELINE_ANALYSIS.CHARTS
-
-
-
-
-
-
-
- ▶
-
- PROJECT_TIMELINE.CHART
-
-
-
- Daily project creation timeline from actual data
-
-
-
-
-
-
-
-
- }
- labelFormatter={(value, payload) => {
- const date = payload?.[0]?.payload?.date;
- return date
- ? format(parseISO(date), "MMM dd, yyyy")
- : value;
- }}
- />
-
-
-
-
-
-
-
-
-
- ▶
-
- MONTHLY_TRENDS.CHART
-
-
-
- Monthly project creation trends
-
-
-
-
-
-
-
-
- } />
-
-
-
-
-
-
-
-
-
- ▶
-
- PLATFORM_DISTRIBUTION.PIE
-
-
-
- Operating system distribution
-
-
-
-
-
- }
- />
-
- `${name} ${(percent * 100).toFixed(0)}%`
- }
- outerRadius={80}
- fill="hsl(var(--chart-1))"
- dataKey="value"
- >
- {getPlatformData().map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
- ▶
-
- HOURLY_DISTRIBUTION.BAR
-
-
-
- Projects created by hour of day (UTC)
-
-
-
-
-
-
-
-
- }
- labelFormatter={(value, payload) => {
- const hour = payload?.[0]?.payload?.displayHour;
- return hour ? `${hour} UTC` : value;
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
-
- STACK_CONFIGURATION.DB
-
-
-
-
- [CORE_COMPONENTS]
-
-
-
-
-
-
- ▶
-
- POPULAR_STACK_COMBINATIONS.BAR
-
-
-
- Most popular frontend + backend combinations
-
-
-
-
-
-
-
-
- } />
-
-
-
-
-
-
-
-
-
- ▶
-
- FRONTEND_FRAMEWORKS.BAR
-
-
-
- Frontend framework and meta-framework usage
-
-
-
-
-
-
-
-
- } />
-
- {getFrontendData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
-
- ▶
-
- BACKEND_FRAMEWORKS.BAR
-
-
-
- Backend framework distribution
-
-
-
-
-
-
-
-
- } />
-
- {getBackendData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
- ▶
-
- DATABASE_DISTRIBUTION.BAR
-
-
-
- Database technology distribution
-
-
-
-
-
-
-
-
- } />
-
- {getDatabaseData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
- ▶
-
- ORM_DISTRIBUTION.BAR
-
-
-
- ORM/Database layer distribution
-
-
-
-
-
-
-
-
- } />
-
- {getORMData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
- ▶
-
- DATABASE_HOSTING.BAR
-
-
-
- Database hosting service preferences
-
-
-
-
-
-
-
-
- } />
-
- {getDBSetupData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
- ▶
- API_LAYER.PIE
-
-
- API layer technology distribution
-
-
-
-
-
- }
- />
-
- {getAPIData().map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
- ▶
-
- AUTH_ADOPTION.PIE
-
-
-
- Authentication implementation rate
-
-
-
-
-
- }
- />
-
- `${name} ${(percent * 100).toFixed(0)}%`
- }
- >
- {getAuthData().map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
- ▶
-
- RUNTIME_DISTRIBUTION.PIE
-
-
-
- JavaScript runtime preference distribution
-
-
-
-
-
- }
- />
-
- `${name} ${(percent * 100).toFixed(0)}%`
- }
- >
- {runtimeData.map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
- ▶
-
- PROJECT_TYPES.PIE
-
-
-
- Full-stack vs Frontend-only vs Backend-only projects
-
-
-
-
-
- }
- />
-
- `${name} ${(percent * 100).toFixed(0)}%`
- }
- >
- {projectTypeData.map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
-
- ▶
-
- DATABASE_ORM_COMBINATIONS.BAR
-
-
-
- Popular database + ORM combinations
-
-
-
-
-
-
-
-
- } />
-
-
-
-
-
-
-
-
-
- ▶
- ADDONS_USAGE.BAR
-
-
- Additional features and tooling adoption
-
-
-
-
-
-
-
-
- } />
-
- {getAddonsData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
- ▶
-
- EXAMPLES_USAGE.BAR
-
-
-
- Example applications included in projects
-
-
-
-
-
-
-
-
- } />
-
- {getExamplesData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
-
DEV_ENVIRONMENT.CONFIG
-
-
-
-
-
-
-
- ▶
-
- GIT_INITIALIZATION.PIE
-
-
-
- Git repository initialization rate
-
-
-
-
-
- }
- />
-
- `${name} ${(percent * 100).toFixed(0)}%`
- }
- >
- {getGitData().map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
- ▶
-
- PACKAGE_MANAGER.BAR
-
-
-
- Package manager usage distribution
-
-
-
-
-
-
-
-
- } />
-
- {getPackageManagerData().map((entry) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
- ▶
-
- INSTALL_PREFERENCE.PIE
-
-
-
- Automatic dependency installation preference
-
-
-
-
-
- }
- />
-
- `${name} ${(percent * 100).toFixed(0)}%`
- }
- >
- {getInstallData().map((entry) => (
- |
- ))}
-
- } />
-
-
-
-
-
-
-
-
- ▶
-
- NODE_VERSIONS.BAR
-
-
-
- Node.js version distribution (major versions)
-
-
-
-
-
-
-
-
- } />
-
-
-
-
-
-
-
-
-
-
- ▶
- CLI_VERSIONS.BAR
-
-
- CLI version distribution across project creations
-
-
-
-
-
-
-
-
- } />
-
-
-
-
-
-
+