diff --git a/services/dashboard-service/.gitignore b/services/dashboard-service/.gitignore new file mode 100644 index 0000000..e985853 --- /dev/null +++ b/services/dashboard-service/.gitignore @@ -0,0 +1 @@ +.vercel diff --git a/services/dashboard-service/src/app/api/applications/route.ts b/services/dashboard-service/src/app/api/applications/route.ts new file mode 100644 index 0000000..412c904 --- /dev/null +++ b/services/dashboard-service/src/app/api/applications/route.ts @@ -0,0 +1,90 @@ +import { NextResponse } from 'next/server'; + +// Mock data for demonstration - simulates job application logs +const mockLogs = [ + { + id: 1, + job_id: 'LI-2024-001', + platform: 'LinkedIn', + status: 'success', + details: 'Applied to Senior Software Engineer at Google', + created_at: new Date(Date.now() - 1000 * 60 * 5).toISOString(), + }, + { + id: 2, + job_id: 'GD-2024-042', + platform: 'Glassdoor', + status: 'success', + details: 'Applied to Full Stack Developer at Stripe', + created_at: new Date(Date.now() - 1000 * 60 * 15).toISOString(), + }, + { + id: 3, + job_id: 'WF-2024-108', + platform: 'Wellfound', + status: 'success', + details: 'Applied to Founding Engineer at AI Startup', + created_at: new Date(Date.now() - 1000 * 60 * 30).toISOString(), + }, + { + id: 4, + job_id: 'LI-2024-002', + platform: 'LinkedIn', + status: 'failure', + details: 'Application blocked - requires Easy Apply', + created_at: new Date(Date.now() - 1000 * 60 * 45).toISOString(), + }, + { + id: 5, + job_id: 'LI-2024-003', + platform: 'LinkedIn', + status: 'success', + details: 'Applied to Backend Engineer at Meta', + created_at: new Date(Date.now() - 1000 * 60 * 60).toISOString(), + }, + { + id: 6, + job_id: 'GD-2024-043', + platform: 'Glassdoor', + status: 'success', + details: 'Applied to DevOps Engineer at Netflix', + created_at: new Date(Date.now() - 1000 * 60 * 90).toISOString(), + }, + { + id: 7, + job_id: 'WF-2024-109', + platform: 'Wellfound', + status: 'failure', + details: 'Position closed before application submitted', + created_at: new Date(Date.now() - 1000 * 60 * 120).toISOString(), + }, + { + id: 8, + job_id: 'LI-2024-004', + platform: 'LinkedIn', + status: 'success', + details: 'Applied to ML Engineer at OpenAI', + created_at: new Date(Date.now() - 1000 * 60 * 180).toISOString(), + }, + { + id: 9, + job_id: 'GD-2024-044', + platform: 'Glassdoor', + status: 'success', + details: 'Applied to Platform Engineer at Vercel', + created_at: new Date(Date.now() - 1000 * 60 * 240).toISOString(), + }, + { + id: 10, + job_id: 'LI-2024-005', + platform: 'LinkedIn', + status: 'success', + details: 'Applied to Senior Frontend Engineer at Airbnb', + created_at: new Date(Date.now() - 1000 * 60 * 300).toISOString(), + }, +]; + +export async function GET() { + await new Promise(resolve => setTimeout(resolve, 100)); + return NextResponse.json(mockLogs); +} diff --git a/services/dashboard-service/src/app/page.tsx b/services/dashboard-service/src/app/page.tsx index 9ddcc09..3c888a2 100644 --- a/services/dashboard-service/src/app/page.tsx +++ b/services/dashboard-service/src/app/page.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; -// Define the type for a single log entry interface LogEntry { id: number; job_id: string; @@ -12,25 +11,26 @@ interface LogEntry { created_at: string; } -// The API URL for the monitoring service -const API_URL = process.env.NEXT_PUBLIC_API_URL - ? `${process.env.NEXT_PUBLIC_API_URL}/api/logs` - : '/api/logs'; - export default function Dashboard() { const [logs, setLogs] = useState([]); const [error, setError] = useState(null); const [isInitialLoad, setIsInitialLoad] = useState(true); + const [stats, setStats] = useState({ total: 0, success: 0, failure: 0 }); useEffect(() => { const fetchLogs = async () => { try { - const response = await fetch(API_URL); + const response = await fetch('/api/applications'); if (!response.ok) { - throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`); + throw new Error(`Failed to fetch data: ${response.status}`); } const data: LogEntry[] = await response.json(); setLogs(data); + setStats({ + total: data.length, + success: data.filter(l => l.status === 'success').length, + failure: data.filter(l => l.status === 'failure').length, + }); setError(null); } catch (err) { console.error('Error fetching logs:', err); @@ -41,67 +41,144 @@ export default function Dashboard() { }; fetchLogs(); - // Optional: Set up polling to refresh data periodically - const interval = setInterval(fetchLogs, 5000); // Refresh every 5 seconds + const interval = setInterval(fetchLogs, 10000); return () => clearInterval(interval); }, []); + const getPlatformColor = (platform: string) => { + switch (platform.toLowerCase()) { + case 'linkedin': return '#0077B5'; + case 'glassdoor': return '#0CAA41'; + case 'wellfound': return '#CC0000'; + default: return '#666'; + } + }; + return ( -
-

Agent Application Dashboard

-

- Monitor your automated job application activities in real-time -

- - {isInitialLoad &&

Loading application logs...

} - +
+
+

+ Agent Application Dashboard +

+

+ Monitor your automated job application activities in real-time +

+
+ + {/* Stats Cards */} +
+
+

Total Applications

+

{stats.total}

+
+
+

Successful

+

{stats.success}

+
+
+

Failed

+

{stats.failure}

+
+
+

Success Rate

+

+ {stats.total > 0 ? Math.round((stats.success / stats.total) * 100) : 0}% +

+
+
+ + {isInitialLoad && ( +
+

Loading application logs...

+
+ )} + {error && (

- Notice: The monitoring service is not currently available. - This is expected if the backend services are not running. {error} + Notice: Could not connect to monitoring service. {error}

)} - - - - - - - - - - - - - {!isInitialLoad && logs.length === 0 ? ( - - + + {/* Application Logs Table */} +
+
+

Recent Applications

+
+
PlatformJob IDStatusTimestampDetails
- {error ? 'Unable to load application logs.' : 'No application logs available yet.'} -
+ + + + + + + - ) : ( - logs.map((log) => ( - - - - + + {!isInitialLoad && logs.length === 0 ? ( + + - - - )) - )} - -
PlatformJob IDStatusDetailsTime
{log.platform}{log.job_id} - {log.status} +
+ No application logs available yet. {new Date(log.created_at).toLocaleString()}{log.details}
+ ) : ( + logs.map((log, index) => ( + + + + {log.platform} + + + + {log.job_id} + + + + {log.status === 'success' ? '✓' : '✗'} {log.status} + + + + {log.details} + + + {new Date(log.created_at).toLocaleString()} + + + )) + )} + + +
+ +

+ Auto-refreshing every 10 seconds +

); -} \ No newline at end of file +}