From 0974c7b5308510f1a1fa5123ee49148651e1d2d1 Mon Sep 17 00:00:00 2001 From: RanugaVW Date: Fri, 17 Oct 2025 19:10:41 +0530 Subject: [PATCH 1/2] feat: Add Branch Overview feature with account statistics - Created new BranchOverviewSection component with branch selector - Added sidebar tab 'Branch Overview' in MainLayout - Implemented branch account statistics API integration - Display Joint Accounts, Fixed Deposits, and Savings Accounts counts and balances - Added comprehensive error handling and loading states - Fixed dropdown visibility issues with inline styles - Updated API interface to match backend response structure - Added branch summary card with total accounts and holdings --- src/api/branches.ts | 28 +- src/components/layout/MainLayout.tsx | 1 + src/dashboard/Dashboard.tsx | 6 +- .../sections/BranchOverviewSection.tsx | 331 ++++++++++++++++++ src/index.css | 44 ++- 5 files changed, 407 insertions(+), 3 deletions(-) create mode 100644 src/dashboard/sections/BranchOverviewSection.tsx diff --git a/src/api/branches.ts b/src/api/branches.ts index 73f6518..796b958 100644 --- a/src/api/branches.ts +++ b/src/api/branches.ts @@ -3,7 +3,8 @@ import api from './axios'; // Types for branch API export interface BranchDetails { branch_id: string; - branch_name: string; + branch_name?: string; + name?: string; // Alternative field name from API branch_code?: string; city?: string; address?: string; @@ -37,6 +38,18 @@ export interface BranchReportParams { transaction_type?: string; } +export interface BranchAccountStatistics { + branch_id?: string; + branch_name?: string; + branch_address?: string | null; + total_joint_accounts?: number; + joint_accounts_balance?: number; + total_fixed_deposits?: number; + fixed_deposits_amount?: number; + total_savings_accounts?: number; + savings_accounts_balance?: number; +} + // Branch API service export const branchApi = { // Get all branches @@ -68,6 +81,19 @@ export const branchApi = { { params } ); return response.data; + }, + + // Get branch account statistics + getBranchAccountStatistics: async (branchId: string): Promise => { + try { + const response = await api.get(`/api/branches/${branchId}/statistics`); + console.log('API Response for branch statistics:', response.data); + return response.data; + } catch (error: any) { + console.error('API Error in getBranchAccountStatistics:', error); + console.error('Error response:', error.response?.data); + throw error; + } } }; diff --git a/src/components/layout/MainLayout.tsx b/src/components/layout/MainLayout.tsx index cf8cf66..22288f7 100644 --- a/src/components/layout/MainLayout.tsx +++ b/src/components/layout/MainLayout.tsx @@ -18,6 +18,7 @@ const MainLayout = ({ children, user, activeMainTab, onMainTabChange, onLogout } { id: 'create-account' as MainTab, label: 'Account Management', icon: UserPlus }, { id: 'users' as MainTab, label: 'Users', icon: Users }, { id: 'customer-details' as MainTab, label: 'Customer Details', icon: Users }, + { id: 'branch-overview' as MainTab, label: 'Branch Overview', icon: Building2 }, { id: 'branches' as MainTab, label: 'Branch Management', icon: Building2 }, { id: 'savings-plans' as MainTab, label: 'Savings Plans', icon: PiggyBank }, ]; diff --git a/src/dashboard/Dashboard.tsx b/src/dashboard/Dashboard.tsx index 23f8528..c87e223 100644 --- a/src/dashboard/Dashboard.tsx +++ b/src/dashboard/Dashboard.tsx @@ -9,10 +9,11 @@ import CreateAccountSection from "./sections/CreateAccountSection"; import UsersSection from "./sections/UsersSection"; import CustomerDetailsSection from "./sections/CustomerDetailsSection"; import BranchSection from "./sections/BranchSection"; +import BranchOverviewSection from "./sections/BranchOverviewSection"; import SavingsPlansSection from "./sections/SavingsPlansSection"; import MyProfileSection from "./sections/MyProfileSection"; -export type MainTab = 'transactions' | 'summary' | 'accounts' | 'create-account' | 'users' | 'customer-details' | 'branches' | 'savings-plans' | 'my-profile'; +export type MainTab = 'transactions' | 'summary' | 'accounts' | 'create-account' | 'users' | 'customer-details' | 'branch-overview' | 'branches' | 'savings-plans' | 'my-profile'; const Dashboard = () => { const [activeMainTab, setActiveMainTab] = useState('transactions'); @@ -39,6 +40,7 @@ const Dashboard = () => { 'create-account': 'fixed-deposit-new', 'users': 'customers', 'customer-details': 'customer-info', + 'branch-overview': 'overview', 'branches': 'summary', 'savings-plans': 'create', 'my-profile': 'details', @@ -60,6 +62,8 @@ const Dashboard = () => { return ; case 'customer-details': return ; + case 'branch-overview': + return ; case 'branches': return ; case 'savings-plans': diff --git a/src/dashboard/sections/BranchOverviewSection.tsx b/src/dashboard/sections/BranchOverviewSection.tsx new file mode 100644 index 0000000..8be6443 --- /dev/null +++ b/src/dashboard/sections/BranchOverviewSection.tsx @@ -0,0 +1,331 @@ +import { useState, useEffect } from 'react'; +import { Building2, PiggyBank, Wallet, Users, Loader2, AlertCircle } from 'lucide-react'; +import { branchApi } from '../../api/branches'; +import type { BranchDetails, BranchAccountStatistics } from '../../api/branches'; +import { formatCurrency } from '../../utils/formatters'; + +const BranchOverviewSection = () => { + const [branches, setBranches] = useState([]); + const [selectedBranchId, setSelectedBranchId] = useState(''); + const [branchDetails, setBranchDetails] = useState(null); + const [branchStats, setBranchStats] = useState(null); + const [loading, setLoading] = useState(false); + const [loadingBranches, setLoadingBranches] = useState(true); + const [error, setError] = useState(null); + const [debugInfo, setDebugInfo] = useState(''); + + // Fetch all branches on component mount + useEffect(() => { + const fetchBranches = async () => { + try { + setLoadingBranches(true); + const branchList = await branchApi.getAll(); + console.log('Branches loaded:', branchList); // Debug log + setBranches(branchList); + setError(null); + } catch (err: any) { + console.error('Error fetching branches:', err); + setError('Failed to load branches'); + } finally { + setLoadingBranches(false); + } + }; + + fetchBranches(); + }, []); + + // Fetch branch details and statistics when a branch is selected + const handleBranchChange = async (branchId: string) => { + console.log('Branch selection changed:', branchId); + setDebugInfo(`Branch ID selected: ${branchId}`); + + if (!branchId) { + setSelectedBranchId(''); + setBranchDetails(null); + setBranchStats(null); + setDebugInfo(''); + return; + } + + setSelectedBranchId(branchId); + setLoading(true); + setError(null); + setDebugInfo('Loading branch data...'); + + try { + // Fetch branch details + console.log('Fetching branch details for ID:', branchId); + setDebugInfo(`Fetching branch details for ID: ${branchId}`); + const details = await branchApi.getById(branchId); + console.log('Branch details received:', details); + setDebugInfo(`Branch details loaded: ${JSON.stringify(details).substring(0, 100)}`); + setBranchDetails(details); + + // Fetch branch account statistics + console.log('Fetching branch statistics for ID:', branchId); + setDebugInfo(`Fetching branch statistics for ID: ${branchId}`); + const stats = await branchApi.getBranchAccountStatistics(branchId); + console.log('Branch statistics received:', stats); + setDebugInfo(`Statistics loaded successfully`); + + // Validate the stats data + if (!stats || typeof stats !== 'object') { + throw new Error('Invalid statistics data received from API'); + } + + setBranchStats(stats); + setDebugInfo('All data loaded successfully'); + } catch (err: any) { + console.error('Error fetching branch data:', err); + console.error('Error details:', err.response?.data); + const errorMessage = err.response?.data?.detail || err.message || 'Failed to load branch information'; + setError(errorMessage); + setDebugInfo(`Error: ${errorMessage}`); + setBranchDetails(null); + setBranchStats(null); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* Header */} +
+
+ +
+
+

