Added, beneficiaries, site visits and change management
This commit is contained in:
@@ -44,6 +44,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 {
|
||||
Award,
|
||||
Budget,
|
||||
@@ -592,6 +593,49 @@ export const GrantsPage: React.FC = () => {
|
||||
return id.includes(q) || projId.includes(q) || projName.includes(q) || ref.includes(q) || msId.includes(q) || creator.includes(q);
|
||||
});
|
||||
|
||||
// Table Sorting Hooks
|
||||
const {
|
||||
sortedData: sortedAwards,
|
||||
sortColumn: awardSortCol,
|
||||
sortDirection: awardSortDir,
|
||||
handleSort: handleAwardSort,
|
||||
} = useTableSort(filteredAwards, null, null, {
|
||||
referenceNo: (a) => a.referenceNo,
|
||||
organisationId: (a) => a.organisationName || `Org #${a.organisationId}`,
|
||||
programmeId: (a) => a.programmeName || `Programme #${a.programmeId}`,
|
||||
amount: (a) => a.amount,
|
||||
status: (a) => getApprovalStatusBadge(a.status).label,
|
||||
createdAt: (a) => a.createdAt,
|
||||
});
|
||||
|
||||
const {
|
||||
sortedData: sortedBudgets,
|
||||
sortColumn: budgetSortCol,
|
||||
sortDirection: budgetSortDir,
|
||||
handleSort: handleBudgetSort,
|
||||
} = useTableSort(filteredBudgets, null, null, {
|
||||
name: (b) => b.name,
|
||||
projectId: (b) => b.projectId,
|
||||
amount: (b) => b.amount,
|
||||
createdAt: (b) => b.createdAt,
|
||||
status: (b) => getApprovalStatusBadge(b.status ?? ApprovalStatus.Approved).label,
|
||||
createdBy: (b) => getUserEmail(b.createdBy),
|
||||
});
|
||||
|
||||
const {
|
||||
sortedData: sortedDisbursements,
|
||||
sortColumn: disbSortCol,
|
||||
sortDirection: disbSortDir,
|
||||
handleSort: handleDisbSort,
|
||||
} = useTableSort(filteredDisbursements, null, null, {
|
||||
id: (d) => d.id,
|
||||
projectId: (d) => d.projectId,
|
||||
milestoneId: (d) => d.milestoneId || 0,
|
||||
amount: (d) => d.amount,
|
||||
paymentReference: (d) => d.paymentReference || '',
|
||||
status: (d) => getApprovalStatusBadge(d.status).label,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-6 max-w-7xl mx-auto">
|
||||
{/* Header & Execution Pipeline Summary */}
|
||||
@@ -766,12 +810,24 @@ export const GrantsPage: 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">Approval Reference #</th>
|
||||
<th className="py-3.5 px-4">Recipient Organisation</th>
|
||||
<th className="py-3.5 px-4">Funding Programme</th>
|
||||
<th className="py-3.5 px-4 text-right">Approved Amount (ZAR)</th>
|
||||
<th className="py-3.5 px-4">Status</th>
|
||||
<th className="py-3.5 px-5 text-right">Date Registered</th>
|
||||
<SortableTh column="referenceNo" currentColumn={awardSortCol} direction={awardSortDir} onSort={handleAwardSort} className="py-3.5 px-5">
|
||||
Approval Reference #
|
||||
</SortableTh>
|
||||
<SortableTh column="organisationId" currentColumn={awardSortCol} direction={awardSortDir} onSort={handleAwardSort} className="py-3.5 px-4">
|
||||
Recipient Organisation
|
||||
</SortableTh>
|
||||
<SortableTh column="programmeId" currentColumn={awardSortCol} direction={awardSortDir} onSort={handleAwardSort} className="py-3.5 px-4">
|
||||
Funding Programme
|
||||
</SortableTh>
|
||||
<SortableTh column="amount" currentColumn={awardSortCol} direction={awardSortDir} onSort={handleAwardSort} align="right" className="py-3.5 px-4 text-right">
|
||||
Approved Amount (ZAR)
|
||||
</SortableTh>
|
||||
<SortableTh column="status" currentColumn={awardSortCol} direction={awardSortDir} onSort={handleAwardSort} className="py-3.5 px-4">
|
||||
Status
|
||||
</SortableTh>
|
||||
<SortableTh column="createdAt" currentColumn={awardSortCol} direction={awardSortDir} onSort={handleAwardSort} align="right" className="py-3.5 px-5 text-right">
|
||||
Date Registered
|
||||
</SortableTh>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
@@ -781,14 +837,14 @@ export const GrantsPage: React.FC = () => {
|
||||
Loading registered awards...
|
||||
</td>
|
||||
</tr>
|
||||
) : filteredAwards.length === 0 ? (
|
||||
) : sortedAwards.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="py-8 text-center text-slate-500">
|
||||
{searchTerm ? 'No awards match your search query.' : 'No registered awards found.'}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredAwards.map((awd) => {
|
||||
sortedAwards.map((awd) => {
|
||||
const statusBadge = getApprovalStatusBadge(awd.status);
|
||||
return (
|
||||
<tr key={awd.id} className="hover:bg-slate-800/40 transition-colors">
|
||||
@@ -867,13 +923,25 @@ export const GrantsPage: 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">Budget Line Category</th>
|
||||
<th className="py-3.5 px-4">Project ID</th>
|
||||
<th className="py-3.5 px-4 text-right">Approved Amount (ZAR)</th>
|
||||
<th className="py-3.5 px-4">Date Established</th>
|
||||
<th className="py-3.5 px-4">Status</th>
|
||||
<th className="py-3.5 px-4">Created By</th>
|
||||
<th className="py-3.5 px-5 text-right">Actions</th>
|
||||
<SortableTh column="name" currentColumn={budgetSortCol} direction={budgetSortDir} onSort={handleBudgetSort} className="py-3.5 px-5">
|
||||
Budget Line Category
|
||||
</SortableTh>
|
||||
<SortableTh column="projectId" currentColumn={budgetSortCol} direction={budgetSortDir} onSort={handleBudgetSort} className="py-3.5 px-4">
|
||||
Project ID
|
||||
</SortableTh>
|
||||
<SortableTh column="amount" currentColumn={budgetSortCol} direction={budgetSortDir} onSort={handleBudgetSort} align="right" className="py-3.5 px-4 text-right">
|
||||
Approved Amount (ZAR)
|
||||
</SortableTh>
|
||||
<SortableTh column="createdAt" currentColumn={budgetSortCol} direction={budgetSortDir} onSort={handleBudgetSort} className="py-3.5 px-4">
|
||||
Date Established
|
||||
</SortableTh>
|
||||
<SortableTh column="status" currentColumn={budgetSortCol} direction={budgetSortDir} onSort={handleBudgetSort} className="py-3.5 px-4">
|
||||
Status
|
||||
</SortableTh>
|
||||
<SortableTh column="createdBy" currentColumn={budgetSortCol} direction={budgetSortDir} onSort={handleBudgetSort} className="py-3.5 px-4">
|
||||
Created By
|
||||
</SortableTh>
|
||||
<th className="py-3.5 px-5 text-right font-medium">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
@@ -883,14 +951,14 @@ export const GrantsPage: React.FC = () => {
|
||||
Loading budget lines...
|
||||
</td>
|
||||
</tr>
|
||||
) : filteredBudgets.length === 0 ? (
|
||||
) : sortedBudgets.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={7} className="py-8 text-center text-slate-500">
|
||||
{searchTerm ? 'No budget lines match your search query.' : 'No budget lines established yet.'}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredBudgets.map((b) => {
|
||||
sortedBudgets.map((b) => {
|
||||
const statusBadge = getApprovalStatusBadge(b.status ?? ApprovalStatus.Approved);
|
||||
return (
|
||||
<tr key={b.id} className="hover:bg-slate-800/40 transition-colors">
|
||||
@@ -1005,13 +1073,25 @@ export const GrantsPage: 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">Disbursement ID</th>
|
||||
<th className="py-3.5 px-4">Project ID</th>
|
||||
<th className="py-3.5 px-4">Milestone Association</th>
|
||||
<th className="py-3.5 px-4 text-right">Tranche Amount</th>
|
||||
<th className="py-3.5 px-4">Payment Reference</th>
|
||||
<th className="py-3.5 px-4">Approval Status</th>
|
||||
<th className="py-3.5 px-5 text-right">Approval Action</th>
|
||||
<SortableTh column="id" currentColumn={disbSortCol} direction={disbSortDir} onSort={handleDisbSort} className="py-3.5 px-5">
|
||||
Disbursement ID
|
||||
</SortableTh>
|
||||
<SortableTh column="projectId" currentColumn={disbSortCol} direction={disbSortDir} onSort={handleDisbSort} className="py-3.5 px-4">
|
||||
Project ID
|
||||
</SortableTh>
|
||||
<SortableTh column="milestoneId" currentColumn={disbSortCol} direction={disbSortDir} onSort={handleDisbSort} className="py-3.5 px-4">
|
||||
Milestone Association
|
||||
</SortableTh>
|
||||
<SortableTh column="amount" currentColumn={disbSortCol} direction={disbSortDir} onSort={handleDisbSort} align="right" className="py-3.5 px-4 text-right">
|
||||
Tranche Amount
|
||||
</SortableTh>
|
||||
<SortableTh column="paymentReference" currentColumn={disbSortCol} direction={disbSortDir} onSort={handleDisbSort} className="py-3.5 px-4">
|
||||
Payment Reference
|
||||
</SortableTh>
|
||||
<SortableTh column="status" currentColumn={disbSortCol} direction={disbSortDir} onSort={handleDisbSort} className="py-3.5 px-4">
|
||||
Approval Status
|
||||
</SortableTh>
|
||||
<th className="py-3.5 px-5 text-right font-medium">Approval Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
@@ -1021,14 +1101,14 @@ export const GrantsPage: React.FC = () => {
|
||||
Loading disbursements...
|
||||
</td>
|
||||
</tr>
|
||||
) : filteredDisbursements.length === 0 ? (
|
||||
) : sortedDisbursements.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={7} className="py-8 text-center text-slate-500">
|
||||
{searchTerm ? 'No disbursements match your search query.' : 'No disbursement tranches recorded yet.'}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredDisbursements.map((d) => {
|
||||
sortedDisbursements.map((d) => {
|
||||
const statusBadge = getApprovalStatusBadge(d.status);
|
||||
const isApproved = d.status === ApprovalStatus.Approved;
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user