-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add mock data API and improved dashboard UI #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Added /api/logs endpoint with sample job application data - Improved dashboard UI with stats cards (total, success, failure, rate) - Added platform color coding (LinkedIn, Glassdoor, Wellfound) - Better table styling with status badges - Auto-refresh every 10 seconds Dashboard now shows functional demo data without requiring backend services.
- Added /api/applications endpoint with sample job application data - Improved dashboard UI with stats cards (total, success, failure, rate) - Added platform color coding (LinkedIn, Glassdoor, Wellfound) - Better table styling with status badges - Auto-refresh every 10 seconds Dashboard now shows functional demo data without requiring backend services.
Learn moreAll Green is an AI agent that automatically: ✅ Addresses code review comments ✅ Fixes failing CI checks ✅ Resolves merge conflicts |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe pull request adds a new API endpoint that returns mock job application logs and updates the dashboard page to fetch from this endpoint, compute aggregate statistics, and display enhanced UI with styled cards, status badges, platform colors, and a 10-second refresh interval. Changes
Sequence DiagramsequenceDiagram
participant Client as Client<br/>(page.tsx)
participant API as API Route<br/>(route.ts)
participant MockData as Mock Logs
rect rgba(200, 220, 255, 0.3)
Note over Client: Initial Load & Polling
Client->>Client: setInterval 10s
end
rect rgba(255, 240, 200, 0.3)
Note over Client,API: Data Fetch Cycle
Client->>API: GET /api/applications
API->>MockData: Retrieve mock logs
MockData-->>API: 10 log entries
API->>API: Wait 100ms
API-->>Client: JSON response
end
rect rgba(200, 255, 220, 0.3)
Note over Client: Process & Render
Client->>Client: Compute stats<br/>(total, success, failure)
Client->>Client: Map platforms to colors
Client->>Client: Render stat cards<br/>& logs table
end
Note over Client: Auto-refresh cycle repeats every 10s
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @groupthinking, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a robust mock data API for job applications, enabling the dashboard to display sample data independently. Concurrently, it delivers a comprehensive overhaul of the dashboard's user interface, incorporating new statistical visualizations, improved visual cues for application statuses and platforms, and refined aesthetics, all complemented by an automatic data refresh mechanism. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a mock API for job applications and significantly revamps the dashboard UI. The changes are well-structured, with a new API route for mock data and a functional, more informative frontend. My review focuses on improving code quality and maintainability. I've suggested optimizing a data processing step, moving a helper function for better performance, and, most importantly, leveraging the existing Tailwind CSS setup instead of inline styles for better long-term maintainability of the UI components. I also recommended adding explicit types to the mock data to improve type safety.
|
|
||
| {isInitialLoad && <p>Loading application logs...</p>} | ||
|
|
||
| <div style={{ fontFamily: 'system-ui, -apple-system, sans-serif', padding: '24px', maxWidth: '1200px', margin: '0 auto' }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component uses extensive inline styling. While this works, it has several disadvantages:
- Readability: It clutters the JSX, making it harder to read and understand the component's structure.
- Maintainability: Changing styles requires hunting through the JSX. It's harder to maintain consistency.
- Performance: Inline styles can't be cached by the browser as effectively as external stylesheets and can lead to larger component payloads.
Since tailwindcss is already a dependency in your project, I strongly recommend using it to style your components. It will make your code cleaner, more maintainable, and more consistent with modern React development practices.
For example, this line:
<div style={{ fontFamily: 'system-ui, -apple-system, sans-serif', padding: '24px', maxWidth: '1200px', margin: '0 auto' }}>Could be rewritten with Tailwind classes as:
<div className="font-sans p-6 max-w-7xl mx-auto">| import { NextResponse } from 'next/server'; | ||
|
|
||
| // Mock data for demonstration - simulates job application logs | ||
| const mockLogs = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To improve type safety and maintainability, consider explicitly typing the mockLogs array. You could define an interface for the log entries (like the LogEntry in page.tsx) and apply it to this constant. For better code organization, this type could be defined in a shared file (e.g., src/types.ts) and imported both here and in page.tsx.
| setStats({ | ||
| total: data.length, | ||
| success: data.filter(l => l.status === 'success').length, | ||
| failure: data.filter(l => l.status === 'failure').length, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation iterates over the data array twice to calculate the number of successful and failed applications. This can be optimized by using a single reduce operation to compute all stats in one pass. This is more efficient and becomes more important as the dataset grows.
| setStats({ | |
| total: data.length, | |
| success: data.filter(l => l.status === 'success').length, | |
| failure: data.filter(l => l.status === 'failure').length, | |
| }); | |
| const { success, failure } = data.reduce( | |
| (acc, log) => { | |
| log.status === 'success' ? acc.success++ : acc.failure++; | |
| return acc; | |
| }, | |
| { success: 0, failure: 0 } | |
| ); | |
| setStats({ total: data.length, success, failure }); |
| const getPlatformColor = (platform: string) => { | ||
| switch (platform.toLowerCase()) { | ||
| case 'linkedin': return '#0077B5'; | ||
| case 'glassdoor': return '#0CAA41'; | ||
| case 'wellfound': return '#CC0000'; | ||
| default: return '#666'; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getPlatformColor function is a pure function that doesn't depend on any component state or props. To prevent it from being redeclared on every render of the Dashboard component, you can define it outside the component scope (e.g., right before the Dashboard component definition). This is a small but good performance optimization.
| const fetchLogs = async () => { | ||
| try { | ||
| const response = await fetch(API_URL); | ||
| const response = await fetch('/api/applications'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The fetch to /api/applications will be intercepted by the Next.js rewrite rule, preventing it from reaching the internal Route Handler and instead forwarding it to an external backend.
Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
The hardcoded fetch call to /api/applications in page.tsx is intended to hit the new internal Next.js Route Handler. However, the next.config.js file contains a rewrite rule that intercepts all requests matching /api/:path* and proxies them to an external backend service. Because Next.js processes rewrites before resolving Route Handlers, the request will never reach the internal mock API. This will cause the data fetch to fail in all environments, resulting in the dashboard displaying a "Could not connect to monitoring service" error to users.
💡 Suggested Fix
To ensure the fetch request reaches the internal Route Handler, either modify the source path in the next.config.js rewrite rule to be more specific and avoid capturing this internal API call, or change the path of the internal API route to something not captured by /api/:path*, such as /internal-api/applications.
🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: services/dashboard-service/src/app/page.tsx#L23
Potential issue: The hardcoded `fetch` call to `/api/applications` in `page.tsx` is
intended to hit the new internal Next.js Route Handler. However, the `next.config.js`
file contains a rewrite rule that intercepts all requests matching `/api/:path*` and
proxies them to an external backend service. Because Next.js processes rewrites before
resolving Route Handlers, the request will never reach the internal mock API. This will
cause the data fetch to fail in all environments, resulting in the dashboard displaying
a "Could not connect to monitoring service" error to users.
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 7553421
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a mock data API endpoint to enable the dashboard to display sample job application data without requiring backend services, along with significant UI improvements for better visual presentation and usability.
- Added
/api/applicationsendpoint with 10 sample job application records - Enhanced dashboard UI with stats cards, platform color coding, and improved styling
- Increased auto-refresh interval from 5 to 10 seconds
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| services/dashboard-service/src/app/api/applications/route.ts | New API route providing mock job application data with 10 sample records |
| services/dashboard-service/src/app/page.tsx | Major UI overhaul with stats cards, platform badges, status indicators, and refined typography |
| services/dashboard-service/.gitignore | Added Vercel deployment directory to ignore list |
| const getPlatformColor = (platform: string) => { | ||
| switch (platform.toLowerCase()) { | ||
| case 'linkedin': return '#0077B5'; | ||
| case 'glassdoor': return '#0CAA41'; | ||
| case 'wellfound': return '#CC0000'; | ||
| default: return '#666'; | ||
| } | ||
| }; |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new getPlatformColor function lacks test coverage. Tests should verify that the correct colors are returned for LinkedIn, Glassdoor, Wellfound, and the default case to ensure consistent platform branding.
| <div style={{ padding: '16px 20px', borderBottom: '1px solid #e5e7eb' }}> | ||
| <h2 style={{ fontSize: '18px', fontWeight: '600', margin: 0 }}>Recent Applications</h2> | ||
| </div> | ||
| <table style={{ width: '100%', borderCollapse: 'collapse' }}> |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The table is missing a caption element for accessibility. Screen reader users would benefit from a descriptive table caption that explains the purpose of the table, such as "Job application history and status".
| <table style={{ width: '100%', borderCollapse: 'collapse' }}> | |
| <table style={{ width: '100%', borderCollapse: 'collapse' }}> | |
| <caption style={{ | |
| position: 'absolute', | |
| width: '1px', | |
| height: '1px', | |
| padding: 0, | |
| margin: '-1px', | |
| overflow: 'hidden', | |
| clip: 'rect(0,0,0,0)', | |
| border: 0 | |
| }}> | |
| Job application history and status | |
| </caption> |
| setStats({ | ||
| total: data.length, | ||
| success: data.filter(l => l.status === 'success').length, | ||
| failure: data.filter(l => l.status === 'failure').length, | ||
| }); |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stats object is recalculated on every render when the data changes. Consider using useMemo to memoize the stats calculation since it only depends on the logs data, which would improve performance especially as the number of logs grows.
| <span style={{ | ||
| display: 'inline-block', | ||
| padding: '4px 10px', | ||
| borderRadius: '9999px', | ||
| fontSize: '12px', | ||
| fontWeight: '500', | ||
| color: 'white', | ||
| backgroundColor: getPlatformColor(log.platform) | ||
| }}> | ||
| {log.platform} | ||
| </span> |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The platform badges use white text on colored backgrounds. For the default gray color (#666), white text may not meet WCAG AA contrast ratio requirements. Ensure adequate contrast ratios (at least 4.5:1 for normal text) for all platform badges to improve accessibility.
| 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(), | ||
| }, | ||
| ]; |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mock data array lacks explicit type annotations. Consider defining the data with an explicit type annotation using the LogEntry interface to ensure type safety and catch potential mismatches between the API response and the frontend expectations. This would also make the code more maintainable and self-documenting.
| setStats({ | ||
| total: data.length, | ||
| success: data.filter(l => l.status === 'success').length, | ||
| failure: data.filter(l => l.status === 'failure').length, | ||
| }); |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new stats calculation logic (calculating total, success, and failure counts) lacks test coverage. The existing test suite doesn't verify that these statistics are correctly computed and displayed in the stats cards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
services/dashboard-service/src/app/api/applications/route.ts (1)
4-85: Consider adding TypeScript type definitions for type safety.The mock data lacks explicit TypeScript types. Consider defining a shared type (matching the
LogEntryinterface inpage.tsx) to ensure type consistency between the API and client.Add a type definition at the top of the file:
import { NextResponse } from 'next/server'; + +interface LogEntry { + id: number; + job_id: string; + platform: string; + status: 'success' | 'failure'; + details: string; + created_at: string; +}Then type the function return:
-const getMockLogs = () => { +const getMockLogs = (): LogEntry[] => {services/dashboard-service/src/app/page.tsx (3)
5-12: Consider extracting the interface to a shared types file.The
LogEntryinterface is well-defined and matches the API response structure. Since both the API route and this page use the same type, consider extracting it to a shared types file (e.g.,types/index.ts) to maintain a single source of truth.
29-33: Consider optimizing stats computation with a single pass.The current implementation filters the array three times to compute stats. For better performance, use a single reduce operation.
- setStats({ - total: data.length, - success: data.filter(l => l.status === 'success').length, - failure: data.filter(l => l.status === 'failure').length, - }); + const stats = data.reduce( + (acc, log) => { + acc.total++; + if (log.status === 'success') acc.success++; + else acc.failure++; + return acc; + }, + { total: 0, success: 0, failure: 0 } + ); + setStats(stats);
48-55: Move helper function outside component to avoid recreation on every render.The
getPlatformColorfunction is defined inside the component body, causing it to be recreated on every render. Move it outside the component for better performance.+const getPlatformColor = (platform: string) => { + switch (platform.toLowerCase()) { + case 'linkedin': return '#0077B5'; + case 'glassdoor': return '#0CAA41'; + case 'wellfound': return '#CC0000'; + default: return '#666'; + } +}; + export default function Dashboard() { const [logs, setLogs] = useState<LogEntry[]>([]); const [error, setError] = useState<string | null>(null); const [isInitialLoad, setIsInitialLoad] = useState<boolean>(true); const [stats, setStats] = useState({ total: 0, success: 0, failure: 0 }); useEffect(() => { ... }, []); - const getPlatformColor = (platform: string) => { - switch (platform.toLowerCase()) { - case 'linkedin': return '#0077B5'; - case 'glassdoor': return '#0CAA41'; - case 'wellfound': return '#CC0000'; - default: return '#666'; - } - }; - return (
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
services/dashboard-service/.gitignore(1 hunks)services/dashboard-service/src/app/api/applications/route.ts(1 hunks)services/dashboard-service/src/app/page.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow ESLint configuration in
.eslintrc.jsonfor TypeScript/Node.js code
Files:
services/dashboard-service/src/app/api/applications/route.tsservices/dashboard-service/src/app/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use TypeScript with strict type checking
Files:
services/dashboard-service/src/app/api/applications/route.tsservices/dashboard-service/src/app/page.tsx
**/*.{py,ts,tsx,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{py,ts,tsx,js}: Keep functions focused and single-purpose
Write descriptive variable and function names
Add comments only when necessary to explain complex logic
Prefer explicit imports over wildcard imports
Handle errors gracefully with appropriate logging
Files:
services/dashboard-service/src/app/api/applications/route.tsservices/dashboard-service/src/app/page.tsx
🧬 Code graph analysis (1)
services/dashboard-service/src/app/page.tsx (1)
test-10-platform-integration.js (1)
data(89-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Agent
- GitHub Check: Vercel Agent Review
- GitHub Check: Seer Code Review
🔇 Additional comments (8)
services/dashboard-service/.gitignore (1)
1-1: LGTM! Standard Vercel deployment configuration.Adding
.vercelto gitignore is the correct approach to exclude Vercel deployment artifacts from version control.services/dashboard-service/src/app/api/applications/route.ts (2)
1-2: LGTM! Correct Next.js App Router import.The import of
NextResponseis appropriate for the route handler implementation.
87-90: Route handler structure is correct.The GET handler follows Next.js 15 App Router conventions. The 100ms delay is reasonable for simulating network latency in a mock API.
Note: If you apply the previous suggestion to fix the timestamp issue, update line 89 to call the function:
export async function GET() { await new Promise(resolve => setTimeout(resolve, 100)); - return NextResponse.json(mockLogs); + return NextResponse.json(getMockLogs()); }services/dashboard-service/src/app/page.tsx (5)
15-18: LGTM! State initialization is clean and properly typed.All state variables have appropriate initial values and TypeScript types.
43-46: LGTM! Polling setup with proper cleanup.The 10-second auto-refresh interval is well-implemented with proper cleanup to prevent memory leaks.
58-88: Excellent dashboard UI with comprehensive stats cards.The header, description, and stats cards provide clear visibility into application metrics. The success rate calculation correctly handles division by zero. The responsive grid layout adapts well to different screen sizes.
110-177: Well-designed table with rich visual indicators.The application logs table effectively uses platform badges, status indicators with icons, monospace job IDs, and formatted timestamps. The styling provides excellent visual hierarchy and readability.
179-181: LGTM! Clear communication of auto-refresh behavior.The footer note effectively informs users that the dashboard auto-refreshes every 10 seconds, matching the actual implementation.
| 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(), | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timestamps are computed at module load, not per request.
The Date.now() calls are evaluated when the module is first loaded, not when each request is made. This means the timestamps will remain static and won't reflect the actual time of subsequent requests. For mock data that's meant to simulate recent activity, this results in increasingly stale timestamps.
Apply this diff to compute timestamps fresh on each request:
-// 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(),
- },
-];
+// Mock data for demonstration - simulates job application logs
+const getMockLogs = () => {
+ const now = Date.now();
+ return [
+ {
+ id: 1,
+ job_id: 'LI-2024-001',
+ platform: 'LinkedIn',
+ status: 'success',
+ details: 'Applied to Senior Software Engineer at Google',
+ created_at: new 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(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(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(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(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(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(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(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(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(now - 1000 * 60 * 300).toISOString(),
+ },
+ ];
+};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In services/dashboard-service/src/app/api/applications/route.ts around lines
4-85, the mockLogs array computes timestamps at module load (Date.now() used
inline) so they become stale; change the implementation to compute timestamps
when handling each request by turning mockLogs into a factory function (e.g.,
getMockLogs()) or move the array creation inside the route handler so new
Date(Date.now() - ...) is evaluated per request, then call that
function/construct the array inside the handler to return fresh ISO timestamps.
| {log.status} | ||
| </thead> | ||
| <tbody> | ||
| {!isInitialLoad && logs.length === 0 ? ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| {!isInitialLoad && logs.length === 0 ? ( | |
| {!isInitialLoad && logs.length === 0 && !error ? ( |
Error state is conflated with empty data state. When the API fetch fails, both the error message and "No application logs available yet" message appear together, which is confusing to users.
View Details
Analysis
Error state conflated with empty data state in Dashboard
What fails: Dashboard component renders both error notification banner and "No application logs available yet" message simultaneously when API fetch fails, creating a confusing UX that doesn't clearly indicate whether the empty state is due to an error or absence of data.
How to reproduce:
- Start the dashboard with a working API endpoint
- Trigger an API failure (simulate network error or server error by mocking fetch to reject)
- Observe the rendered output
Result: Both appear together:
- Error message: "Could not connect to monitoring service. [error details]"
- Empty state message: "No application logs available yet."
Expected: When an error occurs, only the error message should display. The empty state message should only appear when there's genuinely no data and no error.
Root cause: The table tbody condition !isInitialLoad && logs.length === 0 doesn't check for the presence of an error state. When an API call fails, logs remains empty but error is set, causing both messages to render.
Fix: Added && !error to the condition so the empty state message only displays when there's no error: !isInitialLoad && logs.length === 0 && !error
This ensures:
- Error scenario: Only error notification is shown
- No-data scenario: Only empty state message is shown
- Clear user communication in all cases

Summary
Added functional mock data so the dashboard displays sample job applications without requiring backend services.
Changes
/api/applicationsendpoint with 10 sample job application recordsScreenshot
The dashboard now shows:
Live Demo
https://dashboard-service-garv1.vercel.app
Ready to merge!
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.