@pfm-platform/accounts-feature 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.cjs
CHANGED
|
@@ -98,7 +98,7 @@ function useAccountTypes(userId) {
|
|
|
98
98
|
accounts: grouped.types.flatMap((type) => mappedByType[type] || [])
|
|
99
99
|
})).filter((group) => group.accounts.length > 0).map((accountType) => ({
|
|
100
100
|
...accountType,
|
|
101
|
-
sum: accountType.accounts.reduce((sum, account) => sum + (
|
|
101
|
+
sum: accountType.accounts.reduce((sum, account) => sum + (account.balance || 0), 0)
|
|
102
102
|
}));
|
|
103
103
|
}, [data]);
|
|
104
104
|
}
|
|
@@ -129,7 +129,7 @@ function useAccountSummary(userId) {
|
|
|
129
129
|
totalAccounts: data.accounts.length,
|
|
130
130
|
openedCount: opened.length,
|
|
131
131
|
closedCount: closed.length,
|
|
132
|
-
totalBalance: data.accounts.reduce((sum, account) => sum + (
|
|
132
|
+
totalBalance: data.accounts.reduce((sum, account) => sum + (account.balance || 0), 0),
|
|
133
133
|
hasAccounts: data.accounts.length > 0
|
|
134
134
|
};
|
|
135
135
|
}, [data, opened, closed]);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/utils/groupBy.ts","../src/hooks/useAccountTypes.ts","../src/hooks/useAccountFilters.ts","../src/hooks/useAccountSummary.ts","../src/hooks/useNetWorthSummary.ts","../src/hooks/useNetWorthHistory.ts"],"names":["useAccounts","useMemo","useNetWorth"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Account type display names\n * Maps account_type to human-readable names\n */\nexport const ACCOUNT_TYPE_NAMES = {\n checking: 'Checking',\n savings: 'Savings',\n cards: 'Card',\n student_loans: 'Student Loan',\n bill: 'Bill',\n autos: 'Auto',\n home: 'Mortgage',\n investment: 'Investment',\n loan: 'Loan',\n asset: 'Asset',\n cd: 'Certificate',\n} as const;\n\n/**\n * Grouped account types for categorization\n * Used by useAccountTypes to organize accounts into logical groups\n */\nexport const GROUPED_ACCOUNT_TYPES = [\n {\n name: 'Cash',\n types: ['checking', 'savings', 'money_market'],\n },\n {\n name: 'Credit Cards',\n types: ['cards'],\n },\n {\n name: 'Debts',\n types: ['autos', 'home', 'loan', 'student_loans', 'creditline'],\n },\n {\n name: 'Investments',\n types: ['investment'],\n },\n {\n name: 'Assets',\n types: ['asset', 'cd'],\n },\n {\n name: 'Bills',\n types: ['bill'],\n },\n] as const;\n\n/**\n * User error codes that indicate credentials need updating\n */\nexport const USER_ERROR_CODES = [\n '201',\n '203',\n '204',\n '209',\n '300',\n '301',\n '302',\n '303',\n '304',\n '305',\n '306',\n '307',\n '701',\n '103',\n '108',\n '109',\n '185',\n '187',\n '913',\n '914',\n '931',\n '936',\n] as const;\n","/**\n * Group array items by a key\n * @param array - Array to group\n * @param key - Property key to group by\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {\n return array.reduce(\n (result, item) => {\n const groupKey = String(item[key]);\n if (!result[groupKey]) {\n result[groupKey] = [];\n }\n result[groupKey].push(item);\n return result;\n },\n {} as Record<string, T[]>\n );\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { GROUPED_ACCOUNT_TYPES } from '../constants';\nimport { groupBy } from '../utils/groupBy';\n\nexport interface AccountGroup {\n name: string;\n accounts: any[]; // Using any for now, will use Account type when available\n sum: number;\n}\n\n/**\n * Group accounts by display type and calculate totals\n *\n * Replaces: accountsStore.accountTypes computed property\n *\n * Business logic:\n * - Groups accounts by display_account_type\n * - Organizes into logical categories (Cash, Credit Cards, Debts, etc.)\n * - Calculates sum of balances for each group\n * - Filters out empty groups\n *\n * @param userId - User ID to fetch accounts for\n * @returns Array of account groups with names, accounts, and sums\n */\nexport function useAccountTypes(userId: string): AccountGroup[] {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return [];\n }\n\n // Group accounts by display_account_type\n const mappedByType = groupBy(data.accounts, 'display_account_type');\n\n // Organize into predefined groups and calculate sums\n return GROUPED_ACCOUNT_TYPES.map((grouped) => ({\n name: grouped.name,\n accounts: grouped.types.flatMap((type) => mappedByType[type] || []),\n }))\n .filter((group) => group.accounts.length > 0)\n .map((accountType) => ({\n ...accountType,\n sum: accountType.accounts.reduce((sum, account) => sum + (parseFloat(account.balance) || 0), 0),\n }));\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\n\nexport interface AccountFilters {\n opened: any[]; // Using any for now, will use Account type when available\n closed: any[];\n}\n\n/**\n * Filter accounts into opened/closed categories\n *\n * Replaces: openedAccounts/closedAccounts computed properties\n *\n * Business logic:\n * - Opened: state === 'active' AND include_in_dashboard === true\n * - Closed: state === 'closed' OR state === 'archived' OR include_in_dashboard === false\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with opened and closed account arrays\n */\nexport function useAccountFilters(userId: string): AccountFilters {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return { opened: [], closed: [] };\n }\n\n return {\n opened: data.accounts.filter(\n (account) => account.state === 'active' && account.include_in_dashboard\n ),\n closed: data.accounts.filter(\n (account) =>\n account.state === 'closed' ||\n account.state === 'archived' ||\n !account.include_in_dashboard\n ),\n };\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { useAccountFilters } from './useAccountFilters';\n\nexport interface AccountSummary {\n totalAccounts: number;\n openedCount: number;\n closedCount: number;\n totalBalance: number;\n hasAccounts: boolean;\n}\n\n/**\n * Calculate account summary statistics\n *\n * Replaces: hasAccounts computed property + additional summary logic\n *\n * Business logic:\n * - Counts total, opened, and closed accounts\n * - Calculates total balance across all accounts\n * - Provides hasAccounts boolean for conditional rendering\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with account counts, balance, and hasAccounts flag\n */\nexport function useAccountSummary(userId: string): AccountSummary | null {\n const { data } = useAccounts({ userId });\n const { opened, closed } = useAccountFilters(userId);\n\n return useMemo(() => {\n if (!data?.accounts) {\n return null;\n }\n\n return {\n totalAccounts: data.accounts.length,\n openedCount: opened.length,\n closedCount: closed.length,\n totalBalance: data.accounts.reduce((sum, account) => sum + (parseFloat(account.balance) || 0), 0),\n hasAccounts: data.accounts.length > 0,\n };\n }, [data, opened, closed]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\n\nexport interface NetWorthSummary {\n netWorth: number;\n netWorthChange: number;\n totalAssets: number;\n totalDebts: number;\n assetCount: number;\n debtCount: number;\n manualAssetCount: number;\n manualDebtCount: number;\n aggregatedAssetCount: number;\n aggregatedDebtCount: number;\n hasAssets: boolean;\n hasDebts: boolean;\n hasManualAccounts: boolean;\n isPositiveNetWorth: boolean;\n isIncreasing: boolean;\n}\n\n/**\n * Calculate net worth summary statistics\n *\n * Business logic:\n * - Parses string amounts to numbers for calculations\n * - Separates manual vs aggregated accounts\n * - Provides counts and totals for assets/debts\n * - Calculates trend indicators (positive, increasing)\n *\n * @param userId - User ID to fetch net worth for\n * @returns Object with net worth metrics and boolean flags\n *\n * @example\n * ```tsx\n * function NetWorthDashboard() {\n * const summary = useNetWorthSummary('user123');\n *\n * if (!summary) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <h2>Net Worth: ${summary.netWorth.toLocaleString()}</h2>\n * <p className={summary.isIncreasing ? 'positive' : 'negative'}>\n * Change: ${summary.netWorthChange.toLocaleString()}\n * </p>\n * <p>Assets: {summary.assetCount} (Manual: {summary.manualAssetCount})</p>\n * <p>Debts: {summary.debtCount} (Manual: {summary.manualDebtCount})</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useNetWorthSummary(userId: string): NetWorthSummary | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading) {\n return null;\n }\n\n // Parse string amounts to numbers\n const netWorth = parseFloat(data.meta.net_worth);\n const netWorthChange = parseFloat(data.meta.net_worth_change);\n const totalAssets = parseFloat(data.meta.total_assets);\n const totalDebts = parseFloat(data.meta.total_debts);\n\n // Count manual vs aggregated accounts\n const manualAssets = data.assets.filter(a => a.additional_networth_account);\n const aggregatedAssets = data.assets.filter(a => !a.additional_networth_account);\n const manualDebts = data.debts.filter(d => d.additional_networth_account);\n const aggregatedDebts = data.debts.filter(d => !d.additional_networth_account);\n\n return {\n netWorth,\n netWorthChange,\n totalAssets,\n totalDebts,\n assetCount: data.assets.length,\n debtCount: data.debts.length,\n manualAssetCount: manualAssets.length,\n manualDebtCount: manualDebts.length,\n aggregatedAssetCount: aggregatedAssets.length,\n aggregatedDebtCount: aggregatedDebts.length,\n hasAssets: data.assets.length > 0,\n hasDebts: data.debts.length > 0,\n hasManualAccounts: manualAssets.length > 0 || manualDebts.length > 0,\n isPositiveNetWorth: netWorth >= 0,\n isIncreasing: netWorthChange > 0,\n };\n }, [data, isLoading]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\nimport type { NetWorthHistory } from '@pfm-platform/shared';\n\nexport interface NetWorthHistoryPoint extends NetWorthHistory {\n totalAsNumber: number;\n totalAssetAsNumber: number;\n totalDebtAsNumber: number;\n sinceLastMonthAsNumber: number;\n date: Date;\n formattedDate: string;\n isPositive: boolean;\n isGrowing: boolean;\n}\n\n/**\n * Process net worth history for charting and analysis\n *\n * Business logic:\n * - Converts string amounts to numbers for calculations\n * - Parses month/year into Date objects for charting\n * - Formats dates for display\n * - Adds boolean flags for trend indicators\n *\n * @param userId - User ID to fetch net worth history for\n * @returns Array of processed history points sorted by date (oldest first)\n *\n * @example\n * ```tsx\n * function NetWorthChart() {\n * const history = useNetWorthHistory('user123');\n *\n * if (!history) return <div>Loading...</div>;\n *\n * return (\n * <LineChart data={history.map(point => ({\n * date: point.formattedDate,\n * value: point.totalAsNumber\n * }))} />\n * );\n * }\n * ```\n *\n * @example Find Recent Growth\n * ```tsx\n * const history = useNetWorthHistory('user123');\n * const recentGrowth = history?.filter(p => p.isGrowing).slice(-3);\n * ```\n */\nexport function useNetWorthHistory(userId: string): NetWorthHistoryPoint[] | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading || !data.networth_histories) {\n return null;\n }\n\n return data.networth_histories\n .map((history) => {\n const totalAsNumber = parseFloat(history.total);\n const totalAssetAsNumber = parseFloat(history.total_asset);\n const totalDebtAsNumber = parseFloat(history.total_debt);\n const sinceLastMonthAsNumber = parseFloat(history.since_last_month);\n\n // Parse month/year into Date (use first day of month)\n const year = parseInt(history.year, 10);\n const month = parseInt(history.month, 10) - 1; // JS months are 0-indexed\n const date = new Date(year, month, 1);\n\n // Format for display (e.g., \"Apr 2019\")\n const formattedDate = date.toLocaleDateString('en-US', {\n month: 'short',\n year: 'numeric',\n });\n\n return {\n ...history,\n totalAsNumber,\n totalAssetAsNumber,\n totalDebtAsNumber,\n sinceLastMonthAsNumber,\n date,\n formattedDate,\n isPositive: totalAsNumber >= 0,\n isGrowing: sinceLastMonthAsNumber > 0,\n };\n })\n .sort((a, b) => a.date.getTime() - b.date.getTime()); // Sort oldest to newest\n }, [data, isLoading]);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/utils/groupBy.ts","../src/hooks/useAccountTypes.ts","../src/hooks/useAccountFilters.ts","../src/hooks/useAccountSummary.ts","../src/hooks/useNetWorthSummary.ts","../src/hooks/useNetWorthHistory.ts"],"names":["useAccounts","useMemo","useNetWorth"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Account type display names\n * Maps account_type to human-readable names\n */\nexport const ACCOUNT_TYPE_NAMES = {\n checking: 'Checking',\n savings: 'Savings',\n cards: 'Card',\n student_loans: 'Student Loan',\n bill: 'Bill',\n autos: 'Auto',\n home: 'Mortgage',\n investment: 'Investment',\n loan: 'Loan',\n asset: 'Asset',\n cd: 'Certificate',\n} as const;\n\n/**\n * Grouped account types for categorization\n * Used by useAccountTypes to organize accounts into logical groups\n */\nexport const GROUPED_ACCOUNT_TYPES = [\n {\n name: 'Cash',\n types: ['checking', 'savings', 'money_market'],\n },\n {\n name: 'Credit Cards',\n types: ['cards'],\n },\n {\n name: 'Debts',\n types: ['autos', 'home', 'loan', 'student_loans', 'creditline'],\n },\n {\n name: 'Investments',\n types: ['investment'],\n },\n {\n name: 'Assets',\n types: ['asset', 'cd'],\n },\n {\n name: 'Bills',\n types: ['bill'],\n },\n] as const;\n\n/**\n * User error codes that indicate credentials need updating\n */\nexport const USER_ERROR_CODES = [\n '201',\n '203',\n '204',\n '209',\n '300',\n '301',\n '302',\n '303',\n '304',\n '305',\n '306',\n '307',\n '701',\n '103',\n '108',\n '109',\n '185',\n '187',\n '913',\n '914',\n '931',\n '936',\n] as const;\n","/**\n * Group array items by a key\n * @param array - Array to group\n * @param key - Property key to group by\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {\n return array.reduce(\n (result, item) => {\n const groupKey = String(item[key]);\n if (!result[groupKey]) {\n result[groupKey] = [];\n }\n result[groupKey].push(item);\n return result;\n },\n {} as Record<string, T[]>\n );\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { GROUPED_ACCOUNT_TYPES } from '../constants';\nimport { groupBy } from '../utils/groupBy';\n\nexport interface AccountGroup {\n name: string;\n accounts: any[]; // Using any for now, will use Account type when available\n sum: number;\n}\n\n/**\n * Group accounts by display type and calculate totals\n *\n * Replaces: accountsStore.accountTypes computed property\n *\n * Business logic:\n * - Groups accounts by display_account_type\n * - Organizes into logical categories (Cash, Credit Cards, Debts, etc.)\n * - Calculates sum of balances for each group\n * - Filters out empty groups\n *\n * @param userId - User ID to fetch accounts for\n * @returns Array of account groups with names, accounts, and sums\n */\nexport function useAccountTypes(userId: string): AccountGroup[] {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return [];\n }\n\n // Group accounts by display_account_type\n const mappedByType = groupBy(data.accounts, 'display_account_type');\n\n // Organize into predefined groups and calculate sums\n return GROUPED_ACCOUNT_TYPES.map((grouped) => ({\n name: grouped.name,\n accounts: grouped.types.flatMap((type) => mappedByType[type] || []),\n }))\n .filter((group) => group.accounts.length > 0)\n .map((accountType) => ({\n ...accountType,\n sum: accountType.accounts.reduce((sum, account) => sum + (account.balance || 0), 0),\n }));\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\n\nexport interface AccountFilters {\n opened: any[]; // Using any for now, will use Account type when available\n closed: any[];\n}\n\n/**\n * Filter accounts into opened/closed categories\n *\n * Replaces: openedAccounts/closedAccounts computed properties\n *\n * Business logic:\n * - Opened: state === 'active' AND include_in_dashboard === true\n * - Closed: state === 'closed' OR state === 'archived' OR include_in_dashboard === false\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with opened and closed account arrays\n */\nexport function useAccountFilters(userId: string): AccountFilters {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return { opened: [], closed: [] };\n }\n\n return {\n opened: data.accounts.filter(\n (account) => account.state === 'active' && account.include_in_dashboard\n ),\n closed: data.accounts.filter(\n (account) =>\n account.state === 'closed' ||\n account.state === 'archived' ||\n !account.include_in_dashboard\n ),\n };\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { useAccountFilters } from './useAccountFilters';\n\nexport interface AccountSummary {\n totalAccounts: number;\n openedCount: number;\n closedCount: number;\n totalBalance: number;\n hasAccounts: boolean;\n}\n\n/**\n * Calculate account summary statistics\n *\n * Replaces: hasAccounts computed property + additional summary logic\n *\n * Business logic:\n * - Counts total, opened, and closed accounts\n * - Calculates total balance across all accounts\n * - Provides hasAccounts boolean for conditional rendering\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with account counts, balance, and hasAccounts flag\n */\nexport function useAccountSummary(userId: string): AccountSummary | null {\n const { data } = useAccounts({ userId });\n const { opened, closed } = useAccountFilters(userId);\n\n return useMemo(() => {\n if (!data?.accounts) {\n return null;\n }\n\n return {\n totalAccounts: data.accounts.length,\n openedCount: opened.length,\n closedCount: closed.length,\n totalBalance: data.accounts.reduce((sum, account) => sum + (account.balance || 0), 0),\n hasAccounts: data.accounts.length > 0,\n };\n }, [data, opened, closed]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\n\nexport interface NetWorthSummary {\n netWorth: number;\n netWorthChange: number;\n totalAssets: number;\n totalDebts: number;\n assetCount: number;\n debtCount: number;\n manualAssetCount: number;\n manualDebtCount: number;\n aggregatedAssetCount: number;\n aggregatedDebtCount: number;\n hasAssets: boolean;\n hasDebts: boolean;\n hasManualAccounts: boolean;\n isPositiveNetWorth: boolean;\n isIncreasing: boolean;\n}\n\n/**\n * Calculate net worth summary statistics\n *\n * Business logic:\n * - Parses string amounts to numbers for calculations\n * - Separates manual vs aggregated accounts\n * - Provides counts and totals for assets/debts\n * - Calculates trend indicators (positive, increasing)\n *\n * @param userId - User ID to fetch net worth for\n * @returns Object with net worth metrics and boolean flags\n *\n * @example\n * ```tsx\n * function NetWorthDashboard() {\n * const summary = useNetWorthSummary('user123');\n *\n * if (!summary) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <h2>Net Worth: ${summary.netWorth.toLocaleString()}</h2>\n * <p className={summary.isIncreasing ? 'positive' : 'negative'}>\n * Change: ${summary.netWorthChange.toLocaleString()}\n * </p>\n * <p>Assets: {summary.assetCount} (Manual: {summary.manualAssetCount})</p>\n * <p>Debts: {summary.debtCount} (Manual: {summary.manualDebtCount})</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useNetWorthSummary(userId: string): NetWorthSummary | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading) {\n return null;\n }\n\n // Parse string amounts to numbers\n const netWorth = parseFloat(data.meta.net_worth);\n const netWorthChange = parseFloat(data.meta.net_worth_change);\n const totalAssets = parseFloat(data.meta.total_assets);\n const totalDebts = parseFloat(data.meta.total_debts);\n\n // Count manual vs aggregated accounts\n const manualAssets = data.assets.filter(a => a.additional_networth_account);\n const aggregatedAssets = data.assets.filter(a => !a.additional_networth_account);\n const manualDebts = data.debts.filter(d => d.additional_networth_account);\n const aggregatedDebts = data.debts.filter(d => !d.additional_networth_account);\n\n return {\n netWorth,\n netWorthChange,\n totalAssets,\n totalDebts,\n assetCount: data.assets.length,\n debtCount: data.debts.length,\n manualAssetCount: manualAssets.length,\n manualDebtCount: manualDebts.length,\n aggregatedAssetCount: aggregatedAssets.length,\n aggregatedDebtCount: aggregatedDebts.length,\n hasAssets: data.assets.length > 0,\n hasDebts: data.debts.length > 0,\n hasManualAccounts: manualAssets.length > 0 || manualDebts.length > 0,\n isPositiveNetWorth: netWorth >= 0,\n isIncreasing: netWorthChange > 0,\n };\n }, [data, isLoading]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\nimport type { NetWorthHistory } from '@pfm-platform/shared';\n\nexport interface NetWorthHistoryPoint extends NetWorthHistory {\n totalAsNumber: number;\n totalAssetAsNumber: number;\n totalDebtAsNumber: number;\n sinceLastMonthAsNumber: number;\n date: Date;\n formattedDate: string;\n isPositive: boolean;\n isGrowing: boolean;\n}\n\n/**\n * Process net worth history for charting and analysis\n *\n * Business logic:\n * - Converts string amounts to numbers for calculations\n * - Parses month/year into Date objects for charting\n * - Formats dates for display\n * - Adds boolean flags for trend indicators\n *\n * @param userId - User ID to fetch net worth history for\n * @returns Array of processed history points sorted by date (oldest first)\n *\n * @example\n * ```tsx\n * function NetWorthChart() {\n * const history = useNetWorthHistory('user123');\n *\n * if (!history) return <div>Loading...</div>;\n *\n * return (\n * <LineChart data={history.map(point => ({\n * date: point.formattedDate,\n * value: point.totalAsNumber\n * }))} />\n * );\n * }\n * ```\n *\n * @example Find Recent Growth\n * ```tsx\n * const history = useNetWorthHistory('user123');\n * const recentGrowth = history?.filter(p => p.isGrowing).slice(-3);\n * ```\n */\nexport function useNetWorthHistory(userId: string): NetWorthHistoryPoint[] | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading || !data.networth_histories) {\n return null;\n }\n\n return data.networth_histories\n .map((history) => {\n const totalAsNumber = parseFloat(history.total);\n const totalAssetAsNumber = parseFloat(history.total_asset);\n const totalDebtAsNumber = parseFloat(history.total_debt);\n const sinceLastMonthAsNumber = parseFloat(history.since_last_month);\n\n // Parse month/year into Date (use first day of month)\n const year = parseInt(history.year, 10);\n const month = parseInt(history.month, 10) - 1; // JS months are 0-indexed\n const date = new Date(year, month, 1);\n\n // Format for display (e.g., \"Apr 2019\")\n const formattedDate = date.toLocaleDateString('en-US', {\n month: 'short',\n year: 'numeric',\n });\n\n return {\n ...history,\n totalAsNumber,\n totalAssetAsNumber,\n totalDebtAsNumber,\n sinceLastMonthAsNumber,\n date,\n formattedDate,\n isPositive: totalAsNumber >= 0,\n isGrowing: sinceLastMonthAsNumber > 0,\n };\n })\n .sort((a, b) => a.date.getTime() - b.date.getTime()); // Sort oldest to newest\n }, [data, isLoading]);\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -96,7 +96,7 @@ function useAccountTypes(userId) {
|
|
|
96
96
|
accounts: grouped.types.flatMap((type) => mappedByType[type] || [])
|
|
97
97
|
})).filter((group) => group.accounts.length > 0).map((accountType) => ({
|
|
98
98
|
...accountType,
|
|
99
|
-
sum: accountType.accounts.reduce((sum, account) => sum + (
|
|
99
|
+
sum: accountType.accounts.reduce((sum, account) => sum + (account.balance || 0), 0)
|
|
100
100
|
}));
|
|
101
101
|
}, [data]);
|
|
102
102
|
}
|
|
@@ -127,7 +127,7 @@ function useAccountSummary(userId) {
|
|
|
127
127
|
totalAccounts: data.accounts.length,
|
|
128
128
|
openedCount: opened.length,
|
|
129
129
|
closedCount: closed.length,
|
|
130
|
-
totalBalance: data.accounts.reduce((sum, account) => sum + (
|
|
130
|
+
totalBalance: data.accounts.reduce((sum, account) => sum + (account.balance || 0), 0),
|
|
131
131
|
hasAccounts: data.accounts.length > 0
|
|
132
132
|
};
|
|
133
133
|
}, [data, opened, closed]);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/utils/groupBy.ts","../src/hooks/useAccountTypes.ts","../src/hooks/useAccountFilters.ts","../src/hooks/useAccountSummary.ts","../src/hooks/useNetWorthSummary.ts","../src/hooks/useNetWorthHistory.ts"],"names":["useAccounts","useMemo","useNetWorth"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Account type display names\n * Maps account_type to human-readable names\n */\nexport const ACCOUNT_TYPE_NAMES = {\n checking: 'Checking',\n savings: 'Savings',\n cards: 'Card',\n student_loans: 'Student Loan',\n bill: 'Bill',\n autos: 'Auto',\n home: 'Mortgage',\n investment: 'Investment',\n loan: 'Loan',\n asset: 'Asset',\n cd: 'Certificate',\n} as const;\n\n/**\n * Grouped account types for categorization\n * Used by useAccountTypes to organize accounts into logical groups\n */\nexport const GROUPED_ACCOUNT_TYPES = [\n {\n name: 'Cash',\n types: ['checking', 'savings', 'money_market'],\n },\n {\n name: 'Credit Cards',\n types: ['cards'],\n },\n {\n name: 'Debts',\n types: ['autos', 'home', 'loan', 'student_loans', 'creditline'],\n },\n {\n name: 'Investments',\n types: ['investment'],\n },\n {\n name: 'Assets',\n types: ['asset', 'cd'],\n },\n {\n name: 'Bills',\n types: ['bill'],\n },\n] as const;\n\n/**\n * User error codes that indicate credentials need updating\n */\nexport const USER_ERROR_CODES = [\n '201',\n '203',\n '204',\n '209',\n '300',\n '301',\n '302',\n '303',\n '304',\n '305',\n '306',\n '307',\n '701',\n '103',\n '108',\n '109',\n '185',\n '187',\n '913',\n '914',\n '931',\n '936',\n] as const;\n","/**\n * Group array items by a key\n * @param array - Array to group\n * @param key - Property key to group by\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {\n return array.reduce(\n (result, item) => {\n const groupKey = String(item[key]);\n if (!result[groupKey]) {\n result[groupKey] = [];\n }\n result[groupKey].push(item);\n return result;\n },\n {} as Record<string, T[]>\n );\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { GROUPED_ACCOUNT_TYPES } from '../constants';\nimport { groupBy } from '../utils/groupBy';\n\nexport interface AccountGroup {\n name: string;\n accounts: any[]; // Using any for now, will use Account type when available\n sum: number;\n}\n\n/**\n * Group accounts by display type and calculate totals\n *\n * Replaces: accountsStore.accountTypes computed property\n *\n * Business logic:\n * - Groups accounts by display_account_type\n * - Organizes into logical categories (Cash, Credit Cards, Debts, etc.)\n * - Calculates sum of balances for each group\n * - Filters out empty groups\n *\n * @param userId - User ID to fetch accounts for\n * @returns Array of account groups with names, accounts, and sums\n */\nexport function useAccountTypes(userId: string): AccountGroup[] {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return [];\n }\n\n // Group accounts by display_account_type\n const mappedByType = groupBy(data.accounts, 'display_account_type');\n\n // Organize into predefined groups and calculate sums\n return GROUPED_ACCOUNT_TYPES.map((grouped) => ({\n name: grouped.name,\n accounts: grouped.types.flatMap((type) => mappedByType[type] || []),\n }))\n .filter((group) => group.accounts.length > 0)\n .map((accountType) => ({\n ...accountType,\n sum: accountType.accounts.reduce((sum, account) => sum + (parseFloat(account.balance) || 0), 0),\n }));\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\n\nexport interface AccountFilters {\n opened: any[]; // Using any for now, will use Account type when available\n closed: any[];\n}\n\n/**\n * Filter accounts into opened/closed categories\n *\n * Replaces: openedAccounts/closedAccounts computed properties\n *\n * Business logic:\n * - Opened: state === 'active' AND include_in_dashboard === true\n * - Closed: state === 'closed' OR state === 'archived' OR include_in_dashboard === false\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with opened and closed account arrays\n */\nexport function useAccountFilters(userId: string): AccountFilters {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return { opened: [], closed: [] };\n }\n\n return {\n opened: data.accounts.filter(\n (account) => account.state === 'active' && account.include_in_dashboard\n ),\n closed: data.accounts.filter(\n (account) =>\n account.state === 'closed' ||\n account.state === 'archived' ||\n !account.include_in_dashboard\n ),\n };\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { useAccountFilters } from './useAccountFilters';\n\nexport interface AccountSummary {\n totalAccounts: number;\n openedCount: number;\n closedCount: number;\n totalBalance: number;\n hasAccounts: boolean;\n}\n\n/**\n * Calculate account summary statistics\n *\n * Replaces: hasAccounts computed property + additional summary logic\n *\n * Business logic:\n * - Counts total, opened, and closed accounts\n * - Calculates total balance across all accounts\n * - Provides hasAccounts boolean for conditional rendering\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with account counts, balance, and hasAccounts flag\n */\nexport function useAccountSummary(userId: string): AccountSummary | null {\n const { data } = useAccounts({ userId });\n const { opened, closed } = useAccountFilters(userId);\n\n return useMemo(() => {\n if (!data?.accounts) {\n return null;\n }\n\n return {\n totalAccounts: data.accounts.length,\n openedCount: opened.length,\n closedCount: closed.length,\n totalBalance: data.accounts.reduce((sum, account) => sum + (parseFloat(account.balance) || 0), 0),\n hasAccounts: data.accounts.length > 0,\n };\n }, [data, opened, closed]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\n\nexport interface NetWorthSummary {\n netWorth: number;\n netWorthChange: number;\n totalAssets: number;\n totalDebts: number;\n assetCount: number;\n debtCount: number;\n manualAssetCount: number;\n manualDebtCount: number;\n aggregatedAssetCount: number;\n aggregatedDebtCount: number;\n hasAssets: boolean;\n hasDebts: boolean;\n hasManualAccounts: boolean;\n isPositiveNetWorth: boolean;\n isIncreasing: boolean;\n}\n\n/**\n * Calculate net worth summary statistics\n *\n * Business logic:\n * - Parses string amounts to numbers for calculations\n * - Separates manual vs aggregated accounts\n * - Provides counts and totals for assets/debts\n * - Calculates trend indicators (positive, increasing)\n *\n * @param userId - User ID to fetch net worth for\n * @returns Object with net worth metrics and boolean flags\n *\n * @example\n * ```tsx\n * function NetWorthDashboard() {\n * const summary = useNetWorthSummary('user123');\n *\n * if (!summary) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <h2>Net Worth: ${summary.netWorth.toLocaleString()}</h2>\n * <p className={summary.isIncreasing ? 'positive' : 'negative'}>\n * Change: ${summary.netWorthChange.toLocaleString()}\n * </p>\n * <p>Assets: {summary.assetCount} (Manual: {summary.manualAssetCount})</p>\n * <p>Debts: {summary.debtCount} (Manual: {summary.manualDebtCount})</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useNetWorthSummary(userId: string): NetWorthSummary | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading) {\n return null;\n }\n\n // Parse string amounts to numbers\n const netWorth = parseFloat(data.meta.net_worth);\n const netWorthChange = parseFloat(data.meta.net_worth_change);\n const totalAssets = parseFloat(data.meta.total_assets);\n const totalDebts = parseFloat(data.meta.total_debts);\n\n // Count manual vs aggregated accounts\n const manualAssets = data.assets.filter(a => a.additional_networth_account);\n const aggregatedAssets = data.assets.filter(a => !a.additional_networth_account);\n const manualDebts = data.debts.filter(d => d.additional_networth_account);\n const aggregatedDebts = data.debts.filter(d => !d.additional_networth_account);\n\n return {\n netWorth,\n netWorthChange,\n totalAssets,\n totalDebts,\n assetCount: data.assets.length,\n debtCount: data.debts.length,\n manualAssetCount: manualAssets.length,\n manualDebtCount: manualDebts.length,\n aggregatedAssetCount: aggregatedAssets.length,\n aggregatedDebtCount: aggregatedDebts.length,\n hasAssets: data.assets.length > 0,\n hasDebts: data.debts.length > 0,\n hasManualAccounts: manualAssets.length > 0 || manualDebts.length > 0,\n isPositiveNetWorth: netWorth >= 0,\n isIncreasing: netWorthChange > 0,\n };\n }, [data, isLoading]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\nimport type { NetWorthHistory } from '@pfm-platform/shared';\n\nexport interface NetWorthHistoryPoint extends NetWorthHistory {\n totalAsNumber: number;\n totalAssetAsNumber: number;\n totalDebtAsNumber: number;\n sinceLastMonthAsNumber: number;\n date: Date;\n formattedDate: string;\n isPositive: boolean;\n isGrowing: boolean;\n}\n\n/**\n * Process net worth history for charting and analysis\n *\n * Business logic:\n * - Converts string amounts to numbers for calculations\n * - Parses month/year into Date objects for charting\n * - Formats dates for display\n * - Adds boolean flags for trend indicators\n *\n * @param userId - User ID to fetch net worth history for\n * @returns Array of processed history points sorted by date (oldest first)\n *\n * @example\n * ```tsx\n * function NetWorthChart() {\n * const history = useNetWorthHistory('user123');\n *\n * if (!history) return <div>Loading...</div>;\n *\n * return (\n * <LineChart data={history.map(point => ({\n * date: point.formattedDate,\n * value: point.totalAsNumber\n * }))} />\n * );\n * }\n * ```\n *\n * @example Find Recent Growth\n * ```tsx\n * const history = useNetWorthHistory('user123');\n * const recentGrowth = history?.filter(p => p.isGrowing).slice(-3);\n * ```\n */\nexport function useNetWorthHistory(userId: string): NetWorthHistoryPoint[] | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading || !data.networth_histories) {\n return null;\n }\n\n return data.networth_histories\n .map((history) => {\n const totalAsNumber = parseFloat(history.total);\n const totalAssetAsNumber = parseFloat(history.total_asset);\n const totalDebtAsNumber = parseFloat(history.total_debt);\n const sinceLastMonthAsNumber = parseFloat(history.since_last_month);\n\n // Parse month/year into Date (use first day of month)\n const year = parseInt(history.year, 10);\n const month = parseInt(history.month, 10) - 1; // JS months are 0-indexed\n const date = new Date(year, month, 1);\n\n // Format for display (e.g., \"Apr 2019\")\n const formattedDate = date.toLocaleDateString('en-US', {\n month: 'short',\n year: 'numeric',\n });\n\n return {\n ...history,\n totalAsNumber,\n totalAssetAsNumber,\n totalDebtAsNumber,\n sinceLastMonthAsNumber,\n date,\n formattedDate,\n isPositive: totalAsNumber >= 0,\n isGrowing: sinceLastMonthAsNumber > 0,\n };\n })\n .sort((a, b) => a.date.getTime() - b.date.getTime()); // Sort oldest to newest\n }, [data, isLoading]);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/utils/groupBy.ts","../src/hooks/useAccountTypes.ts","../src/hooks/useAccountFilters.ts","../src/hooks/useAccountSummary.ts","../src/hooks/useNetWorthSummary.ts","../src/hooks/useNetWorthHistory.ts"],"names":["useAccounts","useMemo","useNetWorth"],"mappings":";;;;;;AAIO,IAAM,kBAAA,GAAqB;AAAA,EAChC,QAAA,EAAU,UAAA;AAAA,EACV,OAAA,EAAS,SAAA;AAAA,EACT,KAAA,EAAO,MAAA;AAAA,EACP,aAAA,EAAe,cAAA;AAAA,EACf,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,MAAA;AAAA,EACP,IAAA,EAAM,UAAA;AAAA,EACN,UAAA,EAAY,YAAA;AAAA,EACZ,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,OAAA;AAAA,EACP,EAAA,EAAI;AACN;AAMO,IAAM,qBAAA,GAAwB;AAAA,EACnC;AAAA,IACE,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,CAAC,UAAA,EAAY,SAAA,EAAW,cAAc;AAAA,GAC/C;AAAA,EACA;AAAA,IACE,IAAA,EAAM,cAAA;AAAA,IACN,KAAA,EAAO,CAAC,OAAO;AAAA,GACjB;AAAA,EACA;AAAA,IACE,IAAA,EAAM,OAAA;AAAA,IACN,OAAO,CAAC,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,iBAAiB,YAAY;AAAA,GAChE;AAAA,EACA;AAAA,IACE,IAAA,EAAM,aAAA;AAAA,IACN,KAAA,EAAO,CAAC,YAAY;AAAA,GACtB;AAAA,EACA;AAAA,IACE,IAAA,EAAM,QAAA;AAAA,IACN,KAAA,EAAO,CAAC,OAAA,EAAS,IAAI;AAAA,GACvB;AAAA,EACA;AAAA,IACE,IAAA,EAAM,OAAA;AAAA,IACN,KAAA,EAAO,CAAC,MAAM;AAAA;AAElB;AAKO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF;;;ACrEO,SAAS,OAAA,CAAW,OAAY,GAAA,EAAmC;AACxE,EAAA,OAAO,KAAA,CAAM,MAAA;AAAA,IACX,CAAC,QAAQ,IAAA,KAAS;AAChB,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AACjC,MAAA,IAAI,CAAC,MAAA,CAAO,QAAQ,CAAA,EAAG;AACrB,QAAA,MAAA,CAAO,QAAQ,IAAI,EAAC;AAAA,MACtB;AACA,MAAA,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAC1B,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAC,GACH;AACF;;;ACOO,SAAS,gBAAgB,MAAA,EAAgC;AAC9D,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,WAAA,CAAY,EAAE,QAAQ,CAAA;AAEvC,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAA,IAAI,CAAC,IAAA,EAAM,QAAA,IAAY,IAAA,CAAK,QAAA,CAAS,WAAW,CAAA,EAAG;AACjD,MAAA,OAAO,EAAC;AAAA,IACV;AAGA,IAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,IAAA,CAAK,QAAA,EAAU,sBAAsB,CAAA;AAGlE,IAAA,OAAO,qBAAA,CAAsB,GAAA,CAAI,CAAC,OAAA,MAAa;AAAA,MAC7C,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,QAAA,EAAU,OAAA,CAAQ,KAAA,CAAM,OAAA,CAAQ,CAAC,SAAS,YAAA,CAAa,IAAI,CAAA,IAAK,EAAE;AAAA,KACpE,CAAE,CAAA,CACC,MAAA,CAAO,CAAC,KAAA,KAAU,KAAA,CAAM,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,CAC3C,GAAA,CAAI,CAAC,WAAA,MAAiB;AAAA,MACrB,GAAG,WAAA;AAAA,MACH,GAAA,EAAK,WAAA,CAAY,QAAA,CAAS,MAAA,CAAO,CAAC,GAAA,EAAK,OAAA,KAAY,GAAA,IAAO,OAAA,CAAQ,OAAA,IAAW,CAAA,CAAA,EAAI,CAAC;AAAA,KACpF,CAAE,CAAA;AAAA,EACN,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AACX;AC3BO,SAAS,kBAAkB,MAAA,EAAgC;AAChE,EAAA,MAAM,EAAE,IAAA,EAAK,GAAIA,WAAAA,CAAY,EAAE,QAAQ,CAAA;AAEvC,EAAA,OAAOC,QAAQ,MAAM;AACnB,IAAA,IAAI,CAAC,IAAA,EAAM,QAAA,IAAY,IAAA,CAAK,QAAA,CAAS,WAAW,CAAA,EAAG;AACjD,MAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAG,MAAA,EAAQ,EAAC,EAAE;AAAA,IAClC;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,KAAK,QAAA,CAAS,MAAA;AAAA,QACpB,CAAC,OAAA,KAAY,OAAA,CAAQ,KAAA,KAAU,YAAY,OAAA,CAAQ;AAAA,OACrD;AAAA,MACA,MAAA,EAAQ,KAAK,QAAA,CAAS,MAAA;AAAA,QACpB,CAAC,YACC,OAAA,CAAQ,KAAA,KAAU,YAClB,OAAA,CAAQ,KAAA,KAAU,UAAA,IAClB,CAAC,OAAA,CAAQ;AAAA;AACb,KACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AACX;ACfO,SAAS,kBAAkB,MAAA,EAAuC;AACvE,EAAA,MAAM,EAAE,IAAA,EAAK,GAAID,WAAAA,CAAY,EAAE,QAAQ,CAAA;AACvC,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,kBAAkB,MAAM,CAAA;AAEnD,EAAA,OAAOC,QAAQ,MAAM;AACnB,IAAA,IAAI,CAAC,MAAM,QAAA,EAAU;AACnB,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO;AAAA,MACL,aAAA,EAAe,KAAK,QAAA,CAAS,MAAA;AAAA,MAC7B,aAAa,MAAA,CAAO,MAAA;AAAA,MACpB,aAAa,MAAA,CAAO,MAAA;AAAA,MACpB,YAAA,EAAc,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,CAAC,GAAA,EAAK,OAAA,KAAY,GAAA,IAAO,OAAA,CAAQ,OAAA,IAAW,CAAA,CAAA,EAAI,CAAC,CAAA;AAAA,MACpF,WAAA,EAAa,IAAA,CAAK,QAAA,CAAS,MAAA,GAAS;AAAA,KACtC;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAC,CAAA;AAC3B;ACWO,SAAS,mBAAmB,MAAA,EAAwC;AACzE,EAAA,MAAM,EAAE,IAAA,EAAM,SAAA,KAAc,WAAA,CAAY,EAAE,QAAQ,CAAA;AAElD,EAAA,OAAOA,QAAQ,MAAM;AACnB,IAAA,IAAI,CAAC,QAAQ,SAAA,EAAW;AACtB,MAAA,OAAO,IAAA;AAAA,IACT;AAGA,IAAA,MAAM,QAAA,GAAW,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,SAAS,CAAA;AAC/C,IAAA,MAAM,cAAA,GAAiB,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,gBAAgB,CAAA;AAC5D,IAAA,MAAM,WAAA,GAAc,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,YAAY,CAAA;AACrD,IAAA,MAAM,UAAA,GAAa,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGnD,IAAA,MAAM,eAAe,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,2BAA2B,CAAA;AAC1E,IAAA,MAAM,mBAAmB,IAAA,CAAK,MAAA,CAAO,OAAO,CAAA,CAAA,KAAK,CAAC,EAAE,2BAA2B,CAAA;AAC/E,IAAA,MAAM,cAAc,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,2BAA2B,CAAA;AACxE,IAAA,MAAM,kBAAkB,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,CAAA,KAAK,CAAC,EAAE,2BAA2B,CAAA;AAE7E,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,cAAA;AAAA,MACA,WAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA,EAAY,KAAK,MAAA,CAAO,MAAA;AAAA,MACxB,SAAA,EAAW,KAAK,KAAA,CAAM,MAAA;AAAA,MACtB,kBAAkB,YAAA,CAAa,MAAA;AAAA,MAC/B,iBAAiB,WAAA,CAAY,MAAA;AAAA,MAC7B,sBAAsB,gBAAA,CAAiB,MAAA;AAAA,MACvC,qBAAqB,eAAA,CAAgB,MAAA;AAAA,MACrC,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA;AAAA,MAChC,QAAA,EAAU,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,CAAA;AAAA,MAC9B,iBAAA,EAAmB,YAAA,CAAa,MAAA,GAAS,CAAA,IAAK,YAAY,MAAA,GAAS,CAAA;AAAA,MACnE,oBAAoB,QAAA,IAAY,CAAA;AAAA,MAChC,cAAc,cAAA,GAAiB;AAAA,KACjC;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,SAAS,CAAC,CAAA;AACtB;AC1CO,SAAS,mBAAmB,MAAA,EAA+C;AAChF,EAAA,MAAM,EAAE,IAAA,EAAM,SAAA,KAAcC,WAAAA,CAAY,EAAE,QAAQ,CAAA;AAElD,EAAA,OAAOD,QAAQ,MAAM;AACnB,IAAA,IAAI,CAAC,IAAA,IAAQ,SAAA,IAAa,CAAC,KAAK,kBAAA,EAAoB;AAClD,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,kBAAA,CACT,GAAA,CAAI,CAAC,OAAA,KAAY;AAChB,MAAA,MAAM,aAAA,GAAgB,UAAA,CAAW,OAAA,CAAQ,KAAK,CAAA;AAC9C,MAAA,MAAM,kBAAA,GAAqB,UAAA,CAAW,OAAA,CAAQ,WAAW,CAAA;AACzD,MAAA,MAAM,iBAAA,GAAoB,UAAA,CAAW,OAAA,CAAQ,UAAU,CAAA;AACvD,MAAA,MAAM,sBAAA,GAAyB,UAAA,CAAW,OAAA,CAAQ,gBAAgB,CAAA;AAGlE,MAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AACtC,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,GAAI,CAAA;AAC5C,MAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,IAAA,EAAM,OAAO,CAAC,CAAA;AAGpC,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,kBAAA,CAAmB,OAAA,EAAS;AAAA,QACrD,KAAA,EAAO,OAAA;AAAA,QACP,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,OAAO;AAAA,QACL,GAAG,OAAA;AAAA,QACH,aAAA;AAAA,QACA,kBAAA;AAAA,QACA,iBAAA;AAAA,QACA,sBAAA;AAAA,QACA,IAAA;AAAA,QACA,aAAA;AAAA,QACA,YAAY,aAAA,IAAiB,CAAA;AAAA,QAC7B,WAAW,sBAAA,GAAyB;AAAA,OACtC;AAAA,IACF,CAAC,CAAA,CACA,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,IAAA,CAAK,OAAA,EAAQ,GAAI,CAAA,CAAE,IAAA,CAAK,SAAS,CAAA;AAAA,EACvD,CAAA,EAAG,CAAC,IAAA,EAAM,SAAS,CAAC,CAAA;AACtB","file":"index.js","sourcesContent":["/**\n * Account type display names\n * Maps account_type to human-readable names\n */\nexport const ACCOUNT_TYPE_NAMES = {\n checking: 'Checking',\n savings: 'Savings',\n cards: 'Card',\n student_loans: 'Student Loan',\n bill: 'Bill',\n autos: 'Auto',\n home: 'Mortgage',\n investment: 'Investment',\n loan: 'Loan',\n asset: 'Asset',\n cd: 'Certificate',\n} as const;\n\n/**\n * Grouped account types for categorization\n * Used by useAccountTypes to organize accounts into logical groups\n */\nexport const GROUPED_ACCOUNT_TYPES = [\n {\n name: 'Cash',\n types: ['checking', 'savings', 'money_market'],\n },\n {\n name: 'Credit Cards',\n types: ['cards'],\n },\n {\n name: 'Debts',\n types: ['autos', 'home', 'loan', 'student_loans', 'creditline'],\n },\n {\n name: 'Investments',\n types: ['investment'],\n },\n {\n name: 'Assets',\n types: ['asset', 'cd'],\n },\n {\n name: 'Bills',\n types: ['bill'],\n },\n] as const;\n\n/**\n * User error codes that indicate credentials need updating\n */\nexport const USER_ERROR_CODES = [\n '201',\n '203',\n '204',\n '209',\n '300',\n '301',\n '302',\n '303',\n '304',\n '305',\n '306',\n '307',\n '701',\n '103',\n '108',\n '109',\n '185',\n '187',\n '913',\n '914',\n '931',\n '936',\n] as const;\n","/**\n * Group array items by a key\n * @param array - Array to group\n * @param key - Property key to group by\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {\n return array.reduce(\n (result, item) => {\n const groupKey = String(item[key]);\n if (!result[groupKey]) {\n result[groupKey] = [];\n }\n result[groupKey].push(item);\n return result;\n },\n {} as Record<string, T[]>\n );\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { GROUPED_ACCOUNT_TYPES } from '../constants';\nimport { groupBy } from '../utils/groupBy';\n\nexport interface AccountGroup {\n name: string;\n accounts: any[]; // Using any for now, will use Account type when available\n sum: number;\n}\n\n/**\n * Group accounts by display type and calculate totals\n *\n * Replaces: accountsStore.accountTypes computed property\n *\n * Business logic:\n * - Groups accounts by display_account_type\n * - Organizes into logical categories (Cash, Credit Cards, Debts, etc.)\n * - Calculates sum of balances for each group\n * - Filters out empty groups\n *\n * @param userId - User ID to fetch accounts for\n * @returns Array of account groups with names, accounts, and sums\n */\nexport function useAccountTypes(userId: string): AccountGroup[] {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return [];\n }\n\n // Group accounts by display_account_type\n const mappedByType = groupBy(data.accounts, 'display_account_type');\n\n // Organize into predefined groups and calculate sums\n return GROUPED_ACCOUNT_TYPES.map((grouped) => ({\n name: grouped.name,\n accounts: grouped.types.flatMap((type) => mappedByType[type] || []),\n }))\n .filter((group) => group.accounts.length > 0)\n .map((accountType) => ({\n ...accountType,\n sum: accountType.accounts.reduce((sum, account) => sum + (account.balance || 0), 0),\n }));\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\n\nexport interface AccountFilters {\n opened: any[]; // Using any for now, will use Account type when available\n closed: any[];\n}\n\n/**\n * Filter accounts into opened/closed categories\n *\n * Replaces: openedAccounts/closedAccounts computed properties\n *\n * Business logic:\n * - Opened: state === 'active' AND include_in_dashboard === true\n * - Closed: state === 'closed' OR state === 'archived' OR include_in_dashboard === false\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with opened and closed account arrays\n */\nexport function useAccountFilters(userId: string): AccountFilters {\n const { data } = useAccounts({ userId });\n\n return useMemo(() => {\n if (!data?.accounts || data.accounts.length === 0) {\n return { opened: [], closed: [] };\n }\n\n return {\n opened: data.accounts.filter(\n (account) => account.state === 'active' && account.include_in_dashboard\n ),\n closed: data.accounts.filter(\n (account) =>\n account.state === 'closed' ||\n account.state === 'archived' ||\n !account.include_in_dashboard\n ),\n };\n }, [data]);\n}\n","import { useMemo } from 'react';\nimport { useAccounts } from '@pfm-platform/accounts-data-access';\nimport { useAccountFilters } from './useAccountFilters';\n\nexport interface AccountSummary {\n totalAccounts: number;\n openedCount: number;\n closedCount: number;\n totalBalance: number;\n hasAccounts: boolean;\n}\n\n/**\n * Calculate account summary statistics\n *\n * Replaces: hasAccounts computed property + additional summary logic\n *\n * Business logic:\n * - Counts total, opened, and closed accounts\n * - Calculates total balance across all accounts\n * - Provides hasAccounts boolean for conditional rendering\n *\n * @param userId - User ID to fetch accounts for\n * @returns Object with account counts, balance, and hasAccounts flag\n */\nexport function useAccountSummary(userId: string): AccountSummary | null {\n const { data } = useAccounts({ userId });\n const { opened, closed } = useAccountFilters(userId);\n\n return useMemo(() => {\n if (!data?.accounts) {\n return null;\n }\n\n return {\n totalAccounts: data.accounts.length,\n openedCount: opened.length,\n closedCount: closed.length,\n totalBalance: data.accounts.reduce((sum, account) => sum + (account.balance || 0), 0),\n hasAccounts: data.accounts.length > 0,\n };\n }, [data, opened, closed]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\n\nexport interface NetWorthSummary {\n netWorth: number;\n netWorthChange: number;\n totalAssets: number;\n totalDebts: number;\n assetCount: number;\n debtCount: number;\n manualAssetCount: number;\n manualDebtCount: number;\n aggregatedAssetCount: number;\n aggregatedDebtCount: number;\n hasAssets: boolean;\n hasDebts: boolean;\n hasManualAccounts: boolean;\n isPositiveNetWorth: boolean;\n isIncreasing: boolean;\n}\n\n/**\n * Calculate net worth summary statistics\n *\n * Business logic:\n * - Parses string amounts to numbers for calculations\n * - Separates manual vs aggregated accounts\n * - Provides counts and totals for assets/debts\n * - Calculates trend indicators (positive, increasing)\n *\n * @param userId - User ID to fetch net worth for\n * @returns Object with net worth metrics and boolean flags\n *\n * @example\n * ```tsx\n * function NetWorthDashboard() {\n * const summary = useNetWorthSummary('user123');\n *\n * if (!summary) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <h2>Net Worth: ${summary.netWorth.toLocaleString()}</h2>\n * <p className={summary.isIncreasing ? 'positive' : 'negative'}>\n * Change: ${summary.netWorthChange.toLocaleString()}\n * </p>\n * <p>Assets: {summary.assetCount} (Manual: {summary.manualAssetCount})</p>\n * <p>Debts: {summary.debtCount} (Manual: {summary.manualDebtCount})</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useNetWorthSummary(userId: string): NetWorthSummary | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading) {\n return null;\n }\n\n // Parse string amounts to numbers\n const netWorth = parseFloat(data.meta.net_worth);\n const netWorthChange = parseFloat(data.meta.net_worth_change);\n const totalAssets = parseFloat(data.meta.total_assets);\n const totalDebts = parseFloat(data.meta.total_debts);\n\n // Count manual vs aggregated accounts\n const manualAssets = data.assets.filter(a => a.additional_networth_account);\n const aggregatedAssets = data.assets.filter(a => !a.additional_networth_account);\n const manualDebts = data.debts.filter(d => d.additional_networth_account);\n const aggregatedDebts = data.debts.filter(d => !d.additional_networth_account);\n\n return {\n netWorth,\n netWorthChange,\n totalAssets,\n totalDebts,\n assetCount: data.assets.length,\n debtCount: data.debts.length,\n manualAssetCount: manualAssets.length,\n manualDebtCount: manualDebts.length,\n aggregatedAssetCount: aggregatedAssets.length,\n aggregatedDebtCount: aggregatedDebts.length,\n hasAssets: data.assets.length > 0,\n hasDebts: data.debts.length > 0,\n hasManualAccounts: manualAssets.length > 0 || manualDebts.length > 0,\n isPositiveNetWorth: netWorth >= 0,\n isIncreasing: netWorthChange > 0,\n };\n }, [data, isLoading]);\n}\n","import { useMemo } from 'react';\nimport { useNetWorth } from '@pfm-platform/accounts-data-access';\nimport type { NetWorthHistory } from '@pfm-platform/shared';\n\nexport interface NetWorthHistoryPoint extends NetWorthHistory {\n totalAsNumber: number;\n totalAssetAsNumber: number;\n totalDebtAsNumber: number;\n sinceLastMonthAsNumber: number;\n date: Date;\n formattedDate: string;\n isPositive: boolean;\n isGrowing: boolean;\n}\n\n/**\n * Process net worth history for charting and analysis\n *\n * Business logic:\n * - Converts string amounts to numbers for calculations\n * - Parses month/year into Date objects for charting\n * - Formats dates for display\n * - Adds boolean flags for trend indicators\n *\n * @param userId - User ID to fetch net worth history for\n * @returns Array of processed history points sorted by date (oldest first)\n *\n * @example\n * ```tsx\n * function NetWorthChart() {\n * const history = useNetWorthHistory('user123');\n *\n * if (!history) return <div>Loading...</div>;\n *\n * return (\n * <LineChart data={history.map(point => ({\n * date: point.formattedDate,\n * value: point.totalAsNumber\n * }))} />\n * );\n * }\n * ```\n *\n * @example Find Recent Growth\n * ```tsx\n * const history = useNetWorthHistory('user123');\n * const recentGrowth = history?.filter(p => p.isGrowing).slice(-3);\n * ```\n */\nexport function useNetWorthHistory(userId: string): NetWorthHistoryPoint[] | null {\n const { data, isLoading } = useNetWorth({ userId });\n\n return useMemo(() => {\n if (!data || isLoading || !data.networth_histories) {\n return null;\n }\n\n return data.networth_histories\n .map((history) => {\n const totalAsNumber = parseFloat(history.total);\n const totalAssetAsNumber = parseFloat(history.total_asset);\n const totalDebtAsNumber = parseFloat(history.total_debt);\n const sinceLastMonthAsNumber = parseFloat(history.since_last_month);\n\n // Parse month/year into Date (use first day of month)\n const year = parseInt(history.year, 10);\n const month = parseInt(history.month, 10) - 1; // JS months are 0-indexed\n const date = new Date(year, month, 1);\n\n // Format for display (e.g., \"Apr 2019\")\n const formattedDate = date.toLocaleDateString('en-US', {\n month: 'short',\n year: 'numeric',\n });\n\n return {\n ...history,\n totalAsNumber,\n totalAssetAsNumber,\n totalDebtAsNumber,\n sinceLastMonthAsNumber,\n date,\n formattedDate,\n isPositive: totalAsNumber >= 0,\n isGrowing: sinceLastMonthAsNumber > 0,\n };\n })\n .sort((a, b) => a.date.getTime() - b.date.getTime()); // Sort oldest to newest\n }, [data, isLoading]);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pfm-platform/accounts-feature",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"react": "19.2.
|
|
8
|
-
"@pfm-platform/accounts-data-access": "0.2.
|
|
9
|
-
"@pfm-platform/shared": "0.1
|
|
7
|
+
"react": "19.2.4",
|
|
8
|
+
"@pfm-platform/accounts-data-access": "0.2.1",
|
|
9
|
+
"@pfm-platform/shared": "0.2.1"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@tanstack/react-query": "5.90.
|
|
13
|
-
"@testing-library/react": "^16.3.
|
|
14
|
-
"@types/react": "^19.2.
|
|
15
|
-
"@vitejs/plugin-react": "^5.1.
|
|
16
|
-
"@vitest/coverage-v8": "^4.0.
|
|
12
|
+
"@tanstack/react-query": "5.90.21",
|
|
13
|
+
"@testing-library/react": "^16.3.2",
|
|
14
|
+
"@types/react": "^19.2.14",
|
|
15
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
16
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
17
17
|
"jsdom": "^27.2.0",
|
|
18
|
-
"react-dom": "19.2.
|
|
18
|
+
"react-dom": "19.2.4",
|
|
19
19
|
"typescript": "5.9.3",
|
|
20
|
-
"vitest": "4.0.
|
|
20
|
+
"vitest": "4.0.18"
|
|
21
21
|
},
|
|
22
22
|
"module": "./dist/index.js",
|
|
23
23
|
"types": "./dist/index.d.ts",
|