Implemented data export

This commit is contained in:
2026-09-26 13:45:42 +02:00
parent 15a8d8f449
commit 2949d6218b
12 changed files with 10939 additions and 359 deletions
@@ -22,6 +22,8 @@ import {
import { Modal } from '../ui/Modal';
import { HelpTooltip } from '../ui/HelpTooltip';
import { useToast } from '../ui/Toast';
import { ExportButton } from '../ui/ExportButton';
import { ExportColumn } from '../../utils/exportUtils';
import {
Rule,
Programme,
@@ -231,6 +233,28 @@ export const RulesSection: React.FC<RulesSectionProps> = ({
});
}, [rules, selectedProgrammeFilter, statusFilter, searchTerm, programmeMap]);
// Export columns definition
const ruleExportColumns: ExportColumn<Rule>[] = useMemo(
() => [
{ header: 'Rule ID', key: 'id' },
{ header: 'Rule Name', key: 'name', format: (val) => val || 'Untitled Rule' },
{
header: 'Strategic Programme',
key: 'programmeId',
format: (val) => programmeMap.get(String(val))?.name || `Programme #${val}`,
},
{ header: 'Version', key: 'version', format: (val) => `v${val || 1}` },
{
header: 'Status',
key: 'status',
format: (val) => (Number(val) === ActivityStatus.Active ? 'Active' : 'Inactive'),
},
{ header: 'Rule Description', key: 'description', format: (val) => val || '—' },
{ header: 'Created Date', key: 'createdAt', format: (val) => (val ? formatDate(val) : '—') },
],
[programmeMap]
);
return (
<div className="space-y-4">
{/* Top Filter and Actions Bar */}
@@ -291,25 +315,37 @@ export const RulesSection: React.FC<RulesSectionProps> = ({
</div>
</div>
{/* Action Buttons */}
{/* Action Buttons: Refresh, Export & Add */}
<div className="flex items-center gap-2 self-end lg:self-center">
<button
type="button"
onClick={() => refetch()}
onClick={() => {
refetch();
success('Refreshed', 'Programme rules updated from API.');
}}
disabled={isFetching}
className="inline-flex items-center gap-1.5 px-3 py-2 text-xs font-semibold text-slate-300 bg-slate-800/80 hover:bg-slate-700 rounded-xl border border-slate-700 transition cursor-pointer"
title="Refresh Rules"
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 Programme Rules from API"
>
<RefreshCw className={`w-3.5 h-3.5 ${isFetching ? 'animate-spin' : ''}`} />
Refresh
<RefreshCw className={`w-3.5 h-3.5 text-blue-400 ${isFetching ? 'animate-spin' : ''}`} />
<span>Refresh</span>
</button>
<ExportButton
title="Programme Rules Registry"
subtitle="Statutory, regulatory, and eligibility rules governing strategic funding programmes"
filename={`pfm-programme-rules-${new Date().toISOString().split('T')[0]}`}
columns={ruleExportColumns}
data={filteredRules}
/>
<button
type="button"
onClick={() => openCreateModal()}
className="inline-flex items-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" />
Add Rule
<span>Add Rule</span>
</button>
</div>
</div>