diff --git a/.gitignore b/.gitignore index 9b1913ec..cef619f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env.local # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies diff --git a/app/auth/register/page.tsx b/app/auth/register/page.tsx index f362eedb..4b00052e 100644 --- a/app/auth/register/page.tsx +++ b/app/auth/register/page.tsx @@ -151,7 +151,6 @@ export default function Register() { const isFormValid = name.trim() && email.trim() && - /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && password.trim() && confirmPassword.trim() && password === confirmPassword && @@ -311,7 +310,7 @@ export default function Register() { }`} /> Email Address - {email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && ( + {email && email.includes("@") && ( )} @@ -325,47 +324,27 @@ export default function Register() { onBlur={() => setFocusedField(null)} placeholder="Enter your email" required - className={`glass-effect focus:ring-yellow-400/20 pl-4 pr-4 py-3 text-sm sm:text-base transition-all duration-300 group-hover:shadow-lg ${ - email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && email.length > 0 - ? "border-red-400/60 focus:border-red-400/80 hover:border-red-400/70" - : "border-yellow-400/30 focus:border-yellow-400/60 hover:border-yellow-400/50" - }`} + className="glass-effect border-yellow-400/30 focus:border-yellow-400/60 focus:ring-yellow-400/20 pl-4 pr-4 py-3 text-sm sm:text-base transition-all duration-300 hover:border-yellow-400/50 group-hover:shadow-lg" disabled={isLoading} />
0 - ? "border-red-400/40 shadow-lg shadow-red-400/20" - : "border-yellow-400/40 shadow-lg shadow-yellow-400/20" - : email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && email.length > 0 - ? "border-red-400/20" + ? "border-yellow-400/40 shadow-lg shadow-yellow-400/20" : "border-yellow-400/20" }`} >
{/* Progress indicator */}
0 - ? "w-full bg-gradient-to-r from-red-400 to-orange-500" + className={`absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-yellow-400 to-blue-500 transition-all duration-300 ${ + email && email.includes("@") + ? "w-full" : email - ? "w-1/2 bg-gradient-to-r from-yellow-400 to-blue-500" + ? "w-1/2" : "w-0" }`} >
- - {/* Email validation feedback */} - {email && email.length > 0 && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && ( -
-

- - Please enter a valid email address -

-
- )} {/* Enhanced Password field with strength indicator */} diff --git a/app/auth/signin/page.tsx b/app/auth/signin/page.tsx index 9caad166..e7a15fec 100644 --- a/app/auth/signin/page.tsx +++ b/app/auth/signin/page.tsx @@ -194,7 +194,7 @@ export default function SignIn() { }`} /> Email Address - {email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && ( + {email && email.includes("@") && ( )} @@ -208,47 +208,27 @@ export default function SignIn() { onBlur={() => setFocusedField(null)} placeholder="Enter your email" required - className={`glass-effect focus:ring-yellow-400/20 pl-4 pr-4 py-3 text-sm sm:text-base transition-all duration-300 group-hover:shadow-lg ${ - email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && email.length > 0 - ? "border-red-400/60 focus:border-red-400/80 hover:border-red-400/70" - : "border-yellow-400/30 focus:border-yellow-400/60 hover:border-yellow-400/50" - }`} + className="glass-effect border-yellow-400/30 focus:border-yellow-400/60 focus:ring-yellow-400/20 pl-4 pr-4 py-3 text-sm sm:text-base transition-all duration-300 hover:border-yellow-400/50 group-hover:shadow-lg" disabled={isLoading} />
0 - ? "border-red-400/40 shadow-lg shadow-red-400/20" - : "border-yellow-400/40 shadow-lg shadow-yellow-400/20" - : email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && email.length > 0 - ? "border-red-400/20" + ? "border-yellow-400/40 shadow-lg shadow-yellow-400/20" : "border-yellow-400/20" }`} >
{/* Progress indicator */}
0 - ? "w-full bg-gradient-to-r from-red-400 to-orange-500" + className={`absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-yellow-400 to-blue-500 transition-all duration-300 ${ + email && email.includes("@") + ? "w-full" : email - ? "w-1/2 bg-gradient-to-r from-yellow-400 to-blue-500" + ? "w-1/2" : "w-0" }`} >
- - {/* Email validation feedback */} - {email && email.length > 0 && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && ( -
-

- - Please enter a valid email address -

-
- )} {/* Enhanced password field with better UX */} @@ -338,7 +318,7 @@ export default function SignIn() { > - + {/* Download Options */} {letterData && (
@@ -554,33 +541,20 @@ ${letterData.content || ''} Fill in the details to send your letter directly to the recipient. - +
-
- +
- +
- +
); -} +} \ No newline at end of file diff --git a/components/resume/guided-resume-generator.tsx b/components/resume/guided-resume-generator.tsx index bdc76d10..d8543f38 100644 --- a/components/resume/guided-resume-generator.tsx +++ b/components/resume/guided-resume-generator.tsx @@ -61,7 +61,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat }); const [professionalSummary, setProfessionalSummary] = useState(""); - + const [workExperience, setWorkExperience] = useState([{ title: "", company: "", @@ -109,9 +109,6 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat portfolio: "" }); - // Email validation function - const isValidEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); - const steps: { id: ResumeStep; title: string; icon: any; description: string }[] = [ { id: 'personal', title: 'Personal Info', icon: User, description: 'Basic contact information' }, { id: 'summary', title: 'Professional Summary', icon: FileText, description: 'Your professional overview' }, @@ -217,7 +214,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat } const resume = await response.json(); - + if (onResumeGenerated) { onResumeGenerated(resume); } @@ -307,7 +304,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

{stepGuidance.description}

- + {stepGuidance.tips && (

@@ -361,7 +358,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat className="glass-effect" />

- +
- +
- +
- +
{skillList.map((skill, index) => ( ))} - +
- +
- +
- +
- + {jobDescription && (
@@ -989,7 +977,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

{personalInfo.name}

- +
@@ -999,7 +987,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

- +
@@ -1007,7 +995,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

{workExperience.length} entries

- +
@@ -1015,7 +1003,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

{education.length} entries

- +
@@ -1025,7 +1013,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

- +
@@ -1033,7 +1021,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

{projects.length} entries

- +
@@ -1041,7 +1029,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat

{certifications.length} entries

- +
@@ -1112,7 +1100,7 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat const isStepValid = () => { switch (currentStep) { case 'personal': - return personalInfo.name && personalInfo.email && isValidEmail(personalInfo.email) && personalInfo.phone && personalInfo.location && targetRole; + return personalInfo.name && personalInfo.email && personalInfo.phone && personalInfo.location && targetRole; case 'summary': return professionalSummary.length > 0; case 'experience': @@ -1137,11 +1125,11 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat return (
{renderStepIndicator()} - + {renderGuidancePanel()} - + {renderCurrentStep()} - +
- + {currentStep === 'review' ? (
- + {!isGenerating && (
)} @@ -1190,4 +1178,4 @@ export function GuidedResumeGenerator({ onResumeGenerated }: GuidedResumeGenerat
); -} +} \ No newline at end of file diff --git a/components/resume/resume-generator.tsx b/components/resume/resume-generator.tsx index ce59fa78..a8e4e3af 100644 --- a/components/resume/resume-generator.tsx +++ b/components/resume/resume-generator.tsx @@ -303,9 +303,6 @@ export function ResumeGenerator() { > Email - {email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && ( - - )} setEmail(e.target.value)} - className={`glass-effect focus:ring-yellow-400/20 w-full text-base px-3 py-2 ${ - email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && email.length > 0 - ? "border-red-400/60 focus:border-red-400/80" - : "border-yellow-400/30 focus:border-yellow-400/60" - }`} + className="glass-effect border-yellow-400/30 focus:border-yellow-400/60 focus:ring-yellow-400/20 w-full text-base px-3 py-2" disabled={isGenerating} />
diff --git a/components/site-header.tsx b/components/site-header.tsx index 5a60284d..da551239 100644 --- a/components/site-header.tsx +++ b/components/site-header.tsx @@ -55,17 +55,16 @@ export function SiteHeader() { const handleNavClick = () => { setIsSheetOpen(false); }; - return (
- {/* Logo with tooltip for desktop only */} - + disabled={typeof window !== "undefined" && window.innerWidth < 1536}*/} + - + {/* */} {/* Mobile Navigation Sheet */} - + {/*
- )} + )} - {/* Sign In Button for Mobile */} + Sign In Button for Mobile {!user && (
@@ -222,10 +220,10 @@ export function SiteHeader() { )}
- + - {/* Desktop Navigation with Tooltips */} -
- - {/* Right Side Actions */} -
+ + {/* Right Side Actionsx */} +
+ + {/* PWA Install Button */} @@ -348,6 +348,148 @@ export function SiteHeader() { )} + + + + + + + +
+ + +
+ DocMagic +
+ + Access all document creation tools + +
+ +
+ {/* Navigation Items */} + + + {/* User Section in Mobile */} + {user && ( +
+
+ + + + {( + user.user_metadata?.name?.[0] || + user.email?.[0] || + "U" + ).toUpperCase()} + + +
+

+ {user.user_metadata?.name || "User"} +

+

+ {user.email} +

+
+
+ +
+ + + + Profile + + + + + + Settings + + + +
+
+ )} + + {/* Sign In Button for Mobile */} + {!user && ( +
+ + + +
+ )} +
+
+
diff --git a/lib/validation.ts b/lib/validation.ts index 9f6b5917..5b410590 100644 --- a/lib/validation.ts +++ b/lib/validation.ts @@ -1,10 +1,7 @@ import { z } from 'zod'; -// Email validation schema with consistent regex -export const emailSchema = z - .string() - .regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'Please enter a valid email address') - .max(254, 'Email address is too long'); +// Email validation schema +export const emailSchema = z.string().email('Invalid email format').max(254); // Password validation schema export const passwordSchema = z @@ -89,4 +86,4 @@ export function detectSqlInjection(input: string): boolean { // Rate limiting key generator export function generateRateLimitKey(ip: string, endpoint: string): string { return `${ip}:${endpoint}`; -} +} \ No newline at end of file