Skip to content

Commit 5304fcb

Browse files
committed
feat: enhance protected layout and membership card functionality
- Refactored ProtectedLayout to include a sidebar with navigation items for various user activities. - Integrated authentication check with loading state in ProtectedLayout. - Updated ProtectedPage to capitalize user names and removed unnecessary links and comments. - Enhanced MembershipCard component with user data fetching, profile completion checks, and improved PDF generation. - Removed ProfileSettings component as it was refactored into the new structure.
1 parent e27b1ee commit 5304fcb

File tree

7 files changed

+1558
-585
lines changed

7 files changed

+1558
-585
lines changed

app/protected/layout.tsx

Lines changed: 218 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,221 @@
1-
export default function ProtectedLayout({
2-
children,
3-
}: {
4-
children: React.ReactNode;
5-
}) {
6-
return (
7-
<main className="min-h-screen flex flex-col items-center">
8-
<div className="flex-1 w-full flex flex-col items-center">
9-
<div className="flex-1 flex flex-col max-w-5xl p-5">
10-
{children}
1+
'use client'
2+
import type React from "react"
3+
import Link from "next/link"
4+
import { Button } from "@/components/ui/button"
5+
import { StudentSidebar } from "@/components/users/StudentSidebar"
6+
import {
7+
LayoutDashboard,
8+
BookOpen,
9+
Calendar,
10+
Shield,
11+
MessageSquare,
12+
Trophy,
13+
Database,
14+
Target,
15+
GraduationCap,
16+
Handshake,
17+
Heart,
18+
Award,
19+
FileText,
20+
Users,
21+
Lightbulb,
22+
Code,
23+
Briefcase,
24+
Star,
25+
} from "lucide-react"
26+
import { useAuth } from "@/lib/hooks/useAuth"
27+
28+
export type SidebarGroupType = {
29+
title: string;
30+
items: {
31+
title: string;
32+
url: string;
33+
icon: React.ElementType;
34+
}[];
35+
};
36+
37+
const sidebarItems: SidebarGroupType[] = [
38+
{
39+
title: "Dashboard",
40+
items: [
41+
{
42+
title: "Overview",
43+
url: "/protected",
44+
icon: LayoutDashboard,
45+
},
46+
],
47+
},
48+
{
49+
title: "Learning",
50+
items: [
51+
{
52+
title: "My Courses",
53+
url: "/protected/courses",
54+
icon: BookOpen,
55+
},
56+
{
57+
title: "Assignments",
58+
url: "/protected/assignments",
59+
icon: FileText,
60+
},
61+
{
62+
title: "Grades & Progress",
63+
url: "/protected/grades",
64+
icon: Trophy,
65+
},
66+
{
67+
title: "Study Materials",
68+
url: "/protected/materials",
69+
icon: Lightbulb,
70+
},
71+
],
72+
},
73+
{
74+
title: "Activities",
75+
items: [
76+
{
77+
title: "Hackathons",
78+
url: "/protected/hackathons",
79+
icon: Code,
80+
},
81+
{
82+
title: "Events & Workshops",
83+
url: "/protected/events",
84+
icon: Calendar,
85+
},
86+
{
87+
title: "Projects",
88+
url: "/protected/projects",
89+
icon: Briefcase,
90+
},
91+
{
92+
title: "Achievements",
93+
url: "/protected/achievements",
94+
icon: Star,
95+
},
96+
],
97+
},
98+
{
99+
title: "Community",
100+
items: [
101+
{
102+
title: "Study Groups",
103+
url: "/protected/study-groups",
104+
icon: Users,
105+
},
106+
{
107+
title: "Mentorship",
108+
url: "/protected/mentorship",
109+
icon: GraduationCap,
110+
},
111+
{
112+
title: "Collaborations",
113+
url: "/protected/collaborations",
114+
icon: Handshake,
115+
},
116+
{
117+
title: "Volunteering",
118+
url: "/protected/volunteering",
119+
icon: Heart,
120+
},
121+
],
122+
},
123+
{
124+
title: "Career",
125+
items: [
126+
{
127+
title: "Internships",
128+
url: "/protected/internships",
129+
icon: Briefcase,
130+
},
131+
{
132+
title: "Job Opportunities",
133+
url: "/protected/jobs",
134+
icon: Target,
135+
},
136+
{
137+
title: "Resume Builder",
138+
url: "/protected/resume",
139+
icon: FileText,
140+
},
141+
{
142+
title: "Skills Assessment",
143+
url: "/protected/skills",
144+
icon: Award,
145+
},
146+
],
147+
},
148+
{
149+
title: "Analytics",
150+
items: [
151+
{
152+
title: "Learning Progress",
153+
url: "/protected/progress",
154+
icon: Database,
155+
},
156+
{
157+
title: "Performance Reports",
158+
url: "/protected/reports",
159+
icon: Target,
160+
},
161+
],
162+
},
163+
{
164+
title: "Support",
165+
items: [
166+
{
167+
title: "Messages",
168+
url: "/protected/messages",
169+
icon: MessageSquare,
170+
},
171+
{
172+
title: "Help Center",
173+
url: "/protected/help",
174+
icon: Shield,
175+
},
176+
],
177+
},
178+
]
179+
180+
export default function ProtectedLayout({ children }: { children: React.ReactNode }) {
181+
const { user, loading } = useAuth()
182+
183+
if (loading) {
184+
return (
185+
<div className="flex items-center justify-center min-h-screen">
186+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
187+
</div>
188+
)
189+
}
190+
191+
if (!user) {
192+
return (
193+
<div className="flex items-center justify-center min-h-screen px-4">
194+
<div className="text-center max-w-md">
195+
<h1 className="text-2xl font-bold mb-2">Authentication Required</h1>
196+
<p className="text-muted-foreground mb-4">Please sign in to access this page.</p>
197+
<Button asChild>
198+
<Link href="/auth/signin">Sign In</Link>
199+
</Button>
11200
</div>
12201
</div>
13-
</main>
14-
);
202+
)
203+
}
204+
205+
const avatar = user?.user_metadata?.first_name?.[0]?.toUpperCase() || user?.email?.[0]?.toUpperCase() || "S"
206+
const name = user?.user_metadata?.first_name || user?.email || "Student"
207+
const email = user?.email || "student@codeunia.com"
208+
209+
return (
210+
<StudentSidebar
211+
avatar={avatar}
212+
name={name}
213+
email={email}
214+
sidebarItems={sidebarItems}
215+
>
216+
<div className="bg-black min-h-screen w-full">
217+
{children}
218+
</div>
219+
</StudentSidebar>
220+
)
15221
}

app/protected/page.tsx

Lines changed: 16 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { redirect } from "next/navigation";
22
import { createClient } from "@/lib/supabase/server";
3-
import { Sparkles, Rocket, Shield, User, Settings } from "lucide-react";
4-
import Link from "next/link";
5-
import { Button } from "@/components/ui/button";
6-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
3+
import { Sparkles, Rocket, Shield, User } from "lucide-react";
74
import MembershipCard from "@/components/MembershipCard";
5+
6+
// Capitalization helper
7+
function capitalize(word: string) {
8+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
9+
}
10+
811
export default async function ProtectedPage() {
912
const supabase = await createClient();
1013

@@ -13,47 +16,27 @@ export default async function ProtectedPage() {
1316
redirect("/auth/signin");
1417
}
1518

16-
const firstName = data.user.user_metadata?.first_name || "";
17-
const lastName = data.user.user_metadata?.last_name || "";
19+
const firstNameRaw = data.user.user_metadata?.first_name || "";
20+
const lastNameRaw = data.user.user_metadata?.last_name || "";
21+
22+
const firstName = capitalize(firstNameRaw);
23+
const lastName = capitalize(lastNameRaw);
24+
1825
const displayName = firstName || lastName ? `${firstName} ${lastName}`.trim() : "there";
1926

2027
return (
2128
<div className="flex-1 w-full flex flex-col gap-8 p-6 max-w-4xl mx-auto">
22-
<div>
23-
<Link href="/" className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-zinc-900 text-white hover:bg-zinc-800 transition-colors shadow">
24-
← Go Back
25-
</Link>
26-
</div>
2729
<div className="relative overflow-hidden">
2830
<div className="absolute inset-0 bg-gradient-to-r from-blue-600/10 via-purple-600/10 to-pink-600/10 rounded-2xl"></div>
2931
<div className="absolute inset-0 bg-gradient-to-br from-transparent via-white/5 to-transparent rounded-2xl"></div>
30-
31-
{/* <div className="relative bg-white/80 dark:bg-gray-900/80 backdrop-blur-sm border border-gray-200/50 dark:border-gray-700/50 p-6 rounded-2xl shadow-lg">
32-
<div className="flex items-center gap-3 mb-4">
33-
<div className="p-2 bg-green-100 dark:bg-green-900/30 rounded-lg">
34-
<Shield className="h-5 w-5 text-green-600 dark:text-green-400" />
35-
</div>
36-
<div>
37-
<h3 className="font-semibold text-green-800 dark:text-green-200">Protected Area</h3>
38-
<p className="text-sm text-green-600 dark:text-green-400">Authentication verified</p>
39-
</div>
40-
</div>
41-
<div className="flex gap-3 items-start">
42-
<InfoIcon size="16" strokeWidth={2} className="text-green-600 dark:text-green-400 mt-0.5 flex-shrink-0" />
43-
<p className="text-sm text-green-700 dark:text-green-300">
44-
This is a protected page that you can only see as an authenticated user. Your session is secure and verified.
45-
</p>
46-
</div>
47-
</div> */}
4832
</div>
4933

50-
5134
<div className="space-y-6">
5235
<div className="text-center space-y-4">
5336
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
5437
<User className="h-8 w-8 text-white" />
5538
</div>
56-
39+
5740
<div className="space-y-2">
5841
<h1 className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-gray-900 via-blue-800 to-purple-800 dark:from-white dark:via-blue-200 dark:to-purple-200 bg-clip-text text-transparent">
5942
Welcome back, {displayName}!
@@ -69,7 +52,6 @@ export default async function ProtectedPage() {
6952
<MembershipCard uid={data.user.id} />
7053
</div>
7154

72-
7355
<div className="relative group">
7456
<div className="absolute -inset-1 bg-gradient-to-r from-yellow-400 via-orange-500 to-red-500 rounded-2xl blur opacity-25 group-hover:opacity-40 transition duration-1000 group-hover:duration-200"></div>
7557
<div className="relative bg-gradient-to-br from-yellow-50 via-orange-50 to-red-50 dark:from-yellow-900/20 dark:via-orange-900/20 dark:to-red-900/20 border border-yellow-200 dark:border-yellow-700/50 p-6 rounded-2xl">
@@ -91,8 +73,8 @@ export default async function ProtectedPage() {
9173
<div className="flex items-center gap-2 pt-2">
9274
<div className="flex space-x-1">
9375
<div className="w-2 h-2 bg-yellow-500 rounded-full animate-bounce"></div>
94-
<div className="w-2 h-2 bg-yellow-500 rounded-full animate-bounce" style={{animationDelay: '0.1s'}}></div>
95-
<div className="w-2 h-2 bg-yellow-500 rounded-full animate-bounce" style={{animationDelay: '0.2s'}}></div>
76+
<div className="w-2 h-2 bg-yellow-500 rounded-full animate-bounce" style={{ animationDelay: "0.1s" }}></div>
77+
<div className="w-2 h-2 bg-yellow-500 rounded-full animate-bounce" style={{ animationDelay: "0.2s" }}></div>
9678
</div>
9779
<span className="text-sm text-yellow-600 dark:text-yellow-400 font-medium">
9880
Development in progress
@@ -103,28 +85,6 @@ export default async function ProtectedPage() {
10385
</div>
10486
</div>
10587

106-
107-
{/* Profile Settings Card */}
108-
<Card className="mb-8">
109-
<CardHeader>
110-
<CardTitle className="flex items-center gap-2">
111-
<Settings className="h-5 w-5" />
112-
Customize Your Profile
113-
</CardTitle>
114-
<CardDescription>
115-
Add your information, social links, and customize how others see you
116-
</CardDescription>
117-
</CardHeader>
118-
<CardContent>
119-
<Button asChild className="w-full sm:w-auto">
120-
<Link href="/protected/profile" className="flex items-center gap-2">
121-
<Settings className="h-4 w-4" />
122-
Manage Profile
123-
</Link>
124-
</Button>
125-
</CardContent>
126-
</Card>
127-
12888
<div className="grid md:grid-cols-3 gap-6 mt-12">
12989
<div className="group p-6 bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 hover:shadow-lg hover:border-blue-300 dark:hover:border-blue-600 transition-all duration-300">
13090
<div className="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">

0 commit comments

Comments
 (0)