Embryo-One's picture
Upload 49 files
ed9f15f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Embryo Grading System</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=EB+Garamond:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #007B8F 0%, #004E64 100%);
margin: 0;
font-family: 'EB Garamond', serif;
}
.login-container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 50px 40px;
width: 100%;
max-width: 400px;
animation: slideUp 0.5s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.login-header {
text-align: center;
margin-bottom: 0px;
padding-bottom: 0px;
}
.login-header img {
width: 200px;
height: 100px;
margin-bottom: 0px;
object-fit: contain;
}
.login-header p {
margin: 5px 0 0 0;
color: #666;
font-size: 18px;
font-weight: 400;
display: none;
}
.form-group {
margin-bottom: 25px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #2c3e50;
font-weight: 600;
font-size: 16px;
}
.form-group input {
width: 100%;
padding: 14px 16px;
border: 2px solid #e0e0e0;
border-radius: 10px;
font-size: 15px;
font-family: 'EB Garamond', serif;
transition: all 0.3s ease;
box-sizing: border-box;
}
.form-group input:focus {
outline: none;
border-color: #007B8F;
box-shadow: 0 0 0 3px rgba(0, 123, 143, 0.2);
}
.login-btn {
width: 100%;
padding: 16px;
background: linear-gradient(135deg, #007B8F 0%, #004E64 100%);
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
font-weight: 600;
font-family: 'EB Garamond', serif;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 10px;
box-shadow: 0 4px 15px rgba(0, 123, 143, 0.3);
}
.login-btn:hover {
background: linear-gradient(135deg, #004E64 0%, #003847 100%);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 123, 143, 0.4);
}
.login-btn:active {
transform: translateY(0);
box-shadow: 0 2px 10px rgba(0, 123, 143, 0.3);
}
.login-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.error-message {
background: #fee;
color: #c33;
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 14px;
display: none;
border-left: 4px solid #c33;
}
.error-message.show {
display: block;
animation: shake 0.4s ease-in-out;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-10px); }
75% { transform: translateX(10px); }
}
.spinner-container {
display: none;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.spinner-container.show {
display: flex;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #5dade2;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.footer-text {
text-align: center;
margin-top: 30px;
color: #999;
font-size: 13px;
}
.divider {
display: flex;
align-items: center;
text-align: center;
margin: 25px 0;
color: #999;
}
.divider::before,
.divider::after {
content: '';
flex: 1;
border-bottom: 1px solid #e0e0e0;
}
.divider span {
padding: 0 15px;
font-size: 14px;
}
.google-btn {
width: 100%;
padding: 14px 16px;
background: white;
color: #333;
border: 2px solid #e0e0e0;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
font-family: 'EB Garamond', serif;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-bottom: 10px;
}
.google-btn:hover {
background: #f8f9fa;
border-color: #007B8F;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.google-btn:active {
transform: translateY(0);
}
.google-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.google-icon {
width: 20px;
height: 20px;
}
</style>
</head>
<body>
<div class="login-container">
<div class="login-header">
<img src="assets/ismo7.png" alt="Embryo Grading System Logo">
<p>Sign in to continue</p>
</div>
<div id="errorMessage" class="error-message"></div>
<button type="button" class="google-btn" id="googleSignInBtn">
<svg class="google-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
</svg>
Sign in with Google
</button>
<div class="divider">
<span>or</span>
</div>
<form id="loginForm">
<div class="form-group">
<label for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
placeholder="Enter your email"
required
autocomplete="email"
>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
placeholder="Enter your password"
required
autocomplete="current-password"
>
</div>
<button type="submit" class="login-btn" id="loginBtn">
Sign In
</button>
</form>
<div class="spinner-container" id="loadingSpinner">
<div class="spinner"></div>
</div>
<div class="footer-text">
&copy; 2025 Embryo Grading System
</div>
</div>
<script type="module">
import { signIn, signInWithGoogle } from './src/utils/firebaseService.js';
const loginForm = document.getElementById('loginForm');
const errorMessage = document.getElementById('errorMessage');
const loginBtn = document.getElementById('loginBtn');
const googleSignInBtn = document.getElementById('googleSignInBtn');
const loadingSpinner = document.getElementById('loadingSpinner');
// Google Sign-In
googleSignInBtn.addEventListener('click', async () => {
errorMessage.classList.remove('show');
googleSignInBtn.disabled = true;
googleSignInBtn.textContent = 'Signing in with Google...';
loadingSpinner.classList.add('show');
try {
await signInWithGoogle();
window.location.href = 'index.html';
} catch (error) {
console.error('Google sign-in failed:', error);
let errorText = 'Failed to sign in with Google.';
if (error.code === 'auth/popup-closed-by-user') {
errorText = 'Sign-in popup was closed. Please try again.';
} else if (error.code === 'auth/cancelled-popup-request') {
errorText = 'Sign-in cancelled.';
} else if (error.code === 'auth/popup-blocked') {
errorText = 'Popup blocked. Please enable popups for this site.';
} else if (error.code === 'auth/network-request-failed') {
errorText = 'Network error. Please check your connection.';
}
errorMessage.textContent = errorText;
errorMessage.classList.add('show');
googleSignInBtn.disabled = false;
googleSignInBtn.innerHTML = `
<svg class="google-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
</svg>
Sign in with Google
`;
loadingSpinner.classList.remove('show');
}
});
// Email/Password Sign-In
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('email').value.trim();
const password = document.getElementById('password').value;
// Clear previous error
errorMessage.classList.remove('show');
// Show loading state
loginBtn.disabled = true;
loginBtn.textContent = 'Signing in...';
loadingSpinner.classList.add('show');
try {
await signIn(email, password);
// Redirect to main app on successful login
window.location.href = 'index.html';
} catch (error) {
console.error('Login failed:', error);
// Show error message
let errorText = 'Failed to sign in. Please check your credentials.';
if (error.code === 'auth/invalid-email') {
errorText = 'Invalid email address format.';
} else if (error.code === 'auth/user-not-found') {
errorText = 'No account found with this email.';
} else if (error.code === 'auth/wrong-password') {
errorText = 'Incorrect password.';
} else if (error.code === 'auth/too-many-requests') {
errorText = 'Too many failed attempts. Please try again later.';
} else if (error.code === 'auth/network-request-failed') {
errorText = 'Network error. Please check your connection.';
} else if (error.code === 'auth/invalid-credential') {
errorText = 'Invalid credentials. Please check your email and password.';
}
errorMessage.textContent = errorText;
errorMessage.classList.add('show');
// Reset button state
loginBtn.disabled = false;
loginBtn.textContent = 'Sign In';
loadingSpinner.classList.remove('show');
}
});
// Check if user is already logged in
import { onAuthChange } from './src/utils/firebaseService.js';
onAuthChange((user) => {
if (user) {
// User is already logged in, redirect to main app
window.location.href = 'index.html';
}
});
</script>
</body>
</html>