Enhance Treasury page: add state management and account hierarchy visualization
- Added refreshKey state to force re-renders when accounts change - Enhanced account hierarchy display with clickable subledger cards - Improved transfer tab with validation messages - Enhanced report generation with better UX - Auto-select newly created accounts - Update selected account after transfers
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import {
|
||||
getAccountStore,
|
||||
createTreasuryAccount,
|
||||
@@ -23,15 +23,16 @@ export default function TreasuryPage() {
|
||||
const [reportData, setReportData] = useState<SubledgerReport | null>(null);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [refreshKey, setRefreshKey] = useState(0); // Force re-render when accounts change
|
||||
|
||||
// Get all accounts
|
||||
// Get all accounts - refresh when refreshKey changes
|
||||
const treasuryAccounts = useMemo(() => {
|
||||
return accountStore.getAll().filter((acc) => acc.type === 'treasury') as TreasuryAccount[];
|
||||
}, [accountStore]);
|
||||
}, [refreshKey]);
|
||||
|
||||
const subledgerAccounts = useMemo(() => {
|
||||
return accountStore.getAll().filter((acc) => acc.type === 'subledger') as SubledgerAccount[];
|
||||
}, [accountStore]);
|
||||
}, [refreshKey]);
|
||||
|
||||
// Get subledgers for selected treasury account
|
||||
const subledgersForSelected = useMemo(() => {
|
||||
@@ -51,6 +52,8 @@ export default function TreasuryPage() {
|
||||
accountStore.add(account);
|
||||
setShowCreateTreasury(false);
|
||||
setError(null);
|
||||
setRefreshKey((k) => k + 1); // Force re-render
|
||||
setSelectedAccount(account); // Select the newly created account
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to create treasury account');
|
||||
}
|
||||
@@ -62,6 +65,8 @@ export default function TreasuryPage() {
|
||||
accountStore.add(account);
|
||||
setShowCreateSubledger(false);
|
||||
setError(null);
|
||||
setRefreshKey((k) => k + 1); // Force re-render
|
||||
setSelectedAccount(account); // Select the newly created account
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to create subledger account');
|
||||
}
|
||||
@@ -73,6 +78,12 @@ export default function TreasuryPage() {
|
||||
try {
|
||||
executeSubledgerTransfer(fromId, toId, amount, currency, description);
|
||||
setShowTransfer(false);
|
||||
setRefreshKey((k) => k + 1); // Force re-render to update balances
|
||||
// Update selected account if it was involved in the transfer
|
||||
if (selectedAccount && (selectedAccount.id === fromId || selectedAccount.id === toId)) {
|
||||
const updated = accountStore.get(selectedAccount.id);
|
||||
if (updated) setSelectedAccount(updated);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Transfer failed');
|
||||
} finally {
|
||||
@@ -281,21 +292,53 @@ export default function TreasuryPage() {
|
||||
|
||||
{activeTab === 'transfers' && (
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold mb-4">Inter-Subledger Transfers</h2>
|
||||
<button
|
||||
onClick={() => setShowTransfer(true)}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||||
>
|
||||
New Transfer
|
||||
</button>
|
||||
{/* Transfer history would go here */}
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-lg font-semibold">Inter-Subledger Transfers</h2>
|
||||
<button
|
||||
onClick={() => setShowTransfer(true)}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||||
disabled={subledgerAccounts.length < 2}
|
||||
>
|
||||
New Transfer
|
||||
</button>
|
||||
</div>
|
||||
{subledgerAccounts.length < 2 ? (
|
||||
<p className="text-gray-500">You need at least 2 subledger accounts to make transfers</p>
|
||||
) : (
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600 mb-2">Recent transfers will appear here</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'reports' && (
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-lg font-semibold mb-4">Subledger Reports</h2>
|
||||
{reportData && (
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-lg font-semibold">Subledger Reports</h2>
|
||||
{subledgerAccounts.length > 0 && (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (selectedAccount && selectedAccount.type === 'subledger') {
|
||||
const startDate = new Date();
|
||||
startDate.setMonth(startDate.getMonth() - 1);
|
||||
handleGenerateReport(selectedAccount.id, startDate, new Date());
|
||||
}
|
||||
}}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||||
disabled={!selectedAccount || selectedAccount.type !== 'subledger'}
|
||||
>
|
||||
Generate Report for Selected Account
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{!reportData ? (
|
||||
<p className="text-gray-500">
|
||||
{selectedAccount && selectedAccount.type === 'subledger'
|
||||
? 'Click "Generate Report" to create a report for the selected subledger account'
|
||||
: 'Select a subledger account and generate a report'}
|
||||
</p>
|
||||
) : (
|
||||
<div className="mt-4 p-4 bg-gray-50 rounded-md">
|
||||
<h3 className="font-semibold mb-2">Report Summary</h3>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@@ -494,16 +537,31 @@ export default function TreasuryPage() {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{selectedAccount.type === 'treasury' && subledgersForSelected.length > 0 && (
|
||||
{selectedAccount.type === 'treasury' && (
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-2">Subledgers ({subledgersForSelected.length})</p>
|
||||
<ul className="space-y-1">
|
||||
{subledgersForSelected.map((sub) => (
|
||||
<li key={sub.id} className="text-sm text-gray-700">
|
||||
{sub.name} ({sub.accountNumber})
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{subledgersForSelected.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{subledgersForSelected.map((sub) => (
|
||||
<div
|
||||
key={sub.id}
|
||||
className="p-2 bg-gray-50 rounded border border-gray-200 cursor-pointer hover:bg-gray-100"
|
||||
onClick={() => setSelectedAccount(sub)}
|
||||
>
|
||||
<p className="text-sm font-medium text-gray-900">{sub.name}</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{sub.accountNumber} | Balance: {sub.balance.toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}{' '}
|
||||
{sub.currency}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500">No subledgers yet</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{selectedAccount.type === 'subledger' && (
|
||||
|
||||
Reference in New Issue
Block a user