diff --git a/components/journeys/SankeyJourney.tsx b/components/journeys/SankeyJourney.tsx index fe5b858..e097cf0 100644 --- a/components/journeys/SankeyJourney.tsx +++ b/components/journeys/SankeyJourney.tsx @@ -1,7 +1,7 @@ 'use client' +import * as d3 from 'd3' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Sankey } from '@nivo/sankey' import { TreeStructure, X } from '@phosphor-icons/react' import type { PathTransition } from '@/lib/api/journeys' @@ -13,33 +13,59 @@ interface SankeyJourneyProps { depth: number } -interface SankeyNode { +interface SNode { id: string - stepIndex: number + name: string + step: number + height: number + x: number + y: number + count: number + inLinks: SLink[] + outLinks: SLink[] } -interface SankeyLink { +interface SLink { source: string target: string value: number -} - -interface SankeyData { - nodes: SankeyNode[] - links: SankeyLink[] + sourceY?: number + targetY?: number } // ─── Constants ────────────────────────────────────────────────────── -const COLUMN_COLORS = [ - '#FD5E0F', '#3B82F6', '#10B981', '#F59E0B', '#8B5CF6', - '#EC4899', '#06B6D4', '#EF4444', '#84CC16', '#F97316', '#6366F1', -] - +const NODE_WIDTH = 30 +const NODE_GAP = 20 +const MIN_NODE_HEIGHT = 2 +const MAX_LINK_HEIGHT = 100 +const LINK_OPACITY = 0.3 +const LINK_HOVER_OPACITY = 0.6 const MAX_NODES_PER_STEP = 25 +const COLOR_PALETTE = [ + 'hsl(160, 45%, 40%)', 'hsl(220, 45%, 50%)', 'hsl(270, 40%, 50%)', + 'hsl(25, 50%, 50%)', 'hsl(340, 40%, 50%)', 'hsl(190, 45%, 45%)', + 'hsl(45, 45%, 50%)', 'hsl(0, 45%, 50%)', +] + // ─── Helpers ──────────────────────────────────────────────────────── +function pathFromId(id: string): string { + const idx = id.indexOf(':') + return idx >= 0 ? id.slice(idx + 1) : id +} + +function stepFromId(id: string): number { + const idx = id.indexOf(':') + return idx >= 0 ? parseInt(id.slice(0, idx), 10) : 0 +} + +function firstSegment(path: string): string { + const parts = path.split('/').filter(Boolean) + return parts.length > 0 ? `/${parts[0]}` : path +} + function smartLabel(path: string): string { if (path === '/' || path === '(other)') return path const segments = path.replace(/\/$/, '').split('/') @@ -47,147 +73,105 @@ function smartLabel(path: string): string { return `.../${segments[segments.length - 1]}` } -/** Extract the original path from a step-prefixed node id like "0:/blog" */ -function pathFromId(id: string): string { - const idx = id.indexOf(':') - return idx >= 0 ? id.slice(idx + 1) : id -} - -/** Extract the step index from a step-prefixed node id */ -function stepFromId(id: string): number { - const idx = id.indexOf(':') - return idx >= 0 ? parseInt(id.slice(0, idx), 10) : 0 -} - // ─── Data Transformation ──────────────────────────────────────────── -function buildSankeyData( +function buildData( transitions: PathTransition[], filterPath?: string, -): SankeyData { +): { nodes: SNode[]; links: SLink[] } { if (transitions.length === 0) return { nodes: [], links: [] } - // Group transitions by step and count sessions per path at each step + // Group transitions by step, count per path per step const stepPaths = new Map>() - for (const t of transitions) { - // from_path at step_index if (!stepPaths.has(t.step_index)) stepPaths.set(t.step_index, new Map()) const fromMap = stepPaths.get(t.step_index)! fromMap.set(t.from_path, (fromMap.get(t.from_path) ?? 0) + t.session_count) - // to_path at step_index + 1 const nextStep = t.step_index + 1 if (!stepPaths.has(nextStep)) stepPaths.set(nextStep, new Map()) const toMap = stepPaths.get(nextStep)! toMap.set(t.to_path, (toMap.get(t.to_path) ?? 0) + t.session_count) } - // For each step, keep top N paths, group rest into (other) - const topPathsPerStep = new Map>() - for (const [step, pathMap] of stepPaths) { - const sorted = Array.from(pathMap.entries()).sort((a, b) => b[1] - a[1]) - const kept = new Set(sorted.slice(0, MAX_NODES_PER_STEP).map(([p]) => p)) - topPathsPerStep.set(step, kept) + // Keep top N per step, rest → (other) + const topPaths = new Map>() + for (const [step, pm] of stepPaths) { + const sorted = Array.from(pm.entries()).sort((a, b) => b[1] - a[1]) + topPaths.set(step, new Set(sorted.slice(0, MAX_NODES_PER_STEP).map(([p]) => p))) } - // Build links with capping + // Build links const linkMap = new Map() for (const t of transitions) { - const fromStep = t.step_index - const toStep = t.step_index + 1 - const fromTop = topPathsPerStep.get(fromStep)! - const toTop = topPathsPerStep.get(toStep)! - - const fromPath = fromTop.has(t.from_path) ? t.from_path : '(other)' - const toPath = toTop.has(t.to_path) ? t.to_path : '(other)' - - // Skip self-links where both collapse to (other) - if (fromPath === '(other)' && toPath === '(other)') continue - - const sourceId = `${fromStep}:${fromPath}` - const targetId = `${toStep}:${toPath}` - const key = `${sourceId}|${targetId}` + const fromTop = topPaths.get(t.step_index)! + const toTop = topPaths.get(t.step_index + 1)! + const fp = fromTop.has(t.from_path) ? t.from_path : '(other)' + const tp = toTop.has(t.to_path) ? t.to_path : '(other)' + if (fp === '(other)' && tp === '(other)') continue + const src = `${t.step_index}:${fp}` + const tgt = `${t.step_index + 1}:${tp}` + const key = `${src}|${tgt}` linkMap.set(key, (linkMap.get(key) ?? 0) + t.session_count) } - let links: SankeyLink[] = Array.from(linkMap.entries()).map(([key, value]) => { - const [source, target] = key.split('|') - return { source, target, value } + let links: SLink[] = Array.from(linkMap.entries()).map(([k, v]) => { + const [source, target] = k.split('|') + return { source, target, value: v } }) - // Collect all node ids referenced by links + // Collect node IDs const nodeIdSet = new Set() - for (const link of links) { - nodeIdSet.add(link.source) - nodeIdSet.add(link.target) - } + for (const l of links) { nodeIdSet.add(l.source); nodeIdSet.add(l.target) } - let nodes: SankeyNode[] = Array.from(nodeIdSet).map((id) => ({ + let nodes: SNode[] = Array.from(nodeIdSet).map((id) => ({ id, - stepIndex: stepFromId(id), + name: pathFromId(id), + step: stepFromId(id), + height: 0, x: 0, y: 0, count: 0, + inLinks: [], outLinks: [], })) - // ─── Filter by path (BFS forward + backward) ──────────────────── + // Filter by path (BFS forward + backward) if (filterPath) { - const matchingNodeIds = nodes - .filter((n) => pathFromId(n.id) === filterPath) - .map((n) => n.id) + const matchIds = nodes.filter((n) => n.name === filterPath).map((n) => n.id) + if (matchIds.length === 0) return { nodes: [], links: [] } - if (matchingNodeIds.length === 0) return { nodes: [], links: [] } - - // Build adjacency - const forwardAdj = new Map>() - const backwardAdj = new Map>() - for (const link of links) { - if (!forwardAdj.has(link.source)) forwardAdj.set(link.source, new Set()) - forwardAdj.get(link.source)!.add(link.target) - if (!backwardAdj.has(link.target)) backwardAdj.set(link.target, new Set()) - backwardAdj.get(link.target)!.add(link.source) + const fwd = new Map>() + const bwd = new Map>() + for (const l of links) { + if (!fwd.has(l.source)) fwd.set(l.source, new Set()) + fwd.get(l.source)!.add(l.target) + if (!bwd.has(l.target)) bwd.set(l.target, new Set()) + bwd.get(l.target)!.add(l.source) } - const reachable = new Set(matchingNodeIds) - - // BFS forward - let queue = [...matchingNodeIds] + const reachable = new Set(matchIds) + let queue = [...matchIds] while (queue.length > 0) { const next: string[] = [] - for (const nodeId of queue) { - for (const neighbor of forwardAdj.get(nodeId) ?? []) { - if (!reachable.has(neighbor)) { - reachable.add(neighbor) - next.push(neighbor) - } + for (const id of queue) { + for (const nb of fwd.get(id) ?? []) { + if (!reachable.has(nb)) { reachable.add(nb); next.push(nb) } + } + } + queue = next + } + queue = [...matchIds] + while (queue.length > 0) { + const next: string[] = [] + for (const id of queue) { + for (const nb of bwd.get(id) ?? []) { + if (!reachable.has(nb)) { reachable.add(nb); next.push(nb) } } } queue = next } - // BFS backward - queue = [...matchingNodeIds] - while (queue.length > 0) { - const next: string[] = [] - for (const nodeId of queue) { - for (const neighbor of backwardAdj.get(nodeId) ?? []) { - if (!reachable.has(neighbor)) { - reachable.add(neighbor) - next.push(neighbor) - } - } - } - queue = next - } - - links = links.filter( - (l) => reachable.has(l.source) && reachable.has(l.target), - ) - - const filteredNodeIds = new Set() - for (const link of links) { - filteredNodeIds.add(link.source) - filteredNodeIds.add(link.target) - } - nodes = nodes.filter((n) => filteredNodeIds.has(n.id)) + links = links.filter((l) => reachable.has(l.source) && reachable.has(l.target)) + const kept = new Set() + for (const l of links) { kept.add(l.source); kept.add(l.target) } + nodes = nodes.filter((n) => kept.has(n.id)) } return { nodes, links } @@ -202,43 +186,325 @@ export default function SankeyJourney({ }: SankeyJourneyProps) { const [filterPath, setFilterPath] = useState(null) const [isDark, setIsDark] = useState(false) + const svgRef = useRef(null) + const containerRef = useRef(null) + const [containerWidth, setContainerWidth] = useState(900) - // Reactively detect dark mode via MutationObserver + // Detect dark mode useEffect(() => { const el = document.documentElement setIsDark(el.classList.contains('dark')) + const obs = new MutationObserver(() => setIsDark(el.classList.contains('dark'))) + obs.observe(el, { attributes: true, attributeFilter: ['class'] }) + return () => obs.disconnect() + }, []) - const observer = new MutationObserver(() => { - setIsDark(el.classList.contains('dark')) - }) - observer.observe(el, { attributes: true, attributeFilter: ['class'] }) - return () => observer.disconnect() + // Measure container + useEffect(() => { + const el = containerRef.current + if (!el) return + const measure = () => setContainerWidth(el.clientWidth) + measure() + const obs = new ResizeObserver(measure) + obs.observe(el) + return () => obs.disconnect() }, []) const data = useMemo( - () => buildSankeyData(transitions, filterPath ?? undefined), + () => buildData(transitions, filterPath ?? undefined), [transitions, filterPath], ) - const handleClick = useCallback( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (item: any) => { - if (!item.id || typeof item.id !== 'string') return // link click, ignore - const path = pathFromId(item.id) - if (path === '(other)') return - setFilterPath((prev) => (prev === path ? null : path)) - }, - [], - ) - - // Clear filter when data changes - const transitionsKey = transitions.length + '-' + depth - const [prevKey, setPrevKey] = useState(transitionsKey) - if (prevKey !== transitionsKey) { - setPrevKey(transitionsKey) + // Clear filter on data change + const transKey = transitions.length + '-' + depth + const [prevKey, setPrevKey] = useState(transKey) + if (prevKey !== transKey) { + setPrevKey(transKey) if (filterPath !== null) setFilterPath(null) } + const handleNodeClick = useCallback((path: string) => { + if (path === '(other)') return + setFilterPath((prev) => (prev === path ? null : path)) + }, []) + + // ─── D3 Rendering ────────────────────────────────────────────── + useEffect(() => { + if (!svgRef.current || data.nodes.length === 0) return + + const svg = d3.select(svgRef.current) + svg.selectAll('*').remove() + + const { nodes, links } = data + + const linkColor = isDark ? 'rgba(163,163,163,0.5)' : 'rgba(82,82,82,0.5)' + const textColor = isDark ? '#e5e5e5' : '#171717' + + // Wire up node ↔ link references + for (const n of nodes) { n.inLinks = []; n.outLinks = []; n.count = 0 } + const nodeMap = new Map(nodes.map((n) => [n.id, n])) + for (const l of links) { + const src = nodeMap.get(l.source) + const tgt = nodeMap.get(l.target) + if (src) src.outLinks.push(l) + if (tgt) tgt.inLinks.push(l) + } + for (const n of nodes) { + const inVal = n.inLinks.reduce((s, l) => s + l.value, 0) + const outVal = n.outLinks.reduce((s, l) => s + l.value, 0) + n.count = n.step === 0 ? outVal : Math.max(inVal, outVal) + } + + // Calculate node heights (proportional to value) + const maxVal = d3.max(links, (l) => l.value) || 1 + const heightScale = d3.scaleLinear().domain([0, maxVal]).range([0, MAX_LINK_HEIGHT]) + for (const n of nodes) { + const inVal = n.inLinks.reduce((s, l) => s + l.value, 0) + const outVal = n.outLinks.reduce((s, l) => s + l.value, 0) + n.height = Math.max(heightScale(Math.max(inVal, outVal)), MIN_NODE_HEIGHT) + } + + // Group by step, determine layout + const byStep = d3.group(nodes, (n) => n.step) + const numSteps = byStep.size + const width = containerWidth + const stepWidth = width / numSteps + + // Calculate chart height from tallest column + const stepHeights = Array.from(byStep.values()).map( + (ns) => ns.reduce((s, n) => s + n.height, 0) + (ns.length - 1) * NODE_GAP, + ) + const height = Math.max(200, Math.max(...stepHeights) + 20) + + // Position nodes in columns, aligned from top + byStep.forEach((stepNodes, step) => { + let cy = 0 + for (const n of stepNodes) { + n.x = step * stepWidth + n.y = cy + n.height / 2 + cy += n.height + NODE_GAP + } + }) + + // Calculate link y-positions (stacked within each node) + for (const n of nodes) { + n.outLinks.sort((a, b) => b.value - a.value) + n.inLinks.sort((a, b) => b.value - a.value) + + let outY = n.y - n.height / 2 + for (const l of n.outLinks) { + const lh = heightScale(l.value) + l.sourceY = outY + lh / 2 + outY += lh + } + + let inY = n.y - n.height / 2 + for (const l of n.inLinks) { + const lh = heightScale(l.value) + l.targetY = inY + lh / 2 + inY += lh + } + } + + // Color by first path segment + const segCounts = new Map() + for (const n of nodes) { + const seg = firstSegment(n.name) + segCounts.set(seg, (segCounts.get(seg) ?? 0) + 1) + } + const segColors = new Map() + let ci = 0 + segCounts.forEach((count, seg) => { + if (count > 1) { segColors.set(seg, COLOR_PALETTE[ci % COLOR_PALETTE.length]); ci++ } + }) + const defaultColor = isDark ? 'hsl(0, 0%, 50%)' : 'hsl(0, 0%, 45%)' + const nodeColor = (n: SNode) => segColors.get(firstSegment(n.name)) ?? defaultColor + const linkSourceColor = (l: SLink) => { + const src = nodeMap.get(l.source) + return src ? nodeColor(src) : linkColor + } + + // Link path generator + const linkPath = (l: SLink) => { + const src = nodeMap.get(l.source) + const tgt = nodeMap.get(l.target) + if (!src || !tgt) return '' + const sy = l.sourceY ?? src.y + const ty = l.targetY ?? tgt.y + const sx = src.x + NODE_WIDTH + const tx = tgt.x + const gap = tx - sx + const c1x = sx + gap / 3 + const c2x = tx - gap / 3 + return `M ${sx},${sy} C ${c1x},${sy} ${c2x},${ty} ${tx},${ty}` + } + + svg.attr('width', width).attr('height', height) + const g = svg.append('g') + + // ── Draw links ──────────────────────────────────────── + g.selectAll('.link') + .data(links) + .join('path') + .attr('class', 'link') + .attr('d', linkPath) + .attr('fill', 'none') + .attr('stroke', (d) => linkSourceColor(d)) + .attr('stroke-width', (d) => heightScale(d.value)) + .attr('opacity', LINK_OPACITY) + .attr('data-source', (d) => d.source) + .attr('data-target', (d) => d.target) + .style('pointer-events', 'none') + + // ── Tooltip ─────────────────────────────────────────── + const tooltip = d3.select('body').append('div') + .style('position', 'absolute') + .style('visibility', 'hidden') + .style('background', isDark ? '#262626' : '#f5f5f5') + .style('border', `1px solid ${isDark ? '#404040' : '#d4d4d4'}`) + .style('border-radius', '8px') + .style('padding', '8px 12px') + .style('font-size', '12px') + .style('color', isDark ? '#fff' : '#171717') + .style('pointer-events', 'none') + .style('z-index', '9999') + .style('box-shadow', '0 4px 12px rgba(0,0,0,0.15)') + + // ── Draw nodes ──────────────────────────────────────── + const nodeGs = g.selectAll('.node') + .data(nodes) + .join('g') + .attr('class', 'node') + .attr('transform', (d) => `translate(${d.x},${d.y - d.height / 2})`) + .style('cursor', 'pointer') + + // Node bars + nodeGs.append('rect') + .attr('class', 'node-rect') + .attr('width', NODE_WIDTH) + .attr('height', (d) => d.height) + .attr('fill', (d) => nodeColor(d)) + .attr('rx', 2) + .attr('ry', 2) + + // Node labels + nodeGs.append('text') + .attr('class', 'node-text') + .attr('x', NODE_WIDTH + 6) + .attr('y', (d) => d.height / 2 + 4) + .text((d) => smartLabel(d.name)) + .attr('font-size', '12px') + .attr('fill', textColor) + .attr('text-anchor', 'start') + + // ── Hover: find all connected paths ─────────────────── + const findConnected = (startLink: SLink, dir: 'fwd' | 'bwd') => { + const result: SLink[] = [] + const visited = new Set() + const queue = [startLink] + while (queue.length > 0) { + const cur = queue.shift()! + const lid = `${cur.source}|${cur.target}` + if (visited.has(lid)) continue + visited.add(lid) + result.push(cur) + if (dir === 'fwd') { + const tgt = nodeMap.get(cur.target) + if (tgt) tgt.outLinks.forEach((l) => queue.push(l)) + } else { + const src = nodeMap.get(cur.source) + if (src) src.inLinks.forEach((l) => queue.push(l)) + } + } + return result + } + + const highlightPaths = (nodeId: string) => { + const connectedLinks: SLink[] = [] + const connectedNodes = new Set([nodeId]) + const directLinks = links.filter((l) => l.source === nodeId || l.target === nodeId) + for (const dl of directLinks) { + connectedLinks.push(dl, ...findConnected(dl, 'fwd'), ...findConnected(dl, 'bwd')) + } + const connectedLinkIds = new Set(connectedLinks.map((l) => `${l.source}|${l.target}`)) + connectedLinks.forEach((l) => { connectedNodes.add(l.source); connectedNodes.add(l.target) }) + + g.selectAll('.link') + .attr('opacity', function () { + const s = d3.select(this).attr('data-source') + const t = d3.select(this).attr('data-target') + return connectedLinkIds.has(`${s}|${t}`) ? LINK_HOVER_OPACITY : 0.05 + }) + g.selectAll('.node-rect') + .attr('opacity', (d) => connectedNodes.has(d.id) ? 1 : 0.15) + g.selectAll('.node-text') + .attr('opacity', (d) => connectedNodes.has(d.id) ? 1 : 0.2) + } + + const resetHighlight = () => { + g.selectAll('.link').attr('opacity', LINK_OPACITY) + .attr('stroke', (d: unknown) => linkSourceColor(d as SLink)) + g.selectAll('.node-rect').attr('opacity', 1) + g.selectAll('.node-text').attr('opacity', 1) + tooltip.style('visibility', 'hidden') + } + + // Node hover + nodeGs + .on('mouseenter', function (event, d) { + tooltip.style('visibility', 'visible') + .html(`
${d.name}
${d.count.toLocaleString()} sessions
`) + .style('top', `${event.pageY - 10}px`).style('left', `${event.pageX + 12}px`) + highlightPaths(d.id) + }) + .on('mousemove', (event) => { + tooltip.style('top', `${event.pageY - 10}px`).style('left', `${event.pageX + 12}px`) + }) + .on('mouseleave', resetHighlight) + .on('click', (_, d) => handleNodeClick(d.name)) + + // Link hit areas (wider invisible paths for easier hovering) + g.selectAll('.link-hit') + .data(links) + .join('path') + .attr('class', 'link-hit') + .attr('d', linkPath) + .attr('fill', 'none') + .attr('stroke', 'transparent') + .attr('stroke-width', (d) => Math.max(heightScale(d.value), 14)) + .attr('data-source', (d) => d.source) + .attr('data-target', (d) => d.target) + .style('cursor', 'pointer') + .on('mouseenter', function (event, d) { + const src = nodeMap.get(d.source) + const tgt = nodeMap.get(d.target) + tooltip.style('visibility', 'visible') + .html(`
${src?.name ?? '?'} → ${tgt?.name ?? '?'}
${d.value.toLocaleString()} sessions
`) + .style('top', `${event.pageY - 10}px`).style('left', `${event.pageX + 12}px`) + // Highlight this link's connected paths + const all = [d, ...findConnected(d, 'fwd'), ...findConnected(d, 'bwd')] + const lids = new Set(all.map((l) => `${l.source}|${l.target}`)) + const nids = new Set() + all.forEach((l) => { nids.add(l.source); nids.add(l.target) }) + g.selectAll('.link') + .attr('opacity', function () { + const s = d3.select(this).attr('data-source') + const t = d3.select(this).attr('data-target') + return lids.has(`${s}|${t}`) ? LINK_HOVER_OPACITY : 0.05 + }) + g.selectAll('.node-rect') + .attr('opacity', (nd) => nids.has(nd.id) ? 1 : 0.15) + g.selectAll('.node-text') + .attr('opacity', (nd) => nids.has(nd.id) ? 1 : 0.2) + }) + .on('mousemove', (event) => { + tooltip.style('top', `${event.pageY - 10}px`).style('left', `${event.pageX + 12}px`) + }) + .on('mouseleave', resetHighlight) + + return () => { tooltip.remove() } + }, [data, containerWidth, isDark, handleNodeClick]) + // ─── Empty state ──────────────────────────────────────────────── if (!transitions.length || data.nodes.length === 0) { return ( @@ -256,34 +522,10 @@ export default function SankeyJourney({ ) } - const labelColor = isDark ? '#a3a3a3' : '#525252' - - // * Scale height based on max nodes in any step so blocks aren't too compressed - const stepCounts = new Map() - for (const n of data.nodes) { - stepCounts.set(n.stepIndex, (stepCounts.get(n.stepIndex) ?? 0) + 1) - } - const maxNodesInStep = Math.max(1, ...Array.from(stepCounts.values())) - const chartHeight = Math.max(400, Math.min(700, maxNodesInStep * 45)) - const containerRef = useRef(null) - const [chartWidth, setChartWidth] = useState(800) - - // * Measure container width and resize chart to fit without horizontal scroll - useEffect(() => { - const el = containerRef.current - if (!el) return - const measure = () => setChartWidth(el.clientWidth) - measure() - const observer = new ResizeObserver(measure) - observer.observe(el) - return () => observer.disconnect() - }, []) - return (
- {/* Filter reset bar */} {filterPath && ( -
+
Showing flows through{' '} @@ -301,71 +543,8 @@ export default function SankeyJourney({
)} -
-
- - data={data} - width={chartWidth} - height={chartHeight} - margin={{ top: 8, right: 16, bottom: 8, left: 16 }} - align="justify" - sort="descending" - colors={(node) => - COLUMN_COLORS[node.stepIndex % COLUMN_COLORS.length] - } - nodeThickness={100} - nodeSpacing={4} - nodeInnerPadding={2} - nodeBorderWidth={0} - nodeBorderRadius={4} - nodeOpacity={0.9} - nodeHoverOpacity={1} - nodeHoverOthersOpacity={0.3} - linkOpacity={0.12} - linkHoverOpacity={0.4} - linkHoverOthersOpacity={0.02} - linkContract={2} - enableLinkGradient - enableLabels - label={(node) => smartLabel(pathFromId(node.id))} - labelPosition="inside" - labelPadding={8} - labelTextColor="#ffffff" - isInteractive - onClick={handleClick} - nodeTooltip={({ node }) => ( -
-
- {pathFromId(node.id)} -
-
- Step {node.stepIndex + 1} ·{' '} - {node.value.toLocaleString()} sessions -
-
- )} - linkTooltip={({ link }) => ( -
-
- {pathFromId(link.source.id)} →{' '} - {pathFromId(link.target.id)} -
-
- {link.value.toLocaleString()} sessions -
-
- )} - theme={{ - tooltip: { - container: { - background: 'transparent', - boxShadow: 'none', - padding: 0, - }, - }, - }} - /> -
+
+
) diff --git a/package-lock.json b/package-lock.json index 55577fe..df9c23b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,15 +10,16 @@ "dependencies": { "@ciphera-net/ui": "^0.2.7", "@ducanh2912/next-pwa": "^10.2.9", - "@nivo/sankey": "^0.99.0", "@phosphor-icons/react": "^2.1.10", "@simplewebauthn/browser": "^13.2.2", "@stripe/react-stripe-js": "^5.6.0", "@stripe/stripe-js": "^8.7.0", "@tanstack/react-virtual": "^3.13.21", + "@types/d3": "^7.4.3", "class-variance-authority": "^0.7.1", "cobe": "^0.6.5", "country-flag-icons": "^1.6.4", + "d3": "^7.9.0", "d3-scale": "^4.0.2", "framer-motion": "^12.23.26", "html-to-image": "^1.11.13", @@ -3270,174 +3271,6 @@ "node": ">= 10" } }, - "node_modules/@nivo/colors": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/colors/-/colors-0.99.0.tgz", - "integrity": "sha512-hyYt4lEFIfXOUmQ6k3HXm3KwhcgoJpocmoGzLUqzk7DzuhQYJo+4d5jIGGU0N/a70+9XbHIdpKNSblHAIASD3w==", - "license": "MIT", - "dependencies": { - "@nivo/core": "0.99.0", - "@nivo/theming": "0.99.0", - "@types/d3-color": "^3.0.0", - "@types/d3-scale": "^4.0.8", - "@types/d3-scale-chromatic": "^3.0.0", - "d3-color": "^3.1.0", - "d3-scale": "^4.0.2", - "d3-scale-chromatic": "^3.0.0", - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, - "node_modules/@nivo/core": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/core/-/core-0.99.0.tgz", - "integrity": "sha512-olCItqhPG3xHL5ei+vg52aB6o+6S+xR2idpkd9RormTTUniZb8U2rOdcQojOojPY5i9kVeQyLFBpV4YfM7OZ9g==", - "license": "MIT", - "dependencies": { - "@nivo/theming": "0.99.0", - "@nivo/tooltip": "0.99.0", - "@react-spring/web": "9.4.5 || ^9.7.2 || ^10.0", - "@types/d3-shape": "^3.1.6", - "d3-color": "^3.1.0", - "d3-format": "^1.4.4", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-scale-chromatic": "^3.0.0", - "d3-shape": "^3.2.0", - "d3-time-format": "^3.0.0", - "lodash": "^4.17.21", - "react-virtualized-auto-sizer": "^1.0.26", - "use-debounce": "^10.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nivo/donate" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, - "node_modules/@nivo/core/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/@nivo/core/node_modules/d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@nivo/core/node_modules/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "2" - } - }, - "node_modules/@nivo/core/node_modules/d3-time-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", - "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-time": "1 - 2" - } - }, - "node_modules/@nivo/core/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "license": "ISC" - }, - "node_modules/@nivo/legends": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/legends/-/legends-0.99.0.tgz", - "integrity": "sha512-P16FjFqNceuTTZphINAh5p0RF0opu3cCKoWppe2aRD9IuVkvRm/wS5K1YwMCxDzKyKh5v0AuTlu9K6o3/hk8hA==", - "license": "MIT", - "dependencies": { - "@nivo/colors": "0.99.0", - "@nivo/core": "0.99.0", - "@nivo/text": "0.99.0", - "@nivo/theming": "0.99.0", - "@types/d3-scale": "^4.0.8", - "d3-scale": "^4.0.2" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, - "node_modules/@nivo/sankey": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/sankey/-/sankey-0.99.0.tgz", - "integrity": "sha512-u5hySywsachjo9cHdUxCR9qwD6gfRVPEAcpuIUKiA0WClDjdGbl3vkrQcQcFexJUBThqSSbwGCDWR+2INXSbTw==", - "license": "MIT", - "dependencies": { - "@nivo/colors": "0.99.0", - "@nivo/core": "0.99.0", - "@nivo/legends": "0.99.0", - "@nivo/text": "0.99.0", - "@nivo/theming": "0.99.0", - "@nivo/tooltip": "0.99.0", - "@react-spring/web": "9.4.5 || ^9.7.2 || ^10.0", - "@types/d3-sankey": "^0.11.2", - "@types/d3-shape": "^3.1.6", - "d3-sankey": "^0.12.3", - "d3-shape": "^3.2.0", - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, - "node_modules/@nivo/text": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/text/-/text-0.99.0.tgz", - "integrity": "sha512-ho3oZpAZApsJNjsIL5WJSAdg/wjzTBcwo1KiHBlRGUmD+yUWO8qp7V+mnYRhJchwygtRVALlPgZ/rlcW2Xr/MQ==", - "license": "MIT", - "dependencies": { - "@nivo/core": "0.99.0", - "@nivo/theming": "0.99.0", - "@react-spring/web": "9.4.5 || ^9.7.2 || ^10.0" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, - "node_modules/@nivo/theming": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/theming/-/theming-0.99.0.tgz", - "integrity": "sha512-KvXlf0nqBzh/g2hAIV9bzscYvpq1uuO3TnFN3RDXGI72CrbbZFTGzprPju3sy/myVsauv+Bb+V4f5TZ0jkYKRg==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, - "node_modules/@nivo/tooltip": { - "version": "0.99.0", - "resolved": "https://registry.npmjs.org/@nivo/tooltip/-/tooltip-0.99.0.tgz", - "integrity": "sha512-weoEGR3xAetV4k2P6k96cdamGzKQ5F2Pq+uyDaHr1P3HYArM879Pl+x+TkU0aWjP6wgUZPx/GOBiV1Hb1JxIqg==", - "license": "MIT", - "dependencies": { - "@nivo/core": "0.99.0", - "@nivo/theming": "0.99.0", - "@react-spring/web": "9.4.5 || ^9.7.2 || ^10.0" - }, - "peerDependencies": { - "react": "^16.14 || ^17.0 || ^18.0 || ^19.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4994,78 +4827,6 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, - "node_modules/@react-spring/animated": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-10.0.3.tgz", - "integrity": "sha512-7MrxADV3vaUADn2V9iYhaIL6iOWRx9nCJjYrsk2AHD2kwPr6fg7Pt0v+deX5RnCDmCKNnD6W5fasiyM8D+wzJQ==", - "license": "MIT", - "dependencies": { - "@react-spring/shared": "~10.0.3", - "@react-spring/types": "~10.0.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-spring/core": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-10.0.3.tgz", - "integrity": "sha512-D4DwNO68oohDf/0HG2G0Uragzb9IA1oXblxrd6MZAcBcUQG2EHUWXewjdECMPLNmQvlYVyyBRH6gPxXM5DX7DQ==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~10.0.3", - "@react-spring/shared": "~10.0.3", - "@react-spring/types": "~10.0.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-spring/rafz": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-10.0.3.tgz", - "integrity": "sha512-Ri2/xqt8OnQ2iFKkxKMSF4Nqv0LSWnxXT4jXFzBDsHgeeH/cHxTLupAWUwmV9hAGgmEhBmh5aONtj3J6R/18wg==", - "license": "MIT" - }, - "node_modules/@react-spring/shared": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-10.0.3.tgz", - "integrity": "sha512-geCal66nrkaQzUVhPkGomylo+Jpd5VPK8tPMEDevQEfNSWAQP15swHm+MCRG4wVQrQlTi9lOzKzpRoTL3CA84Q==", - "license": "MIT", - "dependencies": { - "@react-spring/rafz": "~10.0.3", - "@react-spring/types": "~10.0.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-spring/types": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-10.0.3.tgz", - "integrity": "sha512-H5Ixkd2OuSIgHtxuHLTt7aJYfhMXKXT/rK32HPD/kSrOB6q6ooeiWAXkBy7L8F3ZxdkBb9ini9zP9UwnEFzWgQ==", - "license": "MIT" - }, - "node_modules/@react-spring/web": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-10.0.3.tgz", - "integrity": "sha512-ndU+kWY81rHsT7gTFtCJ6mrVhaJ6grFmgTnENipzmKqot4HGf5smPNK+cZZJqoGeDsj9ZsiWPW4geT/NyD484A==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~10.0.3", - "@react-spring/core": "~10.0.3", - "@react-spring/shared": "~10.0.3", - "@react-spring/types": "~10.0.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", @@ -5833,24 +5594,159 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, "node_modules/@types/d3-array": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", "license": "MIT" }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", "license": "MIT" }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, "node_modules/@types/d3-ease": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "license": "MIT" }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", @@ -5866,29 +5762,23 @@ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", "license": "MIT" }, - "node_modules/@types/d3-sankey": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/@types/d3-sankey/-/d3-sankey-0.11.2.tgz", - "integrity": "sha512-U6SrTWUERSlOhnpSrgvMX64WblX1AxX6nEjI2t3mLK2USpQrnbwYYK+AS9SwiE7wgYmOsSSKoSdr8aoKBH0HgQ==", - "license": "MIT", - "dependencies": { - "@types/d3-shape": "^1" - } - }, - "node_modules/@types/d3-sankey/node_modules/@types/d3-path": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.11.tgz", - "integrity": "sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==", + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", "license": "MIT" }, - "node_modules/@types/d3-sankey/node_modules/@types/d3-shape": { - "version": "1.3.12", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.12.tgz", - "integrity": "sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "^1" - } + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" }, "node_modules/@types/d3-scale": { "version": "4.0.9", @@ -5905,6 +5795,12 @@ "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", "license": "MIT" }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, "node_modules/@types/d3-shape": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", @@ -5920,12 +5816,37 @@ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", "license": "MIT" }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, "node_modules/@types/d3-timer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -5977,6 +5898,12 @@ "@types/estree": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -8078,6 +8005,47 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-array": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", @@ -8090,6 +8058,43 @@ "node": ">=12" } }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -8099,6 +8104,121 @@ "node": ">=12" } }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-format": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", @@ -8108,6 +8228,27 @@ "node": ">=12" } }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-interpolate": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", @@ -8129,46 +8270,33 @@ "node": ">=12" } }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "license": "ISC" - }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", @@ -8198,6 +8326,16 @@ "node": ">=12" } }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-shape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", @@ -8243,6 +8381,41 @@ "node": ">=12" } }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -8446,6 +8619,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -10074,6 +10256,18 @@ "node": ">= 14" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", @@ -13017,16 +13211,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/react-virtualized-auto-sizer": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz", - "integrity": "sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==", - "license": "MIT", - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -13306,6 +13490,12 @@ "node": ">= 0.8.15" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, "node_modules/rollup": { "version": "2.79.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", @@ -13345,6 +13535,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -13417,6 +13613,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -14977,18 +15179,6 @@ } } }, - "node_modules/use-debounce": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.1.0.tgz", - "integrity": "sha512-lu87Za35V3n/MyMoEpD5zJv0k7hCn0p+V/fK2kWD+3k2u3kOCwO593UArbczg1fhfs2rqPEnHpULJ3KmGdDzvg==", - "license": "MIT", - "engines": { - "node": ">= 16.0.0" - }, - "peerDependencies": { - "react": "*" - } - }, "node_modules/use-sidecar": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", @@ -15087,15 +15277,6 @@ "d3-timer": "^3.0.1" } }, - "node_modules/victory-vendor/node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", diff --git a/package.json b/package.json index bd8319e..0c5d64a 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,16 @@ "dependencies": { "@ciphera-net/ui": "^0.2.7", "@ducanh2912/next-pwa": "^10.2.9", - "@nivo/sankey": "^0.99.0", "@phosphor-icons/react": "^2.1.10", "@simplewebauthn/browser": "^13.2.2", "@stripe/react-stripe-js": "^5.6.0", "@stripe/stripe-js": "^8.7.0", "@tanstack/react-virtual": "^3.13.21", + "@types/d3": "^7.4.3", "class-variance-authority": "^0.7.1", "cobe": "^0.6.5", "country-flag-icons": "^1.6.4", + "d3": "^7.9.0", "d3-scale": "^4.0.2", "framer-motion": "^12.23.26", "html-to-image": "^1.11.13",