Skip to content

Commit 931dc45

Browse files
authored
Collins (#335)
* refactor: update project components and API for voting functionality - Adjust layout and styling in ProjectsPage and ProjectCard components. - Enhance ProjectSidebar to include crowdfund data and voting logic. - Implement new API methods for voting, retrieving votes, and removing votes. - Update types for vote handling in the API. - Clean up unused code and improve responsiveness across components. * chore: update project dependencies and enhance project details layout * feat: added static api for staging * fix: made few changes * fix: minor changes * feat: chaged test data * fix: minor fixes * refactor: update project components and improve functionality * feat: file clean up * feat: added extra validation * feat: integrate wallet protection and validation across components * feat: enhance profile components and add activity tracking - Refactor ProfileData to use ProfileDataClient for improved user data display. - Introduce ActivityFeed and ActivityTab components for tracking user activities. - Implement filtering and sorting functionality in the new ProjectsTab and FilterControls components. - Add modal support for displaying followers and following lists. - Create reusable TeamList component for displaying team members across various sections. - Integrate mock data for testing and development purposes. - Update UserStats to include clickable follower and following counts for better user interaction. * feat: add lodash.debounce and enhance profile components with user data integration - Added lodash.debounce for improved performance in user interactions. - Updated ProfileDataClient to utilize user data for displaying activities and stats. - Refactored ActivityFeed and ActivityTab components to fetch real user activities and statistics. - Removed mock data dependencies from FollowersModal and ProjectsTab, integrating real user data instead. - Enhanced ProjectsTab to support infinite scrolling and dynamic project loading based on user data. * feat: enhance navbar and project components for improved responsiveness and user experience - Integrated useWindowSize hook to adjust component layouts and styles based on screen size. - Updated Navbar component to improve spacing and responsiveness for various screen sizes. - Refactored ProjectCard to support full-width display and adjusted styles accordingly. - Enhanced ProjectList and ProjectsTab to utilize updated ProjectCard properties and improve project status mapping. - Improved MobileMenu and AuthenticatedNav for better user interaction and accessibility. * feat: enhance organization layout and settings components for improved user experience - Updated OrganizationsLayout to include relative positioning for better layout control. - Refactored OrganizationContent to replace Button with BoundlessButton for consistency. - Enhanced OrganizationHeader with sticky positioning and improved link styling. - Introduced new tabs in OrganizationSettings for Profile, Links, Members, and Transfer Ownership, improving organization management. - Added new components for managing organization links and members, including EmailInviteSection and PermissionsTable. - Improved styling and layout across various components for better responsiveness and user interaction. * feat: implemeted the organisation features
1 parent be2dbfe commit 931dc45

25 files changed

Lines changed: 4002 additions & 315 deletions

app/(landing)/organizations/[id]/grants/page.tsx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
1-
'use client';
2-
3-
import { useParams } from 'next/navigation';
4-
import OrganizationHeader from '@/components/organization/OrganizationHeader';
5-
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';
6-
71
export default function GrantsPage() {
8-
const params = useParams();
9-
const organizationId = params.id as string;
10-
112
return (
123
<div className='min-h-screen bg-black text-white'>
13-
<OrganizationHeader />
144
<div className='flex'>
15-
<OrganizationSidebar organizationId={organizationId} />
165
<main className='flex-1 p-8'>
176
<h1 className='text-2xl font-bold'>Grants</h1>
187
<p className='mt-2 text-zinc-400'>Manage your grants here</p>

app/(landing)/organizations/[id]/hackathons/page.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
'use client';
22

3-
import { useParams } from 'next/navigation';
4-
import OrganizationHeader from '@/components/organization/OrganizationHeader';
5-
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';
6-
73
export default function HackathonsPage() {
8-
const params = useParams();
9-
const organizationId = params.id as string;
10-
114
return (
125
<div className='min-h-screen bg-black text-white'>
13-
<OrganizationHeader />
146
<div className='flex'>
15-
<OrganizationSidebar organizationId={organizationId} />
167
<main className='flex-1 p-8'>
178
<h1 className='text-2xl font-bold'>Hackathons</h1>
189
<p className='mt-2 text-zinc-400'>Manage your hackathons here</p>

app/(landing)/organizations/[id]/settings/page.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
'use client';
22

33
import { useParams } from 'next/navigation';
4-
import OrganizationHeader from '@/components/organization/OrganizationHeader';
5-
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';
64
import OrganizationSettings from '@/components/organization/OrganizationSettings';
75

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

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

2017
return (
2118
<div className='min-h-screen bg-black text-white'>
22-
<OrganizationHeader />
2319
<div className='flex'>
24-
<OrganizationSidebar organizationId={organizationId} />
2520
<OrganizationSettings
2621
organizationId={organizationId}
2722
initialData={mockOrgData}

app/(landing)/organizations/layout.tsx

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type React from 'react';
55
import OrganizationHeader from '@/components/organization/OrganizationHeader';
66
import OrganizationSidebar from '@/components/organization/OrganizationSidebar';
77
import { usePathname } from 'next/navigation';
8+
import { OrganizationProvider } from '@/lib/providers';
89

910
export default function OrganizationsLayout({
1011
children,
@@ -14,20 +15,34 @@ export default function OrganizationsLayout({
1415
const pathname = usePathname();
1516

1617
const showSidebar =
17-
pathname.includes('/new') ||
18-
(pathname.split('/').length > 4 && pathname !== '/dashboard/organizations');
18+
pathname !== '/organizations' && pathname.startsWith('/organizations');
19+
20+
const getOrgIdFromPath = () => {
21+
if (pathname.startsWith('/organizations/')) {
22+
const pathParts = pathname.split('/');
23+
const orgId = pathParts[2];
24+
if (orgId && /^[a-f0-9]{24}$/.test(orgId)) {
25+
return orgId;
26+
}
27+
}
28+
return null;
29+
};
30+
31+
const initialOrgId = getOrgIdFromPath();
1932

2033
return (
21-
<div className='relative min-h-screen bg-black text-white'>
22-
<OrganizationHeader />
23-
{showSidebar ? (
24-
<div className='relative border-t border-t-zinc-800'>
25-
<OrganizationSidebar />
26-
<main className='md:ml-[350px]'>{children}</main>
27-
</div>
28-
) : (
29-
<main>{children}</main>
30-
)}
31-
</div>
34+
<OrganizationProvider initialOrgId={initialOrgId || undefined}>
35+
<div className='relative min-h-screen bg-black text-white'>
36+
<OrganizationHeader />
37+
{showSidebar ? (
38+
<div className='relative border-t border-t-zinc-800'>
39+
<OrganizationSidebar />
40+
<main className='md:ml-[350px]'>{children}</main>
41+
</div>
42+
) : (
43+
<main>{children}</main>
44+
)}
45+
</div>
46+
</OrganizationProvider>
3247
);
3348
}

app/globals.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@
5757
--color-success-900: #004617;
5858

5959
--color-active-bg: rgba(167, 249, 80, 0.08);
60+
--color-active-bg2: rgba(167, 249, 80, 0.32);
6061
}
61-
/* Custom scrollbar styles for comment modal */
62+
6263
.custom-scrollbar {
6364
scrollbar-width: thin;
6465
scrollbar-color: #4a4a4a #1a1a1a;

components/organization/OrganizationContent.tsx

Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,14 @@ import { Input } from '@/components/ui/input';
55
import OrganizationCard from './cards/OrganzationCards';
66
import Link from 'next/link';
77
import { BoundlessButton } from '../buttons';
8-
9-
const mockOrganizations = [
10-
{
11-
id: '1',
12-
name: 'Tech Innovators Hub',
13-
logo: '/organization-logo.svg',
14-
createdAt: '2 days ago',
15-
hackathons: { count: 3, submissions: 1 },
16-
grants: { count: 7, applications: 3 },
17-
},
18-
{
19-
id: '2',
20-
name: 'Green Energy Initiative',
21-
logo: '/organization-logo.svg',
22-
createdAt: '1 week ago',
23-
hackathons: { count: 5, submissions: 2 },
24-
grants: { count: 12, applications: 8 },
25-
},
26-
{
27-
id: '3',
28-
name: 'AI Research Collective',
29-
logo: '/organization-logo.svg',
30-
createdAt: '3 weeks ago',
31-
hackathons: { count: 8, submissions: 5 },
32-
grants: { count: 15, applications: 10 },
33-
},
34-
{
35-
id: '4',
36-
name: 'Community Builders Network',
37-
logo: '/organization-logo.svg',
38-
createdAt: '1 month ago',
39-
hackathons: { count: 2, submissions: 0 },
40-
grants: { count: 4, applications: 2 },
41-
},
42-
];
8+
import { useOrganization } from '@/lib/providers/OrganizationProvider';
439

4410
export default function OrganizationContent() {
45-
const hasorganizations = mockOrganizations.length > 0;
11+
const { organizations } = useOrganization();
12+
const hasOrganizations = organizations.length > 0;
4613
return (
4714
<main className=''>
48-
{/* Search and Sort Bar */}
49-
{hasorganizations && (
15+
{hasOrganizations && (
5016
<section className='mb-8 hidden border-b border-b-zinc-800 px-8 md:block'>
5117
<div className='mx-auto flex max-w-5xl items-center gap-4'>
5218
<div className='relative flex-1'>
@@ -64,7 +30,7 @@ export default function OrganizationContent() {
6430
<ArrowUpDown className='h-4 w-4' />
6531
Sort
6632
</Button>
67-
{hasorganizations && (
33+
{hasOrganizations && (
6834
<div className='flex h-23 items-center border-l border-l-zinc-800 pl-4'>
6935
<Link href='/organizations/new'>
7036
<BoundlessButton
@@ -83,21 +49,27 @@ export default function OrganizationContent() {
8349
)}
8450

8551
<section className='mx-auto max-w-5xl px-8 py-8 md:px-0'>
86-
{/* Add Organization Section */}
87-
{hasorganizations && (
52+
{hasOrganizations && (
8853
<div className='grid grid-cols-1 gap-6'>
89-
{mockOrganizations.map(org => (
90-
<Link href={`/organization/${org.id}`} key={org.id}>
91-
<OrganizationCard {...org} />
54+
{organizations.map(org => (
55+
<Link href={`/organizations/${org._id}/settings`} key={org._id}>
56+
<OrganizationCard
57+
id={org._id}
58+
name={org.name}
59+
logo={org.logo || ''}
60+
createdAt={org.createdAt}
61+
hackathons={{ count: 0, submissions: 0 }}
62+
grants={{ count: 0, applications: 0 }}
63+
/>
9264
</Link>
9365
))}
9466
</div>
9567
)}
9668

97-
{!hasorganizations && (
98-
<div className='mx-8 flex items-center justify-center rounded-lg border-1 border-dashed border-green-600 p-32'>
69+
{!hasOrganizations && (
70+
<div className='border-active-bg2 mx-8 flex items-center justify-center rounded-lg border-1 border-dashed p-32'>
9971
<Link href='/organizations/new'>
100-
<button className='text-primary flex items-center gap-2 font-medium transition-colors hover:text-lime-400'>
72+
<button className='text-primary hover:text-primary flex items-center gap-2 font-medium transition-colors'>
10173
<span>Add Organization</span>
10274
<Plus className='h-5 w-5' size={100} />
10375
</button>

components/organization/OrganizationHeader.tsx

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ import { useAuthActions, useAuthStatus } from '@/hooks/use-auth';
1414
import Link from 'next/link';
1515
import { usePathname } from 'next/navigation';
1616
import OrganizationSelector from './cards/OrganizationSelector';
17+
import { useOrganization } from '@/lib/providers/OrganizationProvider';
1718

1819
export default function OrganizationHeader() {
1920
const { isLoading, user } = useAuthStatus();
2021
const { logout } = useAuthActions();
2122
const pathname = usePathname();
2223
const isOnOrganizationsPage = pathname === '/dashboard/organizations';
23-
24+
const { organizations, activeOrg, setActiveOrg } = useOrganization();
2425
const showOrgSelector =
25-
pathname.includes('/new') ||
26-
(pathname.split('/').length > 4 && pathname !== '/dashboard/organizations');
26+
pathname !== '/organizations' && pathname.startsWith('/organizations');
2727

2828
return (
2929
<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'>
@@ -37,18 +37,37 @@ export default function OrganizationHeader() {
3737
/>
3838
</div>
3939

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

48-
{showOrgSelector && <OrganizationSelector />}
47+
{showOrgSelector && organizations && organizations.length > 0 && (
48+
<OrganizationSelector
49+
organizations={organizations}
50+
currentOrganization={
51+
activeOrg
52+
? {
53+
_id: activeOrg._id,
54+
name: activeOrg.name,
55+
logo: activeOrg.logo,
56+
tagline: activeOrg.tagline,
57+
isProfileComplete: activeOrg.isProfileComplete,
58+
role: 'owner',
59+
memberCount: activeOrg.members.length,
60+
hackathonCount: activeOrg.hackathons.length,
61+
grantCount: activeOrg.grants.length,
62+
createdAt: activeOrg.createdAt,
63+
}
64+
: undefined
65+
}
66+
onOrganizationChange={orgId => setActiveOrg(orgId)}
67+
/>
68+
)}
4969
</div>
5070

51-
{/* User Avatar Dropdown */}
5271
<DropdownMenu>
5372
<DropdownMenuTrigger asChild>
5473
<button className='flex items-center space-x-2 rounded-full p-1 transition-colors hover:bg-white/10'>
@@ -58,10 +77,6 @@ export default function OrganizationHeader() {
5877
alt={user?.name || user?.profile?.firstName || ''}
5978
/>
6079
<AvatarFallback>
61-
{/* {user?.name?.charAt(0) ||
62-
user?.profile?.firstName?.charAt(0) ||
63-
user?.email?.charAt(0) ||
64-
'U'} */}
6580
<Image
6681
src={
6782
user?.image ||
@@ -115,7 +130,7 @@ export default function OrganizationHeader() {
115130
asChild
116131
>
117132
<Link
118-
href='/dashboard/organizations'
133+
href='/organizations'
119134
className='group-hover:text-primary flex items-center'
120135
>
121136
<Building2 className='group-hover:!text-primary mr-2 h-4 w-4 text-white' />

components/organization/OrganizationPage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
'use client';
2-
// import OrganizationHeader from './OrganizationHeader';
32
import OrganizationContent from './OrganizationContent';
43

54
export default function OrganizationPage() {
65
return (
76
<div className='min-h-screen bg-black text-white'>
8-
{/* <OrganizationHeader /> */}
97
<OrganizationContent />
108
</div>
119
);

components/organization/OrganizationSidebar.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,42 @@ export default function OrganizationSidebar({
1616
const pathname = usePathname();
1717
const { height } = useWindowSize();
1818

19+
const derivedOrgId =
20+
organizationId ||
21+
(() => {
22+
if (!pathname) return undefined;
23+
const parts = pathname.split('/');
24+
if (parts.length >= 3 && parts[1] === 'organizations') {
25+
return parts[2];
26+
}
27+
return undefined;
28+
})();
29+
30+
const normalizedPath =
31+
pathname?.endsWith('/') && pathname !== '/'
32+
? pathname.slice(0, -1)
33+
: pathname;
34+
1935
const menuItems = [
2036
{
2137
icon: Trophy,
2238
label: 'Hackathons',
23-
href: organizationId
24-
? `/organizations/${organizationId}/hackathons`
25-
: '#',
39+
href: derivedOrgId ? `/organizations/${derivedOrgId}/hackathons` : '#',
2640
},
2741
{
2842
icon: HandCoins,
2943
label: 'Grants',
30-
href: organizationId ? `/organizations/${organizationId}/grants` : '#',
44+
href: derivedOrgId ? `/organizations/${derivedOrgId}/grants` : '#',
3145
},
3246
{
3347
icon: Settings,
3448
label: 'Settings',
35-
href: organizationId
36-
? `/organizations/${organizationId}/settings`
49+
href: derivedOrgId
50+
? `/organizations/${derivedOrgId}/settings`
3751
: '/organizations/new',
3852
},
3953
];
4054

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

@@ -54,8 +66,11 @@ export default function OrganizationSidebar({
5466
</h3>
5567
{menuItems.map(item => {
5668
const Icon = item.icon;
69+
const isValidHref = item.href !== '#';
5770
const isActive =
58-
pathname === item.href || pathname?.startsWith(item.href + '/');
71+
isValidHref &&
72+
(normalizedPath === item.href ||
73+
normalizedPath?.startsWith(item.href + '/'));
5974

6075
return (
6176
<Link

0 commit comments

Comments
 (0)