Removed settings page mock data and replaced with api calls

This commit is contained in:
2026-09-27 10:58:08 +02:00
parent 6ab01830f3
commit fb27cc8c92
10 changed files with 1067 additions and 557 deletions
@@ -22,6 +22,7 @@ import {
Paperclip,
Check,
ShieldCheck,
RotateCcw,
} from 'lucide-react';
import {
getContracts,
@@ -39,6 +40,8 @@ import { toolsService } from '../../services/toolsService';
import { Modal } from '../ui/Modal';
import { useToast } from '../ui/Toast';
import { ProblemDetailsAlert } from '../ui/ProblemDetailsAlert';
import { ExportButton } from '../ui/ExportButton';
import { ExportColumn } from '../../utils/exportUtils';
import {
Contract,
Award,
@@ -390,6 +393,19 @@ export const ContractsManagement: React.FC<ContractsManagementProps> = ({
Number(c.status) === ContractStatus.PendingApproval
).length;
const contractExportColumns: ExportColumn<Contract>[] = [
{ header: 'Contract ID', key: 'id' },
{ header: 'Description / Title', key: 'description' },
{ header: 'Project', key: 'projectId', format: (val) => (val ? `Project #${val}` : '—') },
{ header: 'Award ID', key: 'awardId', format: (val) => (val ? `Award #${val}` : '—') },
{ header: 'Organisation ID', key: 'organisationId', format: (val) => (val ? `Org #${val}` : '—') },
{ header: 'Status', key: 'status', format: (val) => getContractStatusBadge(val).label },
{ header: 'Signed Date', key: 'signedDate', format: (val) => (val ? formatDate(val) : '—') },
{ header: 'Expiry Date', key: 'expiryDate', format: (val) => (val ? formatDate(val) : '—') },
{ header: 'Budget Allocated', key: 'budgetAllocated', format: (val) => (val ? formatZAR(val) : '—') },
{ header: 'Document URI', key: 'fileUri', format: (val) => val || '—' },
];
return (
<div className="space-y-6">
{/* Summary Cards */}
@@ -466,27 +482,30 @@ export const ContractsManagement: React.FC<ContractsManagementProps> = ({
</div>
{/* Action and Filter Bar */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 bg-[#0f1523]/80 backdrop-blur-md p-4 rounded-2xl border border-slate-800/80 shadow-md">
<div className="flex flex-wrap items-center gap-2 flex-1">
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-3 bg-[#0d121f]/90 p-4 rounded-2xl border border-slate-800/80 backdrop-blur-sm">
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 flex-1 flex-wrap">
{/* Search */}
<div className="relative flex-1 min-w-[220px] max-w-sm">
<Search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
<div className="relative flex-1 min-w-[200px] max-w-md">
<Search className="w-4 h-4 text-slate-500 absolute left-3 top-3" />
<input
type="text"
placeholder="Search contracts, award, org, programme..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full bg-[#151b2c] border border-slate-700/80 rounded-xl pl-9 pr-3 py-1.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-blue-500"
className="w-full pl-9 pr-4 py-2.5 text-xs bg-[#090d16] border border-slate-700/80 rounded-xl focus:outline-none focus:ring-1 focus:ring-blue-500 text-slate-200 placeholder:text-slate-500 transition shadow-xs"
/>
</div>
{/* Status Filter */}
<div className="flex items-center gap-1.5">
<Filter className="w-3.5 h-3.5 text-slate-400" />
<div className="flex items-center gap-2">
<label className="text-xs text-slate-400 whitespace-nowrap flex items-center gap-1 font-medium">
<Filter className="w-3.5 h-3.5 text-slate-400" />
Status:
</label>
<select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
className="bg-[#151b2c] border border-slate-700/80 rounded-xl px-2.5 py-1.5 text-xs text-slate-200 focus:outline-none focus:border-blue-500"
className="px-3 py-2 text-xs bg-[#090d16] border border-slate-700/80 rounded-xl text-slate-200 focus:outline-none focus:ring-1 focus:ring-blue-500 [&>option]:bg-slate-900"
>
<option value="all">All Statuses</option>
{CONTRACT_STATUS_OPTIONS.map((opt) => (
@@ -497,28 +516,57 @@ export const ContractsManagement: React.FC<ContractsManagementProps> = ({
</select>
</div>
<button
type="button"
onClick={() => refetchContracts()}
disabled={refetchingContracts}
className="p-2 rounded-xl bg-[#151b2c] border border-slate-700/80 text-slate-400 hover:text-white hover:bg-slate-800 transition"
title="Refresh contracts"
>
<RefreshCw className={`w-3.5 h-3.5 ${refetchingContracts ? 'animate-spin' : ''}`} />
</button>
{(searchTerm || statusFilter !== 'all') && (
<button
type="button"
onClick={() => {
setSearchTerm('');
setStatusFilter('all');
}}
className="inline-flex items-center gap-1 text-[11px] font-semibold text-slate-400 hover:text-white px-2.5 py-2 bg-slate-800 hover:bg-slate-700 rounded-xl transition cursor-pointer"
>
<RotateCcw className="w-3 h-3" />
<span>Reset</span>
</button>
)}
</div>
<button
type="button"
onClick={() => {
resetCreateForm();
setCreateModalOpen(true);
}}
className="inline-flex items-center justify-center gap-1.5 px-4 py-2 text-xs font-semibold text-white bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500 rounded-xl shadow-lg shadow-blue-500/20 transition cursor-pointer"
>
<Plus className="w-4 h-4" />
Create Contract & Upload PDF
</button>
{/* Action Buttons: Refresh, Export & Create */}
<div className="flex items-center gap-2 self-end lg:self-center">
<button
type="button"
onClick={() => {
refetchContracts();
success('Refreshed', 'Contract registry and award linkages updated.');
}}
disabled={refetchingContracts}
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 text-xs font-semibold text-slate-300 bg-[#0d121f] hover:bg-slate-800 border border-slate-700/80 rounded-xl transition cursor-pointer shadow-xs disabled:opacity-50"
title="Refresh contracts from API"
>
<RefreshCw className={`w-3.5 h-3.5 text-blue-400 ${refetchingContracts ? 'animate-spin' : ''}`} />
<span>Refresh</span>
</button>
<ExportButton
title="Contract Registry & Agreements"
subtitle="Authoritative repository of bilateral legal agreements and contract files"
filename={`pfm-contracts-${new Date().toISOString().split('T')[0]}`}
columns={contractExportColumns}
data={filteredContracts}
/>
<button
type="button"
onClick={() => {
resetCreateForm();
setCreateModalOpen(true);
}}
className="inline-flex items-center justify-center gap-1.5 px-4 py-2 text-xs font-semibold text-white bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500 rounded-xl shadow-md shadow-blue-500/20 transition cursor-pointer"
>
<Plus className="w-4 h-4" />
Create Contract
</button>
</div>
</div>
{/* Contracts Table */}