diff --git a/apps/www/components/SurveyResults/AgeChart.tsx b/apps/www/components/SurveyResults/AgeChart.tsx deleted file mode 100644 index 47ec8f63722..00000000000 --- a/apps/www/components/SurveyResults/AgeChart.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -// Generate SQL query for team count chart -function generatePersonAgeSQL(activeFilters) { - const whereClauses = [] - - if (activeFilters.role !== 'unset') { - whereClauses.push(`role = '${activeFilters.role}'`) - } - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT - person_age, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY person_age -ORDER BY person_age ASC` -} - -export function PersonAgeChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/BackendStackChart.tsx b/apps/www/components/SurveyResults/BackendStackChart.tsx deleted file mode 100644 index 41bab7ad19e..00000000000 --- a/apps/www/components/SurveyResults/BackendStackChart.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateBackendStackSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.industry_normalized !== 'unset') { - whereClauses.push(`industry_normalized = '${activeFilters.industry_normalized}'`) - } - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` -SELECT - unnest(backend_stack) AS technology, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY technology -ORDER BY total DESC; -` -} - -export function BackendStackChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/BiggestChallengeChart.tsx b/apps/www/components/SurveyResults/BiggestChallengeChart.tsx index 23fe27ba1bf..9955210b1f2 100644 --- a/apps/www/components/SurveyResults/BiggestChallengeChart.tsx +++ b/apps/www/components/SurveyResults/BiggestChallengeChart.tsx @@ -1,7 +1,7 @@ import { GenericChartWithQuery } from './GenericChartWithQuery' // Generate SQL query for team count chart -function generateBiggestChallengeSQL(activeFilters) { +function generateBiggestChallengeSQL(activeFilters: Record) { const whereClauses = [] // Always filter out NULL values diff --git a/apps/www/components/SurveyResults/CloudProvidersChart.tsx b/apps/www/components/SurveyResults/CloudProvidersChart.tsx deleted file mode 100644 index e84a5a55c9b..00000000000 --- a/apps/www/components/SurveyResults/CloudProvidersChart.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateCloudProvidersSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - if (activeFilters.backend_stack !== 'unset') { - whereClauses.push(`'${activeFilters.backend_stack}' = ANY(backend_stack)`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `WITH expanded AS ( - SELECT unnest(cloud_providers) AS technology - FROM responses_2025${whereClause ? '\n' + whereClause : ''} -) -SELECT - technology, - COUNT(*) AS total -FROM expanded -GROUP BY technology -ORDER BY total DESC;` -} - -export function CloudProvidersChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/DataWarehousesChart.tsx b/apps/www/components/SurveyResults/DataWarehousesChart.tsx deleted file mode 100644 index 383b2acf830..00000000000 --- a/apps/www/components/SurveyResults/DataWarehousesChart.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateDataWarehousesSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.industry_normalized !== 'unset') { - whereClauses.push(`industry_normalized = '${activeFilters.industry_normalized}'`) - } - - if (activeFilters.person_age !== 'unset') { - whereClauses.push(`person_age = '${activeFilters.person_age}'`) - } - - if (activeFilters.frontend_stack !== 'unset') { - whereClauses.push(`'${activeFilters.frontend_stack}' = ANY(frontend_stack)`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` -WITH expanded AS ( - SELECT unnest(data_warehouses) AS technology -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -) -SELECT - technology, - COUNT(*) AS total -FROM expanded -WHERE technology NOT IN ('We don''t use a data warehouse', 'Not sure') -GROUP BY technology -ORDER BY total DESC; -` -} - -export function DataWarehousesChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/FullTimeEmployees.tsx b/apps/www/components/SurveyResults/FullTimeEmployees.tsx deleted file mode 100644 index d41f27ec327..00000000000 --- a/apps/www/components/SurveyResults/FullTimeEmployees.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client' - -// import { Bar, BarChart, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts' -// import { ChartContainer } from 'ui' - -import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis, Cell } from 'recharts' - -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from 'ui' -import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from 'ui' - -export const description = 'A bar chart with a custom label' - -// const chartData = [ -// { month: "January", desktop: 186, mobile: 80 }, -// { month: "February", desktop: 305, mobile: 200 }, -// { month: "March", desktop: 237, mobile: 120 }, -// { month: "April", desktop: 73, mobile: 190 }, -// { month: "May", desktop: 209, mobile: 130 }, -// { month: "June", desktop: 214, mobile: 140 }, -// ] - -const chartData = [ - { label: '1–10', value: 91.1 }, - { label: '11–50', value: 6.3 }, - { label: '51–100', value: 1.2 }, - { label: '101–250', value: 0.8 }, - { label: '250+', value: 0.6 }, -] - -const chartConfig = { - value: { - label: 'Value', - color: 'hsl(var(--brand-default))', - }, - label: { - color: '#ff0000', - }, -} satisfies ChartConfig - -export function FullTimeEmployees() { - return ( - - - How many full-time employees does your startup have? - - - - - - value.slice(0, 3)} - hide={false} // Show the YAxis - width={64} // Adjust width as needed for your labels - tick={{ - // fill: 'hsl(var(--foreground-lighter))', // Text color TODO not working - className: 'text-foreground-lighter', // Also not accessible - fontSize: 12, - textAnchor: 'start', // Left align - dx: 0, // Horizontal offset - }} - /> - - {/* } - /> */} - - {/* To set per-bar colors in Recharts, `fill` should be applied to the Cell child */} - {chartData.map((entry, index) => ( - - ))} - `${value}%`} - /> - - - - - - ) -} diff --git a/apps/www/components/SurveyResults/FundingStageChart.tsx b/apps/www/components/SurveyResults/FundingStageChart.tsx index 70edddfcbdc..d571e583d31 100644 --- a/apps/www/components/SurveyResults/FundingStageChart.tsx +++ b/apps/www/components/SurveyResults/FundingStageChart.tsx @@ -1,6 +1,6 @@ import { GenericChartWithQuery } from './GenericChartWithQuery' -function generateFundingStageSQL(activeFilters) { +function generateFundingStageSQL(activeFilters: Record) { const whereClauses = [] if (activeFilters.headquarters !== 'unset') { diff --git a/apps/www/components/SurveyResults/IndustryChart.tsx b/apps/www/components/SurveyResults/IndustryChart.tsx index 24f96e8658d..4a1f2ef81f0 100644 --- a/apps/www/components/SurveyResults/IndustryChart.tsx +++ b/apps/www/components/SurveyResults/IndustryChart.tsx @@ -1,7 +1,7 @@ import { GenericChartWithQuery } from './GenericChartWithQuery' // Generate SQL query for team count chart -function generateIndustrySQL(activeFilters) { +function generateIndustrySQL(activeFilters: Record) { const whereClauses = [] // Always filter out NULL values diff --git a/apps/www/components/SurveyResults/MonetizationChart.tsx b/apps/www/components/SurveyResults/MonetizationChart.tsx deleted file mode 100644 index 55ccd49858e..00000000000 --- a/apps/www/components/SurveyResults/MonetizationChart.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateMonetizationSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT - currently_monetizing, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY currently_monetizing -ORDER BY total DESC;` -} - -export function MonetizationChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/MustHaveDevToolsChart.tsx b/apps/www/components/SurveyResults/MustHaveDevToolsChart.tsx deleted file mode 100644 index be2b14a6089..00000000000 --- a/apps/www/components/SurveyResults/MustHaveDevToolsChart.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateMustHaveDevToolsSQL(activeFilters: Record) { - const whereClauses = [] - - // Always filter out NULL values - whereClauses.push(`must_have_dev_tools IS NOT NULL`) - whereClauses.push(`must_have_dev_tools != ''`) - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - if (activeFilters.backend_stack !== 'unset') { - whereClauses.push(`'${activeFilters.backend_stack}' = ANY(backend_stack)`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` -SELECT - must_have_dev_tools, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY must_have_dev_tools -ORDER BY total DESC -LIMIT 100;` -} - -export function MustHaveDevToolsChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/NewslettersPaidForChart.tsx b/apps/www/components/SurveyResults/NewslettersPaidForChart.tsx deleted file mode 100644 index 09c04148a38..00000000000 --- a/apps/www/components/SurveyResults/NewslettersPaidForChart.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateNewslettersPaidForSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.industry_normalized !== 'unset') { - whereClauses.push(`industry_normalized = '${activeFilters.industry_normalized}'`) - } - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` -SELECT - unnest(newsletters_paid_for) AS technology, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY technology -UNION ALL -SELECT - 'None of the above' AS technology, - COUNT(*) AS total -FROM responses_2025 -WHERE newsletters_paid_for = '{}' -ORDER BY total DESC; -` -} - -export function NewslettersPaidForChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/ObservabilityChart.tsx b/apps/www/components/SurveyResults/ObservabilityChart.tsx deleted file mode 100644 index 96dbeffe8b2..00000000000 --- a/apps/www/components/SurveyResults/ObservabilityChart.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateObservabilitySQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `WITH expanded AS ( - SELECT unnest(observability) AS technology -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -) -SELECT - technology, - COUNT(*) AS total -FROM expanded -WHERE technology NOT IN ('We don''t use observability tools yet', 'Not yet') -GROUP BY technology -ORDER BY total DESC;` -} - -export function ObservabilityChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/OpenTelemetryChart.tsx b/apps/www/components/SurveyResults/OpenTelemetryChart.tsx deleted file mode 100644 index 0d77917cf00..00000000000 --- a/apps/www/components/SurveyResults/OpenTelemetryChart.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateOpenTelemetrySQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT - use_opentelemetry, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY use_opentelemetry -ORDER BY - CASE use_opentelemetry - WHEN 'Yes' THEN 1 - WHEN 'Not yet, but planning to' THEN 2 - WHEN 'No' THEN 3 -END;` -} - -export function OpenTelemetryChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/PivotFreqChart.tsx b/apps/www/components/SurveyResults/PivotFreqChart.tsx deleted file mode 100644 index 0eb7d688ae4..00000000000 --- a/apps/www/components/SurveyResults/PivotFreqChart.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generatePivotFreqSQL(activeFilters: Record) { - const whereClauses = [] - - // if (activeFilters.headquarters !== 'unset') { - // whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - // } - - // if (activeFilters.currently_monetizing !== 'unset') { - // whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - // } - - if (activeFilters.person_age !== 'unset') { - whereClauses.push(`person_age = '${activeFilters.person_age}'`) - } - - if (activeFilters.previous_company !== 'unset') { - // Convert string to boolean for the SQL query - const previousCompanyBool = activeFilters.previous_company === 'true' - whereClauses.push(`previous_company = ${previousCompanyBool}`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT - pivots_before_current, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY pivots_before_current -ORDER BY - CASE pivots_before_current - WHEN 'Never' THEN 1 - WHEN 'Once' THEN 2 - WHEN 'Twice' THEN 3 - WHEN 'More than twice' THEN 4 - END;` -} - -export function PivotFreqChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/PodcastsListenedToChart.tsx b/apps/www/components/SurveyResults/PodcastsListenedToChart.tsx deleted file mode 100644 index 6eec7ca173c..00000000000 --- a/apps/www/components/SurveyResults/PodcastsListenedToChart.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generatePodcastsListenedToSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.industry_normalized !== 'unset') { - whereClauses.push(`industry_normalized = '${activeFilters.industry_normalized}'`) - } - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT technology, COUNT(*) AS total -FROM ( - SELECT - CASE - WHEN podcasts_listened_to = '{}' THEN 'None' - WHEN podcast = 'I don''t listen to podcasts regularly' THEN 'None' - ELSE podcast - END AS technology - FROM responses_2025 - CROSS JOIN LATERAL unnest( - CASE WHEN podcasts_listened_to = '{}' THEN ARRAY['None'] ELSE podcasts_listened_to END - ) AS podcast${whereClause ? '\n' + whereClause : ''} -) t -GROUP BY technology -ORDER BY total DESC;` -} - -export function PodcastsListenedToChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/PreviousCompanyChart.tsx b/apps/www/components/SurveyResults/PreviousCompanyChart.tsx deleted file mode 100644 index 50a86e7eda9..00000000000 --- a/apps/www/components/SurveyResults/PreviousCompanyChart.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generatePreviousCompanySQL(activeFilters: Record) { - const whereClauses = [] - - // Always filter out NULL values - whereClauses.push(`previous_company IS NOT NULL`) - - if (activeFilters.person_age !== 'unset') { - whereClauses.push(`person_age = '${activeFilters.person_age}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - const whereClause = `WHERE ${whereClauses.join('\n AND ')}` - - return `SELECT - CASE - WHEN previous_company = true THEN 'Yes' - WHEN previous_company = false THEN 'No' - -- ELSE 'Unknown' - END AS previous_company, - COUNT(*) AS total -FROM responses_2025 -${whereClause} -GROUP BY previous_company -ORDER BY total DESC;` -} - -export function PreviousCompanyChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/PricingChart.tsx b/apps/www/components/SurveyResults/PricingChart.tsx deleted file mode 100644 index f6d38151052..00000000000 --- a/apps/www/components/SurveyResults/PricingChart.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generatePricingSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.team_count !== 'unset') { - whereClauses.push(`team_count = '${activeFilters.team_count}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` - SELECT - unnest(pricing) AS pricing_model, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY pricing_model -ORDER BY total DESC; -` -} - -export function PricingChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/ProblemBeingSolvedChart.tsx b/apps/www/components/SurveyResults/ProblemBeingSolvedChart.tsx deleted file mode 100644 index 9c62630645b..00000000000 --- a/apps/www/components/SurveyResults/ProblemBeingSolvedChart.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateProblemBeingSolvedSQL(activeFilters: Record) { - const whereClauses = [] - - // Always filter out NULL values - whereClauses.push(`problem_solving IS NOT NULL`) - whereClauses.push(`problem_solving != ''`) - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - if (activeFilters.backend_stack !== 'unset') { - whereClauses.push(`'${activeFilters.backend_stack}' = ANY(backend_stack)`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` -SELECT - problem_solving, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY problem_solving -ORDER BY total DESC -LIMIT 100;` -} - -export function ProblemBeingSolvedChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/RechartsChartWithQuery.tsx b/apps/www/components/SurveyResults/RechartsChartWithQuery.tsx deleted file mode 100644 index 8154c2eebc9..00000000000 --- a/apps/www/components/SurveyResults/RechartsChartWithQuery.tsx +++ /dev/null @@ -1,349 +0,0 @@ -'use client' -import { useEffect, useState } from 'react' -import { createClient } from '@supabase/supabase-js' - -import { - Bar, - BarChart, - PieChart, - Pie, - CartesianGrid, - LabelList, - XAxis, - YAxis, - Cell, -} from 'recharts' - -import { Card, CardContent, CardHeader, CardTitle } from 'ui' -import CodeBlock from '~/components/CodeBlock/CodeBlock' -import type { LANG } from '~/components/CodeBlock/CodeBlock' -import { ChartConfig, ChartContainer } from 'ui' -import { SurveyCodeWindow } from './SurveyCodeWindow' -import { FilterDropdown } from './FilterDropdown' - -const chartConfig = { - value: { - label: 'Value', - color: 'hsl(var(--brand-default))', - }, - label: { - color: '#ff0000', - }, -} satisfies ChartConfig - -// Create a separate Supabase client for your external project -const externalSupabase = createClient( - process.env.NEXT_PUBLIC_SURVEY_SUPABASE_URL, - process.env.NEXT_PUBLIC_SURVEY_SUPABASE_ANON_KEY -) - -// Custom hook to fetch filter options from Supabase -function useFilterOptions(filterColumns: string[]) { - const [filters, setFilters] = useState({}) - const [isLoading, setIsLoading] = useState(true) - const [error, setError] = useState(null) - - useEffect(() => { - async function fetchFilterOptions() { - try { - setIsLoading(true) - setError(null) - - console.log('Fetching filter options from Supabase for columns:', filterColumns) - - const filterOptions = {} - - for (const column of filterColumns) { - const { data, error: fetchError } = await externalSupabase - .from('responses_2025') - .select(column) - .not(column, 'is', null) - // Remove this line for boolean columns - it excludes false values - // .not(column, 'eq', '') - - if (fetchError) { - console.error(`Error fetching ${column} options:`, fetchError) - continue - } - - console.log(`Raw data for ${column}:`, data) - - // Extract all individual values from PostgreSQL arrays - const allValues = data.flatMap((row) => { - const value = row[column] - console.log( - `Processing value for ${column}:`, - value, - typeof value, - Array.isArray(value) - ) - - if (Array.isArray(value)) { - // Handle PostgreSQL arrays - return value.filter((item) => item && item.length > 0) - } else if (typeof value === 'string' && value.startsWith('{') && value.endsWith('}')) { - // Handle PostgreSQL array format like {Firebase,MongoDB,MySQL} - const innerContent = value.slice(1, -1) // Remove { and } - if (innerContent.trim() === '') { - return [] - } - return innerContent - .split(',') - .map((item) => item.trim()) - .filter((item) => item.length > 0) - } - return [value] - }) - - console.log(`All values for ${column}:`, allValues) - - // Get unique values and sort them - const uniqueValues = [...new Set(allValues)].sort() - - console.log(`Unique values for ${column}:`, uniqueValues) - - filterOptions[column] = { - label: column.replace('_', ' ').replace(/\b\w/g, (l) => l.toUpperCase()), - options: [ - { - value: 'unset', - label: `All ${column.replace('_', ' ').replace(/\b\w/g, (l) => l.toUpperCase())}s`, - }, - ...uniqueValues.map((value) => ({ - value: String(value), // Convert to string for React - label: String(value), // Convert to string for display - })), - ], - } - } - - console.log('Final filter options:', filterOptions) - setFilters(filterOptions) - } catch (err) { - console.error('Error fetching filter options:', err) - setError(err.message) - } finally { - setIsLoading(false) - } - } - - fetchFilterOptions() - }, [filterColumns]) - - return { filters, isLoading, error } -} - -// Custom hook to fetch survey data using SQL query via RPC -// TODO: switch out to use supabase-js instead -function useSurveyData(sqlQuery: string) { - const [chartData, setChartData] = useState([]) - const [isLoading, setIsLoading] = useState(true) - const [error, setError] = useState(null) - - useEffect(() => { - async function fetchData() { - try { - setIsLoading(true) - setError(null) - - console.log('Executing SQL query:', sqlQuery) - - // Execute the SQL query using Supabase RPC - const { data, error: fetchError } = await externalSupabase.rpc('execute_sql', { - query: sqlQuery, - }) - - if (fetchError) { - console.error('Error executing SQL query:', fetchError) - setError(fetchError.message) - return - } - - console.log('Raw data from SQL query:', data) - - // Calculate total for percentage calculation - const total = data.reduce((sum, row) => sum + parseInt(row.total), 0) - - // Transform the data to match chart format - const processedData = data.map((row) => ({ - label: row.label || row.value || row[Object.keys(row)[0]], // Get the first column as label - value: total > 0 ? parseFloat(((parseInt(row.total) / total) * 100).toFixed(1)) : 0, - })) - - console.log('Processed chart data:', processedData) - setChartData(processedData) - } catch (err) { - console.error('Error in fetchData:', err) - setError(err.message) - } finally { - setIsLoading(false) - } - } - - fetchData() - }, [sqlQuery]) - - return { chartData, isLoading, error } -} - -export function RechartsChartWithQuery({ - title, - targetColumn, - filterColumns, - generateSQLQuery, - chartType = 'bar', // TODO: Could be 'bar', 'pie', etc. -}) { - // Get dynamic filter options - const { - filters, - isLoading: filtersLoading, - error: filtersError, - } = useFilterOptions(filterColumns) - - // Start with all filters unset (showing "all") - const [activeFilters, setActiveFilters] = useState( - filterColumns.reduce((acc, col) => ({ ...acc, [col]: 'unset' }), {}) - ) - - // Generate the SQL query string - const sqlQuery = generateSQLQuery(activeFilters) - - // Use the custom hook to fetch data - const { chartData, isLoading: dataLoading, error: dataError } = useSurveyData(sqlQuery) - - const setFilterValue = (filterKey, value) => { - setActiveFilters((prev) => ({ - ...prev, - [filterKey]: value, - })) - } - - const isLoading = filtersLoading || dataLoading - const error = filtersError || dataError - - // Two different color arrays - // One for gradation (e.g. "Yes", "Maybe", "No") - const COLORS = [ - 'hsl(var(--brand-default))', - 'hsl(var(--brand-500))', - 'hsl(var(--foreground-light))', - ] - // One for a clear binary answer (e.g. "Yes", "No") - const COLORS_BINARY = ['hsl(var(--brand-default))', 'hsl(var(--foreground-muted))'] - - return ( - - - {title} - - - {isLoading ? ( - - Loading data... - - ) : error ? ( - - Error: {error} - - ) : ( - - {chartType === 'bar' && ( - - - - - - {chartData.map((entry, index) => ( - - ))} - `${value}%`} - /> - - - )} - {chartType === 'pie' && ( - - - {chartData.map((entry, index) => ( - - ))} - - - )} - - )} - - {/* */} - - {filters && activeFilters && setFilterValue && ( - - {Object.entries(filters).map(([filterKey, filterConfig]) => ( - - ))} - - )} - - {/* - {sqlQuery} - */} - - - ) -} diff --git a/apps/www/components/SurveyResults/RoleChart.tsx b/apps/www/components/SurveyResults/RoleChart.tsx index 8015ca5b381..11b48f21328 100644 --- a/apps/www/components/SurveyResults/RoleChart.tsx +++ b/apps/www/components/SurveyResults/RoleChart.tsx @@ -1,7 +1,7 @@ import { GenericChartWithQuery } from './GenericChartWithQuery' // Generate SQL query for team count chart -function generateRoleSQL(activeFilters) { +function generateRoleSQL(activeFilters: Record) { const whereClauses = [] if (activeFilters.person_age !== 'unset') { diff --git a/apps/www/components/SurveyResults/SubscriptionsChart.tsx b/apps/www/components/SurveyResults/SubscriptionsChart.tsx deleted file mode 100644 index 1b1456b8a13..00000000000 --- a/apps/www/components/SurveyResults/SubscriptionsChart.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateSubscriptionsSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.industry_normalized !== 'unset') { - whereClauses.push(`industry_normalized = '${activeFilters.industry_normalized}'`) - } - - if (activeFilters.currently_monetizing !== 'unset') { - whereClauses.push(`currently_monetizing = '${activeFilters.currently_monetizing}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return ` -SELECT - unnest(subscriptions) AS technology, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY technology -UNION ALL -SELECT - 'None of the above' AS technology, - COUNT(*) AS total -FROM responses_2025 -WHERE subscriptions = '{}' -ORDER BY total DESC; -` -} - -export function SubscriptionsChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/SurveyChapterSection.tsx b/apps/www/components/SurveyResults/SurveyChapterSection.tsx index dfb370a7514..b63b2db8b3a 100644 --- a/apps/www/components/SurveyResults/SurveyChapterSection.tsx +++ b/apps/www/components/SurveyResults/SurveyChapterSection.tsx @@ -1,53 +1,23 @@ -// import SectionContainer from '~/components/Layouts/SectionContainer' import { SurveyStatCard } from './SurveyStatCard' -import { SurveyPullQuote } from './SurveyPullQuote' import { SurveyWordCloud } from './SurveyWordCloud' import { SurveySummarizedAnswer } from './SurveySummarizedAnswer' import { SurveyRankedAnswersPair } from './SurveyRankedAnswersPair' - -// Charts (TODO: delete unused) +import { SurveySectionBreak } from './SurveySectionBreak' import { AcceleratorParticipationChart } from './AcceleratorParticipationChart' import { RoleChart } from './RoleChart' import { IndustryChart } from './IndustryChart' import { FundingStageChart } from './FundingStageChart' import { DatabasesChart } from './DatabasesChart' import { AIModelsChart } from './AIModelsChart' -// import { TeamCountChart } from '~/components/SurveyResults/TeamCountChart' -// import { TechnicalFoundersChart } from '~/components/SurveyResults/TechnicalFoundersChart' -// import { PreviousCompanyChart } from '~/components/SurveyResults/PreviousCompanyChart' -// import { FundingStageChart } from '~/components/SurveyResults/FundingStageChart' import { HeadquartersChart } from '~/components/SurveyResults/HeadquartersChart' -// import { SurveyPullQuote } from '~/components/SurveyResults/SurveyPullQuote' -// import { SurveyStatCard } from '~/components/SurveyResults/SurveyStatCard' -// import { RoleChart } from '~/components/SurveyResults/RoleChart' -// import { ProblemBeingSolvedChart } from '~/components/SurveyResults/ProblemBeingSolvedChart' -// import { PersonAgeChart } from '~/components/SurveyResults/AgeChart' -// import { MonetizationChart } from '~/components/SurveyResults/MonetizationChart' -// import { AcceleratorParticipationChart } from '~/components/SurveyResults/AcceleratorParticipationChart' -// import { PivotFreqChart } from '~/components/SurveyResults/PivotFreqChart' -// import { FrontendStackChart } from '~/components/SurveyResults/FrontendStackChart' -// import { BackendStackChart } from '~/components/SurveyResults/BackendStackChart' import { SalesToolsChart } from '~/components/SurveyResults/SalesToolsChart' -// import { PricingChart } from '~/components/SurveyResults/PricingChart' -// import { IndustryChart } from '~/components/SurveyResults/IndustryChart' -// import { DataWarehousesChart } from '~/components/SurveyResults/DataWarehousesChart' -// import { VectorDatabasesChart } from '~/components/SurveyResults/VectorDatabasesChart' -// import { OpenTelemetryChart } from '~/components/SurveyResults/OpenTelemetryChart' -// import { ObservabilityChart } from '~/components/SurveyResults/ObservabilityChart' -// import { CloudProvidersChart } from '~/components/SurveyResults/CloudProvidersChart' -// import { MustHaveDevToolsChart } from '~/components/SurveyResults/MustHaveDevToolsChart' import { AICodingToolsChart } from './AICodingToolsChart' -// import { SubscriptionsChart } from '~/components/SurveyResults/SubscriptionsChart' -// import { NewslettersPaidForChart } from '~/components/SurveyResults/NewslettersPaidForChart' -// import { PodcastsListenedToChart } from '~/components/SurveyResults/PodcastsListenedToChart' import { RegularSocialMediaUseChart } from '~/components/SurveyResults/RegularSocialMediaUseChart' import { NewIdeasChart } from '~/components/SurveyResults/NewIdeasChart' import { InitialPayingCustomersChart } from '~/components/SurveyResults/InitialPayingCustomersChart' import { WorldOutlookChart } from '~/components/SurveyResults/WorldOutlookChart' import { BiggestChallengeChart } from '~/components/SurveyResults/BiggestChallengeChart' -import { SurveySectionBreak } from '~/components/SurveyResults/SurveySectionBreak' - interface SurveyChapterSectionProps { number: string title: string diff --git a/apps/www/components/SurveyResults/TeamCountChart.tsx b/apps/www/components/SurveyResults/TeamCountChart.tsx deleted file mode 100644 index 08f07ae9696..00000000000 --- a/apps/www/components/SurveyResults/TeamCountChart.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -// Generate SQL query for team count chart -function generateTeamCountSQL(activeFilters) { - const whereClauses = [] - - if (activeFilters.headquarters !== 'unset') { - whereClauses.push(`headquarters = '${activeFilters.headquarters}'`) - } - - if (activeFilters.funding_stage !== 'unset') { - whereClauses.push(`funding_stage = '${activeFilters.funding_stage}'`) - } - - if (activeFilters.person_age !== 'unset') { - whereClauses.push(`person_age = '${activeFilters.person_age}'`) - } - - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT - team_count, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY team_count -ORDER BY - CASE team_count - WHEN '1-10' THEN 1 - WHEN '11-50' THEN 2 - WHEN '51-100' THEN 3 - WHEN '101-250' THEN 4 - WHEN '250' THEN 5 - END;` -} - -export function TeamCountChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/TechnicalFoundersChart.tsx b/apps/www/components/SurveyResults/TechnicalFoundersChart.tsx deleted file mode 100644 index b5e7c2ef6d8..00000000000 --- a/apps/www/components/SurveyResults/TechnicalFoundersChart.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateTechnicalFoundersSQL(activeFilters: Record) { - const whereClauses = [] - - // Always filter out NULL values - whereClauses.push(`founders_are_technical IS NOT NULL`) - - if (activeFilters.person_age !== 'unset') { - whereClauses.push(`person_age = '${activeFilters.person_age}'`) - } - - if (activeFilters.money_raised !== 'unset') { - whereClauses.push(`money_raised = '${activeFilters.money_raised}'`) - } - - const whereClause = `WHERE ${whereClauses.join('\n AND ')}` - - return `SELECT - CASE - WHEN founders_are_technical = true THEN 'Yes' - WHEN founders_are_technical = false THEN 'No' - -- ELSE 'Unknown' - END AS founders_are_technical, - COUNT(*) AS total -FROM responses_2025 -${whereClause} -GROUP BY founders_are_technical -ORDER BY total DESC;` -} - -export function TechnicalFoundersChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/VectorDatabasesChart.tsx b/apps/www/components/SurveyResults/VectorDatabasesChart.tsx deleted file mode 100644 index 42036a84c1e..00000000000 --- a/apps/www/components/SurveyResults/VectorDatabasesChart.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { GenericChartWithQuery } from './GenericChartWithQuery' - -function generateVectorDatabasesSQL(activeFilters: Record) { - const whereClauses = [] - - if (activeFilters.databases !== 'unset') { - whereClauses.push(`'${activeFilters.databases}' = ANY(databases)`) - } - - // TODO: Returns actual row values (e.g. "Weweb,Svelte,React") instead of just "Svelte" - // FilterDropdown should be able to handle these as separate, unique values - const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join('\n AND ')}` : '' - - return `SELECT - unnest(vector_databases) AS technology, - COUNT(*) AS total -FROM responses_2025${whereClause ? '\n' + whereClause : ''} -GROUP BY technology -ORDER BY total DESC; -` -} - -export function VectorDatabasesChart() { - return ( - - ) -} diff --git a/apps/www/components/SurveyResults/WorldOutlookChart.tsx b/apps/www/components/SurveyResults/WorldOutlookChart.tsx index 8aa35b98e17..53431dfc649 100644 --- a/apps/www/components/SurveyResults/WorldOutlookChart.tsx +++ b/apps/www/components/SurveyResults/WorldOutlookChart.tsx @@ -1,7 +1,7 @@ import { GenericChartWithQuery } from './GenericChartWithQuery' // Generate SQL query for team count chart -function generateWorldOutlookSQL(activeFilters) { +function generateWorldOutlookSQL(activeFilters: Record) { const whereClauses = [] if (activeFilters.role !== 'unset') {