'use client'
import React, { useState, useMemo } from 'react'
interface MatrixData {
rows: string[]
columns: string[]
values: number[][]
}
interface TooltipState {
visible: boolean
x: number
y: number
content: string
}
const MatrixTable: React.FC = () => {
const [tooltipState, setTooltipState] = useState<TooltipState>({
visible: false,
x: 0,
y: 0,
content: '',
})
// Demo data: Sales by Region and Quarter
const data: MatrixData = {
rows: ['North Region', 'South Region', 'East Region', 'West Region', 'Central Region'],
columns: ['Q1 2024', 'Q2 2024', 'Q3 2024', 'Q4 2024'],
values: [
[45000, 52000, 48000, 61000],
[38000, 41000, 39000, 52000],
[62000, 58000, 65000, 72000],
[51000, 54000, 49000, 58000],
[33000, 36000, 41000, 47000],
],
}
// Calculate row and column totals
const { rowTotals, columnTotals, maxValue, minValue } = useMemo(() => {
const rowTotals = data.values.map((row) =>
row.reduce((sum, val) => sum + val, 0)
)
const columnTotals = data.columns.map((_, colIndex) =>
data.values.reduce((sum, row) => sum + row[colIndex], 0)
)
const allValues = data.values.flat()
return {
rowTotals,
columnTotals,
maxValue: Math.max(...allValues),
minValue: Math.min(...allValues),
}
}, [])
// Function to calculate color intensity based on value
const getHeatmapColor = (value: number): string => {
if (maxValue === minValue) {
return 'bg-blue-500'
}
const normalized = (value - minValue) / (maxValue - minValue)
// Blue intensity scale: light blue to dark blue
if (normalized < 0.25) {
return 'bg-blue-200'
} else if (normalized < 0.5) {
return 'bg-blue-400'
} else if (normalized < 0.75) {
return 'bg-blue-600'
} else {
return 'bg-blue-800'
}
}
// Format number with thousand separator
const formatNumber = (num: number): string => {
return num.toLocaleString('en-US')
}
const handleCellHover = (
e: React.MouseEvent<HTMLDivElement>,
rowLabel: string,
colLabel: string,
value: number
) => {
const rect = e.currentTarget.getBoundingClientRect()
setTooltipState({
visible: true,
x: rect.left + rect.width / 2,
y: rect.top - 10,
content: `${rowLabel} - ${colLabel}: ${formatNumber(value)}`,
})
}
const handleMouseLeave = () => {
setTooltipState({ ...tooltipState, visible: false })
}
return (
<div className="w-full bg-[#0A0A0A] text-[#F5F5F0] p-8 rounded-lg">
<div className="mb-6">
<h2 className="text-2xl font-bold mb-2 text-[#C9A84C]">Matrix Table</h2>
<p className="text-sm text-gray-400">Sales Performance by Region and Quarter</p>
</div>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr>
<th className="bg-gray-900 border border-[#C9A84C] p-3 text-left text-[#C9A84C] font-semibold w-32">
Region
</th>
{data.columns.map((col, idx) => (
<th
key={idx}
className="bg-gray-900 border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-semibold min-w-24"
>
{col}
</th>
))}
<th className="bg-gray-900 border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-semibold min-w-24">
Total
</th>
</tr>
</thead>
<tbody>
{data.rows.map((row, rowIdx) => (
<tr key={rowIdx}>
<td className="bg-gray-900 border border-gray-700 p-3 text-[#C9A84C] font-semibold">
{row}
</td>
{data.values[rowIdx].map((value, colIdx) => (
<td
key={`${rowIdx}-${colIdx}`}
className={`border border-gray-700 p-3 text-center text-white cursor-pointer transition-opacity hover:opacity-80 ${getHeatmapColor(value)}`}
onMouseEnter={(e) => handleCellHover(e, row, data.columns[colIdx], value)}
onMouseLeave={handleMouseLeave}
>
<span className="font-medium">{formatNumber(value)}</span>
</td>
))}
<td className="bg-gray-900 border border-gray-700 p-3 text-center text-[#C9A84C] font-bold">
{formatNumber(rowTotals[rowIdx])}
</td>
</tr>
))}
<tr className="bg-gray-900">
<td className="border border-[#C9A84C] p-3 text-[#C9A84C] font-bold">Total</td>
{columnTotals.map((total, idx) => (
<td
key={idx}
className="border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-bold"
>
{formatNumber(total)}
</td>
))}
<td className="border border-[#C9A84C] p-3 text-center text-[#C9A84C] font-bold">
{formatNumber(rowTotals.reduce((a, b) => a + b, 0))}
</td>
</tr>
</tbody>
</table>
</div>
{/* Heatmap legend */}
<div className="mt-6 flex items-center gap-4">
<span className="text-sm font-semibold text-[#C9A84C]">Intensity Scale:</span>
<div className="flex gap-2 items-center">
<div className="w-6 h-6 bg-blue-200 border border-gray-600"></div>
<span className="text-xs text-gray-400">Low</span>
<div className="w-6 h-6 bg-blue-400 border border-gray-600"></div>
<div className="w-6 h-6 bg-blue-600 border border-gray-600"></div>
<div className="w-6 h-6 bg-blue-800 border border-gray-600"></div>
<span className="text-xs text-gray-400">High</span>
</div>
</div>
{/* Tooltip */}
{tooltipState.visible && (
<div
className="fixed bg-gray-800 text-white px-3 py-2 rounded text-sm border border-[#C9A84C] pointer-events-none z-50"
style={{
left: `${tooltipState.x}px`,
top: `${tooltipState.y}px`,
transform: 'translate(-50%, -100%)',
}}
>
{tooltipState.content}
</div>
)}
</div>
)
}
export default MatrixTable