+
@@ -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.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 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 @@
+