From 7da58904a706871dc45673b10f2d0f92dbe94bdf Mon Sep 17 00:00:00 2001 From: Anjali Chourasia Date: Sat, 26 Jul 2025 19:30:13 +0530 Subject: [PATCH 1/4] Fix: Center-align footer content on mobile view (issue #114) --- components/layout/Footer.tsx | 252 +++++++++++++++++++++++++---------- 1 file changed, 180 insertions(+), 72 deletions(-) diff --git a/components/layout/Footer.tsx b/components/layout/Footer.tsx index cbf6e36..724bb17 100644 --- a/components/layout/Footer.tsx +++ b/components/layout/Footer.tsx @@ -1,15 +1,15 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { BRAND_NAME, WHATSAPP_COMMUNITY_LINK } from '../../constants'; -import { +import React from "react"; +import { Link } from "react-router-dom"; +import { BRAND_NAME, WHATSAPP_COMMUNITY_LINK } from "../../constants"; +import { WhatsAppIcon, YoutubeIcon, LinkedinIcon, InstagramIcon, TelegramIcon, MailIcon, - PhoneIcon -} from '../icons'; // Assuming MailIcon and PhoneIcon exist or will be added + PhoneIcon, +} from "../icons"; // Assuming MailIcon and PhoneIcon exist or will be added interface FooterProps { layoutStyle?: React.CSSProperties; @@ -18,110 +18,218 @@ interface FooterProps { const Footer: React.FC = ({ layoutStyle }) => { const currentYear = new Date().getFullYear(); const socialLinks = [ - { href: WHATSAPP_COMMUNITY_LINK, icon: WhatsAppIcon, label: 'WhatsApp', color: 'hover:text-green-500' }, - { href: 'https://www.youtube.com/@techxninjas?sub_confirmation=1', icon: YoutubeIcon, label: 'YouTube', color: 'hover:text-red-600' }, - { href: 'https://www.linkedin.com/company/techxninjas', icon: LinkedinIcon, label: 'LinkedIn', color: 'hover:text-blue-700' }, - { href: 'https://www.instagram.com/cipherschools', icon: InstagramIcon, label: 'Instagram', color: 'hover:text-pink-600' }, // Note: Instagram link is to cipherschools as per prompt - { href: 'https://t.me/thetechxninjas', icon: TelegramIcon, label: 'Telegram', color: 'hover:text-blue-500' }, + { + href: WHATSAPP_COMMUNITY_LINK, + icon: WhatsAppIcon, + label: "WhatsApp", + color: "hover:text-green-500", + }, + { + href: "https://www.youtube.com/@techxninjas?sub_confirmation=1", + icon: YoutubeIcon, + label: "YouTube", + color: "hover:text-red-600", + }, + { + href: "https://www.linkedin.com/company/techxninjas", + icon: LinkedinIcon, + label: "LinkedIn", + color: "hover:text-blue-700", + }, + { + href: "https://www.instagram.com/cipherschools", + icon: InstagramIcon, + label: "Instagram", + color: "hover:text-pink-600", + }, // Note: Instagram link is to cipherschools as per prompt + { + href: "https://t.me/thetechxninjas", + icon: TelegramIcon, + label: "Telegram", + color: "hover:text-blue-500", + }, ]; return ( -
-
- +
{/* Column 1: TechXNinjas */} -
-
{BRAND_NAME}
+
+
+ {BRAND_NAME} +

- Your gateway to hackathons, tech challenges, inspiring speakers, and innovation. We empower tech enthusiasts across India. + Your gateway to hackathons, tech challenges, inspiring speakers, + and innovation. We empower tech enthusiasts across India.

{/* Column 2: Pages */} -
-
Pages
+
+
+ Pages +
    -
  • Home
  • -
  • Events
  • -
  • Courses
  • -
  • Giveaways
  • -
  • Articles
  • +
  • + + Home + +
  • +
  • + + Events + +
  • +
  • + + Courses + +
  • +
  • + + Giveaways + +
  • +
  • + + Articles + +
{/* Column 3: Support & Community */} -
-
Support & Community
+
+
+ Support & Community +
-
- + {/* Column 4: Contact Info */} -
-
Contact Info
+ - - {/* Column 5: Connect With Us */} -
-
Connect with us
- -
-
+ {/* Column 5: Connect With Us */} +
+
+ Connect with us +
+ +
+
-

© {currentYear + 1} {BRAND_NAME}. All rights reserved.

-

Built with ❤️ by TechXNinjas Student Community.

-
- +

+ © {currentYear + 1} {BRAND_NAME}. All rights reserved. +

+

Built with ❤️ by TechXNinjas Student Community.

+
); }; -export default Footer; \ No newline at end of file +export default Footer; From ab4a80165a3653df4ede609fbf1396e9ab52e72a Mon Sep 17 00:00:00 2001 From: Anjali Chourasia Date: Sat, 26 Jul 2025 23:09:54 +0530 Subject: [PATCH 2/4] fix: change pagination dots color to orange for visibility --- components/MentorsSlider.tsx | 60 ++++++++++-------- components/TestimonialsSlider.tsx | 65 +++++++++++-------- index.css | 102 ++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 52 deletions(-) diff --git a/components/MentorsSlider.tsx b/components/MentorsSlider.tsx index 1f3d3d4..7d64795 100644 --- a/components/MentorsSlider.tsx +++ b/components/MentorsSlider.tsx @@ -1,14 +1,11 @@ -import React, { useEffect, useState } from 'react'; -import { ChevronLeft, ChevronRight } from 'lucide-react'; -import { HomepageMentor } from '../types'; -import { useInView } from 'react-intersection-observer'; +import React, { useEffect, useState } from "react"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import { HomepageMentor } from "../types"; +import { useInView } from "react-intersection-observer"; // Import Swiper React components and styles -import { Swiper, SwiperSlide } from 'swiper/react'; -import { Navigation, Autoplay, Pagination } from 'swiper/modules'; -import 'swiper/css'; -import 'swiper/css/navigation'; -import 'swiper/css/pagination'; +import { Swiper, SwiperSlide } from "swiper/react"; +import { Navigation, Autoplay, Pagination } from "swiper/modules"; interface MentorsSliderProps { mentors: HomepageMentor[]; @@ -21,7 +18,7 @@ const MentorsSlider: React.FC = ({ mentors, autoplay = true, delay = 3000, - className = '' + className = "", }) => { const [isMounted, setIsMounted] = useState(false); const { ref, inView } = useInView({ @@ -47,34 +44,47 @@ const MentorsSlider: React.FC = ({ breakpoints={{ 480: { slidesPerView: 3 }, 640: { slidesPerView: 4 }, - 1024: { slidesPerView: 5 } + 1024: { slidesPerView: 5 }, }} navigation={{ - prevEl: '.mentor-prev', - nextEl: '.mentor-next', + prevEl: ".mentor-prev", + nextEl: ".mentor-next", }} pagination={{ clickable: true, - el: '.mentor-pagination', + el: ".mentor-pagination", }} - autoplay={autoplay && inView ? { - delay: delay, - disableOnInteraction: false, - pauseOnMouseEnter: true - } : false} + autoplay={ + autoplay && inView + ? { + delay: delay, + disableOnInteraction: false, + pauseOnMouseEnter: true, + } + : false + } loop={true} className="py-8" > {mentors.map((mentor) => (
- {mentor.name} -

