feat: improve landing page and login

This commit is contained in:
2025-12-03 16:42:52 -03:00
parent ae7d5aa5b5
commit 63312b1bc0
6 changed files with 535 additions and 77 deletions
+340 -14
View File
@@ -19,6 +19,13 @@
min-height: 50px; min-height: 50px;
} }
.logo h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.logo h2 { .logo h2 {
margin: 0; margin: 0;
font-size: 1.3rem; font-size: 1.3rem;
@@ -30,20 +37,108 @@
align-items: center; align-items: center;
} }
.auth-form { .auth-buttons {
background: rgba(40, 40, 40, 0.9); display: flex;
backdrop-filter: blur(10px); align-items: center;
border-radius: 6px; gap: 12px;
padding: 25px; }
min-width: 300px;
.auth-buttons .header-btn.primary {
background: linear-gradient(135deg, #6B8DD6 0%, #5A7BC6 100%);
border: 1px solid rgba(107, 141, 214, 0.6);
}
.auth-buttons .header-btn.primary:hover {
box-shadow: 0 4px 15px rgba(107, 141, 214, 0.4);
}
/* Authentication Modal */
.auth-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
backdrop-filter: blur(8px);
animation: fadeIn 0.3s ease-out;
}
.auth-modal-content {
background: rgba(40, 40, 40, 0.95);
backdrop-filter: blur(20px);
border-radius: 12px;
padding: 40px;
max-width: 450px;
width: 90%;
border: 1px solid rgba(100, 100, 100, 0.3); border: 1px solid rgba(100, 100, 100, 0.3);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
position: relative;
animation: slideUp 0.3s ease-out;
}
.close-modal-btn {
position: absolute;
top: 15px;
right: 15px;
background: transparent;
border: none;
color: rgba(255, 255, 255, 0.6);
font-size: 2rem;
cursor: pointer;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: all 0.2s ease;
line-height: 1;
}
.close-modal-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.auth-form {
background: transparent;
border-radius: 0;
padding: 0;
min-width: auto;
border: none;
} }
.auth-form h3 { .auth-form h3 {
margin: 0 0 20px 0; margin: 0 0 25px 0;
text-align: center; text-align: center;
font-size: 1.3rem; font-size: 1.6rem;
font-weight: 500; font-weight: 600;
color: white;
} }
.auth-form .form-group { .auth-form .form-group {
@@ -308,16 +403,30 @@
order: 1; order: 1;
} }
.logo h1 {
font-size: 1.4rem;
}
.logo h2 { .logo h2 {
font-size: 1.1rem; font-size: 1.1rem;
} }
.auth-section { .auth-section {
order: 2; order: 2;
width: 100%; width: 100%;
justify-content: center; justify-content: center;
} }
.auth-buttons {
order: 2;
gap: 8px;
}
.auth-buttons .header-btn {
font-size: 0.85rem;
padding: 8px 14px;
}
.user-info { .user-info {
order: 2; order: 2;
position: relative; position: relative;
@@ -356,6 +465,163 @@
} }
} }
/* Landing Page Styles */
.landing-page {
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);
min-height: calc(100vh - 80px);
padding: 60px 20px;
}
.landing-container {
max-width: 1200px;
margin: 0 auto;
}
.landing-hero {
text-align: center;
margin-bottom: 60px;
animation: fadeInUp 0.8s ease-out;
}
.landing-title {
font-size: 3rem;
font-weight: 700;
color: white;
margin: 0 0 20px 0;
line-height: 1.2;
background: linear-gradient(135deg, #6B8DD6 0%, #9B7BC6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.landing-subtitle {
font-size: 1.3rem;
color: rgba(255, 255, 255, 0.85);
max-width: 800px;
margin: 0 auto;
line-height: 1.6;
}
.landing-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-bottom: 60px;
animation: fadeInUp 0.8s ease-out 0.2s backwards;
}
.feature-card {
background: rgba(40, 40, 40, 0.6);
backdrop-filter: blur(10px);
border: 1px solid rgba(100, 100, 100, 0.3);
border-radius: 12px;
padding: 30px;
text-align: center;
transition: all 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
border-color: rgba(107, 141, 214, 0.5);
box-shadow: 0 8px 30px rgba(107, 141, 214, 0.2);
}
.feature-icon {
font-size: 3rem;
margin-bottom: 15px;
display: block;
}
.feature-card h3 {
color: white;
font-size: 1.3rem;
margin: 0 0 12px 0;
font-weight: 600;
}
.feature-card p {
color: rgba(255, 255, 255, 0.75);
font-size: 1rem;
line-height: 1.5;
margin: 0;
}
.landing-cta {
text-align: center;
background: rgba(74, 93, 138, 0.4);
backdrop-filter: blur(10px);
border: 1px solid rgba(100, 100, 100, 0.3);
border-radius: 12px;
padding: 50px 30px;
animation: fadeInUp 0.8s ease-out 0.4s backwards;
}
.landing-cta h2 {
color: white;
font-size: 2rem;
margin: 0 0 15px 0;
font-weight: 600;
}
.landing-cta p {
color: rgba(255, 255, 255, 0.85);
font-size: 1.1rem;
margin: 0 0 30px 0;
}
.cta-buttons {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.cta-btn {
padding: 15px 40px;
border-radius: 8px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
border: none;
min-width: 220px;
}
.cta-btn.primary {
background: linear-gradient(135deg, #6B8DD6 0%, #5A7BC6 100%);
color: white;
box-shadow: 0 4px 15px rgba(107, 141, 214, 0.4);
}
.cta-btn.primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(107, 141, 214, 0.5);
}
.cta-btn.secondary {
background: rgba(40, 40, 40, 0.8);
color: white;
border: 1px solid rgba(107, 141, 214, 0.5);
}
.cta-btn.secondary:hover {
background: rgba(60, 60, 60, 0.9);
border-color: rgba(107, 141, 214, 0.8);
transform: translateY(-2px);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* For very small screens */ /* For very small screens */
@media (max-width: 480px) { @media (max-width: 480px) {
.auth-container { .auth-container {
@@ -363,20 +629,80 @@
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
} }
.user-details span { .user-details span {
font-size: 0.75rem; font-size: 0.75rem;
max-width: 150px; max-width: 150px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.mobile-menu { .mobile-menu {
width: 160px; width: 160px;
right: -10px; right: -10px;
} }
.logo h1 {
font-size: 1.3rem;
}
.logo h2 { .logo h2 {
font-size: 1rem; font-size: 1rem;
} }
.auth-buttons {
gap: 8px;
}
.auth-buttons .header-btn {
padding: 6px 10px;
font-size: 0.8rem;
min-width: auto;
}
.auth-modal-content {
padding: 30px 20px;
}
}
/* Landing Page Responsive */
@media (max-width: 768px) {
.landing-page {
padding: 40px 15px;
}
.landing-title {
font-size: 2rem;
}
.landing-subtitle {
font-size: 1.1rem;
}
.landing-features {
grid-template-columns: 1fr;
gap: 20px;
}
.feature-card {
padding: 25px;
}
.landing-cta {
padding: 35px 20px;
}
.landing-cta h2 {
font-size: 1.5rem;
}
.cta-buttons {
flex-direction: column;
align-items: center;
}
.cta-btn {
width: 100%;
max-width: 300px;
}
} }
+108 -41
View File
@@ -30,49 +30,13 @@
<header class="auth-header"> <header class="auth-header">
<div class="auth-container"> <div class="auth-container">
<div class="logo"> <div class="logo">
<h2>🍎 Food Tracker</h2> <h1>🍎 Food Tracker</h1>
</div> </div>
<!-- Authentication Forms -->
<div id="auth-section" class="auth-section">
<!-- Login Form -->
<div id="login-form" class="auth-form">
<h3>Login</h3>
<form id="loginForm">
<div class="form-group">
<input type="email" id="loginEmail" placeholder="Email" required>
</div>
<div class="form-group">
<input type="password" id="loginPassword" placeholder="Password" required>
</div>
<button type="submit" class="auth-btn">Login</button>
<p class="auth-switch">
Don't have an account?
<a href="#" id="showRegister">Register here</a>
</p>
</form>
</div>
<!-- Register Form --> <!-- Authentication Buttons (shown when not logged in) -->
<div id="register-form" class="auth-form hidden"> <div id="auth-buttons" class="auth-buttons">
<h3>Register</h3> <button id="open-login-btn" class="header-btn">Login</button>
<form id="registerForm"> <button id="open-register-btn" class="header-btn primary">Get Started</button>
<div class="form-group">
<input type="text" id="registerName" placeholder="Name" required>
</div>
<div class="form-group">
<input type="email" id="registerEmail" placeholder="Email" required>
</div>
<div class="form-group">
<input type="password" id="registerPassword" placeholder="Password" required minlength="8">
</div>
<button type="submit" class="auth-btn">Register</button>
<p class="auth-switch">
Already have an account?
<a href="#" id="showLogin">Login here</a>
</p>
</form>
</div>
</div> </div>
<!-- User Info (shown when logged in) --> <!-- User Info (shown when logged in) -->
@@ -99,6 +63,109 @@
</div> </div>
</header> </header>
<!-- Authentication Modal -->
<div id="auth-modal" class="auth-modal hidden">
<div class="auth-modal-content">
<button id="close-auth-modal" class="close-modal-btn">×</button>
<!-- Login Form -->
<div id="login-form" class="auth-form">
<h3>Welcome Back</h3>
<form id="loginForm">
<div class="form-group">
<input type="email" id="loginEmail" placeholder="Email" required>
</div>
<div class="form-group">
<input type="password" id="loginPassword" placeholder="Password" required>
</div>
<button type="submit" class="auth-btn">Login</button>
<p class="auth-switch">
Don't have an account?
<a href="#" id="showRegister">Register here</a>
</p>
</form>
</div>
<!-- Register Form -->
<div id="register-form" class="auth-form hidden">
<h3>Create Your Account</h3>
<form id="registerForm">
<div class="form-group">
<input type="text" id="registerName" placeholder="Name" required>
</div>
<div class="form-group">
<input type="email" id="registerEmail" placeholder="Email" required>
</div>
<div class="form-group">
<input type="password" id="registerPassword" placeholder="Password" required minlength="8">
</div>
<button type="submit" class="auth-btn">Register</button>
<p class="auth-switch">
Already have an account?
<a href="#" id="showLogin">Login here</a>
</p>
</form>
</div>
</div>
</div>
<!-- Landing Page Content (shown when not logged in) -->
<section id="landing-page" class="landing-page hidden">
<div class="landing-container">
<div class="landing-hero">
<h1 class="landing-title">Track Your Nutrition, Transform Your Health</h1>
<p class="landing-subtitle">Take control of your diet with our comprehensive calorie and nutrition tracker. Perfect for anyone who wants to maintain a balanced diet and achieve their health goals.</p>
</div>
<div class="landing-features">
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>Detailed Tracking</h3>
<p>Monitor calories, protein, fat, carbs, and fiber for every meal. Get a complete picture of your daily nutrition.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎯</div>
<h3>Set Goals</h3>
<p>Define your daily macros targets and track your progress. Stay motivated and on track with your health journey.</p>
</div>
<div class="feature-card">
<div class="feature-icon">📅</div>
<h3>Calendar View</h3>
<p>Visualize your nutrition history with an intuitive calendar. See patterns and maintain consistency over time.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🥗</div>
<h3>Alkaline Balance</h3>
<p>Track your alkaline food intake to maintain a healthy pH balance and support overall wellness.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔍</div>
<h3>Smart Search</h3>
<p>Quickly find and log foods from our comprehensive database. Make tracking effortless and accurate.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔗</div>
<h3>Share Progress</h3>
<p>Share your daily food log with friends, trainers, or nutritionists. Get support and accountability.</p>
</div>
</div>
<div class="landing-cta">
<h2>Ready to Start Your Health Journey?</h2>
<p>Join today and take the first step towards a healthier, more balanced lifestyle.</p>
<div class="cta-buttons">
<button id="cta-register-btn" class="cta-btn primary">Get Started Free</button>
<button id="cta-login-btn" class="cta-btn secondary">Already Have an Account?</button>
</div>
</div>
</div>
</section>
<!-- Loading Overlay --> <!-- Loading Overlay -->
<div id="loading-overlay" class="loading-overlay hidden"> <div id="loading-overlay" class="loading-overlay hidden">
<div class="loading-spinner"></div> <div class="loading-spinner"></div>
+8 -8
View File
@@ -9,12 +9,12 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"appwrite": "^21.3.0", "appwrite": "^21.5.0",
"sweetalert": "^2.1.2" "sweetalert": "^2.1.2"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^7.1.12" "vite": "^7.2.6"
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
@@ -747,9 +747,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/appwrite": { "node_modules/appwrite": {
"version": "21.3.0", "version": "21.5.0",
"resolved": "https://registry.npmjs.org/appwrite/-/appwrite-21.3.0.tgz", "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-21.5.0.tgz",
"integrity": "sha512-b3apD2fiQ7g+2damLZ0Lk9SdCJb32wXKq1ijB7blAl0uItqMx0I34E0RVaC5aER5VTCXHFAlHRjZ1GQypdNt1A==", "integrity": "sha512-643bMRZVYXMluXvSXbdaLAi9qqTJLWbVGguKH4vH6IdKHur6gGIirhCOqAEt33pV4TOFJ55VBu8c/+Ft1ke2SA==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/es6-object-assign": { "node_modules/es6-object-assign": {
@@ -999,9 +999,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "7.1.12", "version": "7.2.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz",
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+2 -2
View File
@@ -13,10 +13,10 @@
"description": "", "description": "",
"devDependencies": { "devDependencies": {
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^7.1.12" "vite": "^7.2.6"
}, },
"dependencies": { "dependencies": {
"appwrite": "^21.3.0", "appwrite": "^21.5.0",
"sweetalert": "^2.1.2" "sweetalert": "^2.1.2"
} }
} }
+37 -2
View File
@@ -1,18 +1,53 @@
import { getDivById } from "./DomUtils"; import { getDivById } from "./DomUtils";
export function showAuthForms() { export function showAuthForms() {
getDivById('auth-section').classList.remove('hidden'); getDivById('auth-buttons').classList.remove('hidden');
getDivById('user-info').classList.add('hidden'); getDivById('user-info').classList.add('hidden');
getDivById('app-content').classList.add('hidden'); getDivById('app-content').classList.add('hidden');
getDivById('landing-page').classList.remove('hidden');
}
export function hideAuthForms() {
getDivById('auth-buttons').classList.add('hidden');
getDivById('auth-modal').classList.add('hidden');
getDivById('landing-page').classList.add('hidden');
}
export function openAuthModal() {
getDivById('auth-modal').classList.remove('hidden');
}
export function closeAuthModal() {
getDivById('auth-modal').classList.add('hidden');
} }
export function toggleAuthForms() { export function toggleAuthForms() {
document.getElementById('login-form')?.classList.toggle('hidden'); document.getElementById('login-form')?.classList.toggle('hidden');
document.getElementById('register-form')?.classList.toggle('hidden'); document.getElementById('register-form')?.classList.toggle('hidden');
const logForm = document.getElementById('loginForm') as HTMLFormElement; const logForm = document.getElementById('loginForm') as HTMLFormElement;
logForm.reset(); logForm.reset();
const resetForm = document.getElementById('registerForm') as HTMLFormElement; const resetForm = document.getElementById('registerForm') as HTMLFormElement;
resetForm.reset(); resetForm.reset();
} }
export function showRegisterForm() {
document.getElementById('login-form')?.classList.add('hidden');
document.getElementById('register-form')?.classList.remove('hidden');
const resetForm = document.getElementById('registerForm') as HTMLFormElement;
resetForm.reset();
openAuthModal();
}
export function showLoginForm() {
document.getElementById('login-form')?.classList.remove('hidden');
document.getElementById('register-form')?.classList.add('hidden');
const logForm = document.getElementById('loginForm') as HTMLFormElement;
logForm.reset();
openAuthModal();
}
+40 -10
View File
@@ -4,7 +4,7 @@ import { DailyTotalCalories, FoodItem, FoodStorage } from './types.js';
import { AppwriteAuth, AppwriteDB } from './appwrite.js'; import { AppwriteAuth, AppwriteDB } from './appwrite.js';
import swal from 'sweetalert'; import swal from 'sweetalert';
import { closeMobileMenu, delay, getCleanName, getIcon, handleMobileCalendarClick, hideLoading, hideSearchResults, navigateResultsKeyboard, QUICK_DELAY, scrollToCalendarView, showLoading, toggleCardHandler, toggleMobileMenu } from './Utils.ts'; import { closeMobileMenu, delay, getCleanName, getIcon, handleMobileCalendarClick, hideLoading, hideSearchResults, navigateResultsKeyboard, QUICK_DELAY, scrollToCalendarView, showLoading, toggleCardHandler, toggleMobileMenu } from './Utils.ts';
import { showAuthForms, toggleAuthForms } from './auth.ts'; import { showAuthForms, toggleAuthForms, showRegisterForm, showLoginForm, hideAuthForms, closeAuthModal } from './auth.ts';
import { appState } from "./state"; import { appState } from "./state";
// App state // App state
@@ -62,14 +62,15 @@ async function handleRegister(e: SubmitEvent) {
try { try {
await AppwriteAuth.register(email, password, name); await AppwriteAuth.register(email, password, name);
// Auto login after registration // Auto login after registration
await AppwriteAuth.login(email, password); await AppwriteAuth.login(email, password);
currentUser = await AppwriteAuth.getCurrentUser(); currentUser = await AppwriteAuth.getCurrentUser();
closeAuthModal();
await showMainApp(); await showMainApp();
selectDate(selectedDate); selectDate(selectedDate);
swal('Registration successful! Welcome to Food Tracker.'); swal('Registration successful! Welcome to Food Tracker.');
} catch (error) { } catch (error) {
console.error('Registration error:', error); console.error('Registration error:', error);
@@ -91,10 +92,11 @@ async function handleLogin(e: SubmitEvent) {
try { try {
await AppwriteAuth.login(email, password); await AppwriteAuth.login(email, password);
currentUser = await AppwriteAuth.getCurrentUser(); currentUser = await AppwriteAuth.getCurrentUser();
closeAuthModal();
await showMainApp(); await showMainApp();
selectDate(selectedDate); selectDate(selectedDate);
} catch (error) { } catch (error) {
console.error('Login error:', error); console.error('Login error:', error);
if (error instanceof Error) { if (error instanceof Error) {
@@ -245,14 +247,14 @@ async function handleMobileSettingsClick() {
} }
async function showMainApp() { async function showMainApp() {
getDivById('auth-section').classList.add('hidden'); hideAuthForms();
getDivById('user-info').classList.remove('hidden'); getDivById('user-info').classList.remove('hidden');
getDivById('app-content').classList.remove('hidden'); getDivById('app-content').classList.remove('hidden');
if (currentUser) { if (currentUser) {
getDivById('userName').textContent = `Welcome, ${currentUser.name}!`; getDivById('userName').textContent = `Welcome, ${currentUser.name}!`;
} }
updateCurrentDate(); updateCurrentDate();
// Get daily entries with total from Appwrite to display in calendar // Get daily entries with total from Appwrite to display in calendar
@@ -423,6 +425,34 @@ const setupEventListeners = () => {
document.getElementById('close-share-modal')?.addEventListener('click', closeShareModal); document.getElementById('close-share-modal')?.addEventListener('click', closeShareModal);
document.getElementById('copy-share-link')?.addEventListener('click', copyShareLink); document.getElementById('copy-share-link')?.addEventListener('click', copyShareLink);
// Landing page CTA button event listeners
document.getElementById('cta-register-btn')?.addEventListener('click', () => {
showRegisterForm();
});
document.getElementById('cta-login-btn')?.addEventListener('click', () => {
showLoginForm();
});
// Header auth button event listeners
document.getElementById('open-login-btn')?.addEventListener('click', () => {
showLoginForm();
});
document.getElementById('open-register-btn')?.addEventListener('click', () => {
showRegisterForm();
});
// Close auth modal
document.getElementById('close-auth-modal')?.addEventListener('click', () => {
closeAuthModal();
});
// Close modal when clicking outside
document.getElementById('auth-modal')?.addEventListener('click', (e) => {
if (e.target === document.getElementById('auth-modal')) {
closeAuthModal();
}
});
// Search functionality // Search functionality
getInputById('foodSearchInput').addEventListener('input', function(e: Event) { getInputById('foodSearchInput').addEventListener('input', function(e: Event) {
const target = e.target as HTMLInputElement; const target = e.target as HTMLInputElement;
@@ -1442,7 +1472,7 @@ async function checkSharedView() {
}); });
// Hide auth section and show app content // Hide auth section and show app content
getDivById('auth-section').classList.add('hidden'); getDivById('auth-buttons').classList.add('hidden');
getDivById('user-info').classList.add('hidden'); getDivById('user-info').classList.add('hidden');
getDivById('app-content').classList.remove('hidden'); getDivById('app-content').classList.remove('hidden');