Added, beneficiaries, site visits and change management

This commit is contained in:
2026-09-27 11:40:28 +02:00
parent fb27cc8c92
commit 36e5d3d7c8
16 changed files with 3132 additions and 212 deletions
@@ -41,6 +41,7 @@ import { HelpTooltip } from '../components/ui/HelpTooltip';
import { useToast } from '../components/ui/Toast';
import { ExportButton } from '../components/ui/ExportButton';
import { ExportColumn } from '../utils/exportUtils';
import { useTableSort, SortableTh } from '../utils/tableSort';
import { ContractsManagement } from '../components/monitoring/ContractsManagement';
import { EvidenceReviewQueue } from '../components/monitoring/EvidenceReviewQueue';
import { RiskHeatmapMatrix } from '../components/ui/RiskHeatmapMatrix';
@@ -373,6 +374,37 @@ export const MonitoringPage: React.FC = () => {
return name.includes(q) || desc.includes(q) || projId.includes(q) || owner.includes(q) || mitig.includes(q);
});
// Table Sorting Hooks
const {
sortedData: sortedIndicators,
sortColumn: indSortCol,
sortDirection: indSortDir,
handleSort: handleIndSort,
} = useTableSort(filteredIndicators, null, null, {
name: (ind) => ind.name,
projectId: (ind) => ind.projectId,
unitOfMeasure: (ind) => getUnitOfMeasureLabel(ind.unitOfMeasure),
baselineAmount: (ind) => ind.baselineAmount,
targetAmount: (ind) => ind.targetAmount,
actualAmount: (ind) => ind.actualAmount,
status: (ind) => getIndicatorStatus(ind.actualAmount, ind.targetAmount).label,
});
const {
sortedData: sortedRisks,
sortColumn: riskSortCol,
sortDirection: riskSortDir,
handleSort: handleRiskSort,
} = useTableSort(filteredRisks, null, null, {
name: (rk) => rk.name,
projectId: (rk) => rk.projectId,
likelihood: (rk) => rk.likelihood,
impact: (rk) => rk.impact,
residualScore: (rk) => rk.likelihood * rk.impact,
owner: (rk) => getUserEmail(rk.owner),
mitigationAction: (rk) => rk.mitigationAction || '',
});
return (
<div className="space-y-6 max-w-7xl mx-auto">
{/* Header */}
@@ -643,14 +675,28 @@ export const MonitoringPage: React.FC = () => {
<table className="w-full text-left text-xs border-collapse">
<thead>
<tr className="bg-slate-900/60 border-b border-slate-800/80 text-slate-400 uppercase tracking-wider font-semibold">
<th className="py-3.5 px-5">Indicator Name</th>
<th className="py-3.5 px-4">Project ID</th>
<th className="py-3.5 px-4">Unit of Measure</th>
<th className="py-3.5 px-4 text-right">Baseline</th>
<th className="py-3.5 px-4 text-right">Target</th>
<th className="py-3.5 px-4 text-right">Actual Recorded</th>
<th className="py-3.5 px-4">Status</th>
<th className="py-3.5 px-5 text-right">Action</th>
<SortableTh column="name" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} className="py-3.5 px-5">
Indicator Name
</SortableTh>
<SortableTh column="projectId" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} className="py-3.5 px-4">
Project ID
</SortableTh>
<SortableTh column="unitOfMeasure" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} className="py-3.5 px-4">
Unit of Measure
</SortableTh>
<SortableTh column="baselineAmount" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} align="right" className="py-3.5 px-4 text-right">
Baseline
</SortableTh>
<SortableTh column="targetAmount" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} align="right" className="py-3.5 px-4 text-right">
Target
</SortableTh>
<SortableTh column="actualAmount" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} align="right" className="py-3.5 px-4 text-right">
Actual Recorded
</SortableTh>
<SortableTh column="status" currentColumn={indSortCol} direction={indSortDir} onSort={handleIndSort} className="py-3.5 px-4">
Status
</SortableTh>
<th className="py-3.5 px-5 text-right font-medium">Action</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-800/50">
@@ -661,7 +707,7 @@ export const MonitoringPage: React.FC = () => {
Loading project indicators from API...
</td>
</tr>
) : filteredIndicators.length === 0 ? (
) : sortedIndicators.length === 0 ? (
<tr>
<td colSpan={8} className="py-12 text-center text-slate-500">
<TrendingUp className="w-8 h-8 text-slate-600 mx-auto mb-2" />
@@ -671,7 +717,7 @@ export const MonitoringPage: React.FC = () => {
</td>
</tr>
) : (
filteredIndicators.map((ind) => {
sortedIndicators.map((ind) => {
const statusObj = getIndicatorStatus(ind.actualAmount, ind.targetAmount);
return (
<tr key={ind.id} className="hover:bg-slate-800/40 transition-colors">
@@ -833,14 +879,28 @@ export const MonitoringPage: React.FC = () => {
<table className="w-full text-left text-xs border-collapse">
<thead>
<tr className="bg-slate-900/60 border-b border-slate-800/80 text-slate-400 uppercase tracking-wider font-semibold">
<th className="py-3.5 px-5">Risk Title & Description</th>
<th className="py-3.5 px-4">Project ID</th>
<th className="py-3.5 px-4 text-center">Likelihood (1–5)</th>
<th className="py-3.5 px-4 text-center">Impact (1–5)</th>
<th className="py-3.5 px-4 text-center">Residual Score</th>
<th className="py-3.5 px-4">Risk Owner</th>
<th className="py-3.5 px-5">Mitigation Control</th>
<th className="py-3.5 px-4 text-right">Actions</th>
<SortableTh column="name" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} className="py-3.5 px-5">
Risk Title & Description
</SortableTh>
<SortableTh column="projectId" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} className="py-3.5 px-4">
Project ID
</SortableTh>
<SortableTh column="likelihood" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} align="center" className="py-3.5 px-4 text-center">
Likelihood (1–5)
</SortableTh>
<SortableTh column="impact" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} align="center" className="py-3.5 px-4 text-center">
Impact (1–5)
</SortableTh>
<SortableTh column="residualScore" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} align="center" className="py-3.5 px-4 text-center">
Residual Score
</SortableTh>
<SortableTh column="owner" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} className="py-3.5 px-4">
Risk Owner
</SortableTh>
<SortableTh column="mitigationAction" currentColumn={riskSortCol} direction={riskSortDir} onSort={handleRiskSort} className="py-3.5 px-5">
Mitigation Control
</SortableTh>
<th className="py-3.5 px-4 text-right font-medium">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-800/50">
@@ -851,7 +911,7 @@ export const MonitoringPage: React.FC = () => {
Loading risk register from API...
</td>
</tr>
) : filteredRisks.length === 0 ? (
) : sortedRisks.length === 0 ? (
<tr>
<td colSpan={8} className="py-12 text-center text-slate-500">
<ShieldAlert className="w-8 h-8 text-slate-600 mx-auto mb-2" />
@@ -861,7 +921,7 @@ export const MonitoringPage: React.FC = () => {
</td>
</tr>
) : (
filteredRisks.map((rk) => {
sortedRisks.map((rk) => {
const score = rk.likelihood * rk.impact;
const color = getRiskScoreColor(rk.likelihood, rk.impact);
return (