A very performant and light (2mb in memory) link shortener and tracker. Written in Rust and React and uses Postgres/SQLite.

Fix source count in statistics

Changed files
+17 -5
frontend
src
components
+17 -5
frontend/src/components/StatisticsModal.tsx
··· 10 } from "recharts"; 11 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 12 import { toast } from "@/hooks/use-toast"; 13 - import { useState, useEffect } from "react"; 14 15 import { getLinkClickStats, getLinkSourceStats } from "../api/client"; 16 import { ClickStats, SourceStats } from "../types/api"; ··· 97 } 98 }, [isOpen, linkId]); 99 100 return ( 101 <Dialog open={isOpen} onOpenChange={onClose}> 102 <DialogContent className="max-w-3xl"> ··· 138 </CardHeader> 139 <CardContent> 140 <ul className="space-y-2"> 141 - {sourcesData.map((source, index) => ( 142 <li 143 key={source.source} 144 className="flex items-center justify-between py-2 border-b last:border-0" ··· 149 </span> 150 {source.source} 151 </span> 152 - <span className="text-sm font-medium"> 153 - {source.count} clicks 154 - </span> 155 </li> 156 ))} 157 </ul>
··· 10 } from "recharts"; 11 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 12 import { toast } from "@/hooks/use-toast"; 13 + import { useState, useEffect, useMemo } from "react"; 14 15 import { getLinkClickStats, getLinkSourceStats } from "../api/client"; 16 import { ClickStats, SourceStats } from "../types/api"; ··· 97 } 98 }, [isOpen, linkId]); 99 100 + const aggregatedSources = useMemo(() => { 101 + const sourceMap = sourcesData.reduce<Record<string, number>>( 102 + (acc, { source, count }) => ({ 103 + ...acc, 104 + [source]: (acc[source] || 0) + count 105 + }), 106 + {} 107 + ); 108 + 109 + return Object.entries(sourceMap) 110 + .map(([source, count]) => ({ source, count })) 111 + .sort((a, b) => b.count - a.count); 112 + }, [sourcesData]); 113 + 114 return ( 115 <Dialog open={isOpen} onOpenChange={onClose}> 116 <DialogContent className="max-w-3xl"> ··· 152 </CardHeader> 153 <CardContent> 154 <ul className="space-y-2"> 155 + {aggregatedSources.map((source, index) => ( 156 <li 157 key={source.source} 158 className="flex items-center justify-between py-2 border-b last:border-0" ··· 163 </span> 164 {source.source} 165 </span> 166 + <span className="text-sm font-medium">{source.count} clicks</span> 167 </li> 168 ))} 169 </ul>