From 82df369dcf0e525a76f133cbd3be2e3be06bbe1e Mon Sep 17 00:00:00 2001 From: Siya Saxena Date: Sat, 23 May 2026 21:57:38 +0530 Subject: [PATCH] responsive style for signup --- src/pages/Signup/Signup.tsx | 847 ++++++++++++++++++++++++++---------- 1 file changed, 619 insertions(+), 228 deletions(-) diff --git a/src/pages/Signup/Signup.tsx b/src/pages/Signup/Signup.tsx index b55df05d..c5fe9a97 100644 --- a/src/pages/Signup/Signup.tsx +++ b/src/pages/Signup/Signup.tsx @@ -1,249 +1,640 @@ -import React, { useState, useContext } from "react"; -import axios from "axios"; -import { useNavigate, Link } from "react-router-dom"; -import { motion } from "framer-motion"; -import { User, Mail, Lock, Eye, EyeOff } from "lucide-react"; -import { ThemeContext } from "../../context/ThemeContext"; -import type { ThemeContextType } from "../../context/ThemeContext"; - -const backendUrl = import.meta.env.VITE_BACKEND_URL; - -interface SignUpFormData { - username: string; +import { useState, ChangeEvent } from "react"; +import { Link } from "react-router-dom"; + +const styles = ` + @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700&family=DM+Sans:wght@300;400;500&display=swap'); + + * { box-sizing: border-box; margin: 0; padding: 0; } + + .rp-root { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: #f5f4f0; + font-family: 'DM Sans', sans-serif; + padding: 24px; + } + + .rp-card { + width: 100%; + max-width: 420px; + border-radius: 20px; + overflow: hidden; + box-shadow: 0 24px 80px rgba(0,0,0,0.12), 0 4px 20px rgba(0,0,0,0.06); + animation: rp-rise 0.5s cubic-bezier(0.22, 1, 0.36, 1) both; + } + + @keyframes rp-rise { + from { opacity: 0; transform: translateY(24px); } + to { opacity: 1; transform: translateY(0); } + } + + .rp-hero { + background: #1a1a2e; + padding: 36px 32px 32px; + position: relative; + overflow: hidden; + } + + .rp-hero::before { + content: ''; + position: absolute; + inset: 0; + background: + radial-gradient(ellipse 60% 80% at 10% 110%, rgba(124,58,237,0.35) 0%, transparent 60%), + radial-gradient(ellipse 50% 60% at 90% -10%, rgba(99,102,241,0.2) 0%, transparent 55%); + pointer-events: none; + } + + .rp-hero-orb { + position: absolute; + top: -40px; + right: -40px; + width: 160px; + height: 160px; + border-radius: 50%; + background: radial-gradient(circle, rgba(124,58,237,0.15) 0%, transparent 70%); + } + + .rp-brand { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 20px; + position: relative; + animation: rp-fade 0.5s 0.1s both; + } + + @keyframes rp-fade { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } + } + + .rp-brand-icon { + width: 32px; + height: 32px; + border-radius: 8px; + background: linear-gradient(135deg, #7c3aed, #6366f1); + display: flex; + align-items: center; + justify-content: center; + } + + .rp-brand-icon svg { + width: 16px; + height: 16px; + fill: none; + stroke: #fff; + stroke-width: 2; + stroke-linecap: round; + } + + .rp-brand-name { + font-family: 'Syne', sans-serif; + font-size: 16px; + font-weight: 700; + color: #fff; + letter-spacing: 0.04em; + } + + .rp-hero-label { + font-size: 11px; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; + color: #a78bfa; + margin-bottom: 6px; + position: relative; + animation: rp-fade 0.5s 0.18s both; + } + + .rp-hero-title { + font-family: 'Syne', sans-serif; + font-size: 28px; + font-weight: 700; + color: #fff; + line-height: 1.15; + position: relative; + animation: rp-fade 0.5s 0.24s both; + } + + .rp-hero-sub { + font-size: 13px; + color: #8b8fa8; + margin-top: 8px; + line-height: 1.5; + position: relative; + animation: rp-fade 0.5s 0.3s both; + } + + .rp-hero-dots { + display: flex; + gap: 6px; + margin-top: 20px; + position: relative; + animation: rp-fade 0.5s 0.36s both; + } + + .rp-dot { + width: 6px; height: 6px; + border-radius: 50%; + background: rgba(255,255,255,0.15); + } + .rp-dot.active { background: #7c3aed; width: 18px; border-radius: 3px; } + + .rp-form { + background: #fff; + padding: 28px 32px 32px; + display: flex; + flex-direction: column; + gap: 16px; + } + + .rp-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + } + + .rp-field { + display: flex; + flex-direction: column; + gap: 6px; + animation: rp-fade 0.4s both; + } + + .rp-field:nth-child(1) { animation-delay: 0.32s; } + .rp-field:nth-child(2) { animation-delay: 0.38s; } + .rp-row + .rp-field:nth-child(1) { animation-delay: 0.44s; } + + .rp-label { + font-size: 11.5px; + font-weight: 500; + color: #374151; + letter-spacing: 0.01em; + } + + .rp-input-wrap { + position: relative; + } + + .rp-input { + width: 100%; + height: 42px; + border: 1.5px solid #e5e7eb; + border-radius: 10px; + background: #fafafa; + font-family: 'DM Sans', sans-serif; + font-size: 14px; + color: #111; + padding: 0 12px 0 38px; + outline: none; + transition: border-color 0.18s, box-shadow 0.18s, background 0.18s; + } + + .rp-input:focus { + border-color: #7c3aed; + background: #fff; + box-shadow: 0 0 0 3px rgba(124,58,237,0.1); + } + + .rp-input.no-icon { + padding-left: 12px; + } + + .rp-input-icon { + position: absolute; + left: 11px; + top: 50%; + transform: translateY(-50%); + color: #9ca3af; + display: flex; + align-items: center; + pointer-events: none; + transition: color 0.18s; + } + + .rp-input-wrap:focus-within .rp-input-icon { + color: #7c3aed; + } + + .rp-eye { + position: absolute; + right: 11px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + cursor: pointer; + color: #9ca3af; + display: flex; + padding: 2px; + transition: color 0.18s; + } + .rp-eye:hover { color: #6b7280; } + + .rp-btn { + width: 100%; + height: 46px; + background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%); + border: none; + border-radius: 12px; + font-family: 'DM Sans', sans-serif; + font-size: 14.5px; + font-weight: 500; + color: #fff; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + letter-spacing: 0.01em; + transition: transform 0.15s, box-shadow 0.15s, filter 0.15s; + box-shadow: 0 4px 16px rgba(124,58,237,0.3); + animation: rp-fade 0.4s 0.5s both; + margin-top: 2px; + } + + .rp-btn:hover { + transform: translateY(-1px); + box-shadow: 0 8px 24px rgba(124,58,237,0.35); + filter: brightness(1.05); + } + + .rp-btn:active { + transform: translateY(0); + box-shadow: 0 2px 8px rgba(124,58,237,0.25); + } + + .rp-btn:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; + } + + .rp-divider { + display: flex; + align-items: center; + gap: 12px; + animation: rp-fade 0.4s 0.54s both; + } + + .rp-divider hr { + flex: 1; + border: none; + border-top: 1px solid #f0f0f0; + } + + .rp-divider span { + font-size: 11.5px; + color: #b0b3bd; + white-space: nowrap; + } + + .rp-social { + display: flex; + gap: 10px; + animation: rp-fade 0.4s 0.58s both; + } + + .rp-social-btn { + flex: 1; + height: 40px; + border: 1.5px solid #e5e7eb; + border-radius: 10px; + background: #fff; + font-family: 'DM Sans', sans-serif; + font-size: 13px; + font-weight: 500; + color: #374151; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 7px; + transition: border-color 0.15s, background 0.15s, transform 0.12s; + } + + .rp-social-btn:hover { + border-color: #d1d5db; + background: #fafafa; + transform: translateY(-1px); + } + + .rp-social-btn:active { transform: translateY(0); } + + .rp-footer { + text-align: center; + font-size: 13px; + color: #9ca3af; + animation: rp-fade 0.4s 0.62s both; + } + + .rp-footer button { + background: none; + border: none; + font-family: 'DM Sans', sans-serif; + font-size: 13px; + font-weight: 500; + color: #7c3aed; + cursor: pointer; + padding: 0; + transition: color 0.15s; + } + .rp-footer button:hover { color: #6d28d9; text-decoration: underline; } + + .rp-terms { + font-size: 11px; + color: #c4c7d0; + text-align: center; + line-height: 1.6; + animation: rp-fade 0.4s 0.48s both; + } + .rp-terms a { color: #a78bfa; text-decoration: none; } + .rp-terms a:hover { text-decoration: underline; } + + .rp-error { + font-size: 11px; + color: #ef4444; + margin-top: 2px; + } + + .rp-success { + background: #fff; + padding: 48px 32px; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + text-align: center; + animation: rp-rise 0.4s both; + } + + .rp-success-icon { + width: 56px; + height: 56px; + border-radius: 50%; + background: linear-gradient(135deg, #7c3aed22, #6366f122); + border: 2px solid #7c3aed44; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 4px; + } + + .rp-success-title { + font-family: 'Syne', sans-serif; + font-size: 20px; + font-weight: 700; + color: #1a1a2e; + } + + .rp-success-sub { + font-size: 13px; + color: #9ca3af; + line-height: 1.6; + } +`; + +interface FormState { + firstName: string; + lastName: string; email: string; password: string; } -const SignUp: React.FC = () => { - const [formData, setFormData] = useState({ - username: "", - email: "", - password: "", - }); - const [message, setMessage] = useState(""); - const [isLoading, setIsLoading] = useState(false); - const [showPassword, setShowPassword] = useState(false); - - const navigate = useNavigate(); - const themeContext = useContext(ThemeContext) as ThemeContextType; - const { mode } = themeContext; - - const handleChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - setFormData({ ...formData, [name]: value }); +interface FormErrors { + firstName?: string; + lastName?: string; + email?: string; + password?: string; +} + +interface IconEyeProps { + open: boolean; +} + +function IconUser(): JSX.Element { + return ( + + + + ); +} + +function IconMail(): JSX.Element { + return ( + + + + ); +} + +function IconLock(): JSX.Element { + return ( + + + + ); +} + +function IconEye({ open }: IconEyeProps): JSX.Element { + return open ? ( + + + + ) : ( + + + + ); +} + +function IconArrow(): JSX.Element { + return ( + + + + ); +} + +function IconCheck(): JSX.Element { + return ( + + + + ); +} + +function GoogleIcon(): JSX.Element { + return ( + + + + + + + ); +} + +function GitHubIcon(): JSX.Element { + return ( + + + + ); +} + +export default function RegisterPage(): JSX.Element { + const [form, setForm] = useState({ firstName: "", lastName: "", email: "", password: "" }); + const [showPass, setShowPass] = useState(false); + const [errors, setErrors] = useState({}); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + + const validate = (): FormErrors => { + const e: FormErrors = {}; + if (!form.firstName.trim()) e.firstName = "Required"; + if (!form.lastName.trim()) e.lastName = "Required"; + if (!form.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) e.email = "Enter a valid email"; + if (form.password.length < 8) e.password = "Min 8 characters"; + return e; }; - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsLoading(true); - - try { - const response = await axios.post(`${backendUrl}/api/auth/signup`, formData); - setMessage(response.data.message); - - if (response.data.message === "User created successfully") { - navigate("/login"); - } - } catch (error: any) { - setMessage(error.response?.data?.message || "Something went wrong. Please try again."); - } finally { - setIsLoading(false); - } + const handleChange = (field: keyof FormState) => (ev: ChangeEvent): void => { + setForm((f) => ({ ...f, [field]: ev.target.value })); + if (errors[field]) setErrors((e) => ({ ...e, [field]: undefined })); }; - return ( -
-
-
-
-
-
-
+ const handleSubmit = async (): Promise => { + const e = validate(); + if (Object.keys(e).length) { setErrors(e); return; } + setLoading(true); + await new Promise((r) => setTimeout(r, 1400)); + setLoading(false); + setSuccess(true); + }; -
- -
- Logo -
-

- GitHubTracker -

-

- Join your GitHub journey -

-
- - -

- Create Account -

- -
-
-
- + return ( + <> + +
+
+
+
+
+
+ logo
- + GitHub Tracker
+
Welcome to
+
Your new
workspace
+
Create your account and start building something great today.
+
+
+
+
+
+
-
-
- -
- + {success ? ( +
+
+
Account created!
+
Welcome to GitHub Tracker, {form.firstName}.
Check your email to verify your account.
+ ) : ( +
+
+
+ +
+ + +
+ {errors.firstName && {errors.firstName}} +
+
+ +
+ + +
+ {errors.lastName && {errors.lastName}} +
+
+ +
+ +
+ + +
+ {errors.email && {errors.email}} +
-
-
- +
+ +
+ + + +
+ {errors.password && {errors.password}}
- - -
- - - - {message && ( -
- {message} +
+
or continue with
+
+ +

+ Already have an account?{" "} + + Sign in + +

)} - -
-

- Already have an account? - - Sign in here - -

-
- +
- -
-
+ ); -}; - -export default SignUp; \ No newline at end of file +}