🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
231 lines
12 KiB
TypeScript
231 lines
12 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { ChevronDown, ChevronRight, Target, Users, Building, Code, Shield, DollarSign, Briefcase, Heart, TrendingUp } from 'lucide-react';
|
|
|
|
const ExecutiveDashboard = () => {
|
|
const [expandedDimension, setExpandedDimension] = useState(null);
|
|
|
|
const data = {
|
|
strategy: { kpis: 6, open: 15, completed: 15, progress: 50 },
|
|
operational: {
|
|
sales: { kpis: 3, open: 5, completed: 3, progress: 38, icon: TrendingUp, shade: '#10B981' },
|
|
product: { kpis: 5, open: 19, completed: 5, progress: 21, icon: Target, shade: '#3B82F6' },
|
|
organization: { kpis: 5, open: 5, completed: 5, progress: 50, icon: Building, shade: '#8B5CF6' },
|
|
engineering: { kpis: 5, open: 28, completed: 4, progress: 13, icon: Code, shade: '#F59E0B' },
|
|
devops: { kpis: 3, open: 6, completed: 3, progress: 33, icon: Shield, shade: '#EF4444' },
|
|
cyber: { kpis: 6, open: 29, completed: 6, progress: 17, icon: Shield, shade: '#6B7280' },
|
|
finance: { kpis: 13, open: 3, completed: 13, progress: 81, icon: DollarSign, shade: '#059669' }
|
|
},
|
|
foundation: {
|
|
people: { kpis: 5, open: 7, completed: 5, progress: 42, icon: Users, shade: '#DC2626' },
|
|
culture: { kpis: 3, open: 2, completed: 3, progress: 60, icon: Heart, shade: '#7C3AED' }
|
|
}
|
|
};
|
|
|
|
const toggleDimension = (dimension) => {
|
|
setExpandedDimension(expandedDimension === dimension ? null : dimension);
|
|
};
|
|
|
|
const ProgressBar = ({ progress, shade = '#333333', height = '8px' }) => {
|
|
return (
|
|
<div
|
|
className="bg-gray-200 rounded-full relative overflow-hidden"
|
|
style={{ height, width: '96px' }}
|
|
>
|
|
<div
|
|
className="h-full rounded-full transition-all duration-300 ease-in-out"
|
|
style={{
|
|
width: `${Math.min(progress, 100)}%`,
|
|
backgroundColor: shade
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const DetailedView = () => (
|
|
<div className="space-y-6">
|
|
{/* Strategy Section */}
|
|
<div className="bg-white rounded-xl border-2 border-gray-300 p-6 shadow-sm">
|
|
<div className="border-2 border-gray-400 rounded-lg">
|
|
<button
|
|
onClick={() => toggleDimension('strategy')}
|
|
className="w-full p-4 flex items-center justify-between hover:bg-gray-100 rounded-lg transition-colors"
|
|
>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="p-2 border-2 border-gray-400 rounded-full bg-gray-100">
|
|
<Briefcase className="w-5 h-5 text-gray-700" />
|
|
</div>
|
|
<div className="text-left">
|
|
<h4 className="font-bold text-gray-900 text-lg">Strategy</h4>
|
|
<p className="text-sm text-gray-700 font-medium">{data.strategy.kpis} KPIs • {data.strategy.open + data.strategy.completed} total items</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="text-right">
|
|
<div className="font-bold text-xl text-gray-900">{Math.round(data.strategy.progress)}%</div>
|
|
<div className="w-24">
|
|
<ProgressBar progress={data.strategy.progress} shade="#4F46E5" height="6px" />
|
|
</div>
|
|
</div>
|
|
{expandedDimension === 'strategy' ? <ChevronDown className="w-5 h-5 text-gray-600" /> : <ChevronRight className="w-5 h-5 text-gray-600" />}
|
|
</div>
|
|
</button>
|
|
|
|
{expandedDimension === 'strategy' && (
|
|
<div className="px-4 pb-4 border-t-2 border-gray-300 bg-gray-50 rounded-b-lg">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-900">{data.strategy.kpis}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Key Performance Indicators</div>
|
|
</div>
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-700">{data.strategy.open}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Open Items</div>
|
|
</div>
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-900">{data.strategy.completed}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Completed Items</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* All Operational Dimensions */}
|
|
<div className="bg-white rounded-xl border-2 border-gray-300 p-6 shadow-sm">
|
|
<div className="space-y-4">
|
|
{Object.entries(data.operational).map(([name, dim]) => {
|
|
const Icon = dim.icon;
|
|
const isExpanded = expandedDimension === name;
|
|
|
|
return (
|
|
<div key={name} className="border-2 border-gray-400 rounded-lg">
|
|
<button
|
|
onClick={() => toggleDimension(name)}
|
|
className="w-full p-4 flex items-center justify-between hover:bg-gray-100 rounded-lg transition-colors"
|
|
>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="p-2 border-2 border-gray-400 rounded-full bg-gray-100">
|
|
<Icon className="w-5 h-5" style={{ color: dim.shade }} />
|
|
</div>
|
|
<div className="text-left">
|
|
<h4 className="font-bold text-gray-900 capitalize">{name}</h4>
|
|
<p className="text-sm text-gray-700 font-medium">{dim.kpis} KPIs • {dim.open + dim.completed} total items</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="text-right">
|
|
<div className="font-bold text-lg" style={{ color: dim.shade }}>{Math.round(dim.progress)}%</div>
|
|
<div className="w-24">
|
|
<ProgressBar progress={dim.progress} shade={dim.shade} height="4px" />
|
|
</div>
|
|
</div>
|
|
{isExpanded ? <ChevronDown className="w-5 h-5 text-gray-600" /> : <ChevronRight className="w-5 h-5 text-gray-600" />}
|
|
</div>
|
|
</button>
|
|
|
|
{isExpanded && (
|
|
<div className="px-4 pb-4 border-t-2 border-gray-300 bg-gray-50 rounded-b-lg">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold" style={{ color: dim.shade }}>{dim.kpis}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Key Performance Indicators</div>
|
|
</div>
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-700">{dim.open}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Open Items</div>
|
|
</div>
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-900">{dim.completed}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Completed Items</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Foundation Dimensions */}
|
|
<div className="bg-white rounded-xl border-2 border-gray-300 p-6 shadow-sm">
|
|
<div className="space-y-4">
|
|
{Object.entries(data.foundation).map(([name, dim]) => {
|
|
const Icon = dim.icon;
|
|
const isExpanded = expandedDimension === name;
|
|
|
|
return (
|
|
<div key={name} className="border-2 border-gray-400 rounded-lg">
|
|
<button
|
|
onClick={() => toggleDimension(name)}
|
|
className="w-full p-4 flex items-center justify-between hover:bg-gray-100 rounded-lg transition-colors"
|
|
>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="p-2 border-2 border-gray-400 rounded-full bg-gray-100">
|
|
<Icon className="w-5 h-5" style={{ color: dim.shade }} />
|
|
</div>
|
|
<div className="text-left">
|
|
<h4 className="font-bold text-gray-900 capitalize">{name}</h4>
|
|
<p className="text-sm text-gray-700 font-medium">{dim.kpis} KPIs • {dim.open + dim.completed} total items</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="text-right">
|
|
<div className="font-bold text-lg" style={{ color: dim.shade }}>{Math.round(dim.progress)}%</div>
|
|
<div className="w-24">
|
|
<ProgressBar progress={dim.progress} shade={dim.shade} height="4px" />
|
|
</div>
|
|
</div>
|
|
{isExpanded ? <ChevronDown className="w-5 h-5 text-gray-600" /> : <ChevronRight className="w-5 h-5 text-gray-600" />}
|
|
</div>
|
|
</button>
|
|
|
|
{isExpanded && (
|
|
<div className="px-4 pb-4 border-t-2 border-gray-300 bg-gray-50 rounded-b-lg">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold" style={{ color: dim.shade }}>{dim.kpis}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Key Performance Indicators</div>
|
|
</div>
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-700">{dim.open}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Open Items</div>
|
|
</div>
|
|
<div className="text-center p-4 bg-white rounded-lg border-2 border-gray-300">
|
|
<div className="text-3xl font-bold text-gray-900">{dim.completed}</div>
|
|
<div className="text-sm text-gray-700 font-medium">Completed Items</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 p-6 print:bg-white print:p-4">
|
|
<div className="max-w-7xl mx-auto">
|
|
<style jsx>{`
|
|
@media print {
|
|
body { -webkit-print-color-adjust: exact; }
|
|
.shadow-sm { box-shadow: none !important; }
|
|
.shadow-lg { box-shadow: none !important; }
|
|
.hover\\:bg-gray-100:hover { background-color: transparent !important; }
|
|
button { cursor: default !important; }
|
|
}
|
|
`}</style>
|
|
|
|
{/* Content */}
|
|
<DetailedView />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ExecutiveDashboard; |