|
|
<!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">
|
|
|
© 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');
|
|
|
|
|
|
|
|
|
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');
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
loginForm.addEventListener('submit', async (e) => {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const email = document.getElementById('email').value.trim();
|
|
|
const password = document.getElementById('password').value;
|
|
|
|
|
|
|
|
|
errorMessage.classList.remove('show');
|
|
|
|
|
|
|
|
|
loginBtn.disabled = true;
|
|
|
loginBtn.textContent = 'Signing in...';
|
|
|
loadingSpinner.classList.add('show');
|
|
|
|
|
|
try {
|
|
|
await signIn(email, password);
|
|
|
|
|
|
window.location.href = 'index.html';
|
|
|
} catch (error) {
|
|
|
console.error('Login failed:', error);
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
|
|
|
|
loginBtn.disabled = false;
|
|
|
loginBtn.textContent = 'Sign In';
|
|
|
loadingSpinner.classList.remove('show');
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
import { onAuthChange } from './src/utils/firebaseService.js';
|
|
|
onAuthChange((user) => {
|
|
|
if (user) {
|
|
|
|
|
|
window.location.href = 'index.html';
|
|
|
}
|
|
|
});
|
|
|
</script>
|
|
|
</body>
|
|
|
</html>
|
|
|
|