feat(frontend): add skeleton loading state for dashboard

Replace simple spinner with skeleton loading placeholders that match
the dashboard layout structure, providing better visual feedback during
data loading.
This commit is contained in:
Radosław Gierwiało
2025-11-21 21:37:05 +01:00
parent 87b0079b84
commit c15031db9f
3 changed files with 124 additions and 12 deletions

View File

@@ -6,6 +6,7 @@ import { useAuth } from '../contexts/AuthContext';
import { dashboardAPI, matchesAPI } from '../services/api';
import { connectSocket, disconnectSocket, getSocket } from '../services/socket';
import HeatBadges from '../components/heats/HeatBadges';
import { DashboardSkeleton } from '../components/common/Skeleton';
import {
Calendar,
MapPin,
@@ -139,12 +140,7 @@ const DashboardPage = () => {
if (loading) {
return (
<Layout>
<div className="max-w-5xl mx-auto flex items-center justify-center min-h-[400px]">
<div className="flex flex-col items-center gap-3">
<Loader2 className="w-12 h-12 animate-spin text-primary-600" />
<p className="text-gray-600">Loading dashboard...</p>
</div>
</div>
<DashboardSkeleton />
</Layout>
);
}

View File

@@ -59,12 +59,14 @@ describe('DashboardPage', () => {
});
describe('Loading State', () => {
it('should show loading spinner while fetching data', async () => {
it('should show skeleton loading state while fetching data', async () => {
dashboardAPI.getData.mockImplementation(() => new Promise(() => {}));
renderWithRouter(<DashboardPage />);
expect(screen.getByText('Loading dashboard...')).toBeInTheDocument();
// Skeleton uses animate-pulse class for loading animation
const skeletons = document.querySelectorAll('.animate-pulse');
expect(skeletons.length).toBeGreaterThan(0);
});
});
@@ -410,14 +412,13 @@ describe('DashboardPage', () => {
renderWithRouter(<DashboardPage />);
// Wait for content to load
await waitFor(() => {
expect(screen.getByText('John Lead')).toBeInTheDocument();
});
// Find the accept button by title attribute
const acceptButton = document.querySelector('button[title="Accept"]');
expect(acceptButton).toBeTruthy();
// Click accept using fireEvent which is synchronous
const acceptButton = screen.getByRole('button', { name: /accept/i });
fireEvent.click(acceptButton);
await waitFor(() => {