Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions Frontend/src/pages/BasicDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,40 @@ import { UserNav } from "../components/user-nav";
import { Link } from "react-router-dom";
import { ModeToggle } from "../components/mode-toggle";

/**
* BasicDetails Component
*
* Multi-step onboarding form for collecting user profile information.
* Supports both influencer and brand account types with different workflows.
*
* Features:
* - Multi-step form with animated transitions
* - Role-specific fields (influencer vs brand)
* - Social media profile collection
* - Contact information capture
* - Autocomplete attributes for all input fields:
* - given-name/family-name for names
* - email for email addresses
* - tel for phone numbers
* - organization for company names
* - url for website fields
* - WCAG 2.1 compliant form inputs for accessibility
*
* @component
* @returns {JSX.Element} The onboarding details form
*/
export default function BasicDetails() {
const { user } = useParams();
const [step, setStep] = useState(0);
const [animationDirection, setAnimationDirection] = useState(0);

const totalSteps = user === "influencer" ? 3 : 2;

/**
* Advances to the next step in the onboarding flow
*
* @returns {void}
*/
const nextStep = () => {
if ((user === "influencer" && step < 2) || (user === "brand" && step < 1)) {
setAnimationDirection(1);
Expand All @@ -48,6 +76,11 @@ export default function BasicDetails() {
}
};

/**
* Returns to the previous step in the onboarding flow
*
* @returns {void}
*/
const prevStep = () => {
if (step > 0) {
setAnimationDirection(-1);
Expand All @@ -72,6 +105,8 @@ export default function BasicDetails() {
<Label htmlFor="firstName">First Name</Label>
<Input
id="firstName"
name="firstName"
autoComplete="given-name"
placeholder="John"
className="border border-gray-300"
/>
Expand All @@ -80,6 +115,8 @@ export default function BasicDetails() {
<Label htmlFor="lastName">Last Name</Label>
<Input
id="lastName"
name="lastName"
autoComplete="family-name"
placeholder="Doe"
className="border border-gray-300"
/>
Expand All @@ -89,7 +126,9 @@ export default function BasicDetails() {
<Label htmlFor="email">Email</Label>
<Input
id="email"
name="email"
type="email"
autoComplete="email"
placeholder="john@example.com"
className="border border-gray-300"
/>
Expand All @@ -98,7 +137,9 @@ export default function BasicDetails() {
<Label htmlFor="phone">Phone Number</Label>
<Input
id="phone"
name="phone"
type="tel"
autoComplete="tel"
placeholder="+1 (555) 000-0000"
className="border border-gray-300"
/>
Expand Down Expand Up @@ -224,11 +265,11 @@ export default function BasicDetails() {
Brand Information
</h3>
<Label htmlFor="companyName">Company Name</Label>
<Input id="companyName" placeholder="Brand Inc." />
<Input id="companyName" name="companyName" autoComplete="organization" placeholder="Brand Inc." />
</div>
<div className="space-y-2">
<Label htmlFor="website">Company Website</Label>
<Input id="website" type="url" placeholder="https://www.example.com" />
<Input id="website" name="website" type="url" autoComplete="url" placeholder="https://www.example.com" />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
Expand Down
29 changes: 29 additions & 0 deletions Frontend/src/pages/ForgotPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,40 @@ import { Link } from "react-router-dom";
import { ArrowLeft, Check, Rocket } from "lucide-react";
import { supabase } from "../utils/supabase";

/**
* ForgotPasswordPage Component
*
* Handles password reset request initiation by sending reset links
* to registered user emails. Includes validation and user feedback.
*
* Features:
* - Email validation against user database
* - Secure password reset link generation via Supabase
* - Case-sensitive email handling
* - New user detection with signup prompt
* - Autocomplete attribute (autoComplete="email") for browser autofill
* - WCAG 2.1 accessible form inputs
*
* @component
* @returns {JSX.Element} The forgot password page with email form
*/
export default function ForgotPasswordPage() {
const [email, setEmail] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const [error, setError] = useState("");
const [showSignupPrompt, setShowSignupPrompt] = useState(false);

/**
* Handles forgot password form submission
*
* Validates that the email exists in the database, then sends a
* password reset link via Supabase Auth. Provides appropriate
* feedback for both existing and non-existing emails.
*
* @param {React.FormEvent<HTMLFormElement>} e - Form submission event
* @returns {Promise<void>}
*/
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true);
Expand Down Expand Up @@ -124,7 +151,9 @@ export default function ForgotPasswordPage() {
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
Expand Down
39 changes: 39 additions & 0 deletions Frontend/src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ import { Eye, EyeOff, Rocket } from "lucide-react";
import { supabase } from "../utils/supabase";
import { useAuth } from "../context/AuthContext";

/**
* LoginPage Component
*
* Provides user authentication functionality with email and password.
* Includes accessibility features such as autocomplete attributes for
* enhanced user experience and WCAG 2.1 compliance.
*
* Features:
* - Email/password authentication via Supabase
* - Google OAuth integration
* - Password visibility toggle
* - Form validation and error handling
* - Autocomplete attributes for browser autofill and password managers
*
* @component
* @returns {JSX.Element} The login page with authentication form
*/
export default function LoginPage() {
const Navigate = useNavigate();
const { login } = useAuth();
Expand All @@ -13,6 +30,16 @@ export default function LoginPage() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");

/**
* Handles form submission for user login
*
* Authenticates user credentials via Supabase and manages navigation
* based on user onboarding status and role. Includes comprehensive
* error handling and loading state management.
*
* @param {React.FormEvent<HTMLFormElement>} e - Form submission event
* @returns {Promise<void>}
*/
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true);
Expand All @@ -39,6 +66,14 @@ export default function LoginPage() {
}
};

/**
* Initiates Google OAuth authentication flow
*
* Redirects user to Google sign-in page and handles the OAuth
* authentication process via Supabase.
*
* @returns {Promise<void>}
*/
const handleGoogleLogin = async () => {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "google",
Expand Down Expand Up @@ -104,7 +139,9 @@ export default function LoginPage() {
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
Expand All @@ -131,7 +168,9 @@ export default function LoginPage() {
<div className="relative">
<input
id="password"
name="password"
type={showPassword ? "text" : "password"}
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
Expand Down
31 changes: 31 additions & 0 deletions Frontend/src/pages/ResetPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ import { useNavigate, useParams } from "react-router-dom";
import { Check, Eye, EyeOff, Rocket } from "lucide-react";
import { supabase } from "../utils/supabase";

/**
* ResetPasswordPage Component
*
* Allows users to reset their password after receiving a reset link.
* Implements security best practices and accessibility features.
*
* Features:
* - Password strength validation
* - Password confirmation matching
* - Visual password strength indicator
* - Auto-redirect after successful reset
* - Autocomplete attributes (autoComplete="new-password") for password managers
* - WCAG 2.1 compliant form inputs
*
* @component
* @returns {JSX.Element} The password reset page with validation
*/
export default function ResetPasswordPage() {
const router = useNavigate();
const searchParams = useParams();
Expand Down Expand Up @@ -41,6 +58,16 @@ export default function ResetPasswordPage() {
};
}, [isSuccess, router]);

/**
* Handles password reset form submission
*
* Validates password match and strength, then updates user password
* via Supabase authentication. Shows success message and redirects
* to dashboard on successful reset.
*
* @param {React.FormEvent<HTMLFormElement>} e - Form submission event
* @returns {Promise<void>}
*/
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

Expand Down Expand Up @@ -167,7 +194,9 @@ export default function ResetPasswordPage() {
<div className="relative">
<input
id="password"
name="password"
type={showPassword ? "text" : "password"}
autoComplete="new-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
Expand Down Expand Up @@ -283,7 +312,9 @@ export default function ResetPasswordPage() {
<div className="relative">
<input
id="confirmPassword"
name="confirmPassword"
type={showPassword ? "text" : "password"}
autoComplete="new-password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
Expand Down
45 changes: 42 additions & 3 deletions Frontend/src/pages/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ import { supabase } from "../utils/supabase";
import { useAuth } from "@/context/AuthContext";
import { demoInsert } from '../utils/demoInsert';

/**
* SignupPage Component
*
* Handles new user registration with comprehensive form validation.
* Implements accessibility features including autocomplete attributes
* for improved UX and WCAG 2.1 compliance.
*
* Features:
* - Multi-step registration process
* - Account type selection (influencer/brand)
* - Email/password validation
* - Password confirmation matching
* - Demo data insertion for new users
* - Autocomplete attributes for browser autofill (autoComplete="new-password")
*
* @component
* @returns {JSX.Element} The signup page with registration form
*/
export default function SignupPage() {
const navigate = useNavigate();
const [formData, setFormData] = useState({
Expand All @@ -21,16 +39,37 @@ export default function SignupPage() {
const [user, setuser] = useState("influencer");
const { login } = useAuth();

/**
* Handles input field changes and updates form state
*
* @param {React.ChangeEvent<HTMLInputElement>} e - Input change event
* @returns {void}
*/
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};

/**
* Updates the selected account type (influencer or brand)
*
* @param {string} type - Account type to set
* @returns {void}
*/
const handleAccountTypeChange = (type: string) => {
setuser(type);
setFormData((prev) => ({ ...prev, accountType: type }));
};

/**
* Handles form submission for user registration
*
* Validates password confirmation, creates new user account via Supabase,
* inserts demo data, and navigates to appropriate onboarding flow.
*
* @param {React.FormEvent<HTMLFormElement>} e - Form submission event
* @returns {Promise<void>}
*/
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (formData.password !== formData.confirmPassword) {
Expand Down Expand Up @@ -164,15 +203,15 @@ export default function SignupPage() {
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<label htmlFor="email" className="text-sm font-medium text-gray-700 dark:text-gray-300">Email</label>
<input id="email" name="email" type="email" value={formData.email} onChange={handleChange} required className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200" placeholder="you@example.com" />
<input id="email" name="email" type="email" autoComplete="email" value={formData.email} onChange={handleChange} required className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200" placeholder="you@example.com" />
</div>
<div className="space-y-2">
<label htmlFor="password" className="text-sm font-medium text-gray-700 dark:text-gray-300">Password</label>
<input id="password" name="password" type={showPassword ? "text" : "password"} value={formData.password} onChange={handleChange} required className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200" placeholder="Password" />
<input id="password" name="password" type={showPassword ? "text" : "password"} autoComplete="new-password" value={formData.password} onChange={handleChange} required className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200" placeholder="Password" />
</div>
<div className="space-y-2">
<label htmlFor="confirmPassword" className="text-sm font-medium text-gray-700 dark:text-gray-300">Confirm Password</label>
<input id="confirmPassword" name="confirmPassword" type={showPassword ? "text" : "password"} value={formData.confirmPassword} onChange={handleChange} required className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200" placeholder="Confirm Password" />
<input id="confirmPassword" name="confirmPassword" type={showPassword ? "text" : "password"} autoComplete="new-password" value={formData.confirmPassword} onChange={handleChange} required className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200" placeholder="Confirm Password" />
</div>
<button type="submit" className="w-full py-3 px-4 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 transition-colors duration-200" disabled={isLoading}>{isLoading ? "Signing up..." : "Sign Up"}</button>
</form>
Expand Down