Skip to content

Commit 50656e1

Browse files
feat: Add authentication system and UI improvements
- Add login/signup functionality with localStorage - Implement collapsible sidebar menu - Add Communication & Tools section to Debtor Profile - Add activity logs with Account Created & DVN sent entries - Update Reports page to use real payment data - Add campaign creation functionality - Add debtor names to Communications table - All sensitive data uses mock/demo values only
1 parent cd7b6d1 commit 50656e1

File tree

12 files changed

+879
-154
lines changed

12 files changed

+879
-154
lines changed

src/App.jsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,48 @@
11
import './App.css'
22
import Pages from "@/pages/index.jsx"
3+
import Login from "@/pages/Login.jsx"
34
import { Toaster } from "@/components/ui/toaster"
5+
import { Toaster as SonnerToaster } from 'sonner'
6+
import { Loader2 } from 'lucide-react'
7+
import { useState, useEffect } from 'react'
48

59
function App() {
10+
const [isLoggedIn, setIsLoggedIn] = useState(false);
11+
const [isLoading, setIsLoading] = useState(true);
12+
13+
const checkAuthStatus = () => {
14+
const currentUser = localStorage.getItem('currentUser');
15+
setIsLoggedIn(!!currentUser);
16+
setIsLoading(false);
17+
};
18+
19+
useEffect(() => {
20+
checkAuthStatus();
21+
}, []);
22+
23+
const handleLogin = () => {
24+
checkAuthStatus();
25+
};
26+
27+
if (isLoading) {
28+
return (
29+
<div className="min-h-screen flex items-center justify-center">
30+
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
31+
</div>
32+
);
33+
}
34+
635
return (
736
<>
8-
<Pages />
37+
{isLoggedIn ? (
38+
<Pages />
39+
) : (
40+
<Login onLogin={handleLogin} />
41+
)}
942
<Toaster />
43+
<SonnerToaster richColors />
1044
</>
1145
)
1246
}
1347

14-
export default App
48+
export default App

src/api/entities.js

Lines changed: 185 additions & 29 deletions
Large diffs are not rendered by default.

src/components/debts/ActivityLogModal.jsx

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
1313
import { Badge } from "@/components/ui/badge";
1414
import { Communication } from '@/api/entities';
1515
import { Payment } from '@/api/entities';
16+
import { ActivityLog } from '@/api/entities';
1617
import {
1718
Loader2,
1819
MessageSquare,
@@ -88,18 +89,15 @@ export default function ActivityLogModal({ isOpen, onClose, caseId, debtorName }
8889
const fetchActivityLog = async () => {
8990
setIsLoading(true);
9091
try {
91-
let [communications, payments] = await Promise.all([
92+
let [communications, payments, activityLogs] = await Promise.all([
9293
Communication.filter({ case_id: caseId }, '-created_date'),
93-
Payment.filter({ case_id: caseId }, '-payment_date')
94+
Payment.filter({ case_id: caseId }, '-payment_date'),
95+
ActivityLog.filter({ case_id: caseId }, '-activity_date')
9496
]);
9597

96-
// Generate fake data if real data is empty
97-
if (communications.length === 0) {
98-
communications = generateFakeCommunications(caseId, debtorName);
99-
}
100-
if (payments.length === 0) {
101-
payments = generateFakePayments(caseId);
102-
}
98+
// Debug: Log the fetched data
99+
console.log('Fetched activity logs:', activityLogs);
100+
console.log('Case ID:', caseId);
103101

104102
// Combine all activities with unified structure
105103
const allActivities = [
@@ -136,28 +134,24 @@ export default function ActivityLogModal({ isOpen, onClose, caseId, debtorName }
136134
},
137135
icon: DollarSign,
138136
color: 'green'
139-
}))
140-
];
141-
142-
// Add mock status change events (in a real app, these would come from an audit log)
143-
const statusChanges = [
144-
{
145-
id: 'status_1',
146-
type: 'status_change',
147-
timestamp: subDays(new Date(), 5).toISOString(),
148-
title: 'Status Updated',
149-
description: 'Status changed to In Collection',
137+
})),
138+
...activityLogs.map(log => ({
139+
id: `activity_${log.id}`,
140+
type: 'activity',
141+
subType: log.activity_type,
142+
timestamp: log.activity_date,
143+
title: log.description,
144+
description: log.metadata ? JSON.parse(log.metadata).event || '' : '',
150145
details: {
151-
fromStatus: 'new',
152-
toStatus: 'in_collection',
153-
changedBy: 'system'
146+
performedBy: log.performed_by,
147+
metadata: log.metadata
154148
},
155-
icon: UserCheck,
156-
color: 'purple'
157-
}
149+
icon: log.activity_type === 'debt_validation_sent' ? Send : UserCheck,
150+
color: 'orange'
151+
}))
158152
];
159153

