diff --git a/web/app/signin/page.tsx b/web/app/signin/page.tsx new file mode 100644 index 0000000..6a9b252 --- /dev/null +++ b/web/app/signin/page.tsx @@ -0,0 +1,376 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { Eye, EyeOff, Mail, Lock, Users, TrendingUp, ArrowRight, Loader2, Moon, Sun } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { useRouter } from 'next/navigation'; + +interface FormData { + email: string; + password: string; + rememberMe: boolean; +} + +const SignInPage = () => { + const router = useRouter(); + const [showPassword, setShowPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + const [darkMode, setDarkMode] = useState(true); + const [formData, setFormData] = useState({ + email: '', + password: '', + rememberMe: false + }); + + useEffect(() => { + // Check localStorage for theme preference on client side only + if (typeof window !== 'undefined') { + const savedTheme = localStorage.getItem('theme'); + + if (savedTheme) { + const isDark = savedTheme === 'dark'; + setDarkMode(isDark); + if (isDark) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + } else { + // Default to dark mode + setDarkMode(true); + document.documentElement.classList.add('dark'); + localStorage.setItem('theme', 'dark'); + } + } + }, []); + + const toggleTheme = () => { + if (typeof window !== 'undefined') { + const newDarkMode = !darkMode; + setDarkMode(newDarkMode); + + if (newDarkMode) { + document.documentElement.classList.add('dark'); + localStorage.setItem('theme', 'dark'); + } else { + document.documentElement.classList.remove('dark'); + localStorage.setItem('theme', 'light'); + } + } + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value, type, checked } = e.target; + setFormData({ + ...formData, + [name]: type === 'checkbox' ? checked : value + }); + if (error) setError(''); + }; + + const handleSubmit = async () => { + if (!formData.email || !formData.password) { + setError('Please fill in all required fields'); + return; + } + + setIsLoading(true); + setError(''); + +try { + await new Promise(resolve => setTimeout(resolve, 1500)); + console.log('Sign In Data:', formData); +} catch { + setError('Invalid email or password. Please try again.'); +} finally { + setIsLoading(false); +} + }; + + const navigateToSignUp = () => { + router.push('/signup'); + }; + + const togglePasswordVisibility = () => { + setShowPassword(prev => !prev); + }; + + return ( +
+ +
+ +
+
+
+
+ +
+
+
+ + ROSTERRUMBLE + +
+
+ +

+ Welcome Back, Champion +

+

+ Sign in to continue your journey to fantasy sports greatness and compete for real cash prizes. +

+
+ +
+
+
+
+ +
+

10M+

+

Active Players

+
+
+
+ +
+

₹5000Cr+

+

Prize Pool

+
+
+
+
+ + +
+
+ +
+
+ + ROSTERRUMBLE + +
+
+ + + + + Welcome Back + + + Enter your credentials to access your account + + + + + {error && ( + + + {error} + + + )} + +
+ +
+ + +
+
+ +
+ +
+ + + +
+
+ +
+
+ { + const isChecked = checked === true; + setFormData(prev => ({ ...prev, rememberMe: isChecked })); + }} + className={`transition-colors ${ + darkMode + ? 'border-gray-600 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600' + : 'border-gray-400 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600' + }`} + /> + +
+ +
+ + +
+ + +

+ Don't have an account?{' '} + +

+
+
+
+
+
+
+ ); +}; + +export default SignInPage; \ No newline at end of file diff --git a/web/app/signup/page.tsx b/web/app/signup/page.tsx new file mode 100644 index 0000000..8da76bb --- /dev/null +++ b/web/app/signup/page.tsx @@ -0,0 +1,614 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { Eye, EyeOff, Mail, Lock, User, Users, TrendingUp, ArrowRight, Loader2, Check, Moon, Sun } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { useRouter } from 'next/navigation'; + +interface FormData { + fullName: string; + email: string; + password: string; + confirmPassword: string; + agreeToTerms: boolean; +} + +interface PasswordStrength { + strength: number; + text: string; + color: string; +} + +const SignUpPage = () => { + const router = useRouter(); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + const [darkMode, setDarkMode] = useState(true); + const [formData, setFormData] = useState({ + fullName: '', + email: '', + password: '', + confirmPassword: '', + agreeToTerms: false + }); + + useEffect(() => { + if (typeof window !== 'undefined') { + const savedTheme = localStorage.getItem('theme'); + + if (savedTheme) { + const isDark = savedTheme === 'dark'; + setDarkMode(isDark); + if (isDark) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + } else { + + setDarkMode(true); + document.documentElement.classList.add('dark'); + localStorage.setItem('theme', 'dark'); + } + } + }, []); + + const toggleTheme = () => { + if (typeof window !== 'undefined') { + const newDarkMode = !darkMode; + setDarkMode(newDarkMode); + + if (newDarkMode) { + document.documentElement.classList.add('dark'); + localStorage.setItem('theme', 'dark'); + } else { + document.documentElement.classList.remove('dark'); + localStorage.setItem('theme', 'light'); + } + } + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value, type, checked } = e.target; + setFormData({ + ...formData, + [name]: type === 'checkbox' ? checked : value + }); + if (error) setError(''); + if (success) setSuccess(''); + }; + + const validateForm = (): boolean => { + if (!formData.fullName.trim()) { + setError('Please enter your full name'); + return false; + } + if (!formData.email) { + setError('Please enter your email address'); + return false; + } + if (!/\S+@\S+\.\S+/.test(formData.email)) { + setError('Please enter a valid email address'); + return false; + } + if (formData.password.length < 8) { + setError('Password must be at least 8 characters long'); + return false; + } + if (formData.password !== formData.confirmPassword) { + setError('Passwords do not match'); + return false; + } + if (!formData.agreeToTerms) { + setError('Please agree to the Terms of Service and Privacy Policy'); + return false; + } + return true; + }; + + const handleSubmit = async () => { + if (!validateForm()) { + return; + } + + setIsLoading(true); + setError(''); + + try { + await new Promise(resolve => setTimeout(resolve, 2000)); + + console.log('Sign Up Data:', { + fullName: formData.fullName, + email: formData.email, + password: formData.password + }); + + setSuccess('Account created successfully! Please check your email to verify your account.'); + +} catch { + setError('Failed to create account. Please try again.'); +} finally { + setIsLoading(false); +} + }; + + const navigateToSignIn = () => { + router.push('/signin'); + }; + + const togglePasswordVisibility = () => { + setShowPassword(prev => !prev); + }; + + const toggleConfirmPasswordVisibility = () => { + setShowConfirmPassword(prev => !prev); + }; + + const getPasswordStrength = (): PasswordStrength => { + const password = formData.password; + if (password.length === 0) return { strength: 0, text: '', color: '' }; + if (password.length < 6) return { + strength: 1, + text: 'Weak', + color: darkMode ? 'text-red-400' : 'text-red-500' + }; + if (password.length < 8) return { + strength: 2, + text: 'Fair', + color: darkMode ? 'text-yellow-400' : 'text-yellow-600' + }; + if (password.length >= 8 && /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) { + return { + strength: 4, + text: 'Strong', + color: darkMode ? 'text-green-400' : 'text-green-600' + }; + } + return { + strength: 3, + text: 'Good', + color: darkMode ? 'text-blue-400' : 'text-blue-600' + }; + }; + + const passwordStrength = getPasswordStrength(); + + return ( +
+ + {/* Theme Toggle */} + + + +
+ +
+ + +
+ +
+
+ +
+
+
+ + ROSTERRUMBLE + +
+
+ +

+ Join the Championship +

+

+ Create your account and start building winning fantasy teams with real cash prizes. +

+
+
+
+ +
+ + Free to join & play practice games + +
+
+
+ +
+ + Win real cash prizes daily + +
+
+
+ +
+ + Expert analysis & insights + +
+
+
+ +
+
+
+
+ +
+

10M+

+

Active Players

+
+
+
+ +
+

₹5000Cr+

+

Prize Pool

+
+
+
+
+ + {/* Right Side - Sign Up Form */} +
+
+ {/* Mobile Logo */} +
+
+ + ROSTERRUMBLE + +
+
+ + + + + Create Account + + + Join millions of players and start winning today + + + + + {error && ( + + + {error} + + + )} + + {success && ( + + + {success} + + + )} + +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + +
+ {formData.password && ( +
+
+ {[1, 2, 3, 4].map((level) => ( +
+ ))} +
+ + {passwordStrength.text} + +
+ )} +
+ +
+ +
+ + + +
+
+ +
+ { + const isChecked = checked === true; + setFormData(prev => ({ ...prev, agreeToTerms: isChecked })); + }} + className={`mt-1 transition-colors ${ + darkMode + ? 'border-gray-600 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600' + : 'border-gray-400 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600' + }`} + /> + +
+ + + + + +

+ Already have an account?{' '} + +

+
+ +
+
+
+
+ ); +}; + +export default SignUpPage; \ No newline at end of file diff --git a/web/components/header.tsx b/web/components/header.tsx index 9df5402..287e1b4 100644 --- a/web/components/header.tsx +++ b/web/components/header.tsx @@ -117,15 +117,18 @@ export function Header() {
Not a Member Yet? Register Now
- + +
@@ -163,7 +166,7 @@ export function Header() { {/* Mobile Auth */}
setIsMenuOpen(false)} > diff --git a/web/components/ui/alert.tsx b/web/components/ui/alert.tsx new file mode 100644 index 0000000..5afd41d --- /dev/null +++ b/web/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/web/components/ui/card.tsx b/web/components/ui/card.tsx new file mode 100644 index 0000000..cabfbfc --- /dev/null +++ b/web/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/web/components/ui/checkbox.tsx b/web/components/ui/checkbox.tsx new file mode 100644 index 0000000..c6fdd07 --- /dev/null +++ b/web/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } diff --git a/web/components/ui/input.tsx b/web/components/ui/input.tsx new file mode 100644 index 0000000..69b64fb --- /dev/null +++ b/web/components/ui/input.tsx @@ -0,0 +1,22 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/web/components/ui/label.tsx b/web/components/ui/label.tsx new file mode 100644 index 0000000..5341821 --- /dev/null +++ b/web/components/ui/label.tsx @@ -0,0 +1,26 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/web/package-lock.json b/web/package-lock.json index aee24cf..cb2ace1 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,6 +9,8 @@ "version": "0.1.0", "dependencies": { "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-toast": "^1.2.13", @@ -1601,6 +1603,59 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", @@ -1788,6 +1843,52 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", @@ -2147,6 +2248,39 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-visually-hidden": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.2.tgz", diff --git a/web/package.json b/web/package.json index 3c890e4..16e1025 100644 --- a/web/package.json +++ b/web/package.json @@ -12,6 +12,8 @@ }, "dependencies": { "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-toast": "^1.2.13",