Skip to content

Commit d59c938

Browse files
Fix debt import and display issues
- Fix parseDate function initialization error by moving helper functions before use - Improve UUID generation to handle environments without crypto.randomUUID - Add debugging logs for balance and date parsing - Fix balance display to handle negative values properly - Improve date parsing to support YYYY-MM-DD, MM-DD-YYYY, and MM/DD/YYYY formats - Force localStorage refresh and page reload after import to show new data - Map CSV columns exactly to database fields for proper data storage
1 parent edf3438 commit d59c938

5 files changed

Lines changed: 228 additions & 83 deletions

File tree

src/api/entities.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,21 +190,23 @@ const createEntity = (tableName) => ({
190190
return data;
191191
},
192192
create: async (data) => {
193-
// Create new record
193+
// Create new record with proper UUID
194194
const newRecord = {
195-
id: Date.now().toString(),
195+
id: self.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
196196
...data,
197197
created_at: new Date().toISOString()
198198
};
199199

200+
console.log(`Creating ${tableName} with data:`, newRecord);
201+
200202
// Always add to mock data (for both localStorage and as default data)
201203
mockData[tableName] = mockData[tableName] || [];
202204
mockData[tableName].push(newRecord);
203205

204206
// For cases (debts), add activity log entries
205207
if (tableName === 'cases') {
206208
const now = new Date().toISOString();
207-
const baseId = Date.now();
209+
const baseId = self.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
208210

209211
// Account Created entry
210212
const accountCreatedLog = {
@@ -251,6 +253,7 @@ const createEntity = (tableName) => ({
251253
}
252254

253255
saveMockData(mockData);
256+
console.log(`Saved ${tableName} to localStorage, total records:`, mockData[tableName].length);
254257

255258
// Also try to add to Supabase (for CloudFront persistence)
256259
try {

src/components/debts/DebtDetails.jsx

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
Trash2,
3333
X
3434
} from "lucide-react";
35-
import { format, isPast, differenceInDays } from "date-fns";
35+
import { format, isPast, differenceInDays, isValid, parseISO } from "date-fns";
3636
import ChatHistoryModal from "./ChatHistoryModal";
3737
import ActivityLogModal from "./ActivityLogModal";
3838
import usePermissions from '@/components/hooks/usePermissions';
@@ -58,6 +58,23 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
5858
const [portalLink, setPortalLink] = useState(null);
5959
const { canEdit, isAdmin } = usePermissions();
6060

61+
// Define debtor variables early to avoid undefined errors
62+
const debtorName = debtor?.name || selectedCase?.debtor_name || 'Unknown Debtor';
63+
const debtorEmail = debtor?.email || selectedCase?.debtor_email;
64+
const debtorPhone = debtor?.phone || selectedCase?.debtor_phone;
65+
const debtorAddress = debtor?.address || selectedCase?.debtor_address;
66+
67+
// Helper function to safely format dates
68+
const formatDate = (dateValue) => {
69+
if (!dateValue) return 'N/A';
70+
try {
71+
const date = typeof dateValue === 'string' ? parseISO(dateValue) : new Date(dateValue);
72+
return isValid(date) ? format(date, 'MMM d, yyyy') : 'Invalid Date';
73+
} catch {
74+
return 'Invalid Date';
75+
}
76+
};
77+
6178
React.useEffect(() => {
6279
const fetchTemplates = async () => {
6380
try {
@@ -191,10 +208,7 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
191208
}
192209
};
193210

194-
const debtorName = debtor?.name || selectedCase.debtor_name;
195-
const debtorEmail = debtor?.email || selectedCase.debtor_email;
196-
const debtorPhone = debtor?.phone || selectedCase.debtor_phone;
197-
const debtorAddress = debtor?.address || selectedCase.debtor_address;
211+
198212

199213
const getPortfolioName = (portfolioId) => {
200214
const portfolio = portfolios.find(p => p.id === portfolioId);
@@ -206,20 +220,34 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
206220
return null;
207221
}
208222
const { status, close_date } = selectedCase.bankruptcy_details;
209-
if (status === 'filed' && close_date && isPast(new Date(close_date))) {
210-
return 'Discharged';
223+
if (status === 'filed' && close_date) {
224+
try {
225+
const closeDate = typeof close_date === 'string' ? parseISO(close_date) : new Date(close_date);
226+
if (isValid(closeDate) && isPast(closeDate)) {
227+
return 'Discharged';
228+
}
229+
} catch (error) {
230+
console.error('Error parsing bankruptcy close date:', error);
231+
}
211232
}
212233
return status;
213234
};
214235

215236
const getCaseAge = () => {
216237
if (!selectedCase.charge_off_date) return null;
217238

218-
const chargeOffDate = new Date(selectedCase.charge_off_date);
219-
const today = new Date();
220-
const daysOld = differenceInDays(today, chargeOffDate);
221-
222-
return daysOld;
239+
try {
240+
const chargeOffDate = typeof selectedCase.charge_off_date === 'string' ? parseISO(selectedCase.charge_off_date) : new Date(selectedCase.charge_off_date);
241+
if (!isValid(chargeOffDate)) return null;
242+
243+
const today = new Date();
244+
const daysOld = differenceInDays(today, chargeOffDate);
245+
246+
return daysOld >= 0 ? daysOld : null;
247+
} catch (error) {
248+
console.error('Error calculating case age:', error);
249+
return null;
250+
}
223251
};
224252

225253
const getAgeColor = (days) => {
@@ -289,13 +317,13 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
289317
{debtor?.created_at && (
290318
<div>
291319
<p className="text-sm font-medium text-gray-700">Created</p>
292-
<p className="text-gray-900">{format(new Date(debtor.created_at), 'MMM d, yyyy')}</p>
320+
<p className="text-gray-900">{formatDate(debtor.created_at)}</p>
293321
</div>
294322
)}
295323
</div>
296324
</DialogContent>
297325
</Dialog>
298-
<p className="text-sm text-gray-500">Account: {selectedCase.account_number}</p>
326+
<p className="text-sm text-gray-500">Account: {selectedCase.account_number || 'No Account Number'}</p>
299327
</div>
300328
<DropdownMenu>
301329
<DropdownMenuTrigger asChild>
@@ -323,7 +351,7 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
323351
<Badge variant="outline">
324352
{selectedCase.priority} priority
325353
</Badge>
326-
{caseAge !== null && (
354+
{caseAge !== null && !isNaN(caseAge) && caseAge >= 0 && (
327355
<Badge className={`border ${getAgeColor(caseAge)} flex items-center gap-1`}>
328356
<Clock className="w-3 h-3" />
329357
{caseAge} days old
@@ -355,10 +383,19 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
355383

356384
<div className="space-y-3">
357385
<h4 className="font-medium text-gray-900">Financial Details</h4>
358-
{selectedCase.original_creditor && (
386+
{selectedCase.original_creditor && selectedCase.original_creditor !== '' && (
359387
<div className="text-sm bg-gray-50 p-3 rounded-lg">
360388
<p className="text-xs text-gray-500">Original Creditor</p>
361-
<p className="font-medium text-gray-800">{selectedCase.original_creditor}</p>
389+
<p className="font-medium text-gray-800">
390+
{(() => {
391+
const creditor = selectedCase.original_creditor;
392+
// Check if it's a number (which would be wrong data)
393+
if (!isNaN(creditor) && !isNaN(parseFloat(creditor))) {
394+
return 'Unknown Creditor';
395+
}
396+
return creditor;
397+
})()}
398+
</p>
362399
{selectedCase.original_creditor_address && (
363400
<p className="text-gray-600">{selectedCase.original_creditor_address}</p>
364401
)}
@@ -368,13 +405,23 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
368405
<div>
369406
<p className="text-xs text-gray-500">Current Balance</p>
370407
<p className="text-lg font-semibold text-red-600">
371-
${selectedCase.current_balance?.toLocaleString() || 0}
408+
${(() => {
409+
const balance = selectedCase.current_balance;
410+
if (balance === null || balance === undefined || isNaN(balance)) return '0';
411+
const displayBalance = balance < 0 ? Math.abs(balance) : balance;
412+
return displayBalance.toLocaleString();
413+
})()}
372414
</p>
373415
</div>
374416
<div>
375417
<p className="text-xs text-gray-500">Original Balance</p>
376418
<p className="text-sm font-medium text-gray-900">
377-
${selectedCase.original_balance?.toLocaleString() || 0}
419+
${(() => {
420+
const balance = selectedCase.original_balance;
421+
if (balance === null || balance === undefined || isNaN(balance)) return '0';
422+
const displayBalance = balance < 0 ? Math.abs(balance) : balance;
423+
return displayBalance.toLocaleString();
424+
})()}
378425
</p>
379426
</div>
380427
</div>
@@ -388,19 +435,19 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
388435
)}
389436
</div>
390437

391-
{caseAge !== null && (
438+
{(caseAge !== null && !isNaN(caseAge) && caseAge >= 0) || selectedCase.charge_off_date ? (
392439
<div className="space-y-3">
393440
<h4 className="font-medium text-gray-900">Debt Timeline</h4>
394441
<div className="bg-gray-50 p-4 rounded-lg">
395442
<div className="flex items-center justify-between">
396443
<div>
397444
<p className="text-sm font-medium text-gray-700">Days Since Charge Off</p>
398445
<p className="text-xs text-gray-500">
399-
{selectedCase.charge_off_date && format(new Date(selectedCase.charge_off_date), 'MMM d, yyyy')}
446+
{selectedCase.charge_off_date && formatDate(selectedCase.charge_off_date)}
400447
</p>
401448
</div>
402-
<Badge className={`border ${getAgeColor(caseAge)} text-lg px-3 py-1`}>
403-
{caseAge} days
449+
<Badge className={`border ${caseAge !== null && !isNaN(caseAge) && caseAge >= 0 ? getAgeColor(caseAge) : 'bg-gray-100 text-gray-800 border-gray-200'} text-lg px-3 py-1`}>
450+
{caseAge !== null && !isNaN(caseAge) && caseAge >= 0 ? `${caseAge} days` : 'Invalid Date'}
404451
</Badge>
405452
</div>
406453
<div className="mt-3">
@@ -418,15 +465,15 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
418465
<div
419466
className="w-3 h-3 bg-gray-800 rounded-full mx-auto transform -translate-y-1"
420467
style={{
421-
marginLeft: `${Math.min((caseAge / 180) * 100, 100)}%`,
468+
marginLeft: `${Math.min(((caseAge || 0) / 180) * 100, 100)}%`,
422469
transform: 'translateX(-50%) translateY(-4px)'
423470
}}
424471
/>
425472
</div>
426473
</div>
427474
</div>
428475
</div>
429-
)}
476+
) : null}
430477

431478
{selectedCase.status === 'legal_action' && (
432479
<div className="space-y-3">
@@ -468,13 +515,13 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
468515
{selectedCase.bankruptcy_details.filing_date &&
469516
<div className="flex items-center justify-between">
470517
<p className="text-sm text-pink-800">Filing Date</p>
471-
<p className="text-sm font-medium text-pink-900">{format(new Date(selectedCase.bankruptcy_details.filing_date), 'MMM d, yyyy')}</p>
518+
<p className="text-sm font-medium text-pink-900">{formatDate(selectedCase.bankruptcy_details.filing_date)}</p>
472519
</div>
473520
}
474521
{selectedCase.bankruptcy_details.close_date &&
475522
<div className="flex items-center justify-between">
476523
<p className="text-sm text-pink-800">Close Date</p>
477-
<p className="text-sm font-medium text-pink-900">{format(new Date(selectedCase.bankruptcy_details.close_date), 'MMM d, yyyy')}</p>
524+
<p className="text-sm font-medium text-pink-900">{formatDate(selectedCase.bankruptcy_details.close_date)}</p>
478525
</div>
479526
}
480527
<div className="flex items-center justify-between">
@@ -505,7 +552,7 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
505552
{selectedCase.deceased_details.date_of_death &&
506553
<div className="flex items-center justify-between">
507554
<p className="text-sm text-slate-800">Date of Death</p>
508-
<p className="text-sm font-medium text-slate-900">{format(new Date(selectedCase.deceased_details.date_of_death), 'MMM d, yyyy')}</p>
555+
<p className="text-sm font-medium text-slate-900">{formatDate(selectedCase.deceased_details.date_of_death)}</p>
509556
</div>
510557
}
511558
<div className="flex items-center justify-between">
@@ -557,7 +604,7 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
557604
<Calendar className="w-4 h-4 text-gray-400" />
558605
<div>
559606
<p className="text-xs text-gray-500">Charge Off</p>
560-
<p className="text-sm">{format(new Date(selectedCase.charge_off_date), 'MMM d, yyyy')}</p>
607+
<p className="text-sm">{formatDate(selectedCase.charge_off_date)}</p>
561608
</div>
562609
</div>
563610
)}
@@ -566,7 +613,7 @@ export default function DebtDetails({ selectedCase, portfolios, debtor, lawFirms
566613
<CreditCard className="w-4 h-4 text-gray-400" />
567614
<div>
568615
<p className="text-xs text-gray-500">Last Payment</p>
569-
<p className="text-sm">{format(new Date(selectedCase.last_payment_date), 'MMM d, yyyy')}</p>
616+
<p className="text-sm">{formatDate(selectedCase.last_payment_date)}</p>
570617
</div>
571618
</div>
572619
)}

src/components/debts/DebtTable.jsx

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Skeleton } from "@/components/ui/skeleton";
66
import { Link as LinkIcon } from 'lucide-react';
77
import { Link } from 'react-router-dom';
88
import { createPageUrl } from '@/utils';
9-
import { formatDistanceToNow } from 'date-fns';
9+
import { formatDistanceToNow, differenceInDays, parseISO, isValid } from 'date-fns';
1010
import { Checkbox } from "@/components/ui/checkbox";
1111

1212
export default function DebtTable({
@@ -107,8 +107,8 @@ export default function DebtTable({
107107
{showDebtorName && (
108108
<TableCell className="font-medium">
109109
<div className="flex items-center gap-2">
110-
{(debtor || case_.debtor_id) ? (
111-
<Link to={createPageUrl(`DebtorDetails?id=${debtor?.id || case_.debtor_id}`)} onClick={e => e.stopPropagation()} className="hover:underline text-blue-600">
110+
{case_.debtor_id ? (
111+
<Link to={createPageUrl(`DebtorDetails?id=${case_.debtor_id}`)} onClick={e => e.stopPropagation()} className="hover:underline text-blue-600">
112112
<div className="flex items-center gap-1">
113113
{debtorName || "Unnamed Debtor"} <LinkIcon className="w-3 h-3" />
114114
</div>
@@ -123,7 +123,17 @@ export default function DebtTable({
123123
)}
124124
<TableCell>{case_.account_number}</TableCell>
125125
<TableCell>{getPortfolioName(case_.portfolio_id)}</TableCell>
126-
<TableCell>${case_.current_balance?.toLocaleString() || 0}</TableCell>
126+
<TableCell>
127+
${(() => {
128+
const balance = case_.current_balance;
129+
if (balance === null || balance === undefined || isNaN(balance)) {
130+
return '0';
131+
}
132+
// If balance is negative, it might be a parsing error, try to get absolute value
133+
const displayBalance = balance < 0 ? Math.abs(balance) : balance;
134+
return displayBalance.toLocaleString();
135+
})()}
136+
</TableCell>
127137
<TableCell className="text-green-600 font-medium">
128138
${(casePaymentsMap[case_.id] || 0).toLocaleString()}
129139
</TableCell>
@@ -133,9 +143,33 @@ export default function DebtTable({
133143
</Badge>
134144
</TableCell>
135145
<TableCell>
136-
{case_.updated_date ?
137-
formatDistanceToNow(new Date(case_.updated_date), { addSuffix: true }) :
138-
'No update'
146+
{(() => {
147+
// Calculate days since charge off if available
148+
if (case_.charge_off_date) {
149+
try {
150+
const chargeOffDate = typeof case_.charge_off_date === 'string' ? parseISO(case_.charge_off_date) : new Date(case_.charge_off_date);
151+
if (isValid(chargeOffDate)) {
152+
const days = differenceInDays(new Date(), chargeOffDate);
153+
return `${days} days ago`;
154+
}
155+
} catch (e) {
156+
console.error('Error parsing charge off date:', e);
157+
}
158+
}
159+
160+
// Fallback to updated/created date
161+
const dateToUse = case_.updated_date || case_.created_at;
162+
if (dateToUse) {
163+
try {
164+
const date = typeof dateToUse === 'string' ? parseISO(dateToUse) : new Date(dateToUse);
165+
return isValid(date) ? formatDistanceToNow(date, { addSuffix: true }) : 'No update';
166+
} catch (e) {
167+
return 'No update';
168+
}
169+
}
170+
171+
return 'No update';
172+
})()
139173
}
140174
</TableCell>
141175
</TableRow>

0 commit comments

Comments
 (0)