diff --git a/frontend/__tests__/unit/pages/About.test.tsx b/frontend/__tests__/unit/pages/About.test.tsx index 161ee17f44..1beed73c73 100644 --- a/frontend/__tests__/unit/pages/About.test.tsx +++ b/frontend/__tests__/unit/pages/About.test.tsx @@ -411,9 +411,11 @@ describe('About Component', () => { render() }) await waitFor(() => { - // Look for the element with alt text "Loading indicator" - const spinner = screen.getAllByAltText('Loading indicator') - expect(spinner.length).toBeGreaterThan(0) + // Check for skeleton loading state by looking for skeleton containers + const skeletonContainers = document.querySelectorAll( + String.raw`.bg-gray-100.dark\:bg-gray-800` + ) + expect(skeletonContainers.length).toBeGreaterThan(0) }) }) diff --git a/frontend/__tests__/unit/pages/Snapshots.test.tsx b/frontend/__tests__/unit/pages/Snapshots.test.tsx index f93f1c3f3d..f4f45c3285 100644 --- a/frontend/__tests__/unit/pages/Snapshots.test.tsx +++ b/frontend/__tests__/unit/pages/Snapshots.test.tsx @@ -59,8 +59,8 @@ describe('SnapshotsPage', () => { render() await waitFor(() => { - const loadingSpinners = screen.getAllByAltText('Loading indicator') - expect(loadingSpinners.length).toBe(2) + const loadingSkeletons = screen.getAllByRole('status') + expect(loadingSkeletons.length).toBeGreaterThan(0) }) }) diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index 608269f258..1dd6605ebe 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -37,9 +37,9 @@ import { getMilestoneProgressIcon, getMilestoneProgressText } from 'utils/milest import AnchorTitle from 'components/AnchorTitle' import AnimatedCounter from 'components/AnimatedCounter' import Leaders from 'components/Leaders' -import LoadingSpinner from 'components/LoadingSpinner' import Markdown from 'components/MarkdownWrapper' import SecondaryCard from 'components/SecondaryCard' +import AboutSkeleton from 'components/skeletons/AboutSkeleton' import TopContributorsList from 'components/TopContributorsList' const leaders = { @@ -50,6 +50,26 @@ const leaders = { const projectKey = 'nest' +const getMilestoneStatus = (progress: number): string => { + if (progress === 100) { + return 'Completed' + } + if (progress > 0) { + return 'In Progress' + } + return 'Not Started' +} + +const getMilestoneIcon = (progress: number) => { + if (progress === 100) { + return faCircleCheck + } + if (progress > 0) { + return faUserGear + } + return faClock +} + const About = () => { const { data: projectMetadataResponse, error: projectMetadataRequestError } = useQuery( GetProjectMetadataDocument, @@ -103,7 +123,7 @@ const About = () => { leadersLoading if (isLoading) { - return + return } if (!projectMetadata || !topContributors) { @@ -222,14 +242,19 @@ const About = () => { - + + + @@ -242,14 +267,9 @@ const About = () => { )} }> {projectStory.map((text) => ( -
+ +
+
@@ -265,7 +285,7 @@ const About = () => { )}

{milestone.title}

diff --git a/frontend/src/app/snapshots/page.tsx b/frontend/src/app/snapshots/page.tsx index 724397e543..6b1c24685b 100644 --- a/frontend/src/app/snapshots/page.tsx +++ b/frontend/src/app/snapshots/page.tsx @@ -6,7 +6,7 @@ import React, { useState, useEffect } from 'react' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' import { GetCommunitySnapshotsDocument } from 'types/__generated__/snapshotQueries.generated' import type { Snapshot } from 'types/snapshot' -import LoadingSpinner from 'components/LoadingSpinner' +import SnapshotSkeleton from 'components/skeletons/SnapshotSkeleton' import SnapshotCard from 'components/SnapshotCard' const SnapshotsPage: React.FC = () => { @@ -41,7 +41,7 @@ const SnapshotsPage: React.FC = () => { const renderSnapshotCard = (snapshot: Snapshot) => { const SubmitButton = { - label: 'View Details', + label: 'View Snapshot', icon: , onclick: () => handleButtonClick(snapshot), } @@ -57,22 +57,26 @@ const SnapshotsPage: React.FC = () => { ) } - if (isLoading) { - return - } - return (
-
- {!snapshots?.length ? ( -
No Snapshots found
- ) : ( - snapshots.map((snapshot: Snapshot) => ( -
{renderSnapshotCard(snapshot)}
- )) - )} -
+ {isLoading ? ( +
+ {Array.from({ length: 12 }, (_, index) => ( + + ))} +
+ ) : ( +
+ {!snapshots?.length ? ( +
No Snapshots found
+ ) : ( + snapshots.map((snapshot: Snapshot) => ( +
{renderSnapshotCard(snapshot)}
+ )) + )} +
+ )}
) diff --git a/frontend/src/components/SkeletonsBase.tsx b/frontend/src/components/SkeletonsBase.tsx index 233177a739..2e6c72bbed 100644 --- a/frontend/src/components/SkeletonsBase.tsx +++ b/frontend/src/components/SkeletonsBase.tsx @@ -1,19 +1,34 @@ import { Skeleton } from '@heroui/skeleton' import LoadingSpinner from 'components/LoadingSpinner' +import AboutSkeleton from 'components/skeletons/AboutSkeleton' import CardSkeleton from 'components/skeletons/Card' +import SnapshotSkeleton from 'components/skeletons/SnapshotSkeleton' import UserCardSkeleton from 'components/skeletons/UserCard' function userCardRender() { const cardCount = 12 return (
- {Array.from({ length: cardCount }).map((_, index) => ( + + {Array.from({ length: cardCount }, (_, index) => ( ))}
) } +function snapshotCardRender() { + const cardCount = 12 + return ( +
+ {Array.from({ length: cardCount }, (_, index) => ( + + + ))} +
+ ) +} + const SkeletonBase = ({ indexName, loadingImageUrl, @@ -49,6 +64,12 @@ const SkeletonBase = ({ break case 'users': return userCardRender() + case 'organizations': + return userCardRender() + case 'snapshots': + return snapshotCardRender() + case 'about': + return default: return } diff --git a/frontend/src/components/skeletons/AboutSkeleton.tsx b/frontend/src/components/skeletons/AboutSkeleton.tsx new file mode 100644 index 0000000000..8c8b6a3510 --- /dev/null +++ b/frontend/src/components/skeletons/AboutSkeleton.tsx @@ -0,0 +1,156 @@ +import { Skeleton } from '@heroui/skeleton' + +const AboutSkeleton = () => { + return ( +
+
+ {/* Title Skeleton */} + + + {/* Our Mission and Who It's For Grid */} +
+
+ + + + +
+
+ + + + +
+
+ + {/* Key Features Section */} +
+ +
+ {[1, 2, 3, 4].map((i) => ( +
+ + + +
+ ))} +
+
+ + {/* Leaders Section */} +
+ +
+ {[1, 2, 3].map((i) => ( +
+ + + +
+ ))} +
+
+ + {/* Top Contributors Section */} +
+ +
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((i) => ( +
+ + + +
+ ))} +
+
+ + {/* Technologies Section */} +
+ +
+ {[1, 2, 3, 4].map((i) => ( +
+ +
+ {[1, 2, 3, 4].map((j) => ( +
+ + +
+ ))} +
+
+ ))} +
+
+ + {/* Get Involved Section */} +
+ + +
+ {[1, 2, 3, 4].map((i) => ( + + ))} +
+ +
+ + {/* Roadmap Section */} +
+ +
+ {[1, 2, 3].map((i) => ( +
+ + + +
+ ))} +
+
+ + {/* Our Story Section */} +
+ + {[1, 2, 3].map((i) => ( +
+ + + +
+ ))} +
+ + {/* Project Timeline Section */} +
+ +
+ {[1, 2, 3, 4].map((i) => ( +
+ + + + + +
+ ))} +
+
+ + {/* Stats Grid */} +
+ {[1, 2, 3, 4].map((i) => ( +
+ + +
+ ))} +
+
+
+ ) +} + +export default AboutSkeleton diff --git a/frontend/src/components/skeletons/SnapshotSkeleton.tsx b/frontend/src/components/skeletons/SnapshotSkeleton.tsx new file mode 100644 index 0000000000..f7fce7685c --- /dev/null +++ b/frontend/src/components/skeletons/SnapshotSkeleton.tsx @@ -0,0 +1,36 @@ +import { Skeleton } from '@heroui/skeleton' +import type React from 'react' + +interface SnapshotSkeletonProps { + showTitle?: boolean + showDateRange?: boolean + showViewButton?: boolean +} + +const SnapshotSkeleton: React.FC = ({ + showTitle = true, + showDateRange = true, + showViewButton = true, +}) => { + return ( + +
{showTitle && }
+ + {showDateRange && ( +
+ + +
+ )} + + {showViewButton && ( +
+ + +
+ )} +
+ ) +} + +export default SnapshotSkeleton