diff --git a/app/user/backing-history/page.tsx b/app/user/backing-history/page.tsx index bb21f5a66..b42193df7 100644 --- a/app/user/backing-history/page.tsx +++ b/app/user/backing-history/page.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { Button } from '@/components/ui/button'; -import BackingHistory from '@/components/flows/backing-history/Index'; +import BackingHistory from '@/components/flows/backing-history/index'; // Sample data matching the images const sampleBackers = [ diff --git a/app/user/campaigns/page.tsx b/app/user/campaigns/page.tsx new file mode 100644 index 000000000..179e4b795 --- /dev/null +++ b/app/user/campaigns/page.tsx @@ -0,0 +1,42 @@ +'use client'; + +import CampaignTable from '@/components/campaigns/CampaignTable'; +import PageTransition from '@/components/PageTransition'; +import { Button } from '@/components/ui/button'; +import { PlusIcon } from 'lucide-react'; + +export default function CampaignsPage() { + return ( + +
+
+ {/* Header Section */} +
+
+

+ Campaigns +

+

+ Manage and track all your crowdfunding campaigns +

+
+ +
+ + {/* Campaign Table Section */} +
+ +
+
+
+
+ ); +} diff --git a/components/campaigns/CampaignTable.tsx b/components/campaigns/CampaignTable.tsx index 5c84dcb7e..f2a8ee2f0 100644 --- a/components/campaigns/CampaignTable.tsx +++ b/components/campaigns/CampaignTable.tsx @@ -1,8 +1,6 @@ -import Link from 'next/link'; import React, { useState, useEffect, useCallback } from 'react'; import { Button } from '../ui/button'; import { - ChevronRightIcon, MoreVerticalIcon, CheckIcon, ChevronDownIcon, @@ -21,185 +19,12 @@ import { Badge } from '../ui/badge'; import Image from 'next/image'; import { toast } from 'sonner'; import CampaignSummary from './CampaignSummary'; - -interface Campaign { - id: string; - name: string; - creator: { - name: string; - avatar: string; - verified: boolean; - }; - fundingProgress: { - current: number; - target: number; - }; - endDate: string; - milestones: number; - status: 'live' | 'successful' | 'failed'; - tags: string[]; - likes: number; - comments: number; - createdAt: string; - updatedAt: string; -} - -interface ApiResponse { - data: Campaign[]; - total: number; - page: number; - limit: number; - success: boolean; - message?: string; -} - -type StatusFilter = 'all' | 'live' | 'successful' | 'failed'; -type TabFilter = 'mine' | 'others'; - -const mockApiService = { - async fetchCampaigns( - statusFilter: StatusFilter = 'all', - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _tabFilter: TabFilter = 'mine', - page: number = 1, - limit: number = 10 - ): Promise { - await new Promise(resolve => setTimeout(resolve, 1000)); - - const mockCampaigns: Campaign[] = [ - { - id: '1', - name: 'Boundless Web3 Platform', - creator: { - name: 'Collins Odumeje', - avatar: 'https://github.com/shadcn.png', - verified: true, - }, - fundingProgress: { - current: 23000, - target: 250000, - }, - endDate: 'Sept 30', - milestones: 6, - status: 'live', - tags: ['#web3', '#crowdfunding'], - likes: 29, - comments: 12, - createdAt: '2024-01-15T10:00:00Z', - updatedAt: '2024-01-20T14:30:00Z', - }, - { - id: '2', - name: 'DeFi Innovation Hub', - creator: { - name: 'Sarah Johnson', - avatar: 'https://github.com/shadcn.png', - verified: true, - }, - fundingProgress: { - current: 250000, - target: 250000, - }, - endDate: 'Sept 30', - milestones: 6, - status: 'successful', - tags: ['#defi', '#innovation'], - likes: 29, - comments: 23, - createdAt: '2024-01-10T09:00:00Z', - updatedAt: '2024-01-25T16:45:00Z', - }, - { - id: '3', - name: 'NFT Marketplace', - creator: { - name: 'Mike Chen', - avatar: 'https://github.com/shadcn.png', - verified: false, - }, - fundingProgress: { - current: 23000, - target: 250000, - }, - endDate: 'Expired', - milestones: 6, - status: 'failed', - tags: ['#nft', '#marketplace'], - likes: 29, - comments: 12, - createdAt: '2024-01-05T11:00:00Z', - updatedAt: '2024-01-18T13:20:00Z', - }, - { - id: '4', - name: 'Blockchain Education Platform', - creator: { - name: 'Emma Wilson', - avatar: 'https://github.com/shadcn.png', - verified: true, - }, - fundingProgress: { - current: 180000, - target: 200000, - }, - endDate: 'Oct 15', - milestones: 8, - status: 'live', - tags: ['#education', '#blockchain'], - likes: 45, - comments: 18, - createdAt: '2024-01-12T08:00:00Z', - updatedAt: '2024-01-22T15:10:00Z', - }, - ]; - - let filteredCampaigns = mockCampaigns; - if (statusFilter !== 'all') { - filteredCampaigns = mockCampaigns.filter( - campaign => campaign.status === statusFilter - ); - } - - const startIndex = (page - 1) * limit; - const endIndex = startIndex + limit; - const paginatedCampaigns = filteredCampaigns.slice(startIndex, endIndex); - - return { - data: paginatedCampaigns, - total: filteredCampaigns.length, - page, - limit, - success: true, - message: 'Campaigns fetched successfully', - }; - }, - - async likeCampaign( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _campaignId: string - ): Promise<{ success: boolean; message: string }> { - await new Promise(resolve => setTimeout(resolve, 500)); - return { success: true, message: 'Campaign liked successfully' }; - }, - - async commentCampaign( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _campaignId: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _comment: string - ): Promise<{ success: boolean; message: string }> { - await new Promise(resolve => setTimeout(resolve, 500)); - return { success: true, message: 'Comment added successfully' }; - }, - - async shareCampaign( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _campaignId: string - ): Promise<{ success: boolean; message: string }> { - await new Promise(resolve => setTimeout(resolve, 500)); - return { success: true, message: 'Campaign shared successfully' }; - }, -}; +import { + Campaign, + StatusFilter, + TabFilter, + mockApiService, +} from '@/lib/data/campaigns-mock'; const CampaignRow = ({ campaign, @@ -221,17 +46,9 @@ const CampaignRow = ({ } }; - const getProgressColor = (status: string) => { - switch (status) { - case 'live': - return 'bg-[#1671D9]'; - case 'successful': - return 'bg-primary'; - case 'failed': - return 'bg-[#919191]'; - default: - return 'bg-[#919191]'; - } + const getProgressColor = () => { + // Use the same blue color for all progress bars + return 'bg-[#1671D9]'; }; const progressPercentage = @@ -245,13 +62,13 @@ const CampaignRow = ({ <>
-
+
{campaign.name}
@@ -298,7 +115,7 @@ const CampaignRow = ({
@@ -314,7 +131,7 @@ const CampaignRow = ({
{campaign.status} @@ -326,7 +143,7 @@ const CampaignRow = ({ @@ -423,7 +240,7 @@ const CampaignRow = ({
{campaign.name}
{campaign.status} @@ -450,7 +267,7 @@ const CampaignRow = ({ @@ -575,7 +392,7 @@ const CampaignRow = ({
@@ -603,6 +420,11 @@ const CampaignTable = () => { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [campaignSummaryOpen, setCampaignSummaryOpen] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const itemsPerPage = 10; + + // Quick filter options - keeping it simple for now const filterOptions = [ { value: 'all', label: 'All' }, { value: 'live', label: 'Live' }, @@ -610,69 +432,91 @@ const CampaignTable = () => { { value: 'failed', label: 'Failed' }, ]; - const fetchCampaigns = useCallback(async () => { - try { - setLoading(true); - setError(null); - const response = await mockApiService.fetchCampaigns( - statusFilter, - tabFilter - ); - setCampaigns(response.data); - } catch { - setError('Failed to fetch campaigns'); - toast.error('Failed to fetch campaigns'); - } finally { - setLoading(false); - } - }, [statusFilter, tabFilter]); + const fetchCampaigns = useCallback( + async (page: number = 1) => { + try { + setLoading(true); + setError(null); + const response = await mockApiService.fetchCampaigns( + statusFilter, + tabFilter, + page, + itemsPerPage + ); + // TODO: Handle empty state better - maybe add a refresh button? + setCampaigns(response.data); + setTotalPages(Math.ceil(response.total / itemsPerPage)); + } catch { + setError('Failed to fetch campaigns'); + toast.error('Failed to fetch campaigns'); + } finally { + setLoading(false); + } + }, + [statusFilter, tabFilter, itemsPerPage] + ); + // Handle different campaign actions - TODO: extract this to a separate hook const handleCampaignAction = async (action: string, campaignId: string) => { try { switch (action) { case 'like': await mockApiService.likeCampaign(campaignId); - toast.success('Campaign liked successfully'); + toast.success('Liked!'); break; case 'comment': + // TODO: Open comment modal instead of hardcoded comment await mockApiService.commentCampaign(campaignId, 'Great campaign!'); - toast.success('Comment added successfully'); + toast.success('Comment added'); break; case 'share': await mockApiService.shareCampaign(campaignId); - toast.success('Campaign shared successfully'); + toast.success('Shared!'); break; case 'view-summary': setCampaignSummaryOpen(true); break; case 'view-history': - toast.info('Opening campaign history...'); + // TODO: Navigate to history page + toast.info('Opening history...'); break; case 'campaign-details': - toast.info('Opening campaign details...'); + // TODO: Navigate to details page + toast.info('Opening details...'); break; default: - toast.info(`Action: ${action}`); + toast.info(`${action} clicked`); } - await fetchCampaigns(); + // Refresh the list after actions + fetchCampaigns(); } catch { - toast.error('Action failed'); + toast.error('Something went wrong'); } }; useEffect(() => { - fetchCampaigns(); - }, [fetchCampaigns]); + setCurrentPage(1); + fetchCampaigns(1); + }, [statusFilter, tabFilter, fetchCampaigns]); + + useEffect(() => { + fetchCampaigns(currentPage); + }, [currentPage, fetchCampaigns]); + + const handlePageChange = (page: number) => { + setCurrentPage(page); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; return ( -
+

- Latest Campaigns + Campaigns

- + {/* - + */}
{

{error}

) : campaigns.length === 0 ? ( -
+
-

No campaigns found

-

- Try adjusting your filters +

+ No campaigns available +
+

+ No Active Campaigns +

+

+ Projects you vote on, comment on, or fund will appear here. Get + involved and support ideas that matter to you.

@@ -787,7 +643,78 @@ const CampaignTable = () => { /> )) )} + + {totalPages > 1 && campaigns.length > 0 && ( +
+
+ + Page {currentPage} of {totalPages} + + +
+ + + {/* Basic pagination numbers */} + {Array.from({ length: totalPages }, (_, i) => i + 1).map( + page => { + // Only show if we're within 2 pages or it's first/last + if ( + page === 1 || + page === totalPages || + Math.abs(page - currentPage) <= 1 + ) { + return ( + + ); + } else if ( + page === currentPage - 2 || + page === currentPage + 2 + ) { + return ( + + ... + + ); + } + return null; + } + )} + + +
+
+
+ )}
+ void; } +interface CampaignFormData { + title: string; + description: string; + fundingGoal: string; + category: string; + images: string[]; + duration: string; +} + +interface EscrowData { + network: string; + transactionType: string; + walletAddress: string; + agreedToTerms: boolean; +} + +interface Milestone { + id: string; + title: string; + description: string; + deliveryDate: string; + fundAmount: number; + isExpanded: boolean; +} + const LaunchCampaignFlow: React.FC = ({ onComplete, }) => { @@ -264,7 +289,7 @@ const LaunchCampaignFlow: React.FC = ({ // Campaign Details Form Component const CampaignDetailsForm: React.FC<{ - formData: any; + formData: CampaignFormData; selectedTags: string[]; onInputChange: (field: string, value: string) => void; onTagToggle: (tag: string) => void; @@ -571,9 +596,9 @@ const CampaignDetailsForm: React.FC<{ // Escrow Setup Form Component const EscrowSetupForm: React.FC<{ - escrowData: any; - setEscrowData: (data: any) => void; - milestones: any[]; + escrowData: EscrowData; + setEscrowData: (data: EscrowData) => void; + milestones: Milestone[]; escrowTerms: { title: string; description: string }[]; isEscrowValid: () => boolean; onBack: () => void; diff --git a/components/flows/back-project/back-project-form.tsx b/components/flows/back-project/back-project-form.tsx index 99b54750d..21c52022d 100644 --- a/components/flows/back-project/back-project-form.tsx +++ b/components/flows/back-project/back-project-form.tsx @@ -59,18 +59,17 @@ export function BackProjectForm({ try { await navigator.clipboard.writeText(walletAddress); // Could add toast notification here instead of state - } catch (err) { + } catch { // Fallback for browsers that don't support clipboard API const textArea = document.createElement('textarea'); - console.error(err); - + // Silent fallback for older browsers textArea.value = walletAddress; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); - } catch (copyErr) { - console.error('Failed to copy address:', copyErr); + } catch { + // Copy failed, but no need to log in production } document.body.removeChild(textArea); } diff --git a/components/flows/back-project/index.tsx b/components/flows/back-project/index.tsx index 972d5a0e9..c8a3207ba 100644 --- a/components/flows/back-project/index.tsx +++ b/components/flows/back-project/index.tsx @@ -23,11 +23,12 @@ const BackProject = () => { const [backProjectState, setBackProjectState] = useState('form'); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const handleBackProject = (data: BackProjectData) => { setBackProjectState('loading'); - console.log(data); + // TODO: Send data to actual API endpoint when backend is ready - // Simulate API call + // Simulate API call - data will be used when API is implemented setTimeout(() => { setBackProjectState('success'); }, 2000); diff --git a/components/overview/RecentProjects.tsx b/components/overview/RecentProjects.tsx index 3e001ad1d..5d839118f 100644 --- a/components/overview/RecentProjects.tsx +++ b/components/overview/RecentProjects.tsx @@ -1,6 +1,6 @@ 'use client'; import React, { useState, useEffect, useCallback } from 'react'; -import { RecentProjectsProps } from '@/types/project'; +import { RecentProjectsProps, Project } from '@/types/project'; import { Plus, ChevronRight, ChevronDown, Loader2 } from 'lucide-react'; import ProjectCard from '../project-card'; import EmptyState from '../EmptyState'; @@ -54,20 +54,47 @@ const RecentProjects = () => { setError(null); const response = await getProjects(); - const transformedProjects = (response.projects || []).map( - (project: any) => ({ + // Define API project interface locally since it's different from our UI Project type + interface ApiProject { + _id: string; + title: string; + description: string; + whitepaperUrl?: string; + tags?: string[]; + category?: string; + type?: string; + amount?: number; + status?: string; + createdAt?: string; + updatedAt?: string; + owner?: { + type?: { + _id?: string; + profile?: { + firstName?: string; + lastName?: string; + username?: string; + avatar?: string; + }; + }; + }; + } + + const apiProjects = (response.projects as unknown as ApiProject[]) || []; + const transformedProjects = apiProjects.map( + (project: ApiProject): Project => ({ id: project._id, name: project.title, description: project.description, image: project.whitepaperUrl || '/banner.png', link: `/projects/${project._id}`, tags: project.tags || [], - category: project.category, - type: project.type, - amount: 0, - status: project.status, - createdAt: project.createdAt, - updatedAt: project.updatedAt, + category: project.category || 'uncategorized', + type: project.type || 'unknown', + amount: project.amount || 0, + status: project.status || 'draft', + createdAt: project.createdAt || new Date().toISOString(), + updatedAt: project.updatedAt || new Date().toISOString(), // Add owner information for filtering owner: project.owner?.type?._id || null, ownerName: project.owner?.type?.profile diff --git a/hooks/use-auth.ts b/hooks/use-auth.ts index 967b93188..72c88df56 100644 --- a/hooks/use-auth.ts +++ b/hooks/use-auth.ts @@ -23,7 +23,13 @@ export function useAuth(requireAuth = true) { // Sync with NextAuth session when available useEffect(() => { if (session?.user && status === 'authenticated') { - syncWithSession(session.user).catch(() => { + // Convert NextAuth session user to SessionUser format + const sessionUser = { + ...session.user, + name: session.user.name || undefined, // Convert null to undefined + image: session.user.image || undefined, // Convert null to undefined + }; + syncWithSession(sessionUser).catch(() => { // Silently handle sync failure }); } diff --git a/lib/data/campaigns-mock.ts b/lib/data/campaigns-mock.ts new file mode 100644 index 000000000..d0282f7c1 --- /dev/null +++ b/lib/data/campaigns-mock.ts @@ -0,0 +1,829 @@ +export interface Campaign { + id: string; + name: string; + creator: { + name: string; + avatar: string; + verified: boolean; + }; + fundingProgress: { + current: number; + target: number; + }; + endDate: string; + milestones: number; + status: 'live' | 'successful' | 'failed'; + tags: string[]; + likes: number; + comments: number; + createdAt: string; + updatedAt: string; +} + +export interface ApiResponse { + data: Campaign[]; + total: number; + page: number; + limit: number; + success: boolean; + message: string; +} + +export type StatusFilter = 'all' | 'live' | 'successful' | 'failed'; +export type TabFilter = 'mine' | 'others'; + +export const mockCampaigns: Campaign[] = [ + { + id: '1', + name: 'Boundless Web3 Platform', + creator: { + name: 'Collins Odumeje', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 23000, + target: 250000, + }, + endDate: 'Sept 30', + milestones: 6, + status: 'live', + tags: ['#web3', '#crowdfunding'], + likes: 29, + comments: 12, + createdAt: '2024-01-15T10:00:00Z', + updatedAt: '2024-01-20T14:30:00Z', + }, + { + id: '2', + name: 'Kano Tech Education Initiative', + creator: { + name: 'Salim Hassan', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 250000, + target: 250000, + }, + endDate: 'Aug 15', + milestones: 4, + status: 'successful', + tags: ['#education', '#tech'], + likes: 95, + comments: 34, + createdAt: '2024-01-10T08:30:00Z', + updatedAt: '2024-01-18T16:45:00Z', + }, + { + id: '3', + name: 'Lagos Clean Water Project', + creator: { + name: 'Esther Okonkwo', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 15000, + target: 100000, + }, + endDate: 'Oct 20', + milestones: 5, + status: 'live', + tags: ['#water', '#infrastructure'], + likes: 45, + comments: 18, + createdAt: '2024-01-20T12:00:00Z', + updatedAt: '2024-01-22T09:15:00Z', + }, + { + id: '4', + name: 'Abuja Solar Farm Initiative', + creator: { + name: 'David Jere', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 5000, + target: 500000, + }, + endDate: 'Nov 30', + milestones: 8, + status: 'failed', + tags: ['#renewable', '#energy'], + likes: 67, + comments: 23, + createdAt: '2024-01-05T14:20:00Z', + updatedAt: '2024-01-25T11:30:00Z', + }, + { + id: '5', + name: 'Port Harcourt Tech Hub', + creator: { + name: 'Blessing Okafor', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 180000, + target: 200000, + }, + endDate: 'Dec 15', + milestones: 7, + status: 'live', + tags: ['#technology', '#hub'], + likes: 89, + comments: 42, + createdAt: '2024-01-25T10:00:00Z', + updatedAt: '2024-01-28T14:30:00Z', + }, + { + id: '6', + name: 'Blockchain for Agriculture', + creator: { + name: 'Aminu Yusuf', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 75000, + target: 150000, + }, + endDate: 'Oct 5', + milestones: 5, + status: 'live', + tags: ['#blockchain', '#agriculture'], + likes: 56, + comments: 28, + createdAt: '2024-01-22T08:00:00Z', + updatedAt: '2024-01-26T16:45:00Z', + }, + { + id: '7', + name: 'AI Healthcare Platform', + creator: { + name: 'Dr. Funke Adebayo', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 300000, + target: 300000, + }, + endDate: 'Jul 20', + milestones: 9, + status: 'successful', + tags: ['#AI', '#healthcare'], + likes: 234, + comments: 89, + createdAt: '2024-01-08T12:00:00Z', + updatedAt: '2024-01-24T09:15:00Z', + }, + { + id: '8', + name: 'NFT Art Gallery Lagos', + creator: { + name: 'Tunde Adeleke', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 45000, + target: 100000, + }, + endDate: 'Nov 10', + milestones: 4, + status: 'live', + tags: ['#NFT', '#art'], + likes: 78, + comments: 31, + createdAt: '2024-01-28T14:20:00Z', + updatedAt: '2024-01-30T11:30:00Z', + }, + { + id: '9', + name: 'DeFi Lending Protocol', + creator: { + name: 'Chioma Eze', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 450000, + target: 500000, + }, + endDate: 'Sept 25', + milestones: 10, + status: 'live', + tags: ['#DeFi', '#lending'], + likes: 156, + comments: 67, + createdAt: '2024-01-18T10:00:00Z', + updatedAt: '2024-01-29T14:30:00Z', + }, + { + id: '10', + name: 'Smart City IoT Network', + creator: { + name: 'Ibrahim Musa', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 120000, + target: 600000, + }, + endDate: 'Expired', + milestones: 12, + status: 'failed', + tags: ['#IoT', '#smartcity'], + likes: 89, + comments: 44, + createdAt: '2024-01-02T08:30:00Z', + updatedAt: '2024-01-15T16:45:00Z', + }, + { + id: '11', + name: 'Green Energy Marketplace', + creator: { + name: 'Fatima Abubakar', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 95000, + target: 200000, + }, + endDate: 'Oct 30', + milestones: 6, + status: 'live', + tags: ['#green', '#energy'], + likes: 102, + comments: 38, + createdAt: '2024-01-26T12:00:00Z', + updatedAt: '2024-01-31T09:15:00Z', + }, + { + id: '12', + name: 'Fintech Payment Gateway', + creator: { + name: 'Olumide Bakare', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 500000, + target: 500000, + }, + endDate: 'Jun 30', + milestones: 8, + status: 'successful', + tags: ['#fintech', '#payments'], + likes: 345, + comments: 124, + createdAt: '2024-01-01T14:20:00Z', + updatedAt: '2024-01-20T11:30:00Z', + }, + { + id: '13', + name: 'E-Learning Platform', + creator: { + name: 'Grace Okonkwo', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 65000, + target: 150000, + }, + endDate: 'Dec 20', + milestones: 5, + status: 'live', + tags: ['#education', '#online'], + likes: 67, + comments: 29, + createdAt: '2024-01-30T10:00:00Z', + updatedAt: '2024-02-01T14:30:00Z', + }, + { + id: '14', + name: 'Supply Chain on Blockchain', + creator: { + name: 'Mohammed Sani', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 280000, + target: 400000, + }, + endDate: 'Sept 15', + milestones: 11, + status: 'live', + tags: ['#blockchain', '#supplychain'], + likes: 178, + comments: 72, + createdAt: '2024-01-12T08:00:00Z', + updatedAt: '2024-01-28T16:45:00Z', + }, + { + id: '15', + name: 'Virtual Reality Gaming', + creator: { + name: 'Daniel Ogunleye', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 35000, + target: 300000, + }, + endDate: 'Expired', + milestones: 7, + status: 'failed', + tags: ['#VR', '#gaming'], + likes: 45, + comments: 18, + createdAt: '2024-01-03T12:00:00Z', + updatedAt: '2024-01-10T09:15:00Z', + }, + { + id: '16', + name: 'Crypto Exchange Nigeria', + creator: { + name: 'Kemi Adeyemi', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 750000, + target: 1000000, + }, + endDate: 'Oct 1', + milestones: 15, + status: 'live', + tags: ['#crypto', '#exchange'], + likes: 456, + comments: 189, + createdAt: '2024-01-20T14:20:00Z', + updatedAt: '2024-02-01T11:30:00Z', + }, + { + id: '17', + name: 'Social Impact DAO', + creator: { + name: 'Ngozi Ibe', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 150000, + target: 150000, + }, + endDate: 'May 15', + milestones: 6, + status: 'successful', + tags: ['#DAO', '#social'], + likes: 234, + comments: 91, + createdAt: '2023-12-15T10:00:00Z', + updatedAt: '2024-01-10T14:30:00Z', + }, + { + id: '18', + name: 'Mobile Banking Solution', + creator: { + name: 'Uche Nnamdi', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 88000, + target: 250000, + }, + endDate: 'Nov 25', + milestones: 8, + status: 'live', + tags: ['#mobile', '#banking'], + likes: 112, + comments: 47, + createdAt: '2024-01-29T08:30:00Z', + updatedAt: '2024-02-02T16:45:00Z', + }, + { + id: '19', + name: 'Renewable Energy Fund', + creator: { + name: 'Adaeze Obi', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 320000, + target: 500000, + }, + endDate: 'Dec 31', + milestones: 10, + status: 'live', + tags: ['#renewable', '#fund'], + likes: 267, + comments: 103, + createdAt: '2024-01-24T12:00:00Z', + updatedAt: '2024-02-01T09:15:00Z', + }, + { + id: '20', + name: 'Metaverse Shopping Mall', + creator: { + name: 'Victor Ekwueme', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 50000, + target: 400000, + }, + endDate: 'Expired', + milestones: 9, + status: 'failed', + tags: ['#metaverse', '#shopping'], + likes: 34, + comments: 12, + createdAt: '2023-12-20T14:20:00Z', + updatedAt: '2024-01-05T11:30:00Z', + }, + { + id: '21', + name: 'Agritech Drone Solutions', + creator: { + name: 'Peter Okoye', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 145000, + target: 300000, + }, + endDate: 'Nov 15', + milestones: 7, + status: 'live', + tags: ['#agritech', '#drones'], + likes: 89, + comments: 35, + createdAt: '2024-02-01T10:00:00Z', + updatedAt: '2024-02-03T14:30:00Z', + }, + { + id: '22', + name: 'Digital Identity Platform', + creator: { + name: 'Zainab Mohammed', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 400000, + target: 400000, + }, + endDate: 'Apr 10', + milestones: 12, + status: 'successful', + tags: ['#identity', '#security'], + likes: 312, + comments: 128, + createdAt: '2023-11-15T08:00:00Z', + updatedAt: '2024-01-02T16:45:00Z', + }, + { + id: '23', + name: 'P2P Trading Platform', + creator: { + name: 'Emeka Nwosu', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 78000, + target: 200000, + }, + endDate: 'Oct 25', + milestones: 6, + status: 'live', + tags: ['#P2P', '#trading'], + likes: 67, + comments: 24, + createdAt: '2024-01-31T12:00:00Z', + updatedAt: '2024-02-03T09:15:00Z', + }, + { + id: '24', + name: 'Climate Change Tracker', + creator: { + name: 'Aisha Bello', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 25000, + target: 150000, + }, + endDate: 'Expired', + milestones: 5, + status: 'failed', + tags: ['#climate', '#data'], + likes: 45, + comments: 19, + createdAt: '2023-12-01T14:20:00Z', + updatedAt: '2023-12-25T11:30:00Z', + }, + { + id: '25', + name: 'Music NFT Platform', + creator: { + name: 'Timi Dakolo', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 220000, + target: 350000, + }, + endDate: 'Dec 5', + milestones: 8, + status: 'live', + tags: ['#music', '#NFT'], + likes: 198, + comments: 76, + createdAt: '2024-02-02T10:00:00Z', + updatedAt: '2024-02-04T14:30:00Z', + }, + { + id: '26', + name: 'Logistics Optimization AI', + creator: { + name: 'Yakubu Gowon', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 165000, + target: 400000, + }, + endDate: 'Sept 20', + milestones: 10, + status: 'live', + tags: ['#logistics', '#AI'], + likes: 134, + comments: 52, + createdAt: '2024-01-28T08:30:00Z', + updatedAt: '2024-02-03T16:45:00Z', + }, + { + id: '27', + name: 'Healthcare Records Chain', + creator: { + name: 'Dr. Bisi Adeleke', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 550000, + target: 550000, + }, + endDate: 'Mar 30', + milestones: 14, + status: 'successful', + tags: ['#healthcare', '#blockchain'], + likes: 423, + comments: 167, + createdAt: '2023-10-15T12:00:00Z', + updatedAt: '2023-12-30T09:15:00Z', + }, + { + id: '28', + name: 'Real Estate Tokenization', + creator: { + name: 'Chidi Okafor', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 890000, + target: 1200000, + }, + endDate: 'Oct 15', + milestones: 16, + status: 'live', + tags: ['#realestate', '#tokens'], + likes: 567, + comments: 234, + createdAt: '2024-01-22T14:20:00Z', + updatedAt: '2024-02-04T11:30:00Z', + }, + { + id: '29', + name: 'EdTech for Rural Areas', + creator: { + name: 'Mary Ezekiel', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 42000, + target: 100000, + }, + endDate: 'Nov 30', + milestones: 4, + status: 'live', + tags: ['#education', '#rural'], + likes: 78, + comments: 31, + createdAt: '2024-02-03T10:00:00Z', + updatedAt: '2024-02-05T14:30:00Z', + }, + { + id: '30', + name: 'Carbon Credit Exchange', + creator: { + name: 'Joseph Yobo', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 30000, + target: 500000, + }, + endDate: 'Expired', + milestones: 11, + status: 'failed', + tags: ['#carbon', '#exchange'], + likes: 56, + comments: 22, + createdAt: '2023-11-20T08:00:00Z', + updatedAt: '2023-12-15T16:45:00Z', + }, + { + id: '31', + name: 'Food Delivery Blockchain', + creator: { + name: 'Sandra Okeke', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 125000, + target: 250000, + }, + endDate: 'Dec 10', + milestones: 7, + status: 'live', + tags: ['#food', '#delivery'], + likes: 145, + comments: 58, + createdAt: '2024-02-01T12:00:00Z', + updatedAt: '2024-02-04T09:15:00Z', + }, + { + id: '32', + name: 'Tourism DApp Nigeria', + creator: { + name: 'Kunle Afolayan', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 180000, + target: 180000, + }, + endDate: 'Feb 28', + milestones: 6, + status: 'successful', + tags: ['#tourism', '#dapp'], + likes: 234, + comments: 92, + createdAt: '2023-09-15T14:20:00Z', + updatedAt: '2023-11-30T11:30:00Z', + }, + { + id: '33', + name: 'Insurance Protocol', + creator: { + name: 'Amara Kalu', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 420000, + target: 600000, + }, + endDate: 'Sept 10', + milestones: 13, + status: 'live', + tags: ['#insurance', '#defi'], + likes: 289, + comments: 113, + createdAt: '2024-01-25T10:00:00Z', + updatedAt: '2024-02-05T14:30:00Z', + }, + { + id: '34', + name: 'Gaming Guild Africa', + creator: { + name: 'Victor Moses', + avatar: 'https://github.com/shadcn.png', + verified: false, + }, + fundingProgress: { + current: 95000, + target: 200000, + }, + endDate: 'Oct 20', + milestones: 5, + status: 'live', + tags: ['#gaming', '#guild'], + likes: 167, + comments: 64, + createdAt: '2024-02-02T08:30:00Z', + updatedAt: '2024-02-05T16:45:00Z', + }, + { + id: '35', + name: 'Legal Tech Platform', + creator: { + name: 'Barrister Folake', + avatar: 'https://github.com/shadcn.png', + verified: true, + }, + fundingProgress: { + current: 280000, + target: 350000, + }, + endDate: 'Nov 5', + milestones: 9, + status: 'live', + tags: ['#legal', '#tech'], + likes: 201, + comments: 79, + createdAt: '2024-01-30T12:00:00Z', + updatedAt: '2024-02-04T09:15:00Z', + }, +]; + +export const mockApiService = { + async fetchCampaigns( + statusFilter: StatusFilter = 'all', + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _tabFilter: TabFilter = 'mine', + page: number = 1, + limit: number = 10 + ): Promise { + await new Promise(resolve => setTimeout(resolve, 1000)); + + let filteredCampaigns = mockCampaigns; + if (statusFilter !== 'all') { + filteredCampaigns = mockCampaigns.filter( + campaign => campaign.status === statusFilter + ); + } + + const startIndex = (page - 1) * limit; + const endIndex = startIndex + limit; + const paginatedCampaigns = filteredCampaigns.slice(startIndex, endIndex); + + return { + data: paginatedCampaigns, + total: filteredCampaigns.length, + page, + limit, + success: true, + message: 'Campaigns fetched successfully', + }; + }, + + async likeCampaign( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _campaignId: string + ): Promise<{ success: boolean; message: string }> { + await new Promise(resolve => setTimeout(resolve, 500)); + return { success: true, message: 'Campaign liked successfully' }; + }, + + async commentCampaign( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _campaignId: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _comment: string + ): Promise<{ success: boolean; message: string }> { + await new Promise(resolve => setTimeout(resolve, 500)); + return { success: true, message: 'Comment added successfully' }; + }, + + async shareCampaign( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _campaignId: string + ): Promise<{ success: boolean; message: string }> { + await new Promise(resolve => setTimeout(resolve, 300)); + return { success: true, message: 'Campaign shared successfully' }; + }, +}; diff --git a/lib/stores/auth-store.ts b/lib/stores/auth-store.ts index 86ca17380..ab0f19bb6 100644 --- a/lib/stores/auth-store.ts +++ b/lib/stores/auth-store.ts @@ -3,8 +3,30 @@ import { persist, createJSONStorage } from 'zustand/middleware'; import { getMe, logout } from '@/lib/api/auth'; import Cookies from 'js-cookie'; +// JWT payload interface +interface JWTPayload { + user_id?: string; + userId?: string; + sub?: string; + exp?: number; + iat?: number; + [key: string]: unknown; +} + +// Session user interface for NextAuth +interface SessionUser { + id: string; + _id?: string; + email: string; + name?: string; + image?: string; + role?: string; + accessToken?: string; + [key: string]: unknown; +} + // Simple JWT decode function to extract user ID from token -function decodeJWT(token: string): any { +function decodeJWT(token: string): JWTPayload | null { try { const base64Url = token.split('.')[1]; const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); @@ -55,7 +77,7 @@ export interface AuthState { refreshUser: () => Promise; clearAuth: () => void; updateUser: (updates: Partial) => void; - syncWithSession: (sessionUser: any) => Promise; + syncWithSession: (sessionUser: SessionUser) => Promise; } // Safe localStorage access for SSR @@ -251,7 +273,7 @@ export const useAuthStore = create()( if (sessionUser && sessionUser.accessToken) { try { // Use the access token to fetch fresh user data - const user = await getMe(sessionUser.accessToken); + const user = await getMe(sessionUser.accessToken || ''); const transformedUser: User = { id: (user._id || user.id) as string, @@ -282,8 +304,8 @@ export const useAuthStore = create()( const fallbackUser: User = { id: userId || 'unknown', email: sessionUser.email, - name: sessionUser.name, - image: sessionUser.image, + name: sessionUser.name || null, + image: sessionUser.image || null, role: 'USER', }; diff --git a/package-lock.json b/package-lock.json index c252432b1..2f98b7579 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "js-cookie": "^3.0.5", "lucide-react": "^0.525.0", "next": "15.4.4", - "next-auth": "^4.24.11", + "next-auth": "^5.0.0-beta.29", "next-themes": "^0.4.6", "react": "19.1.0", "react-day-picker": "^9.8.0", @@ -118,6 +118,35 @@ "node": ">=6.0.0" } }, + "node_modules/@auth/core": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.40.0.tgz", + "integrity": "sha512-n53uJE0RH5SqZ7N1xZoMKekbHfQgjd0sAEyUbE+IYJnmuQkbvuZnXItCU7d+i7Fj8VGOgqvNO7Mw4YfBTlZeQw==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/@babel/runtime": { "version": "7.28.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", @@ -9257,15 +9286,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cookie-es": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", @@ -12132,9 +12152,9 @@ } }, "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.13.tgz", + "integrity": "sha512-Yms4GpbmdANamS51kKK6w4hRlKx8KTxbWyAAKT/MhUMtqbIqh5mb2HjhTNUbk7TFL8/MBB5zWSDohL7ed4k/UA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -13368,30 +13388,25 @@ } }, "node_modules/next-auth": { - "version": "4.24.11", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", - "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "version": "5.0.0-beta.29", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.29.tgz", + "integrity": "sha512-Ukpnuk3NMc/LiOl32njZPySk7pABEzbjhMUFd5/n10I0ZNC7NCuVv8IY2JgbDek2t/PUOifQEoUiOOTLy4os5A==", "license": "ISC", "dependencies": { - "@babel/runtime": "^7.20.13", - "@panva/hkdf": "^1.0.2", - "cookie": "^0.7.0", - "jose": "^4.15.5", - "oauth": "^0.9.15", - "openid-client": "^5.4.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" + "@auth/core": "0.40.0" }, "peerDependencies": { - "@auth/core": "0.34.2", - "next": "^12.2.5 || ^13 || ^14 || ^15", + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0-0", "nodemailer": "^6.6.5", - "react": "^17.0.2 || ^18 || ^19", - "react-dom": "^17.0.2 || ^18 || ^19" + "react": "^18.2.0 || ^19.0.0-0" }, "peerDependenciesMeta": { - "@auth/core": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { "optional": true }, "nodemailer": { @@ -13495,11 +13510,14 @@ "node": ">=0.10.0" } }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==", - "license": "MIT" + "node_modules/oauth4webapi": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.7.0.tgz", + "integrity": "sha512-Q52wTPUWPsVLVVmTViXPQFMW2h2xv2jnDGxypjpelCFKaOjLsm7AxYuOk1oQgFm95VNDbuggasu9htXrz6XwKw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } }, "node_modules/object-assign": { "version": "4.1.1", @@ -13510,15 +13528,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -13657,15 +13666,6 @@ "ufo": "^1.5.4" } }, - "node_modules/oidc-token-hash": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz", - "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/on-exit-leak-free": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", @@ -13697,39 +13697,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", - "license": "MIT", - "dependencies": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/openid-client/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/openid-client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -13963,9 +13930,9 @@ } }, "node_modules/preact": { - "version": "10.27.0", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.0.tgz", - "integrity": "sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==", + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", "license": "MIT", "funding": { "type": "opencollective", @@ -13973,13 +13940,10 @@ } }, "node_modules/preact-render-to-string": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", - "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", "license": "MIT", - "dependencies": { - "pretty-format": "^3.8.0" - }, "peerDependencies": { "preact": ">=10" } @@ -14023,12 +13987,6 @@ "node": ">=6.0.0" } }, - "node_modules/pretty-format": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", - "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", - "license": "MIT" - }, "node_modules/process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", diff --git a/public/campaign-banner.svg b/public/campaign-banner.svg new file mode 100644 index 000000000..0ba8da427 --- /dev/null +++ b/public/campaign-banner.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/empty/campaignempty.svg b/public/empty/campaignempty.svg new file mode 100644 index 000000000..4a72569c0 --- /dev/null +++ b/public/empty/campaignempty.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +