Skip to content

Commit 80cb472

Browse files
committed
Update radar and pie chart styling to match consistent design
- Update radar chart connecting line color to olive (#808000) - Enhance pie chart styling to match radar chart clean layout - Add icons and percentages to pie chart legends with card-based layout - Increase text and icon sizes for better readability - Configure responsive grid layouts: Status/Priority (3 cols), Node (4 cols) - Remove size constraints to match radar chart dimensions - Add TaskDistributionRadar legend support with status icons
1 parent 8452a5d commit 80cb472

4 files changed

Lines changed: 310 additions & 201 deletions

File tree

packages/web/src/components/ListView.tsx

Lines changed: 76 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,53 +1152,49 @@ export function ListView() {
11521152
}
11531153

11541154
return (
1155-
<div className="bg-black rounded-lg p-8 border border-gray-700 min-h-[600px] min-w-[600px] relative">
1156-
<h3 className="text-xl font-semibold text-white mb-2 text-center">{title}</h3>
1157-
1158-
{/* Zoom Controls */}
1159-
<div className="absolute top-8 right-8 flex flex-col items-center space-y-2">
1160-
<div className="flex space-x-1">
1155+
<div className="bg-gray-800 border border-gray-600 rounded-lg p-6">
1156+
<div className="mb-4">
1157+
<h3 className="text-lg font-semibold text-white mb-2">{title}</h3>
1158+
</div>
1159+
<div className="relative">
1160+
{/* Zoom Controls */}
1161+
<div className="absolute top-4 right-4 flex flex-col gap-2 z-10">
11611162
<button
11621163
onClick={handleZoomOut}
1163-
className="w-10 h-10 bg-gray-700 hover:bg-gray-600 text-white rounded-lg flex items-center justify-center text-xl font-bold transition-colors"
1164+
className="p-2 bg-gray-600 hover:bg-gray-500 text-white rounded"
11641165
title="Zoom Out"
11651166
>
1166-
1167+
<ZoomOut className="h-4 w-4" />
11671168
</button>
11681169
<button
11691170
onClick={handleZoomIn}
1170-
className="w-10 h-10 bg-gray-700 hover:bg-gray-600 text-white rounded-lg flex items-center justify-center text-xl font-bold transition-colors"
1171+
className="p-2 bg-gray-600 hover:bg-gray-500 text-white rounded"
11711172
title="Zoom In"
11721173
>
1173-
+
1174+
<ZoomIn className="h-4 w-4" />
1175+
</button>
1176+
<button
1177+
onClick={handleResetLayout}
1178+
className="p-2 bg-gray-600 hover:bg-gray-500 text-white rounded"
1179+
title="Reset Zoom"
1180+
>
1181+
<RotateCcw className="h-4 w-4" />
11741182
</button>
11751183
</div>
1176-
<div className="text-xs text-gray-400 text-center mb-2">
1177-
{(zoomLevel * 100).toFixed(0)}%
1178-
</div>
1179-
<button
1180-
onClick={handleResetLayout}
1181-
className="flex items-center space-x-1 px-2 py-1 bg-green-600 hover:bg-green-700 text-white rounded text-xs transition-colors"
1182-
title="Reset Layout"
1183-
>
1184-
<RotateCcw className="h-3 w-3" />
1185-
<span>Reset</span>
1186-
</button>
1187-
</div>
11881184

1189-
<div className="flex flex-col items-center">
1190-
<div className="overflow-hidden" style={{ width: '400px', height: '400px' }}>
1191-
<svg
1192-
width="400"
1193-
height="400"
1194-
viewBox="0 0 100 100"
1195-
className="mt-4 mb-16"
1185+
<div className="flex justify-center">
1186+
<div
11961187
style={{
11971188
transform: `scale(${zoomLevel})`,
11981189
transformOrigin: 'center',
1199-
transition: 'transform 0.3s ease'
1190+
transition: 'transform 0.2s ease-in-out'
12001191
}}
12011192
>
1193+
<svg
1194+
width="500"
1195+
height="500"
1196+
viewBox="0 0 100 100"
1197+
>
12021198
{filteredData.map((item, index) => {
12031199
const percentage = (item.value / total) * 100;
12041200
const path = createPath(percentage, cumulativePercentage);
@@ -1222,165 +1218,62 @@ export function ListView() {
12221218
</g>
12231219
);
12241220
})}
1225-
</svg>
1221+
</svg>
1222+
</div>
12261223
</div>
1227-
{/* Legend section */}
1228-
<div className="mt-8">
1224+
</div>
1225+
{/* Legend section */}
1226+
<div className="mt-6">
12291227
{(() => {
1230-
// Determine chart type based on data labels
1231-
const isStatusChart = filteredData.some(item => ['Proposed', 'Planned', 'In Progress', 'Completed', 'Blocked'].includes(item.label));
1228+
// Determine chart type and set appropriate grid
12321229
const isPriorityChart = filteredData.some(item => ['Critical', 'High', 'Moderate', 'Low', 'Minimal'].includes(item.label));
1230+
const isStatusChart = filteredData.some(item => ['Proposed', 'Planned', 'In Progress', 'Completed', 'Blocked'].includes(item.label));
1231+
const gridCols = (isPriorityChart || isStatusChart) ? "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-3" : "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3";
12331232

1234-
if (isStatusChart) {
1235-
// Status Distribution - 2 lines with icons
1233+
return (
1234+
<div className={gridCols}>
1235+
{filteredData.map((item, index) => {
1236+
const percentage = ((item.value / total) * 100).toFixed(1);
1237+
const getIcon = (label: string) => {
1238+
switch(label) {
1239+
case 'Proposed': return <ClipboardList className="h-5 w-5" style={{ color: item.color }} />;
1240+
case 'Planned': return <Calendar className="h-5 w-5" style={{ color: item.color }} />;
1241+
case 'In Progress': return <Clock className="h-5 w-5" style={{ color: item.color }} />;
1242+
case 'Completed': return <CheckCircle className="h-5 w-5" style={{ color: item.color }} />;
1243+
case 'Blocked': return <AlertCircle className="h-5 w-5" style={{ color: item.color }} />;
1244+
case 'Critical': return <Flame className="h-5 w-5" style={{ color: item.color }} />;
1245+
case 'High': return <Zap className="h-5 w-5" style={{ color: item.color }} />;
1246+
case 'Moderate': return <Triangle className="h-5 w-5" style={{ color: item.color }} />;
1247+
case 'Low': return <Circle className="h-5 w-5" style={{ color: item.color }} />;
1248+
case 'Minimal': return <ArrowDown className="h-5 w-5" style={{ color: item.color }} />;
1249+
case 'Epic': return <Layers className="h-5 w-5" style={{ color: item.color }} />;
1250+
case 'Milestone': return <Trophy className="h-5 w-5" style={{ color: item.color }} />;
1251+
case 'Outcome': return <Target className="h-5 w-5" style={{ color: item.color }} />;
1252+
case 'Feature': return <Sparkles className="h-5 w-5" style={{ color: item.color }} />;
1253+
case 'Task': return <ListTodo className="h-5 w-5" style={{ color: item.color }} />;
1254+
case 'Bug': return <AlertTriangle className="h-5 w-5" style={{ color: item.color }} />;
1255+
case 'Idea': return <Lightbulb className="h-5 w-5" style={{ color: item.color }} />;
1256+
case 'Research': return <Microscope className="h-5 w-5" style={{ color: item.color }} />;
1257+
default: return <div className="w-4 h-4 rounded-full" style={{ backgroundColor: item.color }}></div>;
1258+
}
1259+
};
1260+
12361261
return (
1237-
<div className="space-y-3">
1238-
{/* First row - 3 items */}
1239-
<div className="flex justify-center gap-x-6">
1240-
{filteredData.slice(0, 3).map((item, index) => {
1241-
const percentage = ((item.value / total) * 100).toFixed(1);
1242-
const getIcon = (label: string) => {
1243-
switch(label) {
1244-
case 'Proposed': return <ClipboardList className="h-4 w-4" style={{ color: item.color }} />;
1245-
case 'Planned': return <Calendar className="h-4 w-4" style={{ color: item.color }} />;
1246-
case 'In Progress': return <Clock className="h-4 w-4" style={{ color: item.color }} />;
1247-
case 'Completed': return <CheckCircle className="h-4 w-4" style={{ color: item.color }} />;
1248-
case 'Blocked': return <AlertCircle className="h-4 w-4" style={{ color: item.color }} />;
1249-
default: return <div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }}></div>;
1250-
}
1251-
};
1252-
return (
1253-
<div key={index} className="flex items-center space-x-2">
1254-
{getIcon(item.label)}
1255-
<span className="text-sm font-medium text-gray-300">{item.label}</span>
1256-
<span className="text-sm font-medium text-gray-400">({percentage}%)</span>
1257-
</div>
1258-
);
1259-
})}
1262+
<div key={index} className="bg-gray-700 rounded p-3">
1263+
<div className="flex items-center mb-2">
1264+
{getIcon(item.label)}
1265+
<span className="text-gray-200 text-base ml-2">{item.label}</span>
12601266
</div>
1261-
{/* Second row - remaining items */}
1262-
{filteredData.length > 3 && (
1263-
<div className="flex justify-center gap-x-6">
1264-
{filteredData.slice(3).map((item, index) => {
1265-
const percentage = ((item.value / total) * 100).toFixed(1);
1266-
const getIcon = (label: string) => {
1267-
switch(label) {
1268-
case 'Proposed': return <ClipboardList className="h-4 w-4" style={{ color: item.color }} />;
1269-
case 'Planned': return <Calendar className="h-4 w-4" style={{ color: item.color }} />;
1270-
case 'In Progress': return <Clock className="h-4 w-4" style={{ color: item.color }} />;
1271-
case 'Completed': return <CheckCircle className="h-4 w-4" style={{ color: item.color }} />;
1272-
case 'Blocked': return <AlertCircle className="h-4 w-4" style={{ color: item.color }} />;
1273-
default: return <div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }}></div>;
1274-
}
1275-
};
1276-
return (
1277-
<div key={index + 3} className="flex items-center space-x-2">
1278-
{getIcon(item.label)}
1279-
<span className="text-sm font-medium text-gray-300">{item.label}</span>
1280-
<span className="text-sm font-medium text-gray-400">({percentage}%)</span>
1281-
</div>
1282-
);
1283-
})}
1284-
</div>
1285-
)}
1286-
</div>
1287-
);
1288-
} else if (isPriorityChart) {
1289-
// Priority Distribution - 2 lines layout with icons
1290-
return (
1291-
<div className="space-y-3">
1292-
{/* First row - 3 items */}
1293-
<div className="flex justify-center gap-x-6">
1294-
{filteredData.slice(0, 3).map((item, index) => {
1295-
const percentage = ((item.value / total) * 100).toFixed(1);
1296-
const getIcon = (label: string) => {
1297-
switch(label) {
1298-
case 'Critical': return <Flame className="h-4 w-4" style={{ color: item.color }} />;
1299-
case 'High': return <Zap className="h-4 w-4" style={{ color: item.color }} />;
1300-
case 'Moderate': return <Triangle className="h-4 w-4" style={{ color: item.color }} />;
1301-
case 'Low': return <Circle className="h-4 w-4" style={{ color: item.color }} />;
1302-
case 'Minimal': return <ArrowDown className="h-4 w-4" style={{ color: item.color }} />;
1303-
default: return <div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }}></div>;
1304-
}
1305-
};
1306-
return (
1307-
<div key={index} className="flex items-center space-x-2">
1308-
{getIcon(item.label)}
1309-
<span className="text-sm font-medium text-gray-300">{item.label}</span>
1310-
<span className="text-sm font-medium text-gray-400">({percentage}%)</span>
1311-
</div>
1312-
);
1313-
})}
1267+
<div className="text-right">
1268+
<span className="text-xl font-bold text-white">{item.value}</span>
1269+
<span className="text-base text-gray-400 ml-1">({percentage}%)</span>
13141270
</div>
1315-
{/* Second row - remaining items */}
1316-
{filteredData.length > 3 && (
1317-
<div className="flex justify-center gap-x-6">
1318-
{filteredData.slice(3).map((item, index) => {
1319-
const percentage = ((item.value / total) * 100).toFixed(1);
1320-
const getIcon = (label: string) => {
1321-
switch(label) {
1322-
case 'Critical': return <Flame className="h-4 w-4" style={{ color: item.color }} />;
1323-
case 'High': return <Zap className="h-4 w-4" style={{ color: item.color }} />;
1324-
case 'Moderate': return <Triangle className="h-4 w-4" style={{ color: item.color }} />;
1325-
case 'Low': return <Circle className="h-4 w-4" style={{ color: item.color }} />;
1326-
case 'Minimal': return <ArrowDown className="h-4 w-4" style={{ color: item.color }} />;
1327-
default: return <div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }}></div>;
1328-
}
1329-
};
1330-
return (
1331-
<div key={index + 3} className="flex items-center space-x-2">
1332-
{getIcon(item.label)}
1333-
<span className="text-sm font-medium text-gray-300">{item.label}</span>
1334-
<span className="text-sm font-medium text-gray-400">({percentage}%)</span>
1335-
</div>
1336-
);
1337-
})}
1338-
</div>
1339-
)}
13401271
</div>
1341-
);
1342-
} else {
1343-
// Node Distribution - 3 items per line layout
1344-
const itemsPerRow = 3;
1345-
const rows = [];
1346-
for (let i = 0; i < filteredData.length; i += itemsPerRow) {
1347-
rows.push(filteredData.slice(i, i + itemsPerRow));
1348-
}
1349-
1350-
return (
1351-
<div className="space-y-3">
1352-
{rows.map((row, rowIndex) => (
1353-
<div key={rowIndex} className="flex justify-center gap-x-6">
1354-
{row.map((item, index) => {
1355-
const percentage = ((item.value / total) * 100).toFixed(1);
1356-
const getIcon = (label: string) => {
1357-
switch(label) {
1358-
case 'Epic': return <Layers className="h-4 w-4" style={{ color: item.color }} />;
1359-
case 'Milestone': return <Trophy className="h-4 w-4" style={{ color: item.color }} />;
1360-
case 'Outcome': return <Target className="h-4 w-4" style={{ color: item.color }} />;
1361-
case 'Feature': return <Sparkles className="h-4 w-4" style={{ color: item.color }} />;
1362-
case 'Task': return <ListTodo className="h-4 w-4" style={{ color: item.color }} />;
1363-
case 'Bug': return <AlertTriangle className="h-4 w-4" style={{ color: item.color }} />;
1364-
case 'Idea': return <Lightbulb className="h-4 w-4" style={{ color: item.color }} />;
1365-
case 'Research': return <Microscope className="h-4 w-4" style={{ color: item.color }} />;
1366-
default: return <div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }}></div>;
1367-
}
1368-
};
1369-
return (
1370-
<div key={rowIndex * itemsPerRow + index} className="flex items-center space-x-2">
1371-
{getIcon(item.label)}
1372-
<span className="text-sm font-medium text-gray-300">{item.label}</span>
1373-
<span className="text-sm font-medium text-gray-400">({percentage}%)</span>
1374-
</div>
1375-
);
1376-
})}
1377-
</div>
1378-
))}
1379-
</div>
1380-
);
1381-
}
1272+
);
1273+
})}
1274+
</div>
1275+
);
13821276
})()}
1383-
</div>
13841277
</div>
13851278
</div>
13861279
);
@@ -1521,7 +1414,7 @@ export function ListView() {
15211414
{/* First Row - Status Distribution */}
15221415
<div className="flex flex-col items-center">
15231416
<h2 className="text-2xl font-bold text-white mb-4">Status Distribution</h2>
1524-
<div className="w-full max-w-[600px]">
1417+
<div className="w-full">
15251418
<PieChart
15261419
title=""
15271420
data={[
@@ -1538,7 +1431,7 @@ export function ListView() {
15381431
{/* Second Row - Priority Distribution */}
15391432
<div className="flex flex-col items-center">
15401433
<h2 className="text-2xl font-bold text-white mb-4">Priority Distribution</h2>
1541-
<div className="w-full max-w-[600px]">
1434+
<div className="w-full">
15421435
<PieChart
15431436
title=""
15441437
data={[
@@ -1555,7 +1448,7 @@ export function ListView() {
15551448
{/* Third Row - Node Distribution */}
15561449
<div className="flex flex-col items-center">
15571450
<h2 className="text-2xl font-bold text-white mb-4">Node Distribution</h2>
1558-
<div className="w-full max-w-[600px]">
1451+
<div className="w-full">
15591452
<PieChart
15601453
title=""
15611454
data={Object.entries(stats.typeStats)

0 commit comments

Comments
 (0)