Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
47388ba
refactor: update project components and API for voting functionality
0xdevcollins Oct 11, 2025
72e1dfa
chore: update project dependencies and enhance project details layout
0xdevcollins Oct 12, 2025
78a746f
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 12, 2025
1343b64
feat: added static api for staging
0xdevcollins Oct 12, 2025
a0d3a84
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 12, 2025
79b70a7
fix: made few changes
0xdevcollins Oct 12, 2025
835992e
fix: minor changes
0xdevcollins Oct 12, 2025
369a9d2
feat: chaged test data
0xdevcollins Oct 12, 2025
c45d3bb
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 12, 2025
ec6b0b6
fix: minor fixes
0xdevcollins Oct 12, 2025
a8178de
refactor: update project components and improve functionality
0xdevcollins Oct 14, 2025
ce94b92
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 14, 2025
280db98
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 16, 2025
6cc94ac
feat: file clean up
0xdevcollins Oct 16, 2025
45e9984
feat: added extra validation
0xdevcollins Oct 16, 2025
6fdb5b4
feat: integrate wallet protection and validation across components
0xdevcollins Oct 16, 2025
51fa607
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 18, 2025
b711ef9
feat: enhance profile components and add activity tracking
0xdevcollins Oct 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/(landing)/me/profile-data.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getMe } from '@/lib/api/auth';
import { auth } from '@/auth';
import ProfileOverview from '@/components/profile/ProfileOverview';
import ProfileDataClient from '@/components/profile/ProfileDataClient';

