Skip to content

Commit d169c4d

Browse files
committed
feat: enhance authentication and user profile features
- Refactored authentication logic to include error handling and logging. - Introduced new utility functions for user data extraction and validation. - Added support for token-based and credentials-based authentication. - Enhanced user profile data structure to include organizations, projects, and social links. - Created new components for user profile loading states and layout. - Updated landing page to include a button for creating crowdfunding projects. - Improved session management to populate user data in the session object. - Added custom error classes for better error handling in authentication processes.
1 parent 4396fdf commit d169c4d

30 files changed

Lines changed: 875 additions & 248 deletions
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ReactNode, Suspense } from 'react';
2+
3+
interface ProfileLayoutProps {
4+
children: ReactNode;
5+
params: Promise<{
6+
username: string;
7+
}>;
8+
}
9+
10+
export const dynamic = 'force-dynamic';
11+
12+
export default function ProfileLayout({ children }: ProfileLayoutProps) {
13+
return (
14+
<main className='flex-1 px-6 py-5 md:px-10 md:py-20 xl:px-[100px]'>
15+
<Suspense fallback={<ProfileLoadingFallback />}>{children}</Suspense>
16+
</main>
17+
);
18+
}
19+
20+
function ProfileLoadingFallback() {
21+
return (
22+
<section className='flex min-h-screen items-center justify-center'>
23+
<div className='flex flex-col items-center gap-4'>
24+
<div className='h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600'></div>
25+
<div className='text-white'>Loading profile...</div>
26+
</div>
27+
</section>
28+
);
29+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Skeleton } from '@/components/ui/skeleton';
2+
3+
export default function ProfileLoading() {
4+
return (
5+
<section className='min-h-screen'>
6+
<article className='flex w-[500px] flex-col gap-11 text-white'>
7+
{/* Profile Header Skeleton */}
8+
<main className='flex flex-col gap-6'>
9+
<header className='flex items-end gap-4'>
10+
<Skeleton className='aspect-square size-[150px] rounded-full' />
11+
<div className='flex flex-col gap-3 py-3'>
12+
<Skeleton className='h-8 w-48' />
13+
<Skeleton className='h-4 w-32' />
14+
</div>
15+
</header>
16+
<Skeleton className='h-4 w-full' />
17+
<Skeleton className='h-4 w-3/4' />
18+
19+
{/* Social Links Skeleton */}
20+
<div className='flex items-center space-x-4'>
21+
{[1, 2, 3, 4].map(i => (
22+
<Skeleton key={i} className='h-6 w-6 rounded' />
23+
))}
24+
</div>
25+
26+
{/* Buttons Skeleton */}
27+
<div className='flex gap-4'>
28+
<Skeleton className='h-10 w-24' />
29+
<Skeleton className='h-10 w-20' />
30+
</div>
31+
</main>
32+
33+
{/* Stats Skeleton */}
34+
<div className='flex items-center gap-8'>
35+
{[1, 2, 3, 4].map(i => (
36+
<div key={i} className='flex items-center gap-1'>
37+
<Skeleton className='h-4 w-8' />
38+
<Skeleton className='h-3 w-16' />
39+
</div>
40+
))}
41+
</div>
42+
43+
{/* Organizations Skeleton */}
44+
<main className='flex flex-col gap-3'>
45+
<Skeleton className='h-4 w-32' />
46+
<div className='flex flex-col gap-3'>
47+
{[1, 2].map(i => (
48+
<div key={i} className='flex items-center gap-3 px-3'>
49+
<Skeleton className='size-[46px] rounded-full' />
50+
<Skeleton className='h-4 w-32' />
51+
</div>
52+
))}
53+
</div>
54+
</main>
55+
</article>
56+
</section>
57+
);
58+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ProfileData } from './profile-data';
2+
3+
interface ProfilePageProps {
4+
params: Promise<{
5+
username: string;
6+
}>;
7+
}
8+
9+
export default async function ProfilePage({ params }: ProfilePageProps) {
10+
const { username } = await params;
11+
12+
return (
13+
<section className='min-h-[70vh]'>
14+
<ProfileData username={username} />
15+
</section>
16+
);
17+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getUserProfileByUsername, getMe } from '@/lib/api/auth';
2+
import { GetMeResponse } from '@/lib/api/types';
3+
import { auth } from '@/auth';
4+
import ProfileOverview from '@/components/profile/ProfileOverview';
5+
6+
interface ProfileDataProps {
7+
username: string;
8+
}
9+
10+
export async function ProfileData({ username }: ProfileDataProps) {
11+
try {
12+
const session = await auth();
13+
const isOwnProfile = session?.user?.username === username;
14+
let userData: GetMeResponse;
15+
16+
if (isOwnProfile && session?.user?.accessToken) {
17+
userData = await getMe(session.user.accessToken);
18+
} else {
19+
userData = await getUserProfileByUsername(
20+
username,
21+
session?.user?.accessToken
22+
);
23+
}
24+
25+
return <ProfileOverview username={username} user={userData} />;
26+
} catch {
27+
return (
28+
<section className='flex min-h-screen items-center justify-center'>
29+
<div className='text-red-500'>Failed to load user profile</div>
30+
</section>
31+
);
32+
}
33+
}
File renamed without changes.

app/dashboard/page.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,18 @@ export default function DashboardPage() {
6767
<Avatar className='h-12 w-12'>
6868
<AvatarImage src={session.user.image || ''} />
6969
<AvatarFallback>
70-
{session.user.name?.charAt(0) ||
71-
session.user.email.charAt(0)}
70+
{(session.user.firstName || session.user.lastName)?.charAt(
71+
0
72+
) || session.user.email.charAt(0)}
7273
</AvatarFallback>
7374
</Avatar>
7475
<div>
7576
<p className='font-medium'>
76-
{session.user.name || 'No name'}
77+
{session.user.firstName && session.user.lastName
78+
? `${session.user.firstName} ${session.user.lastName}`
79+
: session.user.firstName ||
80+
session.user.lastName ||
81+
'No name'}
7782
</p>
7883
<p className='text-sm text-gray-500'>{session.user.email}</p>
7984
</div>

app/profile/[username]/layout.tsx

Lines changed: 0 additions & 14 deletions
This file was deleted.

app/profile/[username]/page.tsx

Lines changed: 0 additions & 40 deletions
This file was deleted.

app/user/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export default function UserPage() {
2929
{/* Header Section */}
3030
<div className='mb-8'>
3131
<h1 className='text-2xl leading-[120%] font-medium tracking-[-0.64px] text-white sm:text-3xl lg:text-[32px]'>
32-
Hello, {user?.name?.split(' ')[0] || 'User'}
32+
Hello,{' '}
33+
{user?.profile?.firstName || user?.profile?.lastName || 'User'}
3334
</h1>
3435
</div>
3536
{/* Stats Cards Grid */}

0 commit comments

Comments
 (0)