mirror of
https://github.com/nprasad2077/medicare_enrollment_dashboard.git
synced 2026-11-04 04:44:24 +00:00
seo
This commit is contained in:
1 parent
4b8ae1bbac
commit
9c7dee2c7c
8 files changed
+74
-24
No files matched your search
@@ -4,10 +4,16 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/medical-history-doctor-svgrepo-com.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Interactive Medicare Enrollment Dashboard featuring CMS monthly enrollment data, Medicare Advantage (MA) penetration rates, and state & county trends." />
|
||||
<meta name="theme-color" content="#1e3a5f" />
|
||||
<link rel="apple-touch-icon" href="/medical-history-doctor-192.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="preconnect" href="https://data.cms.gov" />
|
||||
<link rel="preconnect" href="https://a.basemaps.cartocdn.com" />
|
||||
<link rel="preconnect" href="https://b.basemaps.cartocdn.com" />
|
||||
<link rel="preconnect" href="https://c.basemaps.cartocdn.com" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" />
|
||||
<title>Medicare Enrollment Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+32
-7
@@ -72,12 +72,37 @@ function Dashboard() {
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="w-8 h-8 border-4 border-gray-200 border-t-gray-600 rounded-full animate-spin" />
|
||||
<p className="text-sm text-muted-foreground">Loading Medicare Enrollment Data...</p>
|
||||
<main className="min-h-screen bg-background font-[var(--font-family)]" aria-busy="true" aria-label="Loading Medicare Enrollment Dashboard">
|
||||
<div className="w-full max-w-[1560px] mx-auto p-6 lg:p-8 space-y-6 lg:space-y-8 animate-pulse">
|
||||
{/* Header Skeleton */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-gray-200 rounded-xl" />
|
||||
<div className="space-y-2">
|
||||
<div className="w-64 h-7 bg-gray-200 rounded" />
|
||||
<div className="w-40 h-4 bg-gray-200 rounded" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-64 h-10 bg-gray-100 rounded-lg" />
|
||||
</div>
|
||||
|
||||
{/* Metrics Skeleton */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{[1, 2, 3, 4].map(i => (
|
||||
<div key={i} className="h-28 bg-white border border-gray-200 rounded-xl p-5" />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Main Grid Skeleton */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
<div className="h-[480px] bg-white border border-gray-200 rounded-xl" />
|
||||
<div className="h-[360px] bg-white border border-gray-200 rounded-xl" />
|
||||
</div>
|
||||
<div className="h-[520px] bg-white border border-gray-200 rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,7 +114,7 @@ function Dashboard() {
|
||||
const trendLabel = selectedCounty || (selectedState ? stateData?.find(s => s.state === selectedState)?.name : null)
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-[var(--font-family)]">
|
||||
<main className="min-h-screen bg-background font-[var(--font-family)]">
|
||||
<div className="w-full max-w-[1560px] mx-auto p-6 lg:p-8 space-y-6 lg:space-y-8">
|
||||
<Header view={view} setView={setView} latestData={latestData} onReset={handleBackToAll} />
|
||||
<SummaryMetrics data={latestData} view={view} />
|
||||
@@ -135,7 +160,7 @@ function Dashboard() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -180,12 +180,12 @@ export default function StateDetail({ view, state, stateData, countyData, county
|
||||
<table className="w-full text-xs">
|
||||
<thead className="sticky top-0 bg-gray-50 z-10">
|
||||
<tr className="border-b border-gray-200">
|
||||
<th className="text-left py-2 px-3 font-medium text-gray-600">County</th>
|
||||
<th className="text-right py-2 px-3 font-medium text-gray-600">TOTAL</th>
|
||||
<th className="text-right py-2 px-3 font-medium text-gray-600">{cat2}</th>
|
||||
<th className="text-right py-2 px-3 font-medium text-gray-600">{cat1}</th>
|
||||
<th className="text-right py-2 px-3 font-medium text-gray-600">{cat2}%</th>
|
||||
<th className="text-right py-2 px-3 font-medium text-gray-600">{cat1}%</th>
|
||||
<th className="text-left py-2 px-3 font-semibold text-gray-700">County</th>
|
||||
<th className="text-right py-2 px-3 font-semibold text-gray-700">TOTAL</th>
|
||||
<th className="text-right py-2 px-3 font-semibold text-gray-700">{cat2}</th>
|
||||
<th className="text-right py-2 px-3 font-semibold text-gray-700">{cat1}</th>
|
||||
<th className="text-right py-2 px-3 font-semibold text-gray-700">{cat2}%</th>
|
||||
<th className="text-right py-2 px-3 font-semibold text-gray-700">{cat1}%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -210,10 +210,10 @@ export default function StateDetail({ view, state, stateData, countyData, county
|
||||
{countyLoading ? (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="w-6 h-6 border-[3px] border-gray-200 border-t-blue-600 rounded-full animate-spin" />
|
||||
<span className="text-xs text-gray-500">Loading counties...</span>
|
||||
<span className="text-xs text-gray-600">Loading counties...</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs text-gray-400">No county data available</span>
|
||||
<span className="text-xs text-gray-600">No county data available</span>
|
||||
)}
|
||||
</td></tr>
|
||||
)}
|
||||
|
||||
@@ -64,9 +64,9 @@ export default function SummaryMetrics({ data, view }) {
|
||||
<CardContent className="p-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">{m.label}</p>
|
||||
<p className="text-sm font-medium text-gray-700">{m.label}</p>
|
||||
<p className="text-3xl font-bold text-gray-900 mt-1 tracking-tight">{m.value}</p>
|
||||
<p className="text-xs text-gray-400 mt-0.5">{m.sub}</p>
|
||||
<p className="text-xs text-gray-600 font-medium mt-0.5">{m.sub}</p>
|
||||
</div>
|
||||
<div className={`w-11 h-11 ${m.iconBg} rounded-xl flex items-center justify-center shrink-0`}>
|
||||
<m.icon className={`w-5 h-5 ${m.iconColor}`} />
|
||||
|
||||
@@ -178,7 +178,7 @@ export default function TrendPanel({ view, yearlyTrend, monthlyTrend, selectedSt
|
||||
<button
|
||||
onClick={() => setTab('yearly')}
|
||||
className={`px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
|
||||
tab === 'yearly' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-500 hover:text-gray-700'
|
||||
tab === 'yearly' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-700 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
Yearly Trend
|
||||
@@ -186,7 +186,7 @@ export default function TrendPanel({ view, yearlyTrend, monthlyTrend, selectedSt
|
||||
<button
|
||||
onClick={() => setTab('monthly')}
|
||||
className={`px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
|
||||
tab === 'monthly' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-500 hover:text-gray-700'
|
||||
tab === 'monthly' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-700 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
12-Month Trend
|
||||
@@ -195,7 +195,7 @@ export default function TrendPanel({ view, yearlyTrend, monthlyTrend, selectedSt
|
||||
<button
|
||||
onClick={() => setTab('grid')}
|
||||
className={`px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
|
||||
tab === 'grid' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-500 hover:text-gray-700'
|
||||
tab === 'grid' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-700 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
Grid
|
||||
|
||||
@@ -8,8 +8,8 @@ export function CardHeader({ className, ...props }) {
|
||||
return <div className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function CardTitle({ className, ...props }) {
|
||||
return <h3 className={cn("text-lg font-semibold leading-none tracking-tight", className)} {...props} />;
|
||||
export function CardTitle({ className, as: Component = "h2", ...props }) {
|
||||
return <Component className={cn("text-lg font-semibold leading-none tracking-tight", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function CardContent({ className, ...props }) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
@import 'tailwindcss' source(none);
|
||||
@source '../**/*.{js,jsx,ts,tsx}';
|
||||
@import 'tw-animate-css';
|
||||
|
||||
@@ -39,5 +39,25 @@ export default defineConfig({
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (id.includes('node_modules/recharts') || id.includes('node_modules/d3-')) {
|
||||
return 'vendor-charts'
|
||||
}
|
||||
if (id.includes('node_modules/leaflet') || id.includes('node_modules/react-leaflet')) {
|
||||
return 'vendor-maps'
|
||||
}
|
||||
if (id.includes('node_modules/@tanstack')) {
|
||||
return 'vendor-query'
|
||||
}
|
||||
if (id.includes('node_modules/lucide-react')) {
|
||||
return 'vendor-icons'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user