export async function ProfileData() {
const session = await auth();
Expand All @@ -17,9 +17,9 @@ export async function ProfileData() {
try {
const userData = await getMe(session.user.accessToken);
return (
<ProfileOverview
username={session.user.username || 'me'}
<ProfileDataClient
user={userData}
username={session.user.username || 'me'}
/>
);
} catch (error) {
Expand Down
4 changes: 3 additions & 1 deletion components/ProjectCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface ProjectCardProps {
onValidationClick?: () => void;
onVoteClick?: () => void;
className?: string;
isFullWidth?: boolean;
}

const ProjectCard: React.FC<ProjectCardProps> = memo(
Expand All @@ -28,6 +29,7 @@ const ProjectCard: React.FC<ProjectCardProps> = memo(
creatorName = 'Creator Name',
creatorAvatar,
className = '',
isFullWidth = false,
}) => {
const cardRef = useRef<HTMLDivElement>(null);
const imageRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -160,7 +162,7 @@ const ProjectCard: React.FC<ProjectCardProps> = memo(
return (
<div
ref={cardRef}
className={`group mx-auto w-full max-w-[397px] cursor-pointer overflow-hidden rounded-[8px] border border-[#2B2B2B] bg-[#030303] p-3 will-change-transform sm:p-5 ${className}`}
className={`group mx-auto ${isFullWidth ? 'w-full' : 'w-full max-w-[397px]'} cursor-pointer overflow-hidden rounded-[8px] border border-[#2B2B2B] bg-[#030303] p-3 will-change-transform sm:p-5 ${className}`}
style={{ transform: 'translateZ(0)' }}
>
<div className='mb-3 flex items-center justify-between sm:mb-4'>
Expand Down
252 changes: 252 additions & 0 deletions components/profile/ActivityFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
'use client';

import { useState } from 'react';
import { ChevronDown } from 'lucide-react';
import Image from 'next/image';

interface ActivityItem {
id: string;
type: 'comment' | 'back' | 'add' | 'submit' | 'apply' | 'reply';
description: string;
projectName?: string;
amount?: string;
hackathonName?: string;
grantName?: string;
timestamp: string;
emoji?: string;
image?: string;
}

const mockActivities: ActivityItem[] = [
// TODAY
{
id: '1',
type: 'comment',
description: 'Commented on project:',
projectName: 'Bitmed',
emoji: '💡🌍',
timestamp: '2 mins ago',
},
{
id: '2',
type: 'back',
description: 'Backed project:',
projectName: 'SolarPay',
amount: '$250 USDC',
timestamp: '10 mins ago',
},
{
id: '3',
type: 'add',
description: 'Added new project:',
projectName: 'EduChain',
timestamp: '30 mins ago',
},
{
id: '4',
type: 'submit',
description: 'Submitted project:',
projectName: 'EduChain',
hackathonName: 'DeFi Innovators Hackathon',
timestamp: '11 h',
},
// YESTERDAY
{
id: '5',
type: 'comment',
description: 'Commented on project:',
projectName: 'EduChain',
emoji: '📱',
timestamp: 'Yesterday',
},
{
id: '6',
type: 'back',
description: 'Backed project:',
projectName: 'AgriChain',
amount: '$75 USDC',
timestamp: 'Yesterday',
},
// THIS WEEK
{
id: '7',
type: 'apply',
description: 'Applied for grant:',
grantName: 'Open Finance Fund',
timestamp: '2d',
},
{
id: '8',
type: 'submit',
description: 'Submitted project to hackathon:',
hackathonName: 'Global Impact Hack 2025',
timestamp: '2d',
},
{
id: '9',
type: 'reply',
description: 'Replied to comment on',
projectName: 'SolarPay',
emoji: '🔜',
timestamp: '3d',
},
{
id: '10',
type: 'back',
description: 'Backed project:',
projectName: 'GreenGrid',
amount: '$500 USDC',
timestamp: '3d',
},
];

const getActivityDescription = (activity: ActivityItem) => {
let description = activity.description;

if (activity.projectName) {
description += ` ${activity.projectName}`;
}

if (
activity.description.includes('to hackathon:') &&
activity.hackathonName
) {
description += ` ${activity.hackathonName}`;
}

if (activity.amount) {
description += ` — Funded ${activity.amount}`;
}

if (activity.emoji) {
description += ` ${activity.emoji}`;
}

return description;
};

interface ActivityFeedProps {
filter: string;
}

export default function ActivityFeed({ filter }: ActivityFeedProps) {
const [showAll, setShowAll] = useState(false);

const todayActivities = mockActivities.slice(0, 4);
const yesterdayActivities = mockActivities.slice(4, 6);
const weekActivities = mockActivities.slice(6, 10);

// Filter activities based on selected filter
const getFilteredActivities = () => {
switch (filter) {
case 'Today':
return [{ title: 'TODAY', activities: todayActivities }];
case 'Yesterday':
return [{ title: 'YESTERDAY', activities: yesterdayActivities }];
case 'This Week':
return [
{ title: 'TODAY', activities: todayActivities },
{ title: 'YESTERDAY', activities: yesterdayActivities },
{ title: 'THIS WEEK', activities: weekActivities },
];
case 'This Month':
return [
{ title: 'TODAY', activities: todayActivities },
{ title: 'YESTERDAY', activities: yesterdayActivities },
{ title: 'THIS WEEK', activities: weekActivities },
];
case 'This Year':
return [
{ title: 'TODAY', activities: todayActivities },
{ title: 'YESTERDAY', activities: yesterdayActivities },
{ title: 'THIS WEEK', activities: weekActivities },
];
case 'All Time':
return [
{ title: 'TODAY', activities: todayActivities },
{ title: 'YESTERDAY', activities: yesterdayActivities },
{ title: 'THIS WEEK', activities: weekActivities },
];
default: // 'All'
return [
{ title: 'TODAY', activities: todayActivities },
{ title: 'YESTERDAY', activities: yesterdayActivities },
{ title: 'THIS WEEK', activities: weekActivities },
];
}
};

const groupedActivities = getFilteredActivities();

return (
<div className='space-y-6'>
{groupedActivities.map((group, groupIndex) => (
<div key={groupIndex}>
<h3 className='mb-4 text-sm text-white'>{group.title}</h3>
<div className='ml-5 space-y-4'>
{group.activities.map(activity => (
<div key={activity.id} className='flex items-start gap-3'>
<Image
src={activity.image || '/avatar.png'}
alt={activity.projectName || 'Project Image'}
width={20}
height={20}
className='h-8 w-8 rounded-full'
/>
<div className='flex-1'>
<p className='text-sm text-white'>
{getActivityDescription(activity)}
</p>
<p className='mt-1 text-xs text-gray-400'>
{activity.timestamp}
</p>
</div>
</div>
))}
</div>
</div>
))}

{showAll && filter === 'All' && (
<div>
<h3 className='mb-4 text-sm text-white'>EARLIER</h3>
<div className='ml-5 space-y-4'>
{mockActivities.slice(10).map(activity => (
<div key={activity.id} className='flex items-start gap-3'>
<Image
src={activity.image || '/admin.png'}
alt={activity.projectName || 'Project Image'}
width={20}
height={20}
className='h-8 w-8 rounded-full'
/>
<div className='flex-1'>
<p className='text-sm text-white'>
{getActivityDescription(activity)}
</p>
<p className='mt-1 text-xs text-gray-400'>
{activity.timestamp}
</p>
</div>
</div>
))}
</div>
</div>
)}

{filter === 'All' && (
<div className='flex justify-center pt-4'>
<button
onClick={() => setShowAll(!showAll)}
className='flex items-center gap-2 text-gray-400 transition-colors hover:text-white'
>
{showAll ? 'Show Less' : 'Show More'}
<ChevronDown
className={`h-4 w-4 transition-transform ${showAll ? 'rotate-180' : ''}`}
/>
</button>
</div>
)}
</div>
);
}
Loading
Loading