import React, { useState, useEffect } from 'react'; import { TrendingUp, Briefcase, DollarSign, PieChart, Edit3, Trash2, Plus, RefreshCw, Search, CheckCircle, AlertTriangle, FileSpreadsheet, Layers, ArrowUpRight, ArrowDownRight, Sliders, Building2, X, Check, Zap, Info, ShieldCheck } from 'lucide-react'; import { InvestmentSecurity, InvestmentCategory, InvestmentReconciliation, BranchId, User } from '../types'; import { BRANCHES_LIST } from '../types'; interface InvestmentsPortfolioViewProps { currentUser: User | null; activePeriod: string; } export const InvestmentsPortfolioView: React.FC = ({ currentUser, activePeriod, }) => { const isHO = currentUser?.role === 'admin'; const branchId = currentUser?.branchId; const [investments, setInvestments] = useState([]); const [reconciliations, setReconciliations] = useState([]); const [grandTotals, setGrandTotals] = useState(null); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState<'securities' | 'reconciliation' | 'mtm_pricing'>('securities'); // Filters const [searchQuery, setSearchQuery] = useState(''); const [selectedBranchFilter, setSelectedBranchFilter] = useState(isHO ? 'all' : branchId || 'all'); const [selectedCategoryFilter, setSelectedCategoryFilter] = useState('all'); // Modals state const [isModalOpen, setIsModalOpen] = useState(false); const [editingSecurity, setEditingSecurity] = useState | null>(null); // Bulk MTM Modal / Form state const [bulkIsin, setBulkIsin] = useState(''); const [bulkMtmPrice, setBulkMtmPrice] = useState(''); const [bulkMtmYield, setBulkMtmYield] = useState(''); const [mtmSuccessMsg, setMtmSuccessMsg] = useState(''); useEffect(() => { if (!isHO && branchId) { setSelectedBranchFilter(branchId); } }, [currentUser, isHO, branchId]); const fetchInvestments = async () => { setLoading(true); try { const url = !isHO && branchId ? `/api/investments?branchId=${branchId}` : (selectedBranchFilter !== 'all' ? `/api/investments?branchId=${selectedBranchFilter}` : '/api/investments'); const res = await fetch(url); const data = await res.json(); setInvestments(data.investments || []); // Fetch reconciliation const reconRes = await fetch(`/api/investments/reconciliation?period=${activePeriod}`); const reconData = await reconRes.json(); setReconciliations(reconData.reconciliations || []); setGrandTotals(reconData.grandTotals || null); } catch (err) { console.error('Failed to fetch investments:', err); } finally { setLoading(false); } }; useEffect(() => { fetchInvestments(); }, [currentUser, activePeriod, selectedBranchFilter]); // Handle Add/Edit Security Save const handleSaveSecurity = async (e: React.FormEvent) => { e.preventDefault(); if (!editingSecurity) return; try { const res = await fetch('/api/investments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...editingSecurity, branchId: editingSecurity.branchId || branchId || 'bahrain', faceValue: Number(editingSecurity.faceValue || 0), amountInvested: Number(editingSecurity.amountInvested || 0), couponRate: Number(editingSecurity.couponRate || 0), originalPrice: Number(editingSecurity.originalPrice || 100), purchaseYield: Number(editingSecurity.purchaseYield || 0), }), }); if (res.ok) { setIsModalOpen(false); setEditingSecurity(null); fetchInvestments(); } else { const err = await res.json(); alert(`Error: ${err.error || 'Failed to save security'}`); } } catch (err) { console.error('Save security error:', err); } }; // Handle Delete Security const handleDeleteSecurity = async (id: string) => { if (!confirm('Are you sure you want to delete this security from the portfolio?')) return; try { const res = await fetch(`/api/investments/${id}`, { method: 'DELETE' }); if (res.ok) { fetchInvestments(); } } catch (err) { console.error('Delete security error:', err); } }; // Handle HO Bulk MTM Price Update const handleBulkMtmSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!bulkIsin || !bulkMtmPrice) return; try { const res = await fetch('/api/investments/bulk-mtm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ isin: bulkIsin.trim(), mtmPrice: Number(bulkMtmPrice), mtmYield: bulkMtmYield ? Number(bulkMtmYield) : undefined, updatedBy: currentUser?.name || 'Head Office Admin', }), }); if (res.ok) { const data = await res.json(); setMtmSuccessMsg(data.message || 'MTM prices updated successfully'); setTimeout(() => setMtmSuccessMsg(''), 4000); setBulkIsin(''); setBulkMtmPrice(''); setBulkMtmYield(''); fetchInvestments(); } } catch (err) { console.error('Bulk MTM error:', err); } }; // Filtered Securities const filteredSecurities = investments.filter((sec) => { const matchesBranch = selectedBranchFilter === 'all' || sec.branchId === selectedBranchFilter; const matchesCategory = selectedCategoryFilter === 'all' || sec.category === selectedCategoryFilter; const q = searchQuery.toLowerCase(); const matchesSearch = !q || sec.isin?.toLowerCase().includes(q) || sec.securityType?.toLowerCase().includes(q) || sec.country?.toLowerCase().includes(q); return matchesBranch && matchesCategory && matchesSearch; }); // KPI Calculations const totalFaceValue = filteredSecurities.reduce((acc, s) => acc + (s.faceValue || 0), 0); const totalAmountInvested = filteredSecurities.reduce((acc, s) => acc + (s.amountInvested || 0), 0); const totalMtmPnL = filteredSecurities.reduce((acc, s) => acc + (s.mtmPnL || 0), 0); const totalMtmValue = filteredSecurities.reduce((acc, s) => { const mtm = s.mtmPrice !== undefined ? (s.faceValue * s.mtmPrice) / 100 : s.amountInvested; return acc + mtm; }, 0); // Unique ISINs count const uniqueIsins = new Set(investments.map((s) => s.isin)).size; return (
{/* Top Banner / Header */}

Investments Portfolio {isHO ? 'Global Consolidated' : `${currentUser?.branchId?.toUpperCase()} Branch`}

Complete portfolio management (HTM, AFS, HFT) with Head Office MTM revaluation & Balance Sheet reconciliation

{/* KPI Cards */}
Total Face Value
USD {totalFaceValue.toFixed(2)} Mn
{filteredSecurities.length} active portfolio holdings
Book Value (Invested)
USD {totalAmountInvested.toFixed(2)} Mn
Reconciles with Balance Sheet Investments
MTM Market Value
USD {totalMtmValue.toFixed(2)} Mn
HO Mark-to-Market revalued
Unrealized MTM P&L {totalMtmPnL >= 0 ? ( ) : ( )}
= 0 ? 'text-emerald-400' : 'text-rose-400' }`} > {totalMtmPnL >= 0 ? '+' : ''}${(totalMtmPnL / 1000000).toFixed(2)}{' '} Mn
(${totalMtmPnL.toLocaleString('en-US', { maximumFractionDigits: 0 })})
{/* Navigation Tabs */}
{isHO && ( )}
{uniqueIsins} Unique ISINs tracked
{/* TAB 1: SECURITIES MASTER TABLE */} {activeTab === 'securities' && (
{/* Controls Bar */}
setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2 rounded-xl bg-slate-950/80 border border-slate-700/80 text-slate-200 text-sm focus:outline-none focus:border-cyan-500" />
{isHO ? (
Branch:
) : (
Branch Scope: {BRANCHES_LIST.find((b) => b.id === branchId)?.name || branchId?.toUpperCase()}
)}
Category:
{/* Table */}
{filteredSecurities.length === 0 ? ( ) : ( filteredSecurities.map((sec) => { const branchObj = BRANCHES_LIST.find((b) => b.id === sec.branchId); return ( ); }) )}
Branch / Country Cat ISIN / Security Face Value ($M) Amount Invested ($M) Coupon % Maturity Date Orig Price % HO MTM Price % MTM Yield % MTM P&L ($) Actions
No investment securities match the selected filters.
{branchObj?.name || sec.country}
{sec.country}
{sec.category}
{sec.isin}
{sec.securityType}
{sec.faceValue.toFixed(2)} {sec.amountInvested.toFixed(2)} {sec.couponRate ? `${sec.couponRate.toFixed(2)}%` : '—'} {sec.maturityDate} {sec.originalPrice ? sec.originalPrice.toFixed(3) : '100.000'} {sec.mtmPrice !== undefined ? sec.mtmPrice.toFixed(3) : '—'} {sec.mtmYield !== undefined ? `${sec.mtmYield.toFixed(2)}%` : '—'} = 0 ? 'text-emerald-400' : 'text-rose-400' }`} > {sec.mtmPnL !== undefined ? `${sec.mtmPnL >= 0 ? '+' : ''}$${Math.round(sec.mtmPnL).toLocaleString()}` : '—'}
)} {/* TAB 2: RECONCILIATION MATRIX */} {activeTab === 'reconciliation' && (

Portfolio vs Balance Sheet Reconciliation

Verifies that each branch's detailed Investment Portfolio sum matches its Balance Sheet 'Investments' line item for period {activePeriod}.

{grandTotals && (
Grand Portfolio Total: ${grandTotals.portfolioTotalInvested}M Grand BS Total: ${grandTotals.balanceSheetInvestments}M
)}
{reconciliations.map((recon) => ( ))}
Branch Name Securities Count Portfolio Invested ($M) Balance Sheet 'Investments' ($M) Variance ($M) Reconciliation Status
{recon.branchName} {recon.securitiesCount} ${recon.portfolioTotalInvested.toFixed(2)} ${recon.balanceSheetInvestments.toFixed(2)} 0 ? 'text-cyan-400' : 'text-amber-400' }`} > {recon.difference === 0 ? '$0.00' : `${recon.difference > 0 ? '+' : ''}$${recon.difference.toFixed(2)}`} {recon.isMatched ? ( Reconciled ) : ( Mismatch (${Math.abs(recon.difference).toFixed(2)}M) )}
)} {/* TAB 3: HO BULK MTM PRICING TOOL */} {activeTab === 'mtm_pricing' && isHO && (

Head Office Mark-to-Market (MTM) Revaluation Feed

Input MTM prices (% of par) or Yields for specific ISIN securities. All branches holding securities with matching ISIN will be updated and revalued automatically.

{mtmSuccessMsg && (
{mtmSuccessMsg}
)}
setBulkIsin(e.target.value)} required className="w-full px-3.5 py-2.5 rounded-xl bg-slate-950 border border-slate-700 font-mono text-cyan-300 text-sm focus:outline-none focus:border-cyan-500" />
setBulkMtmPrice(e.target.value)} required className="w-full px-3.5 py-2.5 rounded-xl bg-slate-950 border border-slate-700 font-mono text-emerald-400 text-sm focus:outline-none focus:border-cyan-500" />
setBulkMtmYield(e.target.value)} className="w-full px-3.5 py-2.5 rounded-xl bg-slate-950 border border-slate-700 font-mono text-purple-300 text-sm focus:outline-none focus:border-cyan-500" />
{/* Preset MTM Market Feed Quotes */}

Bloomberg / Market Benchmark Reference Quotes (30-Jun-26 Feed) Click quote to auto-fill

{[ { isin: 'XS2232319638', name: 'GOP EuroBond 2031', price: 100.38, yield: 7.28 }, { isin: 'XS1729875598', name: 'GOP EuroBond 2027', price: 100.50, yield: 6.50 }, { isin: 'XS3353874145', name: 'GOP EuroBond 2029', price: 99.75, yield: 7.07 }, { isin: 'XS2419405274', name: 'PAKISTAN SUKUK 7.95%', price: 99.99, yield: 7.95 }, { isin: 'BD927251100', name: 'Bangladesh 10Y T-Bond', price: 98.45, yield: 9.71 }, { isin: 'BD934481203', name: 'Bangladesh 20Y T-Bond', price: 109.90, yield: 10.27 }, ].map((feed) => ( ))}
)} {/* MODAL: ADD / EDIT SECURITY */} {isModalOpen && editingSecurity && (

{editingSecurity.id ? 'Edit Investment Security' : 'Add Investment Security'}

Enter complete security details for portfolio tracking & reconciliation.

setEditingSecurity({ ...editingSecurity, isin: e.target.value })} className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 font-mono text-cyan-300 text-sm" />
setEditingSecurity({ ...editingSecurity, securityType: e.target.value })} className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 text-slate-200 text-sm" />
setEditingSecurity({ ...editingSecurity, faceValue: Number(e.target.value) })} className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 font-mono text-slate-100 text-sm" />
setEditingSecurity({ ...editingSecurity, amountInvested: Number(e.target.value) }) } className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 font-mono text-emerald-400 text-sm" />
setEditingSecurity({ ...editingSecurity, couponRate: Number(e.target.value) })} className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 font-mono text-slate-200 text-sm" />
setEditingSecurity({ ...editingSecurity, originalPrice: Number(e.target.value) }) } className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 font-mono text-slate-200 text-sm" />
setEditingSecurity({ ...editingSecurity, purchaseDate: e.target.value })} className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 text-slate-200 text-sm" />
setEditingSecurity({ ...editingSecurity, maturityDate: e.target.value })} className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-700 text-slate-200 text-sm" />
{isHO && (
Head Office Valuation (MTM)
setEditingSecurity({ ...editingSecurity, mtmPrice: Number(e.target.value) }) } className="w-full px-3 py-1.5 rounded-lg bg-slate-900 border border-slate-700 font-mono text-cyan-300 text-xs" />
setEditingSecurity({ ...editingSecurity, mtmYield: Number(e.target.value) }) } className="w-full px-3 py-1.5 rounded-lg bg-slate-900 border border-slate-700 font-mono text-purple-300 text-xs" />
)}
)}
); };