Skip to content

Commit b5c0def

Browse files
author
Boopathi
committed
fix: resolve TypeScript error in auth action page and address ESLint issues
1 parent 23d2471 commit b5c0def

File tree

20 files changed

+400
-151
lines changed

20 files changed

+400
-151
lines changed

out/404.html

Lines changed: 1 addition & 15 deletions
Large diffs are not rendered by default.

out/index.html

Lines changed: 1 addition & 15 deletions
Large diffs are not rendered by default.

out/index.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
2:"$Sreact.suspense"
2-
3:I[74344,["9990","static/chunks/7c86ec74-5d3e2c324eba8f04.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-71d41ecabde65adb.js","9635","static/chunks/9635-c1242a475b801a6a.js","8437","static/chunks/8437-9cca2dc77cd479a5.js","3145","static/chunks/3145-96a9e135101030c6.js","5935","static/chunks/5935-e7af8502207931af.js","7094","static/chunks/7094-fdb52e9197204303.js","1931","static/chunks/app/page-fe19e23c1c7e01c9.js"],"default"]
3-
4:I[11225,["8068","static/chunks/8068-111f0b00c8797a1e.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-71d41ecabde65adb.js","1915","static/chunks/1915-bfb0f97fd1af2908.js","3185","static/chunks/app/layout-a21bbe360d2476df.js"],"default"]
2+
3:I[74344,["9990","static/chunks/7c86ec74-4d73abbd112d4fa3.js","8415","static/chunks/8415-3647249926a008b4.js","2984","static/chunks/2984-70037d6bbda7b577.js","5844","static/chunks/5844-2fc73a5e88d29aee.js","7423","static/chunks/7423-481c93bc91f92433.js","3145","static/chunks/3145-96a9e135101030c6.js","8437","static/chunks/8437-9cca2dc77cd479a5.js","8626","static/chunks/8626-bf55349e248e624d.js","4090","static/chunks/4090-71fc7452300bacab.js","7094","static/chunks/7094-fdb52e9197204303.js","1931","static/chunks/app/page-6bd92e9c2f682b0c.js"],"default"]
3+
4:I[11225,["8068","static/chunks/8068-111f0b00c8797a1e.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-2fc73a5e88d29aee.js","1915","static/chunks/1915-bfb0f97fd1af2908.js","8003","static/chunks/8003-638df8dcb4557297.js","3185","static/chunks/app/layout-fa5f01628486a6eb.js"],"default"]
44
5:I[4707,[],""]
55
6:I[36423,[],""]
66
7:I[75292,["8415","static/chunks/8415-3647249926a008b4.js","5896","static/chunks/5896-dd3fa908e6e2380d.js","9160","static/chunks/app/not-found-4ad8109c7ea602ca.js"],"default"]
7-
8:I[94886,["8068","static/chunks/8068-111f0b00c8797a1e.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-71d41ecabde65adb.js","1915","static/chunks/1915-bfb0f97fd1af2908.js","3185","static/chunks/app/layout-a21bbe360d2476df.js"],"Toaster"]
8-
9:I[88003,["8068","static/chunks/8068-111f0b00c8797a1e.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-71d41ecabde65adb.js","1915","static/chunks/1915-bfb0f97fd1af2908.js","3185","static/chunks/app/layout-a21bbe360d2476df.js"],""]
7+
8:I[94886,["8068","static/chunks/8068-111f0b00c8797a1e.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-2fc73a5e88d29aee.js","1915","static/chunks/1915-bfb0f97fd1af2908.js","8003","static/chunks/8003-638df8dcb4557297.js","3185","static/chunks/app/layout-fa5f01628486a6eb.js"],"Toaster"]
8+
9:I[88003,["8068","static/chunks/8068-111f0b00c8797a1e.js","8415","static/chunks/8415-3647249926a008b4.js","5844","static/chunks/5844-2fc73a5e88d29aee.js","1915","static/chunks/1915-bfb0f97fd1af2908.js","8003","static/chunks/8003-638df8dcb4557297.js","3185","static/chunks/app/layout-fa5f01628486a6eb.js"],""]
99
a:T43f,
1010
window.$zoho = window.$zoho || {};
1111
window.$zoho.salesiq = window.$zoho.salesiq || {
@@ -32,6 +32,6 @@ s.defer = true;
3232
s.src = "https://salesiq.zohopublic.in/widget";
3333
var t = d.getElementsByTagName("script")[0];
3434
t.parentNode.insertBefore(s, t);
35-
0:["93wN5Evye5YSltvIKc15Z",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$2",null,{"fallback":["$","div",null,{"children":"Loading..."}],"children":["$","$L3",null,{}]}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/02b2aee02a368ff7.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","className":"__variable_19640c __variable_9f6af0","suppressHydrationWarning":true,"children":[["$","head",null,{"children":["$","script",null,{"dangerouslySetInnerHTML":{"__html":"\n (function() {\n try {\n let theme = localStorage.getItem('theme');\n if (!theme) {\n theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n if (theme === 'dark') {\n document.documentElement.classList.add('dark');\n } else {\n document.documentElement.classList.remove('dark');\n }\n } catch (e) {}\n })();\n "}}]}],["$","body",null,{"className":"antialiased bg-background font-sans","children":[["$","div",null,{"className":"flex flex-col min-h-screen","children":[["$","div",null,{"className":"hidden dark:block","children":["$","$L4",null,{}]}],["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":["$","$L7",null,{}],"notFoundStyles":[]}]]}],["$","$L8",null,{}],["$","$L9",null,{"id":"zoho-salesiq-script","strategy":"lazyOnload","children":"$a"}]]}]]}]],null],null],["$Lb",null]]]]
35+
0:["lvt0-UZFRIhp_mECE3FyO",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$2",null,{"fallback":["$","div",null,{"children":"Loading..."}],"children":["$","$L3",null,{}]}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/332ae23d8ed2001c.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","className":"__variable_19640c __variable_9f6af0","suppressHydrationWarning":true,"children":[["$","head",null,{}],["$","body",null,{"className":"antialiased bg-background font-sans","children":[["$","div",null,{"className":"flex flex-col min-h-screen","children":[["$","div",null,{"className":"hidden dark:block","children":["$","$L4",null,{}]}],["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":["$","$L7",null,{}],"notFoundStyles":[]}]]}],["$","$L8",null,{}],["$","$L9",null,{"id":"zoho-salesiq-script","strategy":"lazyOnload","children":"$a"}],["$","$L9",null,{"src":"https://www.google.com/recaptcha/enterprise.js?render=6LfZ4H8rAAAAAA0NMVH1C-sCiE9-Vz4obaWy9eUI","strategy":"lazyOnload"}]]}]]}]],null],null],["$Lb",null]]]]
3636
b:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Hustloop | Connect, Collaborate, Build Stronger Startup & Innovators Meet"}],["$","meta","3",{"name":"description","content":"A prototype startup platform that includes modules such as Blog, Mentors, Incubators, Pricing, and MSMEs."}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"48x48"}],["$","meta","5",{"name":"next-size-adjust"}]]
3737
1:null