Branch Overview

+

View detailed statistics for each branch

+
+
+ + {/* Branch Selector */} +
+ + + {loadingBranches ? ( +
+ + Loading branches... +
+ ) : ( + + )} + + {/* Debug info */} + {!loadingBranches && branches.length === 0 && ( +

No branches found. Check console for errors.

+ )} +
+ + {/* Error State */} + {error && ( +
+ +
+

{error}

+ {debugInfo &&

Debug: {debugInfo}

} +
+
+ )} + + {/* Loading State */} + {loading && ( +
+ + Loading branch information... +
+ )} + + {/* Branch Details and Statistics */} + {!loading && branchDetails && ( + <> + {/* Branch Information Card */} +
+
+

Branch Information

+
+
+
+

Branch Name

+

{branchDetails.branch_name || branchDetails.name}

+
+ {branchDetails.branch_code && ( +
+

Branch Code

+

{branchDetails.branch_code}

+
+ )} + {branchDetails.city && ( +
+

City

+

{branchDetails.city}

+
+ )} + {branchDetails.address && ( +
+

Address

+

{branchDetails.address}

+
+ )} + {branchDetails.contact_number && ( +
+

Contact Number

+

{branchDetails.contact_number}

+
+ )} + {branchDetails.manager_name && ( +
+

Branch Manager

+

{branchDetails.manager_name}

+
+ )} +
+
+ + {/* Account Statistics */} + {branchStats && ( + <> + {/* Statistics Summary Cards */} +
+ {/* Joint Accounts */} +
+
+
+ +
+
+

Joint Accounts

+

{branchStats.total_joint_accounts || 0}

+

+ Total Balance: + {formatCurrency(branchStats.joint_accounts_balance || 0)} + +

+
+ + {/* Fixed Deposit Accounts */} +
+
+
+ +
+
+

Fixed Deposit Accounts

+

{branchStats.total_fixed_deposits || 0}

+

+ Total Amount: + {formatCurrency(branchStats.fixed_deposits_amount || 0)} + +

+
+ + {/* Savings Accounts */} +
+
+
+ +
+
+

Savings Accounts

+

{branchStats.total_savings_accounts || 0}

+

+ Total Balance: + {formatCurrency(branchStats.savings_accounts_balance || 0)} + +

+
+
+ + {/* Total Summary Card */} +
+
+
+

Branch Summary

+
+
+

Total Accounts

+

+ {(branchStats.total_joint_accounts || 0) + + (branchStats.total_fixed_deposits || 0) + + (branchStats.total_savings_accounts || 0)} +

+
+
+

Total Holdings

+

+ {formatCurrency( + (branchStats.joint_accounts_balance || 0) + + (branchStats.fixed_deposits_amount || 0) + + (branchStats.savings_accounts_balance || 0) + )} +

+
+
+

Branch Location

+

+ {branchStats.branch_address || 'N/A'} +

+
+
+
+
+
+ +
+
+
+
+ + )} + + {/* No Statistics Available */} + {!branchStats && ( +
+

No account statistics available for this branch.

+
+ )} + + )} + + {/* Empty State */} + {!loading && !branchDetails && !error && selectedBranchId === '' && ( +
+ +

No Branch Selected

+

Please select a branch from the dropdown above to view its details and statistics.

+
+ )} +
+ ); +}; + +export default BranchOverviewSection; diff --git a/src/index.css b/src/index.css index bd6213e..92f7e5b 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,45 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +/* Fix for branch select dropdown visibility - AGGRESSIVE OVERRIDE */ +select#branch-select, +select#branch-select * { + color: #000 !important; + background-color: #fff !important; + font-size: 16px !important; + font-weight: 500 !important; +} + +select#branch-select option { + color: #000 !important; + background-color: #fff !important; + padding: 10px !important; + font-size: 16px !important; + font-weight: 500 !important; + line-height: 1.5 !important; + display: block !important; +} + +select#branch-select option:hover { + background-color: #e0e7ff !important; + color: #000 !important; +} + +select#branch-select option:checked, +select#branch-select option:focus { + background-color: #3b82f6 !important; + color: #fff !important; +} + +/* Ensure text is visible on all browsers */ +select#branch-select::-ms-expand { + color: #000 !important; +} + +select#branch-select:-webkit-autofill, +select#branch-select:-webkit-autofill:hover, +select#branch-select:-webkit-autofill:focus { + -webkit-text-fill-color: #000 !important; + -webkit-box-shadow: 0 0 0px 1000px #fff inset !important; +} \ No newline at end of file From f14ed7272f6ad30a7c4b70f9e9fdfbec38d4657a Mon Sep 17 00:00:00 2001 From: RanugaVW Date: Fri, 17 Oct 2025 19:23:25 +0530 Subject: [PATCH 2/2] feat: Replace manual branch ID input with dropdown in Branch Summary - Updated BranchSummary component to fetch all branches on mount - Replaced text input with branch dropdown selector (similar to Branch Overview) - Added loading state while fetching branches --- src/dashboard/tables/BranchSummary.tsx | 70 +++++++++++++++++++++----- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/dashboard/tables/BranchSummary.tsx b/src/dashboard/tables/BranchSummary.tsx index 8b6e444..0246261 100644 --- a/src/dashboard/tables/BranchSummary.tsx +++ b/src/dashboard/tables/BranchSummary.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Building2, Search, Loader2, AlertCircle, TrendingUp, TrendingDown, ArrowRightLeft, X, ExternalLink } from 'lucide-react'; import { branchApi, type BranchDetails, type BranchTransactionReport } from '../../api/branches'; import { transactionApi } from '../../api/transactions'; @@ -16,10 +16,12 @@ interface TransactionDetail { } const BranchSummary = () => { + const [branches, setBranches] = useState([]); const [branchId, setBranchId] = useState(''); const [branchDetails, setBranchDetails] = useState(null); const [branchReport, setBranchReport] = useState(null); const [loading, setLoading] = useState(false); + const [loadingBranches, setLoadingBranches] = useState(true); const [error, setError] = useState(null); // Modal state for transaction details @@ -30,6 +32,25 @@ const BranchSummary = () => { const [accountTransactions, setAccountTransactions] = useState([]); const [loadingTransactions, setLoadingTransactions] = useState(false); + // Fetch all branches on component mount + useEffect(() => { + const fetchBranches = async () => { + try { + setLoadingBranches(true); + const branchList = await branchApi.getAll(); + console.log('Branches loaded:', branchList); + setBranches(branchList); + } catch (err: any) { + console.error('Error fetching branches:', err); + setError('Failed to load branches'); + } finally { + setLoadingBranches(false); + } + }; + + fetchBranches(); + }, []); + // Handle search const handleSearch = async () => { if (!branchId.trim()) { @@ -147,7 +168,7 @@ const BranchSummary = () => {

Branch Summary

-

Enter branch ID to view transaction statistics (Last 30 Days)

+

Select a branch to view transaction statistics (Last 30 Days)

@@ -155,29 +176,52 @@ const BranchSummary = () => {
- setBranchId(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && handleSearch()} - placeholder="Enter branch ID (e.g., BR001, b5c3a0d2-...)" - className="w-full px-4 py-3 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> + {loadingBranches ? ( +
+ + Loading branches... +
+ ) : ( + + )}