160-
allActivities.push(...statusChanges);
154+
console.log('All activities:', allActivities);
161155
setActivities(allActivities);
162156
} catch (error) {
163157
console.error("Error fetching activity log:", error);
@@ -250,7 +244,7 @@ export default function ActivityLogModal({ isOpen, onClose, caseId, debtorName }
250244
<SelectItem value="all">All Events</SelectItem>
251245
<SelectItem value="communication">Communications</SelectItem>
252246
<SelectItem value="payment">Payments</SelectItem>
253-
<SelectItem value="status_change">Status Changes</SelectItem>
247+
<SelectItem value="activity">Activity Log</SelectItem>
254248
</SelectContent>
255249
</Select>
256250
<Button

src/pages/Campaigns.jsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
22
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
33
import { Button } from '@/components/ui/button';
44
import { Badge } from '@/components/ui/badge';
5-
import { getCCAiCampaigns } from '@/api/functions';
5+
import { Campaign } from '@/api/entities';
66
import { Plus, Send, Eye, BarChart2 } from 'lucide-react';
77
import { Link } from 'react-router-dom';
88
import { createPageUrl } from '@/utils';
@@ -15,7 +15,7 @@ export default function Campaigns() {
1515
useEffect(() => {
1616
const fetchCampaigns = async () => {
1717
try {
18-
const { data } = await getCCAiCampaigns();
18+
const data = await Campaign.list();
1919
setCampaigns(data || []);
2020
} catch (error) {
2121
console.error("Error fetching campaigns:", error);
@@ -29,6 +29,7 @@ export default function Campaigns() {
2929
const getStatusColor = (status) => {
3030
const colors = {
3131
draft: "bg-gray-100 text-gray-800",
32+
active: "bg-green-100 text-green-800",
3233
sent: "bg-green-100 text-green-800",
3334
sending: "bg-blue-100 text-blue-800",
3435
archived: "bg-yellow-100 text-yellow-800",
@@ -67,14 +68,24 @@ export default function Campaigns() {
6768
<Card key={campaign.id} className="flex flex-col">
6869
<CardHeader>
6970
<div className="flex justify-between items-start">
70-
<CardTitle className="text-lg">{campaign.title}</CardTitle>
71+
<CardTitle className="text-lg">{campaign.name}</CardTitle>
7172
<Badge className={getStatusColor(campaign.status)}>{campaign.status}</Badge>
7273
</div>
7374
</CardHeader>
7475
<CardContent className="flex-grow">
7576
<p className="text-sm text-gray-600 line-clamp-2">
76-
Subject: {campaign.subject}
77+
{campaign.description || 'No description available'}
7778
</p>
79+
<div className="mt-4 grid grid-cols-2 gap-4 text-sm">
80+
<div>
81+
<span className="text-gray-500">Target:</span>
82+
<span className="ml-1 font-medium">{campaign.target_count || 0}</span>
83+
</div>
84+
<div>
85+
<span className="text-gray-500">Sent:</span>
86+
<span className="ml-1 font-medium">{campaign.sent_count || 0}</span>
87+
</div>
88+
</div>
7889
</CardContent>
7990
<div className="p-6 pt-0 flex gap-2">
8091
<Button variant="outline" size="sm" className="w-full">

src/pages/Communications.jsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
import React, { useState, useEffect } from 'react';
3-
import { Communication, Template, Case } from '@/api/entities';
3+
import { Communication, Template, Case, Debtor } from '@/api/entities';
44
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
55
import { Button } from '@/components/ui/button';
66
import { Input } from '@/components/ui/input';
@@ -26,6 +26,8 @@ import { createPageUrl } from '@/utils';
2626
export default function Communications() {
2727
const [communications, setCommunications] = useState([]);
2828
const [templates, setTemplates] = useState([]);
29+
const [cases, setCases] = useState([]);
30+
const [debtors, setDebtors] = useState([]);
2931
const [isLoading, setIsLoading] = useState(true);
3032
const [searchTerm, setSearchTerm] = useState('');
3133
const [filterType, setFilterType] = useState('all');
@@ -36,22 +38,37 @@ export default function Communications() {
3638

3739
const loadData = async () => {
3840
try {
39-
const [commsRes, tempsRes] = await Promise.all([
41+
const [commsRes, tempsRes, casesRes, debtorsRes] = await Promise.all([
4042
Communication.list('-sent_date'),
41-
Template.list()
43+
Template.list(),
44+
Case.list(),
45+
Debtor.list()
4246
]);
4347
setCommunications(commsRes || []);
4448
setTemplates(tempsRes || []);
49+
setCases(casesRes || []);
50+
setDebtors(debtorsRes || []);
4551
} catch (error) {
4652
console.error('Error loading communications:', error);
4753
} finally {
4854
setIsLoading(false);
4955
}
5056
};
5157

58+
const getDebtorName = (caseId) => {
59+
const case_ = cases.find(c => c.id === caseId);
60+
if (case_?.debtor_id) {
61+
const debtor = debtors.find(d => d.id === case_.debtor_id);
62+
return debtor?.name || case_.debtor_name;
63+
}
64+
return case_?.debtor_name || 'Unknown';
65+
};
66+
5267
const filteredCommunications = (communications || []).filter(comm => {
68+
const debtorName = getDebtorName(comm.case_id);
5369
const matchesSearch = comm.subject?.toLowerCase().includes(searchTerm.toLowerCase()) ||
54-
comm.content?.toLowerCase().includes(searchTerm.toLowerCase());
70+
comm.content?.toLowerCase().includes(searchTerm.toLowerCase()) ||
71+
debtorName?.toLowerCase().includes(searchTerm.toLowerCase());
5572
const matchesType = filterType === 'all' || comm.type === filterType;
5673
return matchesSearch && matchesType;
5774
});
@@ -211,6 +228,7 @@ export default function Communications() {
211228
<TableRow>
212229
<TableHead>Type</TableHead>
213230
<TableHead>Subject/Content</TableHead>
231+
<TableHead>Debtor</TableHead>
214232
<TableHead>Case ID</TableHead>
215233
<TableHead>Sent Date</TableHead>
216234
<TableHead>Status</TableHead>
@@ -227,11 +245,12 @@ export default function Communications() {
227245
<TableCell><div className="h-4 bg-gray-200 rounded animate-pulse" /></TableCell>
228246
<TableCell><div className="h-4 bg-gray-200 rounded animate-pulse" /></TableCell>
229247
<TableCell><div className="h-4 bg-gray-200 rounded animate-pulse" /></TableCell>
248+
<TableCell><div className="h-4 bg-gray-200 rounded animate-pulse" /></TableCell>
230249
</TableRow>
231250
))
232251
) : filteredCommunications.length === 0 ? (
233252
<TableRow>
234-
<TableCell colSpan={6} className="text-center py-8">
253+
<TableCell colSpan={7} className="text-center py-8">
235254
<MessageSquare className="w-12 h-12 text-gray-300 mx-auto mb-4" />
236255
<p className="text-gray-500">No communications found</p>
237256
</TableCell>
@@ -253,6 +272,9 @@ export default function Communications() {
253272
</p>
254273
</div>
255274
</TableCell>
275+
<TableCell>
276+
<span className="font-medium text-sm">{getDebtorName(comm.case_id)}</span>
277+
</TableCell>
256278
<TableCell>
257279
<Badge variant="outline">{comm.case_id}</Badge>
258280
</TableCell>

0 commit comments

Comments
 (0)