Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4592,6 +4592,127 @@ body {
font-weight: 600;
}

.profile-modal {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);

display: none;
justify-content: center;
align-items: center;

z-index: 10000;
backdrop-filter: blur(6px);
}

.profile-content {
width: 420px;
max-width: 90vw;

background: #ffffff;
border-radius: 20px;

padding: 28px;

box-shadow:
0 20px 60px rgba(0,0,0,.18);

animation: profileFade .2s ease;
}

@keyframes profileFade {
from {
opacity: 0;
transform: translateY(12px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

.profile-header {
display: flex;
align-items: center;
gap: 16px;

margin-bottom: 24px;
}

.profile-avatar {
width: 60px;
height: 60px;

border-radius: 50%;

background: #111;
color: white;

display: flex;
align-items: center;
justify-content: center;

font-size: 24px;
font-weight: 700;
}

.profile-header h2 {
margin: 0;
font-size: 24px;
}

.profile-subtitle {
margin-top: 4px;
color: #666;
font-size: 14px;
}

.profile-info label {
display: block;

color: #777;

font-size: 13px;
margin-bottom: 6px;
}

.profile-value {
background: #f6f6f6;

padding: 14px;
border-radius: 10px;

font-size: 15px;
font-weight: 500;
}

.profile-actions {
display: flex;
justify-content: flex-end;

margin-top: 24px;
}

.profile-close-btn {
border: none;

background: #111;
color: white;

padding: 12px 20px;
border-radius: 10px;

cursor: pointer;
font-weight: 600;

transition: .2s;
}

.profile-close-btn:hover {
transform: translateY(-1px);
}

/* Footer */
.site-footer {
width: 100%;
Expand Down
88 changes: 69 additions & 19 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
<!-- Auth Modal -->
<div id="auth-modal" style="display:flex; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:9999; align-items:center; justify-content:center;">
<div style="background:white; border-radius:16px; padding:32px; width:360px; box-shadow:0 20px 60px rgba(0,0,0,0.3);">

<h2 id="auth-title" style="margin:0 0 8px; font-size:22px; font-weight:700;">Welcome back</h2>
<p id="auth-subtitle" style="margin:0 0 24px; color:#666; font-size:14px;">Sign in to your StudyPlan account</p>

<input id="auth-email" type="email" placeholder="Email address" style="width:100%; padding:12px; border:1px solid #ddd; border-radius:8px; font-size:14px; margin-bottom:12px; box-sizing:border-box;">

<input id="auth-password" type="password" placeholder="Password" style="width:100%; padding:12px; border:1px solid #ddd; border-radius:8px; font-size:14px; margin-bottom:20px; box-sizing:border-box;">

<button id="auth-submit-btn" style="width:100%; padding:12px; background:#4f46e5; color:white; border:none; border-radius:8px; font-size:15px; font-weight:600; cursor:pointer;">
Expand All @@ -36,8 +36,6 @@ <h2 id="auth-title" style="margin:0 0 8px; font-size:22px; font-weight:700;">Wel
</ul>
</div>

<p id="auth-error" style="color:red; font-size:13px; text-align:center; margin:8px 0 0; display:none;"></p>

<p style="text-align:center; margin:16px 0 0; font-size:13px; color:#666;">
<span id="auth-toggle-text">Don't have an account?</span>
<a id="auth-toggle-btn" href="#" style="color:#4f46e5; font-weight:600; margin-left:4px;">Sign Up</a>
Expand All @@ -61,11 +59,40 @@ <h1 class="site-title">StudyPlan</h1>
</nav>

<div class="header-right">
<button class="profile-btn">Profile</button>
<button class="profile-btn" id="profile-btn">Profile</button>
<button class="profile-btn" id="logout-btn">Logout</button>
</div>
</header>

<div id="profile-modal" class="profile-modal" style="display:none;">
<div class="profile-content">

<div class="profile-header">
<div class="profile-avatar">
M
</div>

<div>
<h2>My Profile</h2>
<p class="profile-subtitle">
StudyPlan Account
</p>
</div>
</div>

<div class="profile-info">
<label>Email</label>
<div id="profile-email" class="profile-value"></div>
</div>

<div class="profile-actions">
<button id="close-profile" class="profile-close-btn">
Close
</button>
</div>

</div>
</div>

<div class="wrapper">
<!-- <p class="label">StudyPlan</p> -->
Expand Down Expand Up @@ -233,12 +260,6 @@ <h1 class="site-title">StudyPlan</h1>
</div>
<div id="summary-box" class="summary-box"></div>

<button id="add-btn" class="add-btn" disabled>
Add items to planner
</button>
</div>


<button id="add-btn" class="add-btn" disabled>Add items to planner</button>
</div>
</div>
Expand Down Expand Up @@ -318,6 +339,11 @@ <h3 style="font-size:12px; font-weight:700; text-transform:uppercase; color:var(
document.getElementById('auth-error').style.display = 'none';
});

function validateEmail(email) {
const emailRegex = /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(email);
}

// Password validation function
function validatePassword(password) {

Expand Down Expand Up @@ -356,6 +382,12 @@ <h3 style="font-size:12px; font-weight:700; text-transform:uppercase; color:var(
return;
}

if(!validateEmail(email)) {
errorEl.textContent = 'Please enter a valid email address';
errorEl.style.display = 'block';
return;
}

const endpoint = isLogin ? '/api/auth/login' : '/api/auth/signup';

try {
Expand Down Expand Up @@ -388,15 +420,33 @@ <h3 style="font-size:12px; font-weight:700; text-transform:uppercase; color:var(

// Logout Button Functionality
const logoutBtn = document.getElementById('logout-btn');
logoutBtn.addEventListener('click', () => {
localStorage.removeItem('studyplan_user');

// Show login modal again
document.getElementById('auth-modal').style.display = 'flex';
if(logoutBtn) {
logoutBtn.addEventListener('click', () => {
localStorage.removeItem('studyplan_user');
window.location.reload();
});
}

// Refresh page state
window.location.reload();
});
const profileBtn = document.getElementById("profile-btn");
const profileModal = document.getElementById("profile-modal");
const closeProfile = document.getElementById("close-profile");
if(profileBtn && profileModal && closeProfile) {
profileBtn.addEventListener("click", () => {
const user = JSON.parse(localStorage.getItem("studyplan_user"));
document.getElementById("profile-email").textContent = user?.email || "Unknown";
const avatar = document.querySelector(".profile-avatar");
avatar.textContent = (user?.email?.charAt(0) || "U").toUpperCase();
profileModal.style.display = "flex";
});
closeProfile.addEventListener("click", () => {
profileModal.style.display = "none";
});
window.addEventListener("click", (e) => {
if (e.target === profileModal) {
profileModal.style.display = "none";
}
});
}

// Settings Modal Logic
const settingsModal = document.getElementById('settings-modal');
Expand Down
9 changes: 9 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,17 @@ Text: "${text}"
// ================= AUTH =================
const users = {}; // Simple in-memory user store

// Email format validator (RFC 5321-compatible)
const EMAIL_REGEX = /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/;

app.post('/api/auth/signup', (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: 'Email and password required' });
}
if (!EMAIL_REGEX.test(email)) {
return res.status(400).json({ error: 'Invalid email address format' });
}
if (users[email]) {
return res.status(400).json({ error: 'User already exists' });
}
Expand All @@ -528,6 +534,9 @@ app.post('/api/auth/login', (req, res) => {
if (!email || !password) {
return res.status(400).json({ error: 'Email and password required' });
}
if (!EMAIL_REGEX.test(email)) {
return res.status(400).json({ error: 'Invalid email address format' });
}
const user = users[email];
if (!user || user.password !== password) {
return res.status(401).json({ error: 'Invalid email or password' });
Expand Down