wip: poc chart

This commit is contained in:
Danny White committed 2025-08-22 12:47:05 +10:00
1 parent 71506f55af
commit cbb6f6c158
2 files changed
+80 -37

No files matched your search

@@ -115,8 +115,14 @@ function useFilterOptions(filterColumns: string[]) {
return { filters }
}
// Custom hook to fetch survey data using SQL query via RPC
function useSurveyData(sqlQuery: string, shouldFetch: boolean) {
// Custom hook to fetch survey data using SQL query via RPC or function call
function useSurveyData(
sqlQuery: string,
shouldFetch: boolean,
functionName?: string,
functionParams?: (activeFilters: Record<string, string>) => Record<string, any>,
activeFilters?: Record<string, string>
) {
const [chartData, setChartData] = useState<ChartDataItem[]>([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
@@ -131,10 +137,30 @@ function useSurveyData(sqlQuery: string, shouldFetch: boolean) {
// console.log('Executing SQL query:', sqlQuery)
// Execute the SQL query using Supabase RPC
const { data, error: fetchError } = await externalSupabase.rpc('execute_sql', {
query: sqlQuery,
})
let data, fetchError
if (functionName && functionParams && activeFilters) {
// Use the safer function approach
console.log('Active filters:', activeFilters)
const functionParamsData = functionParams(activeFilters)
console.log('Calling function:', functionName, 'with params:', functionParamsData)
const { data: functionData, error: functionError } = await externalSupabase.rpc(
functionName,
functionParamsData
)
console.log('Function response:', functionData, 'error:', functionError)
data = functionData
fetchError = functionError
} else if (sqlQuery) {
// Fallback to the original RPC approach (for backward compatibility)
const { data: sqlData, error: sqlError } = await externalSupabase.rpc('execute_sql', {
query: sqlQuery,
})
data = sqlData
fetchError = sqlError
} else {
throw new Error('No data source specified')
}
if (fetchError) {
console.error('Error executing SQL query:', fetchError)
@@ -145,15 +171,19 @@ function useSurveyData(sqlQuery: string, shouldFetch: boolean) {
console.log('Raw data from SQL query:', data)
// Calculate total for percentage calculation
const total = data.reduce((sum: number, row: any) => sum + parseInt(row.total), 0)
const total = data.reduce(
(sum: number, row: any) => sum + parseInt(row.count || row.total),
0
)
// Transform the data to match chart format
const processedData = data.map((row: any) => {
const rawPercentage = total > 0 ? (parseInt(row.total) / total) * 100 : 0
const count = parseInt(row.count || row.total)
const rawPercentage = total > 0 ? (count / total) * 100 : 0
const roundedPercentage = Math.round(rawPercentage)
return {
label: row.label || row.value || row[Object.keys(row)[0]], // Get the first column as label
label: row.accelerator || row.label || row.value || row[Object.keys(row)[0]], // Get the first column as label
value: roundedPercentage,
rawValue: rawPercentage, // Keep the raw value for bar scaling
}
@@ -170,7 +200,7 @@ function useSurveyData(sqlQuery: string, shouldFetch: boolean) {
}
fetchData()
}, [sqlQuery, shouldFetch])
}, [sqlQuery, shouldFetch, functionName, activeFilters])
return { chartData, isLoading, error }
}
@@ -179,7 +209,9 @@ interface SurveyChartProps {
title: string
targetColumn: string
filterColumns: string[]
generateSQLQuery: (activeFilters: Record<string, string>) => string
generateSQLQuery?: (activeFilters: Record<string, string>) => string
functionName?: string
functionParams?: (activeFilters: Record<string, string>) => Record<string, any>
}
export function SurveyChart({
@@ -187,6 +219,8 @@ export function SurveyChart({
targetColumn,
filterColumns,
generateSQLQuery,
functionName,
functionParams,
}: SurveyChartProps) {
const [isInView, setIsInView] = useState(false)
const chartRef = useRef<HTMLDivElement>(null)
@@ -235,11 +269,15 @@ export function SurveyChart({
)
)
// Generate the SQL query string
const sqlQuery = generateSQLQuery(activeFilters)
// Generate the SQL query string or function parameters
const sqlQuery = generateSQLQuery ? generateSQLQuery(activeFilters) : ''
// Use the custom hook to fetch data
const { chartData, isLoading: dataLoading, error: dataError } = useSurveyData(sqlQuery, isInView)
const {
chartData,
isLoading: dataLoading,
error: dataError,
} = useSurveyData(sqlQuery, isInView, functionName, functionParams, activeFilters)
// Reset animation state when filters change
useEffect(() => {
@@ -446,7 +484,13 @@ export function SurveyChart({
</div>
) : (
<div className="px-8 pt-4 pb-8">
<CodeBlock lang="sql">{sqlQuery}</CodeBlock>
{functionName ? (
<CodeBlock lang="ts">
{`// Function call: ${functionName}(${JSON.stringify(functionParams ? functionParams(activeFilters) : {}, null, 2)})`}
</CodeBlock>
) : (
<CodeBlock lang="sql">{sqlQuery}</CodeBlock>
)}
</div>
)}
@@ -1,26 +1,24 @@
import { SurveyChart, buildWhereClause } from '../SurveyChart'
import { SurveyChart } from '../SurveyChart'
function generateAcceleratorParticipationSQL(activeFilters: Record<string, string>) {
const whereClause = buildWhereClause(activeFilters, ['accelerator_participation IS NOT NULL'])
// Helper function to convert filters to the format expected by the database function
function buildFunctionParams(activeFilters: Record<string, string>) {
const params: Record<string, any> = {}
return `WITH accelerator_mapping AS (
SELECT
accelerator_participation,
CASE
WHEN accelerator_participation IN ('YC', 'Techstars', 'EF', '500 Global', 'Plug and Play', 'Antler') THEN accelerator_participation
WHEN accelerator_participation = 'Did not participate in an accelerator' THEN NULL
ELSE 'Other'
END AS accelerator_clean
FROM responses_2025
${whereClause}
)
SELECT
accelerator_clean AS accelerator_participation,
COUNT(*) AS total
FROM accelerator_mapping
WHERE accelerator_clean IS NOT NULL
GROUP BY accelerator_clean
ORDER BY total DESC;`
// Convert single values to arrays for the function parameters
if (activeFilters.person_age && activeFilters.person_age !== 'unset') {
params.person_age_filter = [activeFilters.person_age]
}
if (activeFilters.location && activeFilters.location !== 'unset') {
params.location_filter = [activeFilters.location]
}
if (activeFilters.money_raised && activeFilters.money_raised !== 'unset') {
params.money_raised_filter = [activeFilters.money_raised]
}
// if (activeFilters.team_size && activeFilters.team_size !== 'unset') {
// params.team_size_filter = [activeFilters.team_size]
// }
return params
}
export function AcceleratorParticipationChart() {
@@ -29,7 +27,8 @@ export function AcceleratorParticipationChart() {
title="If your startup has participated in an accelerator, which one?"
targetColumn="accelerator_participation"
filterColumns={['person_age', 'location', 'money_raised']}
generateSQLQuery={generateAcceleratorParticipationSQL}
functionName="get_accelerator_stats"
functionParams={buildFunctionParams}
/>
)
}