From 5de9467fb4b1b12336f02ffd998402f9941607f4 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 6 Aug 2025 20:11:15 +0530 Subject: [PATCH] refactor(web): break down analytics page into smaller components --- apps/web/scripts/generate-analytics.ts | 8 +- .../_components/addons-examples-charts.tsx | 114 + .../_components/analytics-header.tsx | 112 + .../analytics/_components/data-utils.ts | 119 ++ .../_components/dev-environment-charts.tsx | 261 +++ .../app/(home)/analytics/_components/index.ts | 8 + .../analytics/_components/metrics-cards.tsx | 170 ++ .../stack-configuration-charts.tsx | 583 +++++ .../analytics/_components/timeline-charts.tsx | 226 ++ .../app/(home)/analytics/_components/types.ts | 404 ++++ apps/web/src/app/(home)/analytics/page.tsx | 1871 +---------------- 11 files changed, 2033 insertions(+), 1843 deletions(-) create mode 100644 apps/web/src/app/(home)/analytics/_components/addons-examples-charts.tsx create mode 100644 apps/web/src/app/(home)/analytics/_components/analytics-header.tsx create mode 100644 apps/web/src/app/(home)/analytics/_components/data-utils.ts create mode 100644 apps/web/src/app/(home)/analytics/_components/dev-environment-charts.tsx create mode 100644 apps/web/src/app/(home)/analytics/_components/index.ts create mode 100644 apps/web/src/app/(home)/analytics/_components/metrics-cards.tsx create mode 100644 apps/web/src/app/(home)/analytics/_components/stack-configuration-charts.tsx create mode 100644 apps/web/src/app/(home)/analytics/_components/timeline-charts.tsx create mode 100644 apps/web/src/app/(home)/analytics/_components/types.ts 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 +
+ + 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 ( +
+
+ SYSTEM_METRICS.LOG +
+
+ +
+
+
+
+ TOTAL_PROJECTS + +
+
+
+
+ {totalProjects.toLocaleString()} +
+

+ $ ./create-better-t-stack executions +

+
+
+ +
+
+
+ TOP_FRONTEND + +
+
+
+
+ {mostPopularFrontend} +
+

+ $ most_selected_frontend.sh +

+
+
+ +
+
+
+ TOP_BACKEND + +
+
+
+
+ {mostPopularBackend} +
+

+ $ most_selected_backend.sh +

+
+
+ +
+
+
+ TOP_ORM + +
+
+
+
+ {mostPopularORM} +
+

+ $ most_selected_orm.sh +

+
+
+ +
+
+
+ TOP_API + +
+
+
+
+ {mostPopularAPI} +
+

+ $ most_selected_api.sh +

+
+
+ +
+
+
+ AUTH_ADOPTION + +
+
+
+
+ {authEnabledPercent}% +
+

+ $ auth_enabled_percentage.sh +

+
+
+ +
+
+
+ TOP_PKG_MGR + +
+
+
+
+ {mostPopularPackageManager} +
+

+ $ most_used_package_manager.sh +

+
+
+ +
+
+
+ AVG_DAILY + +
+
+
+
+ {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 -
- - DISCORD_NOTIFICATIONS.IRC - -

- Join for LIVE project creation alerts -

-
-
-
- - - JOIN - -
-
- -
+ -
-
- SYSTEM_METRICS.LOG -
-
+ -
-
-
-
- TOTAL_PROJECTS - -
-
-
-
- {totalProjects.toLocaleString()} -
-

- $ ./create-better-t-stack executions -

-
-
+ -
-
-
- TOP_FRONTEND - -
-
-
-
- {mostPopularFrontend} -
-

- $ most_selected_frontend.sh -

-
-
- -
-
-
- TOP_BACKEND - -
-
-
-
- {mostPopularBackend} -
-

- $ most_selected_backend.sh -

-
-
- -
-
-
- TOP_ORM - -
-
-
-
- {data?.summary?.mostPopularORM || "None"} -
-

- $ most_selected_orm.sh -

-
-
- -
-
-
- TOP_API - -
-
-
-
- {data?.summary?.mostPopularAPI || "None"} -
-

- $ most_selected_api.sh -

-
-
- -
-
-
- AUTH_ADOPTION - -
-
-
-
- {authEnabledPercent}% -
-

- $ auth_enabled_percentage.sh -

-
-
- -
-
-
- TOP_PKG_MGR - -
-
-
-
- {data?.summary?.mostPopularPackageManager || "npm"} -
-

- $ most_used_package_manager.sh -

-
-
- -
-
-
- AVG_DAILY - -
-
-
-
- {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 -

-
-
- - - - - - } /> - - - -
-
-
+