src/app/auth/action/page.tsx

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
2+
"use client";
3+
4+
import * as React from 'react';
5+
import { useSearchParams, useRouter } from 'next/navigation';
6+
import { useForm } from "react-hook-form";
7+
import { zodResolver } from "@hookform/resolvers/zod";
8+
import * as z from "zod";
9+
import { useFirebaseAuth } from '@/hooks/use-firebase-auth';
10+
import { checkActionCode, applyActionCode, verifyPasswordResetCode, confirmPasswordReset } from 'firebase/auth';
11+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
12+
import { Button } from '@/components/ui/button';
13+
import { Input } from '@/components/ui/input';
14+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
15+
import { Loader2, CheckCircle, XCircle } from 'lucide-react';
16+
import { useToast } from '@/hooks/use-toast';
17+
import Image from 'next/image';
18+
19+
type Action = 'resetPassword' | 'verifyEmail' | null;
20+
21+
const passwordResetSchema = z.object({
22+
password: z.string()
23+
.min(10, { message: "Password must be at least 10 characters long." })
24+
.regex(/[A-Z]/, { message: "Must contain at least one uppercase letter." })
25+
.regex(/[a-z]/, { message: "Must contain at least one lowercase letter." })
26+
.regex(/[0-9]/, { message: "Must contain at least one number." })
27+
.regex(/[^A-Za-z0-9]/, { message: "Must contain at least one special character." }),
28+
confirmPassword: z.string(),
29+
}).refine(data => data.password === data.confirmPassword, {
30+
message: "Passwords do not match.",
31+
path: ["confirmPassword"],
32+
});
33+
34+
type PasswordResetValues = z.infer<typeof passwordResetSchema>;
35+
36+
const ActionHandlerContent: React.FC = () => {
37+
const router = useRouter();
38+
const searchParams = useSearchParams();
39+
const { toast } = useToast();
40+
const { auth } = useFirebaseAuth();
41+
42+
const [mode, setMode] = React.useState<Action>(null);
43+
const [actionCode, setActionCode] = React.useState<string | null>(null);
44+
const [loading, setLoading] = React.useState(true);
45+
const [error, setError] = React.useState<string | null>(null);
46+
const [info, setInfo] = React.useState<{ email: string; from: 'verifyEmail' | 'resetPassword' } | null>(null);
47+
const [success, setSuccess] = React.useState(false);
48+
49+
const form = useForm<PasswordResetValues>({
50+
resolver: zodResolver(passwordResetSchema),
51+
});
52+
53+
React.useEffect(() => {
54+
const modeParam = searchParams.get('mode') as Action;
55+
const codeParam = searchParams.get('oobCode');
56+
57+
setMode(modeParam);
58+
setActionCode(codeParam);
59+
60+
if (!auth || !modeParam || !codeParam) {
61+
if(!auth) setLoading(false);
62+
if (!modeParam || !codeParam) {
63+
setError("Invalid action link. Please try again.");
64+
setLoading(false);
65+
}
66+
return;
67+
}
68+
69+
const handleAction = async () => {
70+
try {
71+
const actionInfo = await checkActionCode(auth, codeParam);
72+
const { email } = actionInfo.data;
73+
74+
if (!email) {
75+
throw new Error("Invalid action code: email is missing.");
76+
}
77+
78+
if (mode === 'verifyEmail') {
79+
await applyActionCode(auth, codeParam);
80+
setSuccess(true);
81+
setInfo({ email, from: 'verifyEmail' });
82+
} else if (mode === 'resetPassword') {
83+
await verifyPasswordResetCode(auth, codeParam); // Verify code is valid
84+
setInfo({ email, from: 'resetPassword' });
85+
} else {
86+
throw new Error("Unsupported action.");
87+
}
88+
} catch (err: any) {
89+
let message = "Invalid or expired action link. Please try again.";
90+
if (err.code === 'auth/invalid-action-code') {
91+
message = "The link is invalid or has expired. Please request a new one.";
92+
}
93+
setError(message);
94+
} finally {
95+
setLoading(false);
96+
}
97+
};
98+
99+
handleAction();
100+
101+
}, [searchParams, auth]);
102+
103+
const handlePasswordResetSubmit = async (data: PasswordResetValues) => {
104+
if (!auth || !actionCode) return;
105+
106+
try {
107+
await confirmPasswordReset(auth, actionCode, data.password);
108+
setSuccess(true);
109+
} catch (err) {
110+
setError("Failed to reset password. The link may have expired. Please try again.");
111+
}
112+
}
113+
114+
if (loading) {
115+
return (
116+
<div className="text-center">
117+
<Loader2 className="h-12 w-12 animate-spin text-primary mx-auto mb-4" />
118+
<p className="text-muted-foreground">Verifying link...</p>
119+
</div>
120+
);
121+
}
122+
123+
if (error) {
124+
return (
125+
<div className="text-center">
126+
<XCircle className="h-12 w-12 text-destructive mx-auto mb-4" />
127+
<h2 className="text-2xl font-bold mb-2">Action Failed</h2>
128+
<p className="text-muted-foreground mb-6">{error}</p>
129+
<Button onClick={() => router.push('/')}>Go to Homepage</Button>
130+
</div>
131+
);
132+
}
133+
134+
if (success) {
135+
if (info?.from === 'verifyEmail') {
136+
toast({
137+
title: "Email Verified!",
138+
description: "Your account is now active. You can log in.",
139+
});
140+
router.push('/?action=login&from=verification_success');
141+
return null;
142+
}
143+
if (info?.from === 'resetPassword') {
144+
toast({
145+
title: "Password Changed!",
146+
description: "Your password has been reset successfully. Please log in.",
147+
});
148+
router.push('/?action=login&from=reset_success');
149+
return null;
150+
}
151+
}
152+
153+
if (mode === 'resetPassword' && info) {
154+
return (
155+
<div>
156+
<CardHeader className="text-center">
157+
<CardTitle>Reset Your Password</CardTitle>
158+
<CardDescription>Enter a new password for {info.email}</CardDescription>
159+
</CardHeader>
160+
<CardContent>
161+
<Form {...form}>
162+
<form onSubmit={form.handleSubmit(handlePasswordResetSubmit)} className="space-y-4">
163+
<FormField
164+
control={form.control}
165+
name="password"
166+
render={({ field }) => (
167+
<FormItem>
168+
<FormLabel>New Password</FormLabel>
169+
<FormControl><Input type="password" {...field} /></FormControl>
170+
<FormMessage />
171+
</FormItem>
172+
)}
173+
/>
174+
<FormField
175+
control={form.control}
176+
name="confirmPassword"
177+
render={({ field }) => (
178+
<FormItem>
179+
<FormLabel>Confirm New Password</FormLabel>
180+
<FormControl><Input type="password" {...field} /></FormControl>
181+
<FormMessage />
182+
</FormItem>
183+
)}
184+
/>
185+
<Button type="submit" className="w-full" disabled={form.formState.isSubmitting}>
186+
{form.formState.isSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
187+
Save New Password
188+
</Button>
189+
</form>
190+
</Form>
191+
</CardContent>
192+
</div>
193+
);
194+
}
195+
196+
// Fallback for any other state
197+
return (
198+
<div className="text-center">
199+
<h2 className="text-2xl font-bold">Invalid State</h2>
200+
<p className="text-muted-foreground">Something went wrong. Please return to the homepage.</p>
201+
<Button onClick={() => router.push('/')} className="mt-4">Go to Homepage</Button>
202+
</div>
203+
);
204+
};
205+
206+
207+
export default function AuthActionPage() {
208+
const router = useRouter();
209+
210+
return (
211+
<div className="flex flex-col items-center justify-center min-h-screen bg-background p-4">
212+
<div
213+
className="flex items-center gap-2 cursor-pointer mb-8"
214+
onClick={() => router.push('/')}
215+
>
216+
<div className="logo-container" style={{'--logo-size': '2.5rem'} as React.CSSProperties}>
217+
<Image src="/logo.png" alt="Hustloop Logo" width={40} height={40} className="h-10 w-10 logo-image" />
218+
</div>
219+
<span className="font-headline text-2xl" style={{ color: '#facc15' }}>
220+
hustl<strong className="text-3xl align-middle font-bold"></strong>p
221+
</span>
222+
</div>
223+
<Card className="w-full max-w-md">
224+
<React.Suspense fallback={
225+
<div className="flex items-center justify-center p-12">
226+
<Loader2 className="h-12 w-12 animate-spin text-primary" />
227+
</div>
228+
}>
229+
<ActionHandlerContent />
230+
</React.Suspense>
231+
</Card>
232+
</div>
233+
);
234+
}
235+

src/app/globals.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,47 @@
151151
opacity: 0;
152152
transition-delay: 0.15s;
153153
}
154+
155+
.auth-modal-glow::before,
156+
.auth-modal-glow::after {
157+
content: '';
158+
position: absolute;
159+
z-index: -1;
160+
border-radius: 9999px;
161+
filter: blur(50px);
162+
opacity: 0.25;
163+
pointer-events: none;
164+
}
165+
.dark .auth-modal-glow::before {
166+
top: 50%;
167+
left: 50%;
168+
width: 250px;
169+
height: 250px;
170+
transform: translate(-70%, -60%);
171+
background: radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%);
172+
}
173+
.dark .auth-modal-glow::after {
174+
top: 50%;
175+
left: 50%;
176+
width: 200px;
177+
height: 200px;
178+
transform: translate(30%, 40%);
179+
background: radial-gradient(circle, hsl(var(--accent)) 0%, transparent 70%);
180+
}
181+
182+
.light .auth-modal-glow::before {
183+
top: 50%;
184+
left: 50%;
185+
width: 250px;
186+
height: 250px;
187+
transform: translate(-70%, -60%);
188+
background: radial-gradient(circle, hsl(var(--primary) / 0.5) 0%, transparent 70%);
189+
}
190+
.light .auth-modal-glow::after {
191+
top: 50%;
192+
left: 50%;
193+
width: 200px;
194+
height: 200px;
195+
transform: translate(30%, 40%);
196+
background: radial-gradient(circle, hsl(var(--accent) / 0.5) 0%, transparent 70%);
197+
}

