diff --git a/app/me/crowdfunding/page.tsx b/app/me/crowdfunding/page.tsx index c062e5ab3..da91327c0 100644 --- a/app/me/crowdfunding/page.tsx +++ b/app/me/crowdfunding/page.tsx @@ -2,14 +2,14 @@ import * as React from 'react'; import Link from 'next/link'; -import { Plus } from 'lucide-react'; +import { Plus, AlertCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { getMyCrowdfundingProjects } from '@/features/projects/api'; import { CrowdfundingCampaign } from '@/lib/api/types'; import { useAuthStatus } from '@/hooks/use-auth'; import { CrowdfundingDataTable } from '@/components/crowdfunding-data-table'; -import LoadingSpinner from '@/components/LoadingSpinner'; +import EmptyState from '@/components/EmptyState'; import { Card, CardDescription, @@ -18,8 +18,10 @@ import { } from '@/components/ui/card'; export default function MyCrowdfundingPage() { - const { user } = useAuthStatus(); + const { user, isLoading: authLoading } = useAuthStatus(); const [data, setData] = React.useState([]); + const [error, setError] = React.useState(null); + const [hasFetched, setHasFetched] = React.useState(false); const [pagination, setPagination] = React.useState({ page: 1, limit: 10, @@ -31,31 +33,33 @@ export default function MyCrowdfundingPage() { const fetchCampaigns = React.useCallback(async (page = 1, limit = 10) => { try { setLoading(true); + setError(null); const response = await getMyCrowdfundingProjects(page, limit); setData(response.data.data || []); if (response.meta?.pagination) { setPagination(response.meta.pagination); } - } catch { - // Error handled by UI state + } catch (err: any) { + console.error('Failed to fetch crowdfunding campaigns:', err); + setError(err.message || 'Failed to load campaigns. Please try again.'); } finally { setLoading(false); + setHasFetched(true); } }, []); React.useEffect(() => { if (user) { fetchCampaigns(); + } else if (!authLoading && !user) { + setHasFetched(true); + setError('Please log in to view your campaigns.'); } - }, [user, fetchCampaigns]); + }, [user, authLoading, fetchCampaigns]); - if (loading) { - return ( -
- -
- ); - } + const isTableLoading = loading || authLoading || (!hasFetched && !error); + const hasEmptyState = + hasFetched && !isTableLoading && !error && data.length === 0; return ( @@ -82,15 +86,56 @@ export default function MyCrowdfundingPage() {
- - fetchCampaigns(pagination.page, pagination.limit) - } - loading={loading} - /> + {error && data.length > 0 && ( +
+ + {error} +
+ )} + + {error && data.length === 0 ? ( +
+ +
+

+ Failed to load campaigns +

+

{error}

+
+ +
+ ) : hasEmptyState ? ( + + + + Create your first campaign + + + } + /> + ) : ( + + fetchCampaigns(pagination.page, pagination.limit) + } + loading={isTableLoading} + /> + )}
); diff --git a/components/crowdfunding-data-table.tsx b/components/crowdfunding-data-table.tsx index 3d52ac81e..2cbc067b6 100644 --- a/components/crowdfunding-data-table.tsx +++ b/components/crowdfunding-data-table.tsx @@ -10,6 +10,7 @@ import { TableHeader, TableRow, } from '@/components/ui/table'; +import { Skeleton } from '@/components/ui/skeleton'; import { ColumnFiltersState, PaginationState, @@ -135,14 +136,15 @@ export function CrowdfundingDataTable({ {loading ? ( - - - Loading campaigns... - - + Array.from({ length: 5 }).map((_, index) => ( + + {table.getVisibleLeafColumns().map(column => ( + + + + ))} + + )) ) : table.getRowModel().rows?.length ? ( table.getRowModel().rows.map(row => (