Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Auth Modal with TailwindCSS</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
.animate-fade-in { | |
animation: fadeIn 0.3s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; } | |
to { opacity: 1; } | |
} | |
.animate-slide-up { | |
animation: slideUp 0.3s ease-in-out; | |
} | |
@keyframes slideUp { | |
from { transform: translateY(20px); opacity: 0; } | |
to { transform: translateY(0); opacity: 1; } | |
} | |
.input-focus-effect:focus { | |
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5); | |
} | |
.btn-loading { | |
position: relative; | |
overflow: hidden; | |
} | |
.btn-loading::after { | |
content: ""; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); | |
animation: loading 1.5s infinite; | |
} | |
@keyframes loading { | |
0% { transform: translateX(-100%); } | |
100% { transform: translateX(100%); } | |
} | |
</style> | |
</head> | |
<body class="bg-gray-100 min-h-screen flex items-center justify-center"> | |
<div class="text-center"> | |
<h1 class="text-3xl font-bold mb-8 text-gray-800"> | |
Authentication Demo | |
</h1> | |
<div class="space-x-2 sm:space-x-4"> | |
<button | |
id="loginBtn" | |
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 sm:px-6 sm:py-3 rounded-md font-medium transition-colors" | |
> | |
Open Login Modal | |
</button> | |
<button | |
id="signupBtn" | |
class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 sm:px-6 sm:py-3 rounded-md font-medium transition-colors" | |
> | |
Open Signup Modal | |
</button> | |
</div> | |
</div> | |
<!-- Auth Modal --> | |
<div id="authModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden animate-fade-in"> | |
<div class="bg-white rounded-lg p-6 sm:p-8 max-w-md w-full mx-4 relative animate-slide-up"> | |
<button | |
id="closeModal" | |
class="absolute top-4 right-4 text-gray-500 hover:text-gray-700 text-2xl leading-none" | |
> | |
× | |
</button> | |
<h2 id="modalTitle" class="text-2xl font-bold mb-6 text-center"> | |
Welcome Back | |
</h2> | |
<div id="errorMessage" class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded mb-4 hidden"></div> | |
<button | |
id="googleAuthBtn" | |
class="flex items-center justify-center w-full bg-red-600 hover:bg-red-700 disabled:bg-red-400 text-white py-3 rounded-md mb-4 transition-colors" | |
> | |
<i class="fab fa-google mr-2"></i> | |
Continue with Google | |
</button> | |
<div class="relative my-6"> | |
<div class="absolute inset-0 flex items-center"> | |
<div class="w-full border-t border-gray-300"></div> | |
</div> | |
<div class="relative flex justify-center text-sm"> | |
<span class="px-2 bg-white text-gray-500">OR</span> | |
</div> | |
</div> | |
<div> | |
<div id="fullNameContainer" class="mb-4 hidden"> | |
<input | |
type="text" | |
id="fullName" | |
placeholder="Full Name" | |
class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none input-focus-effect" | |
/> | |
</div> | |
<div class="mb-4"> | |
<input | |
type="email" | |
id="email" | |
placeholder="Email" | |
class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none input-focus-effect" | |
/> | |
</div> | |
<div class="mb-6"> | |
<input | |
type="password" | |
id="password" | |
placeholder="Password" | |
class="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none input-focus-effect" | |
/> | |
</div> | |
<button | |
id="submitBtn" | |
class="w-full bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white py-3 rounded-md transition-colors font-medium btn-loading" | |
> | |
Sign In | |
</button> | |
</div> | |
<div class="mt-6 text-center"> | |
<p id="toggleText" class="text-gray-600"> | |
Don't have an account? | |
<button | |
id="toggleModeBtn" | |
class="text-blue-600 hover:text-blue-700 font-medium" | |
> | |
Sign up | |
</button> | |
</p> | |
</div> | |
<div class="mt-4 text-xs text-gray-500 text-center"> | |
<p>Demo: Try 'test@error.com' for signup error, 'wrong' password for login error</p> | |
</div> | |
</div> | |
</div> | |
<script> | |
// State management | |
const state = { | |
mode: 'login', | |
email: '', | |
password: '', | |
fullName: '', | |
error: '', | |
loading: false | |
}; | |
// DOM elements | |
const elements = { | |
authModal: document.getElementById('authModal'), | |
modalTitle: document.getElementById('modalTitle'), | |
errorMessage: document.getElementById('errorMessage'), | |
fullNameContainer: document.getElementById('fullNameContainer'), | |
fullName: document.getElementById('fullName'), | |
email: document.getElementById('email'), | |
password: document.getElementById('password'), | |
submitBtn: document.getElementById('submitBtn'), | |
toggleText: document.getElementById('toggleText'), | |
toggleModeBtn: document.getElementById('toggleModeBtn'), | |
googleAuthBtn: document.getElementById('googleAuthBtn'), | |
closeModal: document.getElementById('closeModal'), | |
loginBtn: document.getElementById('loginBtn'), | |
signupBtn: document.getElementById('signupBtn') | |
}; | |
// Event listeners | |
elements.loginBtn.addEventListener('click', () => openModal('login')); | |
elements.signupBtn.addEventListener('click', () => openModal('signup')); | |
elements.closeModal.addEventListener('click', closeModal); | |
elements.toggleModeBtn.addEventListener('click', toggleMode); | |
elements.submitBtn.addEventListener('click', handleSubmit); | |
elements.googleAuthBtn.addEventListener('click', handleGoogleAuth); | |
// Input change handlers | |
elements.email.addEventListener('input', (e) => { | |
state.email = e.target.value; | |
}); | |
elements.password.addEventListener('input', (e) => { | |
state.password = e.target.value; | |
}); | |
elements.fullName.addEventListener('input', (e) => { | |
state.fullName = e.target.value; | |
}); | |
// Functions | |
function openModal(mode) { | |
state.mode = mode; | |
resetForm(); | |
updateUI(); | |
elements.authModal.classList.remove('hidden'); | |
document.body.style.overflow = 'hidden'; | |
} | |
function closeModal() { | |
elements.authModal.classList.add('hidden'); | |
document.body.style.overflow = 'auto'; | |
} | |
function toggleMode() { | |
state.mode = state.mode === 'login' ? 'signup' : 'login'; | |
resetForm(); | |
updateUI(); | |
} | |
function resetForm() { | |
state.email = ''; | |
state.password = ''; | |
state.fullName = ''; | |
state.error = ''; | |
state.loading = false; | |
elements.email.value = ''; | |
elements.password.value = ''; | |
elements.fullName.value = ''; | |
elements.errorMessage.classList.add('hidden'); | |
} | |
function updateUI() { | |
// Update title | |
elements.modalTitle.textContent = state.mode === 'login' | |
? 'Welcome Back' | |
: 'Create Account'; | |
// Update submit button text | |
elements.submitBtn.textContent = state.mode === 'login' | |
? 'Sign In' | |
: 'Create Account'; | |
// Toggle full name field | |
if (state.mode === 'signup') { | |
elements.fullNameContainer.classList.remove('hidden'); | |
} else { | |
elements.fullNameContainer.classList.add('hidden'); | |
} | |
// Update toggle text | |
elements.toggleText.innerHTML = state.mode === 'login' | |
? "Don't have an account? " | |
: "Already have an account? "; | |
elements.toggleModeBtn.textContent = state.mode === 'login' | |
? 'Sign up' | |
: 'Sign in'; | |
} | |
function setLoading(loading) { | |
state.loading = loading; | |
elements.submitBtn.disabled = loading; | |
elements.googleAuthBtn.disabled = loading; | |
elements.toggleModeBtn.disabled = loading; | |
if (loading) { | |
elements.submitBtn.classList.add('btn-loading'); | |
} else { | |
elements.submitBtn.classList.remove('btn-loading'); | |
} | |
} | |
function showError(message) { | |
state.error = message; | |
elements.errorMessage.textContent = message; | |
elements.errorMessage.classList.remove('hidden'); | |
} | |
function hideError() { | |
state.error = ''; | |
elements.errorMessage.classList.add('hidden'); | |
} | |
// Mock authentication functions | |
async function mockSignUp() { | |
// Simulate API call delay | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
if (state.email === 'test@error.com') { | |
return { | |
data: null, | |
error: { message: 'Email already exists' } | |
}; | |
} | |
return { | |
data: { | |
user: { | |
id: 'user-123', | |
email: state.email, | |
user_metadata: { | |
full_name: state.fullName | |
} | |
} | |
}, | |
error: null | |
}; | |
} | |
async function mockSignIn() { | |
// Simulate API call delay | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
if (state.password === 'wrong') { | |
return { | |
data: null, | |
error: { message: 'Invalid credentials' } | |
}; | |
} | |
return { | |
data: { | |
user: { | |
id: 'user-123', | |
email: state.email | |
} | |
}, | |
error: null | |
}; | |
} | |
async function mockGoogleAuth() { | |
// Simulate API call delay | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
return { error: null }; | |
} | |
async function mockInsertProfile(user) { | |
// Simulate API call delay | |
await new Promise(resolve => setTimeout(resolve, 500)); | |
return { error: null }; | |
} | |
// Event handlers | |
async function handleSubmit() { | |
hideError(); | |
// Basic validation | |
if (!state.email) { | |
showError('Email is required'); | |
return; | |
} | |
if (!state.password) { | |
showError('Password is required'); | |
return; | |
} | |
if (state.mode === 'signup' && !state.fullName) { | |
showError('Full name is required'); | |
return; | |
} | |
setLoading(true); | |
try { | |
if (state.mode === 'login') { | |
const { data, error } = await mockSignIn(); | |
if (error) { | |
showError(error.message); | |
} else { | |
closeModal(); | |
alert(`Welcome back, ${data.user.email}!`); | |
} | |
} else { | |
// Sign up flow | |
const { data: user, error: signUpError } = await mockSignUp(); | |
if (signUpError) { | |
showError(signUpError.message); | |
return; | |
} | |
// Insert profile | |
const { error: profileError } = await mockInsertProfile(user.user); | |
if (profileError) { | |
showError(profileError.message); | |
} else { | |
closeModal(); | |
alert(`Account created for ${user.user.email}!`); | |
} | |
} | |
} catch (err) { | |
showError('An unexpected error occurred'); | |
} finally { | |
setLoading(false); | |
} | |
} | |
async function handleGoogleAuth() { | |
hideError(); | |
setLoading(true); | |
try { | |
const { error } = await mockGoogleAuth(); | |
if (error) { | |
showError(error.message); | |
} else { | |
// In a real app, this would redirect to Google | |
alert('Would redirect to Google OAuth in a real app'); | |
} | |
} catch (err) { | |
showError('An unexpected error occurred'); | |
} finally { | |
setLoading(false); | |
} | |
} | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Rajeshdakulla/rajesh" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |