Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 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
17448eb
feat: add lodash.debounce and enhance profile components with user da…
0xdevcollins Oct 19, 2025
87ad44c
feat: enhance navbar and project components for improved responsivene…
0xdevcollins Oct 19, 2025
8832100
feat: enhance organization layout and settings components for improve…
0xdevcollins Oct 19, 2025
796cdfa
Merge branch 'main' of github.com:0xdevcollins/boundless into collins
0xdevcollins Oct 19, 2025
7f893ba
feat: implemeted the organisation features
0xdevcollins Oct 20, 2025
b662edf
feat: implemeted the organisation features
0xdevcollins Oct 20, 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
11 changes: 0 additions & 11 deletions app/(landing)/organizations/[id]/grants/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
'use client';

import { useParams } from 'next/navigation';
import OrganizationHeader from '@/components/organization/OrganizationHeader';
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';

export default function GrantsPage() {
const params = useParams();
const organizationId = params.id as string;

return (
<div className='min-h-screen bg-black text-white'>
<OrganizationHeader />
<div className='flex'>
<OrganizationSidebar organizationId={organizationId} />
<main className='flex-1 p-8'>
<h1 className='text-2xl font-bold'>Grants</h1>
<p className='mt-2 text-zinc-400'>Manage your grants here</p>
Expand Down
9 changes: 0 additions & 9 deletions app/(landing)/organizations/[id]/hackathons/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
'use client';

import { useParams } from 'next/navigation';
import OrganizationHeader from '@/components/organization/OrganizationHeader';
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';

export default function HackathonsPage() {
const params = useParams();
const organizationId = params.id as string;

return (
<div className='min-h-screen bg-black text-white'>
<OrganizationHeader />
<div className='flex'>
<OrganizationSidebar organizationId={organizationId} />
<main className='flex-1 p-8'>
<h1 className='text-2xl font-bold'>Hackathons</h1>
<p className='mt-2 text-zinc-400'>Manage your hackathons here</p>
Expand Down
7 changes: 1 addition & 6 deletions app/(landing)/organizations/[id]/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
'use client';

import { useParams } from 'next/navigation';
import OrganizationHeader from '@/components/organization/OrganizationHeader';
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';
import OrganizationSettings from '@/components/organization/OrganizationSettings';

export default function OrganizationSettingsPage() {
const params = useParams();
const organizationId = params.id as string;

// TODO: Fetch organization data based on ID
const mockOrgData = {
name: 'Tech Innovators Hub',
name: 'Boundless',
logo: '/tech-company-logo.jpg',
tagline: 'Building the future of technology',
about: 'We are a community of innovators...',
};

return (
<div className='min-h-screen bg-black text-white'>
<OrganizationHeader />
<div className='flex'>
<OrganizationSidebar organizationId={organizationId} />
<OrganizationSettings
organizationId={organizationId}
initialData={mockOrgData}
Expand Down
41 changes: 28 additions & 13 deletions app/(landing)/organizations/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type React from 'react';
import OrganizationHeader from '@/components/organization/OrganizationHeader';
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';
import { usePathname } from 'next/navigation';
import { OrganizationProvider } from '@/lib/providers';

export default function OrganizationsLayout({
children,
Expand All @@ -14,20 +15,34 @@ export default function OrganizationsLayout({
const pathname = usePathname();

const showSidebar =
pathname.includes('/new') ||
(pathname.split('/').length > 4 && pathname !== '/dashboard/organizations');
pathname !== '/organizations' && pathname.startsWith('/organizations');

const getOrgIdFromPath = () => {
if (pathname.startsWith('/organizations/')) {
const pathParts = pathname.split('/');
const orgId = pathParts[2];
if (orgId && /^[a-f0-9]{24}$/.test(orgId)) {
return orgId;
}
}
return null;
};

const initialOrgId = getOrgIdFromPath();

return (
<div className='relative min-h-screen bg-black text-white'>
<OrganizationHeader />
{showSidebar ? (
<div className='relative border-t border-t-zinc-800'>
<OrganizationSidebar />
<main className='md:ml-[350px]'>{children}</main>
</div>
) : (
<main>{children}</main>
)}
</div>
<OrganizationProvider initialOrgId={initialOrgId || undefined}>
<div className='relative min-h-screen bg-black text-white'>
<OrganizationHeader />
{showSidebar ? (
<div className='relative border-t border-t-zinc-800'>
<OrganizationSidebar />
<main className='md:ml-[350px]'>{children}</main>
</div>
) : (
<main>{children}</main>
)}
</div>
</OrganizationProvider>
);
}
3 changes: 2 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@
--color-success-900: #004617;

--color-active-bg: rgba(167, 249, 80, 0.08);
--color-active-bg2: rgba(167, 249, 80, 0.32);
}
/* Custom scrollbar styles for comment modal */

.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: #4a4a4a #1a1a1a;
Expand Down
66 changes: 19 additions & 47 deletions components/organization/OrganizationContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,14 @@ import { Input } from '@/components/ui/input';
import OrganizationCard from './cards/OrganzationCards';
import Link from 'next/link';
import { BoundlessButton } from '../buttons';

const mockOrganizations = [
{
id: '1',
name: 'Tech Innovators Hub',
logo: '/organization-logo.svg',
createdAt: '2 days ago',
hackathons: { count: 3, submissions: 1 },
grants: { count: 7, applications: 3 },
},
{
id: '2',
name: 'Green Energy Initiative',
logo: '/organization-logo.svg',
createdAt: '1 week ago',
hackathons: { count: 5, submissions: 2 },
grants: { count: 12, applications: 8 },
},
{
id: '3',
name: 'AI Research Collective',
logo: '/organization-logo.svg',
createdAt: '3 weeks ago',
hackathons: { count: 8, submissions: 5 },
grants: { count: 15, applications: 10 },
},
{
id: '4',
name: 'Community Builders Network',
logo: '/organization-logo.svg',
createdAt: '1 month ago',
hackathons: { count: 2, submissions: 0 },
grants: { count: 4, applications: 2 },
},
];
import { useOrganization } from '@/lib/providers/OrganizationProvider';

export default function OrganizationContent() {
const hasorganizations = mockOrganizations.length > 0;
const { organizations } = useOrganization();
const hasOrganizations = organizations.length > 0;
return (
<main className=''>
{/* Search and Sort Bar */}
{hasorganizations && (
{hasOrganizations && (
<section className='mb-8 hidden border-b border-b-zinc-800 px-8 md:block'>
<div className='mx-auto flex max-w-5xl items-center gap-4'>
<div className='relative flex-1'>
Expand All @@ -64,7 +30,7 @@ export default function OrganizationContent() {
<ArrowUpDown className='h-4 w-4' />
Sort
</Button>
{hasorganizations && (
{hasOrganizations && (
<div className='flex h-23 items-center border-l border-l-zinc-800 pl-4'>
<Link href='/organizations/new'>
<BoundlessButton
Expand All @@ -83,21 +49,27 @@ export default function OrganizationContent() {
)}

<section className='mx-auto max-w-5xl px-8 py-8 md:px-0'>
{/* Add Organization Section */}
{hasorganizations && (
{hasOrganizations && (
<div className='grid grid-cols-1 gap-6'>
{mockOrganizations.map(org => (
<Link href={`/organization/${org.id}`} key={org.id}>
<OrganizationCard {...org} />
{organizations.map(org => (
<Link href={`/organizations/${org._id}/settings`} key={org._id}>
<OrganizationCard
id={org._id}
name={org.name}
logo={org.logo || ''}
createdAt={org.createdAt}
hackathons={{ count: 0, submissions: 0 }}
grants={{ count: 0, applications: 0 }}
/>
</Link>
))}
</div>
)}

{!hasorganizations && (
<div className='mx-8 flex items-center justify-center rounded-lg border-1 border-dashed border-green-600 p-32'>
{!hasOrganizations && (
<div className='border-active-bg2 mx-8 flex items-center justify-center rounded-lg border-1 border-dashed p-32'>
<Link href='/organizations/new'>
<button className='text-primary flex items-center gap-2 font-medium transition-colors hover:text-lime-400'>
<button className='text-primary hover:text-primary flex items-center gap-2 font-medium transition-colors'>
<span>Add Organization</span>
<Plus className='h-5 w-5' size={100} />
</button>
Expand Down
37 changes: 26 additions & 11 deletions components/organization/OrganizationHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ import { useAuthActions, useAuthStatus } from '@/hooks/use-auth';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import OrganizationSelector from './cards/OrganizationSelector';
import { useOrganization } from '@/lib/providers/OrganizationProvider';

export default function OrganizationHeader() {
const { isLoading, user } = useAuthStatus();
const { logout } = useAuthActions();
const pathname = usePathname();
const isOnOrganizationsPage = pathname === '/dashboard/organizations';

const { organizations, activeOrg, setActiveOrg } = useOrganization();
const showOrgSelector =
pathname.includes('/new') ||
(pathname.split('/').length > 4 && pathname !== '/dashboard/organizations');
pathname !== '/organizations' && pathname.startsWith('/organizations');

return (
<header className='sticky top-0 z-50 flex items-center justify-between border-b-1 border-b-zinc-800 bg-black px-10 py-4'>
Expand All @@ -37,18 +37,37 @@ export default function OrganizationHeader() {
/>
</div>

{/* Home Link */}
<Link href='/'>
<button className='hover:text-primary/80 flex items-center gap-2 text-gray-600 transition-colors'>
<Building2 className='h-5 w-5' />
<span className='text-sm font-medium'>Home</span>
</button>
</Link>

{showOrgSelector && <OrganizationSelector />}
{showOrgSelector && organizations && organizations.length > 0 && (
<OrganizationSelector
organizations={organizations}
currentOrganization={
activeOrg
? {
_id: activeOrg._id,
name: activeOrg.name,
logo: activeOrg.logo,
tagline: activeOrg.tagline,
isProfileComplete: activeOrg.isProfileComplete,
role: 'owner',
memberCount: activeOrg.members.length,
hackathonCount: activeOrg.hackathons.length,
grantCount: activeOrg.grants.length,
createdAt: activeOrg.createdAt,
}
: undefined
}
onOrganizationChange={orgId => setActiveOrg(orgId)}
/>
)}
</div>

{/* User Avatar Dropdown */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className='flex items-center space-x-2 rounded-full p-1 transition-colors hover:bg-white/10'>
Expand All @@ -58,10 +77,6 @@ export default function OrganizationHeader() {
alt={user?.name || user?.profile?.firstName || ''}
/>
<AvatarFallback>
{/* {user?.name?.charAt(0) ||
user?.profile?.firstName?.charAt(0) ||
user?.email?.charAt(0) ||
'U'} */}
<Image
src={
user?.image ||
Expand Down Expand Up @@ -115,7 +130,7 @@ export default function OrganizationHeader() {
asChild
>
<Link
href='/dashboard/organizations'
href='/organizations'
className='group-hover:text-primary flex items-center'
>
<Building2 className='group-hover:!text-primary mr-2 h-4 w-4 text-white' />
Expand Down
2 changes: 0 additions & 2 deletions components/organization/OrganizationPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
'use client';
// import OrganizationHeader from './OrganizationHeader';
import OrganizationContent from './OrganizationContent';

export default function OrganizationPage() {
return (
<div className='min-h-screen bg-black text-white'>
{/* <OrganizationHeader /> */}
<OrganizationContent />
</div>
);
Expand Down
33 changes: 24 additions & 9 deletions components/organization/OrganizationSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,42 @@ export default function OrganizationSidebar({
const pathname = usePathname();
const { height } = useWindowSize();

const derivedOrgId =
organizationId ||
(() => {
if (!pathname) return undefined;
const parts = pathname.split('/');
if (parts.length >= 3 && parts[1] === 'organizations') {
return parts[2];
}
return undefined;
})();

const normalizedPath =
pathname?.endsWith('/') && pathname !== '/'
? pathname.slice(0, -1)
: pathname;

const menuItems = [
{
icon: Trophy,
label: 'Hackathons',
href: organizationId
? `/organizations/${organizationId}/hackathons`
: '#',
href: derivedOrgId ? `/organizations/${derivedOrgId}/hackathons` : '#',
},
{
icon: HandCoins,
label: 'Grants',
href: organizationId ? `/organizations/${organizationId}/grants` : '#',
href: derivedOrgId ? `/organizations/${derivedOrgId}/grants` : '#',
},
{
icon: Settings,
label: 'Settings',
href: organizationId
? `/organizations/${organizationId}/settings`
href: derivedOrgId
? `/organizations/${derivedOrgId}/settings`
: '/organizations/new',
},
];

// Calculate the available height (window height minus header height)
// Assuming header height is around 64px (4rem), adjust as needed
const headerHeight = 64;
const availableHeight = height ? height - headerHeight : 'calc(100vh - 4rem)';

Expand All @@ -54,8 +66,11 @@ export default function OrganizationSidebar({
</h3>
{menuItems.map(item => {
const Icon = item.icon;
const isValidHref = item.href !== '#';
const isActive =
pathname === item.href || pathname?.startsWith(item.href + '/');
isValidHref &&
(normalizedPath === item.href ||
normalizedPath?.startsWith(item.href + '/'));

return (
<Link
Expand Down
Loading
Loading