src/app/layout.tsx

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,7 @@ export default function RootLayout({
3030
}>) {
3131
return (
3232
<html lang="en" className={`${fontSans.variable} ${fontHeadline.variable}`} suppressHydrationWarning>
33-
<head>
34-
<script
35-
dangerouslySetInnerHTML={{
36-
__html: `
37-
(function() {
38-
try {
39-
let theme = localStorage.getItem('theme');
40-
if (!theme) {
41-
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
42-
}
43-
if (theme === 'dark') {
44-
document.documentElement.classList.add('dark');
45-
} else {
46-
document.documentElement.classList.remove('dark');
47-
}
48-
} catch (e) {}
49-
})();
50-
`,
51-
}}
52-
/>
53-
</head>
33+
<head />
5434
<body className="antialiased bg-background font-sans">
5535
<div className="flex flex-col min-h-screen">
5636
<div className="hidden dark:block">
@@ -88,6 +68,10 @@ s.src = "https://salesiq.zohopublic.in/widget";
8868
t.parentNode.insertBefore(s, t);
8969
`}
9070
</Script>
71+
<Script
72+
src="https://www.google.com/recaptcha/enterprise.js?render=6LfZ4H8rAAAAAA0NMVH1C-sCiE9-Vz4obaWy9eUI"
73+
strategy="lazyOnload"
74+
/>
9175
</body>
9276
</html>
9377
);

src/app/privacy-policy/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export default function PrivacyPolicyPage() {
6464
<ScrollArea className="h-[60vh] pr-6">
6565
<div className="space-y-6 prose dark:prose-invert max-w-none">
6666
<p>
67-
Hustloop ("we," "our," or "us") is committed to protecting your privacy. This Privacy Policy explains how your personal information is collected, used, and disclosed by Hustloop.
67+
Hustloop (&quot;we,&quot; &quot;our,&quot; or &quot;us&quot;) is committed to protecting your privacy. This Privacy Policy explains how your personal information is collected, used, and disclosed by Hustloop.
6868
</p>
6969

7070
<h3 className="font-bold text-xl">1. Information We Collect</h3>

0 commit comments

Comments
 (0)