Remediated test #1 results
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
RefreshCw,
|
||||
Clock,
|
||||
ShieldAlert,
|
||||
RotateCcw,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
getDashboard,
|
||||
@@ -36,6 +37,18 @@ export const ExecutiveDashboard: React.FC = () => {
|
||||
const [selectedSector, setSelectedSector] = useState('All');
|
||||
const [selectedPeriod, setSelectedPeriod] = useState('FY 2026/27');
|
||||
|
||||
const isFiltered = selectedProgramme !== 'All' || selectedSector !== 'All' || selectedPeriod !== 'FY 2026/27';
|
||||
const activeFilterCount =
|
||||
(selectedProgramme !== 'All' ? 1 : 0) +
|
||||
(selectedSector !== 'All' ? 1 : 0) +
|
||||
(selectedPeriod !== 'FY 2026/27' ? 1 : 0);
|
||||
|
||||
const resetFilters = () => {
|
||||
setSelectedProgramme('All');
|
||||
setSelectedSector('All');
|
||||
setSelectedPeriod('FY 2026/27');
|
||||
};
|
||||
|
||||
// Data queries
|
||||
const { data: dashboard, isLoading: loadingDashboard, refetch: refetchDashboard, isFetching } = useQuery({
|
||||
queryKey: ['dashboard'],
|
||||
@@ -318,41 +331,79 @@ export const ExecutiveDashboard: React.FC = () => {
|
||||
return (
|
||||
<div className="space-y-6 max-w-7xl mx-auto">
|
||||
{/* Page Title & Filter Bar */}
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 bg-[#0d121f]/90 p-5 rounded-2xl border border-slate-800/80 backdrop-blur-md shadow-xl">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xl font-bold text-white tracking-tight">
|
||||
Executive Portfolio Dashboard
|
||||
</h2>
|
||||
<HelpTooltip
|
||||
title="Executive Dashboard Overview"
|
||||
content="Macro-level command center showing aggregated national portfolio commitments, actual tranche disbursements, delivery pace, and critical project escalations."
|
||||
size="sm"
|
||||
placement="bottom"
|
||||
/>
|
||||
<span className="text-[11px] font-semibold text-blue-400 bg-blue-500/10 px-2.5 py-0.5 rounded-full border border-blue-500/30">
|
||||
Live Portfolio Feed
|
||||
</span>
|
||||
<div className="bg-[#0d121f]/90 p-5 sm:p-6 rounded-2xl border border-slate-800/80 backdrop-blur-md shadow-xl space-y-4">
|
||||
{/* Top Tier: Title, Status Badge, and Primary Actions */}
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2.5 flex-wrap">
|
||||
<h2 className="text-xl sm:text-2xl font-bold text-white tracking-tight">
|
||||
Executive Portfolio Dashboard
|
||||
</h2>
|
||||
<HelpTooltip
|
||||
title="Executive Dashboard Overview"
|
||||
content="Macro-level command center showing aggregated national portfolio commitments, actual tranche disbursements, delivery pace, and critical project escalations."
|
||||
size="sm"
|
||||
placement="bottom"
|
||||
/>
|
||||
<span className="inline-flex items-center gap-1.5 text-[11px] font-semibold text-blue-400 bg-blue-500/10 px-2.5 py-0.5 rounded-full border border-blue-500/30">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse" />
|
||||
Live Portfolio Feed
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-slate-400 mt-1">
|
||||
Real-time portfolio overview, disbursement tracking, risk heatmap, and projects requiring governance action.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2.5 shrink-0 self-start lg:self-center">
|
||||
{isFiltered && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={resetFilters}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-amber-300 bg-amber-500/10 hover:bg-amber-500/20 border border-amber-500/30 rounded-xl transition cursor-pointer shadow-xs"
|
||||
title="Reset all filters to default"
|
||||
>
|
||||
<RotateCcw className="w-3.5 h-3.5" />
|
||||
<span>Reset Filters</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={refetchAll}
|
||||
disabled={isFetching}
|
||||
className="inline-flex items-center gap-1.5 px-3.5 py-1.5 text-xs font-semibold text-slate-200 hover:text-white bg-slate-900 hover:bg-slate-800 border border-slate-700/80 rounded-xl transition cursor-pointer shadow-xs disabled:opacity-50"
|
||||
title="Refresh dashboard metrics"
|
||||
>
|
||||
<RefreshCw className={`w-3.5 h-3.5 ${isFetching ? 'animate-spin text-blue-400' : 'text-slate-400'}`} />
|
||||
<span>{isFetching ? 'Refreshing...' : 'Refresh'}</span>
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400 mt-1">
|
||||
Real-time portfolio overview, disbursement tracking, risk heatmap, and projects requiring governance action.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<div className="flex items-center gap-1.5 text-xs text-slate-400">
|
||||
<Filter className="w-3.5 h-3.5 text-slate-500" />
|
||||
<span>Filter:</span>
|
||||
{/* Dedicated Filter Toolbar */}
|
||||
<div className="pt-4 border-t border-slate-800/80 flex flex-wrap items-center gap-3">
|
||||
<div className="flex items-center gap-2 text-xs font-semibold text-slate-300 pr-1">
|
||||
<Filter className="w-3.5 h-3.5 text-blue-400" />
|
||||
<span>Filters:</span>
|
||||
{activeFilterCount > 0 && (
|
||||
<span className="text-[10px] font-bold text-blue-300 bg-blue-500/20 border border-blue-500/40 px-2 py-0.5 rounded-full">
|
||||
{activeFilterCount} Active
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Programme Filter */}
|
||||
<div className="flex items-center bg-[#090d16] border border-slate-700/80 hover:border-slate-600 rounded-xl px-3 py-1.5 text-xs transition shadow-inner">
|
||||
<span className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider mr-2 shrink-0">
|
||||
Programme
|
||||
</span>
|
||||
<select
|
||||
value={selectedProgramme}
|
||||
onChange={(e) => setSelectedProgramme(e.target.value)}
|
||||
className="text-xs font-medium bg-[#090d16] border border-slate-700/80 rounded-xl px-3 py-1.5 text-slate-200 focus:outline-none focus:ring-1 focus:ring-blue-500 transition cursor-pointer"
|
||||
className="bg-transparent text-xs font-medium text-slate-100 focus:outline-none cursor-pointer pr-1 [&>option]:bg-slate-900 [&>option]:text-white max-w-[200px] truncate"
|
||||
>
|
||||
<option value="All">Programme: All</option>
|
||||
<option value="All">All</option>
|
||||
{programmes.map((pr) => (
|
||||
<option key={pr.id} value={pr.id}>
|
||||
{pr.name}
|
||||
@@ -367,13 +418,17 @@ export const ExecutiveDashboard: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Sector Filter */}
|
||||
<div className="flex items-center bg-[#090d16] border border-slate-700/80 hover:border-slate-600 rounded-xl px-3 py-1.5 text-xs transition shadow-inner">
|
||||
<span className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider mr-2 shrink-0">
|
||||
Sector
|
||||
</span>
|
||||
<select
|
||||
value={selectedSector}
|
||||
onChange={(e) => setSelectedSector(e.target.value)}
|
||||
className="text-xs font-medium bg-[#090d16] border border-slate-700/80 rounded-xl px-3 py-1.5 text-slate-200 focus:outline-none focus:ring-1 focus:ring-blue-500 transition cursor-pointer"
|
||||
className="bg-transparent text-xs font-medium text-slate-100 focus:outline-none cursor-pointer pr-1 [&>option]:bg-slate-900 [&>option]:text-white max-w-[170px] truncate"
|
||||
>
|
||||
<option value="All">Sector: All</option>
|
||||
<option value="All">All</option>
|
||||
{sectorData.map((s) => (
|
||||
<option key={s.name} value={s.name}>
|
||||
{s.name}
|
||||
@@ -388,14 +443,18 @@ export const ExecutiveDashboard: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Financial Period Filter */}
|
||||
<div className="flex items-center bg-[#090d16] border border-slate-700/80 hover:border-slate-600 rounded-xl px-3 py-1.5 text-xs transition shadow-inner">
|
||||
<span className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider mr-2 shrink-0">
|
||||
Period
|
||||
</span>
|
||||
<select
|
||||
value={selectedPeriod}
|
||||
onChange={(e) => setSelectedPeriod(e.target.value)}
|
||||
className="text-xs font-medium bg-[#090d16] border border-slate-700/80 rounded-xl px-3 py-1.5 text-slate-200 focus:outline-none focus:ring-1 focus:ring-blue-500 transition cursor-pointer"
|
||||
className="bg-transparent text-xs font-medium text-slate-100 focus:outline-none cursor-pointer pr-1 [&>option]:bg-slate-900 [&>option]:text-white"
|
||||
>
|
||||
<option value="FY 2026/27">Period: FY 2026/27</option>
|
||||
<option value="FY 2025/26">Period: FY 2025/26</option>
|
||||
<option value="FY 2026/27">FY 2026/27</option>
|
||||
<option value="FY 2025/26">FY 2025/26</option>
|
||||
<option value="All-Time">All-Time Cumulative</option>
|
||||
</select>
|
||||
<HelpTooltip
|
||||
@@ -405,15 +464,6 @@ export const ExecutiveDashboard: React.FC = () => {
|
||||
placement="bottom"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={refetchAll}
|
||||
disabled={isFetching}
|
||||
className="p-2 text-slate-400 hover:text-white bg-slate-900/80 hover:bg-slate-800 border border-slate-700/80 rounded-xl transition cursor-pointer shadow-xs"
|
||||
title="Refresh dashboard metrics"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${isFetching ? 'animate-spin text-blue-400' : ''}`} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -612,7 +662,7 @@ export const ExecutiveDashboard: React.FC = () => {
|
||||
|
||||
<div className="pt-4 border-t border-slate-800/80 mt-4 text-[11px] text-slate-400 flex justify-between">
|
||||
<span>Primary Focus: {sectorData[0]?.name || 'All Sectors'}</span>
|
||||
<Link to="/governance" className="text-blue-400 font-medium hover:underline flex items-center gap-1">
|
||||
<Link to="/governance?tab=portfolios" className="text-blue-400 font-medium hover:underline flex items-center gap-1">
|
||||
View Portfolios <ArrowUpRight className="w-3 h-3" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user