mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 14:19:11 +03:00
fix: type errors
This commit is contained in:
1 parent
dceef509a0
commit
abfe84fa46
26 files changed
+6
-1223
No files matched your search
@@ -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 (
|
||||
<GenericChartWithQuery
|
||||
title="What is your age?"
|
||||
targetColumn="person_age"
|
||||
filterColumns={['role', 'headquarters']}
|
||||
generateSQLQuery={generatePersonAgeSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateBackendStackSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="What is your startup's backend stack?"
|
||||
targetColumn="backend_stack"
|
||||
filterColumns={['headquarters', 'industry_normalized', 'currently_monetizing']}
|
||||
generateSQLQuery={generateBackendStackSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
// Generate SQL query for team count chart
|
||||
function generateBiggestChallengeSQL(activeFilters) {
|
||||
function generateBiggestChallengeSQL(activeFilters: Record<string, string>) {
|
||||
const whereClauses = []
|
||||
|
||||
// Always filter out NULL values
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateCloudProvidersSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which cloud provider(s) is your startup using?"
|
||||
targetColumn="observability"
|
||||
filterColumns={['currently_monetizing', 'money_raised', 'backend_stack']}
|
||||
generateSQLQuery={generateCloudProvidersSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateDataWarehousesSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which data warehouse(s) is your startup using? (Filtered)"
|
||||
targetColumn="data_warehouses"
|
||||
filterColumns={['headquarters', 'industry_normalized', 'person_age', 'frontend_stack']}
|
||||
generateSQLQuery={generateDataWarehousesSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>How many full-time employees does your startup have?</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig}>
|
||||
<BarChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
layout="vertical"
|
||||
margin={{
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid horizontal={false} />
|
||||
<YAxis
|
||||
dataKey="label"
|
||||
type="category"
|
||||
tickLine={false}
|
||||
tickMargin={64}
|
||||
axisLine={false}
|
||||
// tickFormatter={(value) => 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
|
||||
}}
|
||||
/>
|
||||
<XAxis dataKey="value" type="number" hide />
|
||||
{/* <ChartTooltip
|
||||
cursor={false}
|
||||
content={<ChartTooltipContent indicator="line" />}
|
||||
/> */}
|
||||
<Bar dataKey="value" layout="vertical" radius={4}>
|
||||
{/* To set per-bar colors in Recharts, `fill` should be applied to the Cell child */}
|
||||
{chartData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={index === 0 ? 'hsl(var(--brand-default))' : 'hsl(var(--brand-300))'}
|
||||
/>
|
||||
))}
|
||||
<LabelList
|
||||
dataKey="value"
|
||||
position="right"
|
||||
offset={8}
|
||||
className="fill-foreground"
|
||||
fontSize={12}
|
||||
formatter={(value: number) => `${value}%`}
|
||||
/>
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateFundingStageSQL(activeFilters) {
|
||||
function generateFundingStageSQL(activeFilters: Record<string, string>) {
|
||||
const whereClauses = []
|
||||
|
||||
if (activeFilters.headquarters !== 'unset') {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
// Generate SQL query for team count chart
|
||||
function generateIndustrySQL(activeFilters) {
|
||||
function generateIndustrySQL(activeFilters: Record<string, string>) {
|
||||
const whereClauses = []
|
||||
|
||||
// Always filter out NULL values
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateMonetizationSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Is your startup monetizing today?"
|
||||
targetColumn="currently_monetizing"
|
||||
filterColumns={['headquarters', 'money_raised']}
|
||||
generateSQLQuery={generateMonetizationSQL}
|
||||
chartType="pie"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateMustHaveDevToolsSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="What are your must-have dev tools?"
|
||||
targetColumn="must_have_dev_tools"
|
||||
filterColumns={['currently_monetizing', 'money_raised', 'backend_stack']}
|
||||
generateSQLQuery={generateMustHaveDevToolsSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateNewslettersPaidForSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which newsletters do you currently pay for subscriptions?"
|
||||
targetColumn="newsletters_paid_for"
|
||||
filterColumns={['headquarters', 'industry_normalized', 'currently_monetizing']}
|
||||
generateSQLQuery={generateNewslettersPaidForSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateObservabilitySQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which tools are your startup currently using for monitoring and observability? (Filtered)"
|
||||
targetColumn="observability"
|
||||
filterColumns={['currently_monetizing', 'money_raised']}
|
||||
generateSQLQuery={generateObservabilitySQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateOpenTelemetrySQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Do you use OpenTelemetry in your observability stack?"
|
||||
targetColumn="use_opentelemetry"
|
||||
filterColumns={['currently_monetizing', 'money_raised']}
|
||||
generateSQLQuery={generateOpenTelemetrySQL}
|
||||
chartType="pie"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generatePivotFreqSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="How many times did your startup have to pivot before arriving at the current idea?"
|
||||
targetColumn="pivots_before_current"
|
||||
// filterColumns={['headquarters', 'currently_monetizing', 'person_age', 'previous_company']}
|
||||
filterColumns={['person_age', 'previous_company']}
|
||||
generateSQLQuery={generatePivotFreqSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generatePodcastsListenedToSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which of these podcasts do you listen to regularly?"
|
||||
targetColumn="podcasts_listened_to"
|
||||
filterColumns={['headquarters', 'industry_normalized', 'currently_monetizing']}
|
||||
generateSQLQuery={generatePodcastsListenedToSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generatePreviousCompanySQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Had you started a company before your current startup?"
|
||||
targetColumn="previous_company"
|
||||
filterColumns={['person_age', 'money_raised']}
|
||||
generateSQLQuery={generatePreviousCompanySQL}
|
||||
chartType="pie"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generatePricingSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="TK: Pricing question"
|
||||
targetColumn="pricing"
|
||||
filterColumns={['team_count']}
|
||||
generateSQLQuery={generatePricingSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateProblemBeingSolvedSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="What problem is your startup solving?"
|
||||
targetColumn="problem_solving"
|
||||
filterColumns={['currently_monetizing', 'money_raised', 'backend_stack']}
|
||||
generateSQLQuery={generateProblemBeingSolvedSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<Card className="w-full overflow-hidden">
|
||||
<CardHeader>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-foreground-lighter">Loading data...</div>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-red-500">Error: {error}</div>
|
||||
</div>
|
||||
) : (
|
||||
<ChartContainer config={chartConfig}>
|
||||
{chartType === 'bar' && (
|
||||
<BarChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
layout="vertical"
|
||||
margin={{
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid horizontal={false} />
|
||||
<YAxis
|
||||
dataKey="label"
|
||||
type="category"
|
||||
tickLine={false}
|
||||
tickMargin={64}
|
||||
axisLine={false}
|
||||
hide={false}
|
||||
width={64}
|
||||
tick={{
|
||||
className: 'text-foreground-lighter',
|
||||
fontSize: 12,
|
||||
textAnchor: 'start',
|
||||
dx: 0,
|
||||
}}
|
||||
/>
|
||||
<XAxis dataKey="value" type="number" hide />
|
||||
<Bar dataKey="value" layout="vertical" radius={4}>
|
||||
{chartData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={index === 0 ? COLORS_BINARY[0] : COLORS_BINARY[1]}
|
||||
/>
|
||||
))}
|
||||
<LabelList
|
||||
dataKey="value"
|
||||
position="right"
|
||||
offset={8}
|
||||
className="fill-foreground"
|
||||
fontSize={12}
|
||||
formatter={(value: number) => `${value}%`}
|
||||
/>
|
||||
</Bar>
|
||||
</BarChart>
|
||||
)}
|
||||
{chartType === 'pie' && (
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={chartData}
|
||||
dataKey="value"
|
||||
innerRadius={60}
|
||||
outerRadius={100}
|
||||
// startAngle={15}
|
||||
paddingAngle={2}
|
||||
// label={renderCustomizedLabel}
|
||||
>
|
||||
{chartData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={
|
||||
chartData.length === 2
|
||||
? COLORS_BINARY[index % COLORS_BINARY.length]
|
||||
: COLORS[index % COLORS.length]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
)}
|
||||
</ChartContainer>
|
||||
)}
|
||||
</CardContent>
|
||||
{/* <SurveyCodeWindow
|
||||
code={sqlQuery}
|
||||
lang="sql"
|
||||
className="w-full"
|
||||
filters={filters}
|
||||
activeFilters={activeFilters}
|
||||
setFilterValue={setFilterValue}
|
||||
/> */}
|
||||
<div className="flex flex-col">
|
||||
{filters && activeFilters && setFilterValue && (
|
||||
<div className="flex flex-wrap gap-4 p-4 border-b border-border">
|
||||
{Object.entries(filters).map(([filterKey, filterConfig]) => (
|
||||
<FilterDropdown
|
||||
filterKey={filterKey}
|
||||
filterConfig={filterConfig}
|
||||
selectedValue={activeFilters[filterKey]}
|
||||
setFilterValue={setFilterValue}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* <CodeBlock lang="sql" size="small" showLineNumbers={true} rounded={false}>
|
||||
{sqlQuery}
|
||||
</CodeBlock> */}
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
// Generate SQL query for team count chart
|
||||
function generateRoleSQL(activeFilters) {
|
||||
function generateRoleSQL(activeFilters: Record<string, string>) {
|
||||
const whereClauses = []
|
||||
|
||||
if (activeFilters.person_age !== 'unset') {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateSubscriptionsSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which of the following subscriptions does your startup pay for your team?"
|
||||
targetColumn="subscriptions"
|
||||
filterColumns={['headquarters', 'industry_normalized', 'currently_monetizing']}
|
||||
generateSQLQuery={generateSubscriptionsSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
<GenericChartWithQuery
|
||||
title="How many full-time employees does your startup have?"
|
||||
targetColumn="team_count"
|
||||
filterColumns={['headquarters', 'funding_stage', 'person_age']}
|
||||
generateSQLQuery={generateTeamCountSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateTechnicalFoundersSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Are the founder(s) at your current startup technical?"
|
||||
targetColumn="founders_are_technical"
|
||||
filterColumns={['person_age', 'money_raised']}
|
||||
generateSQLQuery={generateTechnicalFoundersSQL}
|
||||
chartType="pie"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
function generateVectorDatabasesSQL(activeFilters: Record<string, string>) {
|
||||
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 (
|
||||
<GenericChartWithQuery
|
||||
title="Which vector database(s) is your startup using? (Filtered)"
|
||||
targetColumn="vector_databases"
|
||||
filterColumns={['databases']}
|
||||
generateSQLQuery={generateVectorDatabasesSQL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GenericChartWithQuery } from './GenericChartWithQuery'
|
||||
|
||||
// Generate SQL query for team count chart
|
||||
function generateWorldOutlookSQL(activeFilters) {
|
||||
function generateWorldOutlookSQL(activeFilters: Record<string, string>) {
|
||||
const whereClauses = []
|
||||
|
||||
if (activeFilters.role !== 'unset') {
|
||||
|
||||
Reference in new issue
Block a user