{mentor.name}

-

{mentor.role}

+

+ {mentor.name} +

+

+ {mentor.role} +

{mentor.company}

@@ -95,4 +105,4 @@ const MentorsSlider: React.FC = ({ ); }; -export default MentorsSlider; \ No newline at end of file +export default MentorsSlider; diff --git a/components/TestimonialsSlider.tsx b/components/TestimonialsSlider.tsx index 572ff01..9d31d3f 100644 --- a/components/TestimonialsSlider.tsx +++ b/components/TestimonialsSlider.tsx @@ -1,14 +1,11 @@ -import React, { useEffect, useState } from 'react'; -import { ChevronLeft, ChevronRight } from 'lucide-react'; -import { Testimonial } from '../types'; -import { useInView } from 'react-intersection-observer'; +import React, { useEffect, useState } from "react"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import { Testimonial } from "../types"; +import { useInView } from "react-intersection-observer"; // Import Swiper React components and styles -import { Swiper, SwiperSlide } from 'swiper/react'; -import { Navigation, Autoplay, Pagination } from 'swiper/modules'; -import 'swiper/css'; -import 'swiper/css/navigation'; -import 'swiper/css/pagination'; +import { Swiper, SwiperSlide } from "swiper/react"; +import { Navigation, Autoplay, Pagination } from "swiper/modules"; interface TestimonialsSliderProps { testimonials: Testimonial[]; @@ -21,7 +18,7 @@ const TestimonialsSlider: React.FC = ({ testimonials, autoplay = true, delay = 3000, - className = '' + className = "", }) => { const [isMounted, setIsMounted] = useState(false); const { ref, inView } = useInView({ @@ -46,37 +43,51 @@ const TestimonialsSlider: React.FC = ({ slidesPerView={1} breakpoints={{ 640: { slidesPerView: 2 }, - 1024: { slidesPerView: 3 } + 1024: { slidesPerView: 3 }, }} navigation={{ - prevEl: '.testimonial-prev', - nextEl: '.testimonial-next', + prevEl: ".testimonial-prev", + nextEl: ".testimonial-next", }} pagination={{ clickable: true, - el: '.testimonial-pagination', + el: ".testimonial-pagination", }} - autoplay={autoplay && inView ? { - delay: delay, - disableOnInteraction: false, - pauseOnMouseEnter: true - } : false} + autoplay={ + autoplay && inView + ? { + delay: delay, + disableOnInteraction: false, + pauseOnMouseEnter: true, + } + : false + } loop={true} className="py-8" > {testimonials.map((testimonial) => (
- - {testimonial.name} -

