import { ExternalLink } from "lucide-react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "~/components/ui/table"; type CountByProperty = [string, string, string?][]; function calculateCountPercentages(countByProperty: CountByProperty) { const totalCount = countByProperty.reduce( (sum, row) => sum + parseInt(row[1]), 0, ); return countByProperty.map((row) => { const count = parseInt(row[1]); const percentage = ((count / totalCount) * 100).toFixed(2); return `${percentage}%`; }); } export default function TableCard({ countByProperty, columnHeaders, onClick, labelFormatter, }: { countByProperty: CountByProperty; columnHeaders: string[]; onClick?: (key: string) => void; labelFormatter?: (label: string) => string; }) { const barChartPercentages = calculateCountPercentages(countByProperty); const countFormatter = Intl.NumberFormat("en", { notation: "compact" }); const gridCols = (columnHeaders || []).length === 3 ? "grid-cols-[minmax(0,1fr),minmax(0,8ch),minmax(0,8ch)]" : "grid-cols-[minmax(0,1fr),minmax(0,8ch)]"; return ( {(columnHeaders || []).map((header: string, index) => ( {header} ))} {(countByProperty || []).map((item, index) => { const desc = item[0]; // the description can be either a single string (that is both the key and the label), // or a tuple of type [key, label] const [key, label] = Array.isArray(desc) ? [desc[0], desc[1] || "(unknown)"] : [desc, desc || "(unknown)"]; const formattedLabel = labelFormatter && typeof label === "string" ? labelFormatter(label) : label; return ( {/^https?:\/\//.test(label) ? ( <> Favicon { // Fallback to external link icon if favicon fails to load const target = e.target as HTMLImageElement; target.style.display = "none"; }} /> {onClick ? ( ) : ( formattedLabel )} ) : ( <> {onClick ? ( ) : ( formattedLabel )} )} {countFormatter.format(parseInt(item[1], 10))} {item.length > 2 && item[2] !== undefined && ( {countFormatter.format( parseInt(item[2], 10), )} )} ); })}
); }