Skip to content

Commit a128e4f

Browse files
author
Boopathi
committed
Update: dashboard, auth, types, and add password change form
1 parent e97a3e5 commit a128e4f

File tree

8 files changed

+253
-21
lines changed

8 files changed

+253
-21
lines changed

src/app/page.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ type User = {
4141
name: string;
4242
email: string;
4343
}
44+
type AuthProvider = 'local' | 'google' | 'linkedin';
4445

4546
export default function Home() {
4647
const [theme, setTheme] = useState<'light' | 'dark' | null>(null);
4748
const [activeView, setActiveView] = useState<View>("home");
4849
const [isLoggedIn, setLoggedIn] = useState(false);
4950
const [userRole, setUserRole] = useState<UserRole | null>(null);
5051
const [user, setUser] = useState<User | null>(null);
52+
const [authProvider, setAuthProvider] = useState<AuthProvider | null>(null);
5153
const [hasSubscription, setHasSubscription] = useState(false);
5254
const [hasUsedFreeSession, setHasUsedFreeSession] = useState(false);
5355
const [appliedPrograms, setAppliedPrograms] = useState<Record<string, string>>({});
@@ -100,16 +102,20 @@ export default function Home() {
100102
});
101103
} else if (token && role && name && email) {
102104
const userData = { name, email };
105+
const authProvider = urlParams.get('authProvider') as AuthProvider || 'local';
103106
localStorage.setItem('isLoggedIn', 'true');
104107
localStorage.setItem('userRole', role);
105108
localStorage.setItem('user', JSON.stringify(userData));
106109
localStorage.setItem('hasSubscription', String(hasSub));
107-
localStorage.setItem('token', token); // Store token for future API calls
110+
localStorage.setItem('token', token);
111+
localStorage.setItem('authProvider', authProvider);
112+
108113

109114
setLoggedIn(true);
110115
setUserRole(role);
111116
setUser(userData);
112117
setHasSubscription(hasSub);
118+
setAuthProvider(authProvider);
113119
toast({ title: "Login Successful", description: `Welcome back, ${name}!` });
114120
setActiveView('dashboard');
115121
setIsLoading(false);
@@ -126,6 +132,7 @@ export default function Home() {
126132
const savedUser = localStorage.getItem('user');
127133
const savedSubscription = localStorage.getItem('hasSubscription');
128134
const savedAppliedPrograms = localStorage.getItem('appliedPrograms');
135+
const savedAuthProvider = localStorage.getItem('authProvider') as AuthProvider | null;
129136

130137
if (savedIsLoggedIn === 'true' && savedUserRole && savedUser) {
131138
setLoggedIn(true);
@@ -136,6 +143,9 @@ export default function Home() {
136143
if (savedAppliedPrograms) {
137144
setAppliedPrograms(JSON.parse(savedAppliedPrograms));
138145
}
146+
if (savedAuthProvider) {
147+
setAuthProvider(savedAuthProvider);
148+
}
139149
}
140150
setIsLoading(false);
141151
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -161,19 +171,22 @@ export default function Home() {
161171
}
162172
};
163173

164-
const handleLoginSuccess = (data: { role: UserRole, token: string, hasSubscription: boolean, name: string, email: string }) => {
165-
const { role, token, hasSubscription, name, email } = data;
174+
const handleLoginSuccess = (data: { role: UserRole, token: string, hasSubscription: boolean, name: string, email: string, authProvider: AuthProvider }) => {
175+
const { role, token, hasSubscription, name, email, authProvider } = data;
166176
const userData = { name, email };
167177
setLoggedIn(true);
168178
setUserRole(role);
169179
setUser(userData);
170180
setHasSubscription(hasSubscription);
181+
setAuthProvider(authProvider);
171182

172183
localStorage.setItem('isLoggedIn', 'true');
173184
localStorage.setItem('userRole', role);
174185
localStorage.setItem('user', JSON.stringify(userData));
175186
localStorage.setItem('hasSubscription', String(hasSubscription));
176187
localStorage.setItem('token', token);
188+
localStorage.setItem('authProvider', authProvider);
189+
177190

178191
setHasUsedFreeSession(false); // Reset for prototype testing
179192
setActiveView('dashboard');
@@ -185,13 +198,15 @@ export default function Home() {
185198
setUser(null);
186199
setHasSubscription(false);
187200
setAppliedPrograms({});
201+
setAuthProvider(null);
188202
// Clear all auth-related items
189203
localStorage.removeItem('isLoggedIn');
190204
localStorage.removeItem('userRole');
191205
localStorage.removeItem('user');
192206
localStorage.removeItem('hasSubscription');
193207
localStorage.removeItem('appliedPrograms');
194208
localStorage.removeItem('token');
209+
localStorage.removeItem('authProvider');
195210
setActiveView('home');
196211
router.push('/');
197212
};
@@ -237,7 +252,7 @@ export default function Home() {
237252
};
238253

239254
const renderDashboard = () => {
240-
if (!isLoggedIn || activeView !== 'dashboard' || !userRole || !user) {
255+
if (!isLoggedIn || activeView !== 'dashboard' || !userRole || !user || !authProvider) {
241256
return null;
242257
}
243258

@@ -249,6 +264,7 @@ export default function Home() {
249264
onOpenChange={handleModalOpenChange('dashboard')}
250265
setActiveView={setActiveView}
251266
user={user}
267+
authProvider={authProvider}
252268
/>
253269
);
254270
case 'incubator':
@@ -257,6 +273,7 @@ export default function Home() {
257273
isOpen={true}
258274
onOpenChange={handleModalOpenChange('dashboard')}
259275
user={user}
276+
authProvider={authProvider}
260277
/>
261278
);
262279
case 'msme':
@@ -265,6 +282,7 @@ export default function Home() {
265282
isOpen={true}
266283
onOpenChange={handleModalOpenChange('dashboard')}
267284
user={user}
285+
authProvider={authProvider}
268286
/>
269287
);
270288
case 'founder':
@@ -275,6 +293,7 @@ export default function Home() {
275293
onOpenChange={handleModalOpenChange('dashboard')}
276294
user={user}
277295
userRole={userRole}
296+
authProvider={authProvider}
278297
hasSubscription={hasSubscription}
279298
setActiveView={setActiveView}
280299
/>
@@ -363,3 +382,5 @@ export default function Home() {
363382
</>
364383
);
365384
}
385+
386+

src/app/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type AppUser = {
2626
name: string;
2727
email: string;
2828
role: UserRole;
29+
auth_provider: 'local' | 'google' | 'linkedin';
2930
is_confirmed: boolean;
3031
is_banned: boolean;
3132
created_at: string;
@@ -62,3 +63,5 @@ export type EducationProgram = {
6263
description: string;
6364
features: EducationFeature[];
6465
};
66+
67+

src/components/auth/login-modal.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ const loginSchema = z.object({
3636

3737
type LoginSchema = z.infer<typeof loginSchema>;
3838

39+
type AuthProvider = 'local' | 'google' | 'linkedin';
40+
3941
interface LoginModalProps {
4042
isOpen: boolean;
4143
setIsOpen: (isOpen: boolean) => void;
42-
onLoginSuccess: (data: { role: UserRole, token: string, hasSubscription: boolean, name: string, email: string }) => void;
44+
onLoginSuccess: (data: { role: UserRole, token: string, hasSubscription: boolean, name: string, email: string, authProvider: AuthProvider }) => void;
4345
}
4446

4547
export default function LoginModal({ isOpen, setIsOpen, onLoginSuccess }: LoginModalProps) {
@@ -171,3 +173,5 @@ export default function LoginModal({ isOpen, setIsOpen, onLoginSuccess }: LoginM
171173
</Dialog>
172174
);
173175
}
176+
177+

src/components/views/dashboard.tsx

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Separator } from "@/components/ui/separator";
2525
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
2626
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
2727
import { API_BASE_URL } from "@/lib/api";
28+
import PasswordChangeForm from './password-change-form';
2829

2930

3031
const settingsFormSchema = z.object({
@@ -61,11 +62,14 @@ type ProgramFormValues = z.infer<typeof programSchema>;
6162

6263

6364
type User = { name: string; email: string; }
65+
type AuthProvider = 'local' | 'google' | 'linkedin';
66+
6467
interface DashboardViewProps {
6568
isOpen: boolean;
6669
onOpenChange: (isOpen: boolean) => void;
6770
user: User;
6871
userRole: UserRole;
72+
authProvider: AuthProvider;
6973
hasSubscription: boolean;
7074
setActiveView: (view: View) => void;
7175
}
@@ -83,7 +87,7 @@ const LockedContent = ({ setActiveView, title }: { setActiveView: (view: View) =
8387
</Card>
8488
);
8589

86-
export default function DashboardView({ isOpen, onOpenChange, user, userRole, hasSubscription, setActiveView }: DashboardViewProps) {
90+
export default function DashboardView({ isOpen, onOpenChange, user, userRole, authProvider, hasSubscription, setActiveView }: DashboardViewProps) {
8791
const { toast } = useToast();
8892
const [activeTab, setActiveTab] = useState<DashboardTab>("overview");
8993
const [isEditingEmail, setIsEditingEmail] = useState(false);
@@ -262,8 +266,8 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
262266
<>
263267
<TabsContent value="users" className="mt-0">
264268
<Card className="bg-card/50 backdrop-blur-sm border-border/50"><CardHeader><CardTitle>User Management</CardTitle><CardDescription>Approve, ban, or delete user accounts.</CardDescription></CardHeader><CardContent>
265-
{isLoadingUsers ? <div className="flex justify-center items-center h-48"><LucideIcons.Loader2 className="h-8 w-8 animate-spin" /></div> : (<Table><TableHeader><TableRow><TableHead>User</TableHead><TableHead>Role</TableHead><TableHead>Joined</TableHead><TableHead>Status</TableHead><TableHead>Actions</TableHead></TableRow></TableHeader><TableBody>
266-
{users.map(u => (<TableRow key={u.id}><TableCell><div className="font-medium">{u.name}</div><div className="text-sm text-muted-foreground">{u.email}</div></TableCell><TableCell className="capitalize">{u.role}</TableCell><TableCell>{new Date(u.created_at).toLocaleDateString()}</TableCell><TableCell><div className="flex flex-col gap-1">{u.is_banned ? <Badge variant="destructive">Banned</Badge> : (u.is_confirmed ? <Badge variant="default">Approved</Badge> : <Badge variant="secondary">Pending</Badge>)}</div></TableCell><TableCell className="space-x-2">
269+
{isLoadingUsers ? <div className="flex justify-center items-center h-48"><LucideIcons.Loader2 className="h-8 w-8 animate-spin" /></div> : (<Table><TableHeader><TableRow><TableHead>User</TableHead><TableHead>Role</TableHead><TableHead>Joined</TableHead><TableHead>Status</TableHead><TableHead>Auth</TableHead><TableHead>Actions</TableHead></TableRow></TableHeader><TableBody>
270+
{users.map(u => (<TableRow key={u.id}><TableCell><div className="font-medium">{u.name}</div><div className="text-sm text-muted-foreground">{u.email}</div></TableCell><TableCell className="capitalize">{u.role}</TableCell><TableCell>{new Date(u.created_at).toLocaleDateString()}</TableCell><TableCell><div className="flex flex-col gap-1">{u.is_banned ? <Badge variant="destructive">Banned</Badge> : (u.is_confirmed ? <Badge variant="default">Approved</Badge> : <Badge variant="secondary">Pending</Badge>)}</div></TableCell><TableCell className="capitalize">{u.auth_provider}</TableCell><TableCell className="space-x-2">
267271
{!u.is_confirmed && !u.is_banned && (<Button size="sm" onClick={() => handleApproveUser(u.id)}><LucideIcons.CheckCircle className="mr-2 h-4 w-4" />Approve</Button>)}
268272
<Button size="sm" variant={u.is_banned ? "outline" : "secondary"} onClick={() => setUserToBan(u)}><LucideIcons.Ban className="mr-2 h-4 w-4" />{u.is_banned ? "Unban" : "Ban"}</Button>
269273
<Button size="sm" variant="destructive" onClick={() => setUserToDelete(u)}><LucideIcons.Trash2 className="mr-2 h-4 w-4" />Delete</Button>
@@ -299,7 +303,34 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
299303
</TabsContent>
300304
</>
301305
)}
302-
<TabsContent value="settings" className="mt-0"><Card className="bg-card/50 backdrop-blur-sm border-border/50"><CardHeader><CardTitle>Account Settings</CardTitle><CardDescription>Manage your account and payment information.</CardDescription></CardHeader><CardContent><Form {...settingsForm}><form onSubmit={settingsForm.handleSubmit(onSettingsSubmit)} className="space-y-8"><div><h3 className="text-lg font-medium mb-4">Profile</h3><div className="space-y-4"><FormField control={settingsForm.control} name="name" render={({ field }) => (<FormItem><FormLabel>Full Name</FormLabel><FormControl><Input placeholder="Your full name" {...field} /></FormControl><FormMessage /></FormItem>)}/><FormField control={settingsForm.control} name="email" render={({ field }) => (<FormItem><div className="flex justify-between items-center"><FormLabel>Email</FormLabel>{!isEditingEmail && (<Button type="button" variant="link" className="p-0 h-auto text-sm" onClick={() => setIsEditingEmail(true)}>Edit</Button>)}</div><FormControl><Input type="email" placeholder="your@email.com" {...field} readOnly={!isEditingEmail} /></FormControl><FormMessage /></FormItem>)}/></div></div><Button type="submit">Save Changes</Button></form></Form></CardContent></Card></TabsContent>
306+
<TabsContent value="settings" className="mt-0">
307+
<Card className="bg-card/50 backdrop-blur-sm border-border/50">
308+
<CardHeader>
309+
<CardTitle>Account Settings</CardTitle>
310+
<CardDescription>Manage your account and payment information.</CardDescription>
311+
</CardHeader>
312+
<CardContent className="space-y-8">
313+
<Form {...settingsForm}>
314+
<form onSubmit={settingsForm.handleSubmit(onSettingsSubmit)} className="space-y-4">
315+
<div>
316+
<h3 className="text-lg font-medium mb-4">Profile</h3>
317+
<div className="space-y-4">
318+
<FormField control={settingsForm.control} name="name" render={({ field }) => (<FormItem><FormLabel>Full Name</FormLabel><FormControl><Input placeholder="Your full name" {...field} /></FormControl><FormMessage /></FormItem>)} />
319+
<FormField control={settingsForm.control} name="email" render={({ field }) => (<FormItem><div className="flex justify-between items-center"><FormLabel>Email</FormLabel>{!isEditingEmail && (<Button type="button" variant="link" className="p-0 h-auto text-sm" onClick={() => setIsEditingEmail(true)}>Edit</Button>)}</div><FormControl><Input type="email" placeholder="your@email.com" {...field} readOnly={!isEditingEmail} /></FormControl><FormMessage /></FormItem>)} />
320+
</div>
321+
</div>
322+
<Button type="submit">Save Changes</Button>
323+
</form>
324+
</Form>
325+
{authProvider === 'local' && (
326+
<>
327+
<Separator />
328+
<PasswordChangeForm />
329+
</>
330+
)}
331+
</CardContent>
332+
</Card>
333+
</TabsContent>
303334
</ScrollArea>
304335
</Tabs>
305336
</div>
@@ -318,3 +349,5 @@ const chartData = [
318349
const chartConfig = {
319350
activity: { label: "Activity", color: "hsl(var(--chart-1))" },
320351
};
352+
353+

src/components/views/incubator-dashboard.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ import { useToast } from "@/hooks/use-toast";
2323
import SubmissionDetailsModal from "./submission-details-modal";
2424
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
2525
import { API_BASE_URL } from "@/lib/api";
26+
import PasswordChangeForm from './password-change-form';
27+
2628

2729
type User = {
2830
name: string;
2931
email: string;
3032
}
33+
type AuthProvider = 'local' | 'google' | 'linkedin';
34+
3135

3236
const profileFormSchema = z.object({
3337
name: z.string().min(1, "Incubator name is required"),
@@ -79,6 +83,7 @@ interface IncubatorDashboardViewProps {
7983
isOpen: boolean;
8084
onOpenChange: (isOpen: boolean) => void;
8185
user: User;
86+
authProvider: AuthProvider;
8287
}
8388

8489
const emptyProfile: ProfileFormValues = {
@@ -103,7 +108,7 @@ const emptyProfile: ProfileFormValues = {
103108
},
104109
};
105110

106-
export default function IncubatorDashboardView({ isOpen, onOpenChange, user }: IncubatorDashboardViewProps) {
111+
export default function IncubatorDashboardView({ isOpen, onOpenChange, user, authProvider }: IncubatorDashboardViewProps) {
107112
const { toast } = useToast();
108113
const [activeTab, setActiveTab] = useState<IncubatorDashboardTab>("overview");
109114
const [submissions, setSubmissions] = useState(initialSubmissionsData);
@@ -481,14 +486,14 @@ export default function IncubatorDashboardView({ isOpen, onOpenChange, user }: I
481486
</Card>
482487
</TabsContent>
483488
<TabsContent value="settings" className="mt-0">
484-
<Card className="bg-card/50 backdrop-blur-sm border-border/50">
489+
<Card className="bg-card/50 backdrop-blur-sm border-border/50">
485490
<CardHeader>
486491
<CardTitle>Account Settings</CardTitle>
487492
<CardDescription>Manage your account settings.</CardDescription>
488493
</CardHeader>
489-
<CardContent>
494+
<CardContent className="space-y-8">
490495
<Form {...settingsForm}>
491-
<form onSubmit={settingsForm.handleSubmit(onSettingsSubmit)} className="space-y-8">
496+
<form onSubmit={settingsForm.handleSubmit(onSettingsSubmit)} className="space-y-4">
492497
<div>
493498
<h3 className="text-lg font-medium mb-4">Profile</h3>
494499
<div className="space-y-4">
@@ -515,10 +520,15 @@ export default function IncubatorDashboardView({ isOpen, onOpenChange, user }: I
515520
/>
516521
</div>
517522
</div>
518-
519523
<Button type="submit">Save Changes</Button>
520524
</form>
521525
</Form>
526+
{authProvider === 'local' && (
527+
<>
528+
<Separator />
529+
<PasswordChangeForm />
530+
</>
531+
)}
522532
</CardContent>
523533
</Card>
524534
</TabsContent>
@@ -551,3 +561,5 @@ const incubatorChartConfig = {
551561
color: "hsl(var(--chart-2))",
552562
},
553563
};
564+
565+

0 commit comments

Comments
 (0)