+
+=======
+ // Responsive Fullscreen login with advanced background & centering
+ return (
+
+
+>>>>>>> 103a9a3825538b99686d415d2314c828cd229ade
)
-}
\ No newline at end of file
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
+}
diff --git a/package-lock.json b/package-lock.json
index f2b09c3..fa777e6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "dumpit",
"version": "0.0.1",
"dependencies": {
+ "@heroicons/react": "^2.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"firebase": "^9.23.0",
@@ -20,8 +21,8 @@
},
"devDependencies": {
"@eslint/js": "^9.9.1",
- "@types/node": "^20.0.0",
- "@types/react": "^18.3.5",
+ "@types/node": "20.19.23",
+ "@types/react": "18.3.26",
"@types/react-dom": "^18.3.0",
"autoprefixer": "^10.4.18",
"eslint": "^9.9.1",
@@ -29,7 +30,7 @@
"globals": "^15.9.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
- "typescript": "^5.5.3",
+ "typescript": "5.9.3",
"typescript-eslint": "^8.3.0"
}
},
@@ -252,6 +253,7 @@
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.13.tgz",
"integrity": "sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@firebase/component": "0.6.4",
"@firebase/logger": "0.4.0",
@@ -309,6 +311,7 @@
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.13.tgz",
"integrity": "sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@firebase/app": "0.9.13",
"@firebase/component": "0.6.4",
@@ -321,7 +324,8 @@
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz",
"integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==",
- "license": "Apache-2.0"
+ "license": "Apache-2.0",
+ "peer": true
},
"node_modules/@firebase/auth": {
"version": "0.23.2",
@@ -731,6 +735,7 @@
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz",
"integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
@@ -1040,6 +1045,15 @@
"node": ">=6"
}
},
+ "node_modules/@heroicons/react": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
+ "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">= 16 || ^19.0.0-rc"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1542,9 +1556,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "20.19.22",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.22.tgz",
- "integrity": "sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==",
+ "version": "20.19.23",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.23.tgz",
+ "integrity": "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -1575,6 +1589,7 @@
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -1686,6 +1701,7 @@
"integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.1",
"@typescript-eslint/types": "8.46.1",
@@ -1917,6 +1933,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2191,6 +2208,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.9",
"caniuse-lite": "^1.0.30001746",
@@ -2703,6 +2721,7 @@
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -4073,6 +4092,7 @@
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -4887,6 +4907,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -5147,6 +5168,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -5159,6 +5181,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -5849,6 +5872,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index 876bb42..17f8751 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@heroicons/react": "^2.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"firebase": "^9.23.0",
@@ -22,8 +23,8 @@
},
"devDependencies": {
"@eslint/js": "^9.9.1",
- "@types/node": "^20.0.0",
- "@types/react": "^18.3.5",
+ "@types/node": "20.19.23",
+ "@types/react": "18.3.26",
"@types/react-dom": "^18.3.0",
"autoprefixer": "^10.4.18",
"eslint": "^9.9.1",
@@ -31,7 +32,7 @@
"globals": "^15.9.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
- "typescript": "^5.5.3",
+ "typescript": "5.9.3",
"typescript-eslint": "^8.3.0"
}
}
diff --git a/public/Google.png b/public/Google.png
new file mode 100644
index 0000000..5a7b3c3
Binary files /dev/null and b/public/Google.png differ
diff --git a/src/components/Auth.tsx b/src/components/Auth.tsx
index 7b239c3..1997142 100644
--- a/src/components/Auth.tsx
+++ b/src/components/Auth.tsx
@@ -1,353 +1,601 @@
'use client'
-import { Loader2, LogIn, UserPlus } from 'lucide-react';
-import { useState } from 'react';
-import { useAuth } from '../contexts/AuthContext';
+import Image from "next/image"
+import { useState } from "react"
+import {
+ GoogleAuthProvider,
+ signInWithPopup,
+ signInWithEmailAndPassword,
+ createUserWithEmailAndPassword,
+} from "firebase/auth"
+import { auth } from "@/lib/firebase"
+<<<<<<< HEAD
+=======
+<<<<<<< HEAD
+=======
-export function Auth() {
- const [isLogin, setIsLogin] = useState(true);
- const [email, setEmail] = useState('');
- const [password, setPassword] = useState('');
- const [username, setUsername] = useState('');
- const [loading, setLoading] = useState(false);
- const [googleLoading, setGoogleLoading] = useState(false);
- const [error, setError] = useState('');
- const [usernameError, setUsernameError] = useState('');
- const [usernameSuggestions, setUsernameSuggestions] = useState
([]);
- const { signIn, signUp, signInWithGoogle } = useAuth();
-
- // Username validation regex: 3-20 characters, lowercase, numbers, underscores, hyphens
- const usernameRegex = /^[a-z0-9_-]{3,20}$/;
-
- // Map Firebase error codes to user-friendly messages
- const getFriendlyErrorMessage = (errorCode: string): string => {
- switch (errorCode) {
- case 'auth/email-already-in-use':
- return 'This email is already registered. Try signing in instead.';
- case 'auth/weak-password':
- return 'Password should be at least 6 characters long.';
- case 'auth/invalid-email':
- return 'Please enter a valid email address.';
- case 'auth/user-not-found':
- return 'No account found with this email. Please sign up first.';
- case 'auth/wrong-password':
- return 'Incorrect password. Please try again.';
- case 'auth/too-many-requests':
- return 'Too many failed attempts. Please try again later.';
- case 'auth/network-request-failed':
- return 'Network error. Please check your connection and try again.';
- case 'auth/user-disabled':
- return 'This account has been disabled. Please contact support.';
- default:
- return 'An unexpected error occurred. Please try again.';
- }
- };
-
- const validateUsernameFormat = (username: string): boolean => {
- return usernameRegex.test(username);
- };
+const GoogleLogo = () => (
+
+
+
+
+
+
+
+
+)
+>>>>>>> 103a9a3825538b99686d415d2314c828cd229ade
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
- const checkUsernameUniqueness = async (username: string): Promise => {
- try {
- const response = await fetch('/api/check-username', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ username }),
- });
+export function Auth() {
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState("")
+<<<<<<< HEAD
+ const [tab, setTab] = useState<"login" | "signup">("login")
+=======
+<<<<<<< HEAD
+ const [tab, setTab] = useState("login")
+=======
+ const [tab, setTab] = useState("login") // login or signup
+>>>>>>> 103a9a3825538b99686d415d2314c828cd229ade
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
+ const [showPassword, setShowPassword] = useState(false)
+ const [email, setEmail] = useState("")
+ const [password, setPassword] = useState("")
+ const [emailTouched, setEmailTouched] = useState(false)
+ const [passwordTouched, setPasswordTouched] = useState(false)
- if (!response.ok) {
- console.error('API error:', response.status);
- return false; // Assume taken on error
- }
+<<<<<<< HEAD
+ const emailValid = email.includes("@") && email.includes(".")
+ const passwordValid = password.length >= 6
- const data = await response.json();
- return data.available;
- } catch (error) {
- console.error('Error checking username uniqueness:', error);
- return false; // Assume taken on error
- }
- };
+=======
+<<<<<<< HEAD
+ const emailValid = email.includes("@") && email.includes(".")
+ const passwordValid = password.length >= 6
- const generateUsernameSuggestions = (baseUsername: string): string[] => {
- const suggestions: string[] = [];
- const cleanBase = baseUsername.replace(/[^a-z0-9_-]/g, '').toLowerCase();
-
- // Add numbers
- for (let i = 1; i <= 5; i++) {
- suggestions.push(`${cleanBase}${i}`);
- }
+=======
+ // Input validations
+ const emailValid = email.includes("@") && email.includes(".")
+ const passwordValid = password.length >= 6
- // Add underscores with numbers
- for (let i = 1; i <= 3; i++) {
- suggestions.push(`${cleanBase}_${i}`);
+ // Email/Password login or signup
+>>>>>>> 103a9a3825538b99686d415d2314c828cd229ade
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
+ const handleFormSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setEmailTouched(true)
+ setPasswordTouched(true)
+<<<<<<< HEAD
+
+=======
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
+ if (!emailValid || !passwordValid) {
+ setError("Please fix the errors above")
+ return
}
+<<<<<<< HEAD
- return suggestions.slice(0, 5); // Return first 5 suggestions
- };
-
- const handleUsernameChange = async (value: string) => {
- setUsername(value);
- setUsernameError('');
- setUsernameSuggestions([]);
-
- if (value.trim() === '') return;
-
- // Check format
- if (!validateUsernameFormat(value)) {
- setUsernameError('Username must be 3-20 characters, lowercase letters, numbers, underscores, or hyphens only.');
- return;
+ setLoading(true)
+ setError("")
+
+ try {
+ if (tab === "login") {
+ await signInWithEmailAndPassword(auth, email, password)
+ } else {
+ await createUserWithEmailAndPassword(auth, email, password)
+ }
+ } catch (e: any) {
+ setError(e.message || "Authentication failed")
+ } finally {
+ setLoading(false)
}
+ }
- // Check uniqueness
- const isAvailable = await checkUsernameUniqueness(value);
- if (!isAvailable) {
- setUsernameError('This username is already taken.');
- const suggestions = generateUsernameSuggestions(value);
- setUsernameSuggestions(suggestions);
+ const handleGoogleSignIn = async () => {
+ setLoading(true)
+ setError("")
+
+ try {
+ const provider = new GoogleAuthProvider()
+ await signInWithPopup(auth, provider)
+ } catch (e: any) {
+ setError("Google sign-in failed: " + (e.message || ""))
+ } finally {
+ setLoading(false)
}
- };
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
- setError('');
- setLoading(true);
-
+=======
+ setLoading(true)
+ setError("")
try {
- if (isLogin) {
- const { error } = await signIn(email, password);
- if (error) {
- setError(getFriendlyErrorMessage(error.code || ''));
- setLoading(false);
- }
- // Success - auth state change will trigger navigation
+ if (tab === "login") {
+ await signInWithEmailAndPassword(auth, email, password)
} else {
- // Validate username for signup
- if (!username.trim()) {
- setError('Username is required');
- setLoading(false);
- return;
- }
-
- if (!validateUsernameFormat(username)) {
- setError('Username must be 3-20 characters, lowercase letters, numbers, underscores, or hyphens only.');
- setLoading(false);
- return;
- }
-
- const isAvailable = await checkUsernameUniqueness(username);
- if (!isAvailable) {
- setError('This username is already taken. Please choose a different one.');
- setLoading(false);
- return;
- }
-
- const { error } = await signUp(email, password, username);
- if (error) {
- setError(getFriendlyErrorMessage(error.code || ''));
- setLoading(false);
- }
- // Success - auth state change will trigger navigation
+ await createUserWithEmailAndPassword(auth, email, password)
}
- } catch (err) {
- console.error('Auth error:', err);
- setError('An unexpected error occurred. Please try again.');
- setLoading(false);
+ } catch (e: any) {
+ setError(e.message || "Authentication failed")
}
- };
-
+ setLoading(false)
+ }
+
+<<<<<<< HEAD
+=======
+ // Google sign in
+>>>>>>> 103a9a3825538b99686d415d2314c828cd229ade
const handleGoogleSignIn = async () => {
- setError('');
- setGoogleLoading(true);
-
+ setLoading(true)
+ setError("")
try {
- const { error } = await signInWithGoogle();
- if (error) {
- setError(getFriendlyErrorMessage(error.code || '') || 'Failed to sign in with Google');
- }
- // Success - auth state change will trigger navigation
- } catch (err) {
- console.error('Google auth error:', err);
- setError('An unexpected error occurred with Google sign-in. Please try again.');
- } finally {
- setGoogleLoading(false);
+ const provider = new GoogleAuthProvider()
+ await signInWithPopup(auth, provider)
+ } catch (e: any) {
+ setError("Google sign-in failed: " + (e.message || ""))
}
- };
+ setLoading(false)
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
+ }
+ // UI
return (
-
-
-
-
-
- D
-
-
DumpIt
-
Your Personal Resource Vault
+<<<<<<< HEAD
+
+
+
-
+
+ DumpIt
+
+
+
+ Your Personal Resource Vault
+
+
+
+ {(["login", "signup"] as const).map((item) => (
setIsLogin(true)}
- className={`flex-1 py-2 px-4 rounded-lg font-medium transition-colors ${
- isLogin
- ? 'bg-blue-600 text-white'
- : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
- }`}
- >
- Login
-
+ key={item}
+ className={`flex-1 py-2.5 rounded-xl text-sm sm:text-base font-bold focus:outline-none focus:ring-2 focus:ring-blue-400 transition-all
+ ${tab === item
+ ? "bg-[#2075f7] text-white shadow-lg hover:bg-[#1a5fd6]"
+ : "bg-white text-[#2075f7] border-2 border-blue-200 hover:bg-blue-50"}`}
+ onClick={() => {
+ setTab(item)
+ setError("")
+ }}
+ aria-selected={tab === item}
+ role="tab"
+=======
+<<<<<<< HEAD
+
+
+
+
+
+
DumpIt
+
Your Personal Resource Vault
+
+ {["login", "signup"].map(item => (
setIsLogin(false)}
- className={`flex-1 py-2 px-4 rounded-lg font-medium transition-colors ${
- !isLogin
- ? 'bg-blue-600 text-white'
- : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
- }`}
+ key={item}
+ className={`flex-1 py-2 rounded-xl text-base font-bold focus:outline-none transition-all
+ ${tab === item
+ ? "bg-[#2075f7] text-white shadow hover:bg-[#2075f7]/90"
+ : "bg-white text-[#2075f7] border border-blue-200/50 hover:bg-blue-50"}`}
+ onClick={() => { setTab(item); setError(""); }}
+ aria-selected={tab === item}
+>>>>>>> 8c5d71f2bcb92fa8a2790cab3afe90a1c657bd0f
>
- Sign Up
+ {item === "login" ? "Login" : "Sign Up"}
-
+ ))}
+
+<<<<<<< HEAD
-