Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions app/profile/[username]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use client';
import ProfileOverview from '@/components/profile/ProfileOverview';
import { useEffect, useState } from 'react';

interface ProfilePageProps {
Expand Down Expand Up @@ -33,6 +34,7 @@ export default function ProfilePage({ params }: ProfilePageProps) {
<div className='container mx-auto px-4 py-8'>
<h1 className='text-2xl font-bold text-white'>Profile: {username}</h1>
</div>
<ProfileOverview username={username} />
</section>
);
}
24 changes: 24 additions & 0 deletions components/profile/OrganizationCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Image from 'next/image';
import { Organization } from '@/types/profile';

interface OrganizationCardProps {
organization: Organization;
}

export default function OrganizationCard({
organization,
}: OrganizationCardProps) {
return (
<div className='flex items-center gap-3 px-3'>
<div className='relative size-[46px] overflow-hidden rounded-full'>
<Image
src={organization.avatarUrl}
alt={`${organization.name} avatar`}
layout='fill'
objectFit='cover'
/>
</div>
<p className='font-base font-normal'>{organization.name}</p>
</div>
);
}
21 changes: 21 additions & 0 deletions components/profile/OrganizationsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Organization } from '@/types/profile';
import OrganizationCard from './OrganizationCard';

interface OrganizationsListProps {
organizations: Organization[];
}

export default function OrganizationsList({
organizations,
}: OrganizationsListProps) {
return (
<main className='flex flex-col gap-3'>
<h5 className='text-sm font-medium text-gray-500'>ORGANIZATIONS</h5>
<main className='flex flex-col gap-3'>
{organizations.map(org => (
<OrganizationCard key={org.name} organization={org} />
))}
</main>
</main>
);
}
65 changes: 65 additions & 0 deletions components/profile/ProfileHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Image from 'next/image';
import Link from 'next/link';
import { UserProfile } from '@/types/profile';
import { BoundlessButton } from '@/components/buttons';
import { BellPlus } from 'lucide-react';
import { ProfileSocialLinks } from '@/lib/config';

interface ProfileHeaderProps {
profile: UserProfile;
}

export default function ProfileHeader({ profile }: ProfileHeaderProps) {
return (
<main className='flex flex-col gap-6'>
<header className='flex items-end gap-4'>
<div className='relative size-[150px] overflow-hidden rounded-full bg-red-500'>
<Image
src={profile.avatarUrl}
alt={`${profile.displayName} avatar`}
layout='fill'
objectFit='cover'
/>
</div>
<div className='flex flex-col gap-3 py-3'>
<h3 className='text-2xl font-medium'>{profile.displayName}</h3>
<p className='text-base font-normal'>@{profile.username}</p>
</div>
</header>
<p className='text-base font-normal'>{profile.bio}</p>
<div className='flex items-center space-x-4'>
{Object.entries(ProfileSocialLinks).map(([name, href], index) => (
<div key={name} className='flex items-center'>
<Link
href={href}
className='rounded transition-opacity hover:opacity-80 focus:ring-2 focus:ring-white/50 focus:outline-none'
target='_blank'
rel='noopener noreferrer'
aria-label={`Follow us on ${name}`}
>
<Image
src={`/footer/${name}.svg`}
alt={`${name} icon`}
width={24}
height={24}
className='h-6 w-6'
/>
</Link>
{index < Object.keys(ProfileSocialLinks).length - 1 && (
<div className='ml-4 h-6 w-0.5 bg-[#2B2B2B]' aria-hidden='true' />
)}
</div>
))}
</div>
<div className='flex gap-4'>
<BoundlessButton>
Follow <BellPlus />
</BoundlessButton>
<BoundlessButton variant='outline'>
share{' '}
<Image src='/share.svg' alt='Share icon' width={16} height={16} />
</BoundlessButton>
</div>
</main>
);
}
48 changes: 48 additions & 0 deletions components/profile/ProfileOverview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client';

import ProfileHeader from './ProfileHeader';
import UserStats from './UserStats';
import OrganizationsList from './OrganizationsList';
import {
UserProfile,
UserStats as UserStatsType,
Organization,
} from '@/types/profile';

interface ProfileOverviewProps {
username: string;
}

export default function ProfileOverview({ username }: ProfileOverviewProps) {
const mockProfile: UserProfile = {
username: username,
displayName: 'Collins Odumeje',
bio: 'To build a secure, transparent, and trusted digital health ecosystem powered by Sonic blockchain for 280M lives in Indonesia.',
avatarUrl: '/team/45d9f6d1-7e6d-41c7-8b16-a045d36e835f.png',
socialLinks: {
twitter: 'https://twitter.com/boundless',
linkedin: 'https://linkedin.com/in/boundless',
github: 'https://github.com/boundless',
},
};

const mockStats: UserStatsType = {
organizations: 3,
projects: 3,
following: 10,
followers: 5,
};

const mockOrganizations: Organization[] = [
{ name: 'Organization 1', avatarUrl: '/blog1.jpg' },
{ name: 'Organization 2', avatarUrl: '/blog2.jpg' },
];

return (
<article className='flex w-[500px] flex-col gap-11 text-white'>
<ProfileHeader profile={mockProfile} />
<UserStats stats={mockStats} />
<OrganizationsList organizations={mockOrganizations} />
</article>
);
}
36 changes: 36 additions & 0 deletions components/profile/UserStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { UserStats as UserStatsType } from '@/types/profile';

interface UserStatsProps {
stats: UserStatsType;
}

export default function UserStats({ stats }: UserStatsProps) {
return (
<div className='flex items-center gap-8'>
<div className='flex items-center gap-1 text-xs font-medium text-gray-500'>
<span className='text-base font-medium text-white'>
{stats.organizations}
</span>
Organizations
</div>
<div className='flex items-center gap-1 text-xs font-medium text-gray-500'>
<span className='text-base font-medium text-white'>
{stats.projects}
</span>
Projects
</div>
<div className='flex items-center gap-1 text-xs font-medium text-gray-500'>
<span className='text-base font-medium text-white'>
{stats.following}
</span>
Following
</div>
<div className='flex items-center gap-1 text-xs font-medium text-gray-500'>
<span className='text-base font-medium text-white'>
{stats.followers}
</span>
Followers
</div>
</div>
);
}
7 changes: 7 additions & 0 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export const socialLinks = {
telegram: 'https://t.me/boundlessfi',
gmail: 'hello@boundlessfi.xyz',
};
export const ProfileSocialLinks = {
discord: 'https://discord.gg/boundlessfi',
telegram: 'https://t.me/boundlessfi',
github: 'https://github.com/boundlessfi',
linkedin: 'https://www.linkedin.com/company/boundlesshq/',
x: 'https://x.com/boundless_fi',
};

export const backedBy = [
{
Expand Down
105 changes: 0 additions & 105 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions public/share.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions types/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface UserProfile {
username: string;
displayName: string;
bio: string;
avatarUrl: string;
socialLinks: Record<string, string>;
}

export interface UserStats {
organizations: number;
projects: number;
following: number;
followers: number;
}

export interface Organization {
name: string;
avatarUrl: string;
}
Loading