"{testimonial.review}"

+

+ "{testimonial.review}" +

-

{testimonial.name}

-

{testimonial.designation}

+

+ {testimonial.name} +

+

+ {testimonial.designation} +

@@ -97,4 +108,4 @@ const TestimonialsSlider: React.FC = ({ ); }; -export default TestimonialsSlider; \ No newline at end of file +export default TestimonialsSlider; diff --git a/index.css b/index.css index 6f2085d..04f9ff1 100644 --- a/index.css +++ b/index.css @@ -427,4 +427,106 @@ html { .register-button-pulse { animation: pulse 2s infinite; +} + +/* Essential Swiper styles for proper functionality */ +.swiper { + margin-left: auto; + margin-right: auto; + position: relative; + overflow: hidden; + list-style: none; + padding: 0; + z-index: 1; +} + +.swiper-wrapper { + position: relative; + width: 100%; + height: 100%; + z-index: 1; + display: flex; + transition-property: transform; + box-sizing: content-box; +} + +.swiper-slide { + flex-shrink: 0; + width: 100%; + height: 100%; + position: relative; + transition-property: transform; +} + +/* Swiper pagination */ +.swiper-pagination { + position: absolute; + text-align: center; + transition: 0.3s opacity; + transform: translate3d(0, 0, 0); + z-index: 10; +} + +.swiper-pagination-bullet { + width: 8px; + height: 8px; + display: inline-block; + border-radius: 50%; + background: #000; + opacity: 0.2; + margin: 0 4px; + cursor: pointer; + transition: all 0.3s; +} + +.swiper-pagination-bullet-active { + opacity: 1; + background: #007aff; +} + +/* Custom pagination colors for better visibility */ +.mentor-pagination .swiper-pagination-bullet { + background-color: #9d9fa0 !important; /* brand-medium-gray for inactive dots */ + opacity: 0.6 !important; +} + +.mentor-pagination .swiper-pagination-bullet-active { + background-color: #ff6806 !important; /* brand-primary orange for active dot */ + opacity: 1 !important; +} + +.mentor-pagination .swiper-pagination-bullet:hover { + background-color: #ff6806 !important; /* brand-primary orange on hover */ + opacity: 0.8 !important; +} + +.testimonial-pagination .swiper-pagination-bullet { + background-color: #9d9fa0 !important; /* brand-medium-gray for inactive dots */ + opacity: 0.6 !important; +} + +.testimonial-pagination .swiper-pagination-bullet-active { + background-color: #ff6806 !important; /* brand-primary orange for active dot */ + opacity: 1 !important; +} + +.testimonial-pagination .swiper-pagination-bullet:hover { + background-color: #ff6806 !important; /* brand-primary orange on hover */ + opacity: 0.8 !important; +} + +/* Dark mode support */ +.dark .mentor-pagination .swiper-pagination-bullet, +.dark .testimonial-pagination .swiper-pagination-bullet { + background-color: #6b7280 !important; /* darker gray for dark mode */ +} + +.dark .mentor-pagination .swiper-pagination-bullet-active, +.dark .testimonial-pagination .swiper-pagination-bullet-active { + background-color: #ff6806 !important; /* same orange for dark mode */ +} + +.dark .mentor-pagination .swiper-pagination-bullet:hover, +.dark .testimonial-pagination .swiper-pagination-bullet:hover { + background-color: #ff6806 !important; /* same orange for dark mode hover */ } \ No newline at end of file From b297711e84eb3ec1863f8fcb911cbe2c8bcf6714 Mon Sep 17 00:00:00 2001 From: Anjali Chourasia Date: Sun, 27 Jul 2025 17:42:54 +0530 Subject: [PATCH 3/4] resolved carousel arrow visibility issue on mobile view --- components/MentorsSlider.tsx | 8 ++++---- components/TestimonialsSlider.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/MentorsSlider.tsx b/components/MentorsSlider.tsx index 7d64795..892c8f8 100644 --- a/components/MentorsSlider.tsx +++ b/components/MentorsSlider.tsx @@ -92,11 +92,11 @@ const MentorsSlider: React.FC = ({ {/* Custom navigation buttons */} - - {/* Custom pagination */} diff --git a/components/TestimonialsSlider.tsx b/components/TestimonialsSlider.tsx index 9d31d3f..3f60ac2 100644 --- a/components/TestimonialsSlider.tsx +++ b/components/TestimonialsSlider.tsx @@ -95,11 +95,11 @@ const TestimonialsSlider: React.FC = ({ {/* Custom navigation buttons */} - - {/* Custom pagination */} From cf681546ef96462deb34600470606ab1d95ef003 Mon Sep 17 00:00:00 2001 From: Anjali Chourasia Date: Sun, 27 Jul 2025 18:24:23 +0530 Subject: [PATCH 4/4] resolved login-register form overflow --- components/auth/AuthModal.tsx | 120 ++++++++------- components/auth/ForgotPasswordForm.tsx | 85 +++++++---- components/auth/LoginForm.tsx | 198 ++++++++++++++++--------- components/auth/RegisterForm.tsx | 129 ++++++++++------ index.css | 32 +++- 5 files changed, 366 insertions(+), 198 deletions(-) diff --git a/components/auth/AuthModal.tsx b/components/auth/AuthModal.tsx index 68101d4..9c116da 100644 --- a/components/auth/AuthModal.tsx +++ b/components/auth/AuthModal.tsx @@ -1,14 +1,12 @@ +import React, { useState, useEffect, useContext } from "react"; +import LoginForm from "./LoginForm"; +import RegisterForm from "./RegisterForm"; +import ForgotPasswordForm from "./ForgotPasswordForm"; +import { CloseIcon } from "../icons"; +import { AuthContext } from "../../contexts/AuthContext"; +import { AuthContextType } from "../../types"; -import React, { useState, useEffect, useContext } from 'react'; -import LoginForm from './LoginForm'; -import RegisterForm from './RegisterForm'; -import ForgotPasswordForm from './ForgotPasswordForm'; -import { CloseIcon } from '../icons'; -import { AuthContext } from '../../contexts/AuthContext'; -import { AuthContextType } from '../../types'; - - -type AuthView = 'login' | 'register' | 'forgotPassword'; +type AuthView = "login" | "register" | "forgotPassword"; interface AuthModalProps { isOpen: boolean; @@ -16,51 +14,63 @@ interface AuthModalProps { initialView?: AuthView; } -const AuthModal: React.FC = ({ isOpen, onClose, initialView = 'login' }) => { +const AuthModal: React.FC = ({ + isOpen, + onClose, + initialView = "login", +}) => { const [currentView, setCurrentView] = useState(initialView); const authContext = useContext(AuthContext); useEffect(() => { if (isOpen) { - setCurrentView(initialView); - authContext?.clearError(); + setCurrentView(initialView); + authContext?.clearError(); } }, [isOpen, initialView, authContext]); - + useEffect(() => { if (authContext?.user && isOpen) { - onClose(); + onClose(); } }, [authContext?.user, isOpen, onClose]); - if (!isOpen) { return null; } - const switchToRegister = () => setCurrentView('register'); - const switchToLogin = () => setCurrentView('login'); - const switchToForgotPassword = () => setCurrentView('forgotPassword'); + const switchToRegister = () => setCurrentView("register"); + const switchToLogin = () => setCurrentView("login"); + const switchToForgotPassword = () => setCurrentView("forgotPassword"); const getTitle = () => { switch (currentView) { - case 'login': return 'Login'; // Title is shorter as per new design - case 'register': return 'Create Account'; - case 'forgotPassword': return 'Reset Password'; - default: return ''; + case "login": + return "Login"; // Title is shorter as per new design + case "register": + return "Create Account"; + case "forgotPassword": + return "Reset Password"; + default: + return ""; } }; - + const handleOverlayClick = (e: React.MouseEvent) => { - if (e.target === e.currentTarget) { - onClose(); + if (e.target === e.currentTarget) { + onClose(); } }; - return ( -
-
+
+
- - {currentView === 'login' && ( + + {currentView === "login" && (
-

+

Welcome to TechXNinjas!

@@ -80,35 +93,40 @@ const AuthModal: React.FC = ({ isOpen, onClose, initialView = 'l

)} -

+

{/* Title is displayed here for register/forgot, sr-only for login as it has custom header */} {getTitle()}

- {authContext?.error && ( -
-

Error: {authContext.error.message}

-
+
+

+ Error: {authContext.error.message} +

+
)} - {currentView === 'login' && ( - - )} - {currentView === 'register' && ( - )} - {currentView === 'forgotPassword' && ( - { switchToLogin(); }} + {currentView === "register" && ( + + )} + {currentView === "forgotPassword" && ( + { + switchToLogin(); + }} /> )}
@@ -116,4 +134,4 @@ const AuthModal: React.FC = ({ isOpen, onClose, initialView = 'l ); }; -export default AuthModal; \ No newline at end of file +export default AuthModal; diff --git a/components/auth/ForgotPasswordForm.tsx b/components/auth/ForgotPasswordForm.tsx index 7bd3d58..21103ec 100644 --- a/components/auth/ForgotPasswordForm.tsx +++ b/components/auth/ForgotPasswordForm.tsx @@ -1,46 +1,52 @@ -import React, { useState, useContext } from 'react'; -import { AuthContext } from '../../contexts/AuthContext'; -import { AuthContextType } from '../../types'; -import { ArrowLeftIcon } from '../icons'; -import { Turnstile } from '@marsidev/react-turnstile'; +import React, { useState, useContext } from "react"; +import { AuthContext } from "../../contexts/AuthContext"; +import { AuthContextType } from "../../types"; +import { ArrowLeftIcon } from "../icons"; +import { Turnstile } from "@marsidev/react-turnstile"; interface ForgotPasswordFormProps { onSwitchToLogin: () => void; onSuccess: () => void; // Called after successful email submission } -const ForgotPasswordForm: React.FC = ({ onSwitchToLogin, onSuccess }) => { - const [email, setEmail] = useState(''); +const ForgotPasswordForm: React.FC = ({ + onSwitchToLogin, + onSuccess, +}) => { + const [email, setEmail] = useState(""); const auth = useContext(AuthContext) as AuthContextType; const [captchaToken, setCaptchaToken] = useState(); const [captchaError, setCaptchaError] = useState(null); - const turnstileSiteKey = '0x4AAAAAABhuYfA0fxpwvokl'; + const turnstileSiteKey = "0x4AAAAAABhuYfA0fxpwvokl"; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!auth) return; if (!captchaToken) { - setCaptchaError("Please complete the CAPTCHA challenge."); - return; + setCaptchaError("Please complete the CAPTCHA challenge."); + return; } - auth.clearError(); + auth.clearError(); // setMessage(null); // Local message not used, AuthModal displays global errors try { await auth.resetPasswordForEmail(email, captchaToken); // Message is handled by AuthContext alert. - onSuccess(); + onSuccess(); } catch (error: any) { console.error("Forgot password failed:", error); } finally { - setCaptchaToken(undefined); // Reset token + setCaptchaToken(undefined); // Reset token } }; return (
-
-
{/* Centering classes moved here */} - {!turnstileSiteKey ? ( -
- CAPTCHA is not configured. Please contact support. +
+ {/* CAPTCHA container with proper sizing to prevent cut-off */} +
+ {!turnstileSiteKey ? ( +
+ CAPTCHA is not configured. Please contact support.
- ) : ( - + { setCaptchaError("CAPTCHA challenge failed. Please refresh and try again."); setCaptchaToken(undefined); }} - onExpire={() => { setCaptchaError("CAPTCHA challenge expired. Please refresh and try again."); setCaptchaToken(undefined); }} - options={{ theme: document.documentElement.classList.contains('dark') ? 'dark' : 'light' }} - // className="flex justify-center" // Removed from here - /> - )} + onError={() => { + setCaptchaError( + "CAPTCHA challenge failed. Please refresh and try again." + ); + setCaptchaToken(undefined); + }} + onExpire={() => { + setCaptchaError( + "CAPTCHA challenge expired. Please refresh and try again." + ); + setCaptchaToken(undefined); + }} + options={{ + theme: document.documentElement.classList.contains("dark") + ? "dark" + : "light", + }} + /> +
+ )} +
{captchaError && ( -

{captchaError}

+

+ {captchaError} +

)}
-
@@ -100,4 +125,4 @@ const ForgotPasswordForm: React.FC = ({ onSwitchToLogin ); }; -export default ForgotPasswordForm; \ No newline at end of file +export default ForgotPasswordForm; diff --git a/components/auth/LoginForm.tsx b/components/auth/LoginForm.tsx index 267b6d5..d34a710 100644 --- a/components/auth/LoginForm.tsx +++ b/components/auth/LoginForm.tsx @@ -1,9 +1,9 @@ -import React, { useState, useContext, useEffect } from 'react'; -import { Link } from 'react-router-dom'; -import { AuthContext } from '../../contexts/AuthContext'; -import { AuthContextType } from '../../types'; -import { GoogleIcon } from '../icons'; -import { Turnstile } from '@marsidev/react-turnstile'; +import React, { useState, useContext, useEffect } from "react"; +import { Link } from "react-router-dom"; +import { AuthContext } from "../../contexts/AuthContext"; +import { AuthContextType } from "../../types"; +import { GoogleIcon } from "../icons"; +import { Turnstile } from "@marsidev/react-turnstile"; interface LoginFormProps { onSwitchToRegister: () => void; @@ -11,20 +11,24 @@ interface LoginFormProps { onSuccess: () => void; } -type LoginTab = 'emailPassword' | 'magicLink'; +type LoginTab = "emailPassword" | "magicLink"; -const LoginForm: React.FC = ({ onSwitchToRegister, onSwitchToForgotPassword, onSuccess }) => { - const [activeTab, setActiveTab] = useState('emailPassword'); - - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [magicLinkEmail, setMagicLinkEmail] = useState(''); +const LoginForm: React.FC = ({ + onSwitchToRegister, + onSwitchToForgotPassword, + onSuccess, +}) => { + const [activeTab, setActiveTab] = useState("emailPassword"); + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [magicLinkEmail, setMagicLinkEmail] = useState(""); const [magicLinkMessage, setMagicLinkMessage] = useState(null); const [magicLinkError, setMagicLinkError] = useState(null); - + const [captchaToken, setCaptchaToken] = useState(); const [captchaError, setCaptchaError] = useState(null); - const turnstileSiteKey = '0x4AAAAAABhuYfA0fxpwvokl'; + const turnstileSiteKey = "0x4AAAAAABhuYfA0fxpwvokl"; const auth = useContext(AuthContext) as AuthContextType; @@ -41,16 +45,16 @@ const LoginForm: React.FC = ({ onSwitchToRegister, onSwitchToFor e.preventDefault(); if (!auth || !captchaToken) return; auth.clearError(); - setMagicLinkMessage(null); + setMagicLinkMessage(null); setMagicLinkError(null); try { await auth.login(email, password, captchaToken); - onSuccess(); + onSuccess(); } catch (error) { console.error("Login failed:", error); // AuthModal will display auth.error } finally { - setCaptchaToken(undefined); // Reset token for re-challenge + setCaptchaToken(undefined); // Reset token for re-challenge } }; @@ -60,14 +64,14 @@ const LoginForm: React.FC = ({ onSwitchToRegister, onSwitchToFor // If you want to gate the button click with Turnstile: // if (!captchaToken) { setCaptchaError("Please complete the CAPTCHA first."); return; } auth.clearError(); - setMagicLinkMessage(null); + setMagicLinkMessage(null); setMagicLinkError(null); try { await auth.signInWithGoogle(captchaToken); // Pass token if gating } catch (error) { console.error("Google Sign in failed from button:", error); } finally { - setCaptchaToken(undefined); // Reset token for Google sign-in if it was used for gating + setCaptchaToken(undefined); // Reset token for Google sign-in if it was used for gating } }; @@ -79,24 +83,34 @@ const LoginForm: React.FC = ({ onSwitchToRegister, onSwitchToFor setMagicLinkError(null); try { await auth.sendMagicLink(magicLinkEmail, captchaToken); - setMagicLinkMessage("If your email is registered, a magic link has been sent. Please check your inbox to log in."); + setMagicLinkMessage( + "If your email is registered, a magic link has been sent. Please check your inbox to log in." + ); } catch (error: any) { - setMagicLinkError(error.message || "Failed to send magic link. Please try again."); + setMagicLinkError( + error.message || "Failed to send magic link. Please try again." + ); } finally { - setCaptchaToken(undefined); // Reset token + setCaptchaToken(undefined); // Reset token } }; - - const TabButton: React.FC<{ tabId: LoginTab; currentTab: LoginTab; onClick: () => void; children: React.ReactNode }> = ({ tabId, currentTab, onClick, children }) => ( + + const TabButton: React.FC<{ + tabId: LoginTab; + currentTab: LoginTab; + onClick: () => void; + children: React.ReactNode; + }> = ({ tabId, currentTab, onClick, children }) => (
-