'use client'
import { useMemo } from 'react'
import {
BarChart, Bar, LineChart, Line, AreaChart, Area,
PieChart, Pie, Cell, RadarChart, Radar, PolarGrid, PolarAngleAxis,
XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
FunnelChart, Funnel, LabelList,
} from 'recharts'
const CHART_COLORS = ['#6366f1', '#22d3ee', '#f59e0b', '#ef4444', '#10b981', '#a78bfa', '#fb923c', '#14b8a6']
interface ChartData {
label: string
value: number
}
interface NoteChartProps {
type: 'bar' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'radar' | 'funnel' | 'gauge'
title?: string
data: ChartData[]
colors?: string[]
showLegend?: boolean
height?: number
}
const isDark = () => document.documentElement.classList.contains('dark')
function NoteChart({ type, title, data, colors, showLegend, height = 250 }: NoteChartProps) {
const chartColors = colors ?? CHART_COLORS
const dark = isDark()
const ttStyle = {
background: dark ? '#1C1C1C' : '#fff',
border: dark ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.08)',
borderRadius: 8,
fontSize: 12,
color: dark ? '#fff' : '#000',
}
const tickStyle = { fill: dark ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)', fontSize: 11 }
const gridStroke = dark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)'
const legendStyle = { fontSize: 12, color: dark ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.6)' }
const xKey = 'label'
const yKeys = ['value']
const renderChart = () => {
switch (type) {
case 'bar':
return (
{showLegend !== false && }
)
case 'horizontal-bar':
return (
{showLegend !== false && }
)
case 'line':
return (
{showLegend !== false && }
)
case 'area':
return (
{showLegend !== false && }
)
case 'pie':
return (
{data.map((_, i) => | )}
{showLegend !== false && }
)
case 'radar':
return (
)
case 'funnel':
return (
({ ...d, fill: chartColors[i % chartColors.length] }))} isAnimationActive>
)
case 'gauge': {
const value = data[0]?.value ?? 0
const max = Math.max(...data.map(d => d.value), 100)
const pct = Math.min(Math.max(value / max, 0), 1)
const color = pct > 0.7 ? '#10b981' : pct > 0.4 ? '#f59e0b' : '#ef4444'
return (
)
}
default:
return Type de graphique non supporté: {type}
}
}
return (
{title &&
{title}
}
{renderChart()}
)
}
// Parse chart from JSON or simple format
export function parseChartFromCode(code: string): NoteChartProps | null {
try {
// Try JSON first
const parsed = JSON.parse(code)
if (parsed.type && parsed.data && Array.isArray(parsed.data)) {
return {
type: parsed.type,
title: parsed.title,
data: parsed.data,
colors: parsed.colors,
showLegend: parsed.showLegend,
height: parsed.height,
}
}
} catch {}
// Try simple format: type, title, then label: value lines
const lines = code.trim().split('\n').map(l => l.trim()).filter(Boolean)
if (lines.length === 0) return null
const firstLine = lines[0].toLowerCase()
let type: NoteChartProps['type'] = 'bar'
let title = ''
if (['bar', 'horizontal-bar', 'line', 'area', 'pie', 'radar', 'funnel', 'gauge'].includes(firstLine)) {
type = firstLine as NoteChartProps['type']
} else if (lines[0].includes(':')) {
// No type specified, default to bar
} else {
title = lines[0]
}
const data: ChartData[] = []
for (const line of lines.slice(title ? 1 : type !== lines[0]?.toLowerCase() ? 0 : 1)) {
const parts = line.split(/[:|]/)
if (parts.length >= 2) {
const label = parts[0].trim()
const value = parseFloat(parts[1].trim())
if (!isNaN(value)) {
data.push({ label, value })
}
}
}
if (data.length === 0) return null
return { type, title: title || undefined, data }
}
export function NoteChartFromCode({ code }: { code: string }) {
const props = useMemo(() => parseChartFromCode(code), [code])
if (!props) return Format de graphique invalide
return
}