appearence overhall

This commit is contained in:
Brandon4466
2025-06-07 03:08:38 -07:00
parent 5b86d31cfd
commit f2503d5ec9
4 changed files with 337 additions and 78 deletions

View File

@@ -3,17 +3,43 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inbox - Bang Webmail</title> <title>Inbox - bang</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<div class="container"> <div class="layout">
<h2>Inbox</h2> <aside class="sidebar">
<button onclick="logout()">Logout</button> <h2>bang</h2>
<button onclick="location.href='send.html'">Compose</button> <nav>
<div id="inboxList"></div> <ul>
<p id="inboxError" class="error"></p> <li><button id="sidebar-inbox" class="sidebar-btn active">Inbox</button></li>
<li><button id="sidebar-sent" class="sidebar-btn">Sent</button></li>
</ul>
</nav>
<button class="compose-btn" id="openComposeBtn">Compose</button>
<button class="logout-btn" onclick="logout()">Logout</button>
</aside>
<main class="main-content">
<div id="email-list"></div>
<div id="email-detail" class="email-detail"></div>
<p id="inboxError" class="error"></p>
</main>
</div> </div>
<div id="composeOverlay" class="modal-overlay" style="display:none;">
<div class="modal-content">
<h2>Compose Email</h2>
<form id="composeForm">
<input type="text" id="composeTo" placeholder="To (person!place.com)" required><br>
<input type="text" id="composeSubject" placeholder="Subject" required><br>
<textarea id="composeBody" placeholder="Message" required></textarea><br>
<button type="submit">Send</button>
<button type="button" id="closeComposeBtn">Cancel</button>
</form>
<p id="composeError" class="error"></p>
</div>
</div>
<script src="main.js"></script> <script src="main.js"></script>
</body> </body>
</html> </html>

View File

@@ -3,17 +3,17 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bang Webmail</title> <title>bang</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>Bang Webmail</h1> <h1>bang</h1>
<nav> <nav>
<a href="login.html">Login</a> <a href="login.html">Login</a>
<a href="register.html">Register</a> <a href="register.html">Register</a>
</nav> </nav>
<p>Welcome to Bang Webmail! Please login or register to continue.</p> <p>Send emails with a !</p>
</div> </div>
</body> </body>
</html> </html>

154
main.js
View File

@@ -59,7 +59,7 @@ if (document.getElementById('loginForm')) {
} }
// Inbox // Inbox
if (document.getElementById('inboxList')) { if (document.getElementById('email-list')) {
const { username, password } = getCredentials(); const { username, password } = getCredentials();
if (!username || !password) { if (!username || !password) {
window.location.href = 'login.html'; window.location.href = 'login.html';
@@ -70,8 +70,6 @@ if (document.getElementById('inboxList')) {
.then(res => res.json()) .then(res => res.json())
.then(emails => { .then(emails => {
if (!Array.isArray(emails)) throw new Error('Invalid mailbox response'); if (!Array.isArray(emails)) throw new Error('Invalid mailbox response');
// Determine current user's full address (username!domain if possible)
// Try to find domain from any received or sent email
let userDomain = null; let userDomain = null;
for (const email of emails) { for (const email of emails) {
if (email.to === username && email.domain) { if (email.to === username && email.domain) {
@@ -84,50 +82,85 @@ if (document.getElementById('inboxList')) {
} }
} }
const userFull = userDomain ? `${username}!${userDomain}` : username; const userFull = userDomain ? `${username}!${userDomain}` : username;
// Inbox: emails where 'to!domain' matches current user
const inboxEmails = emails.filter(email => { const inboxEmails = emails.filter(email => {
if (!email.to) return false; if (!email.to) return false;
const toFull = email.domain ? `${email.to}!${email.domain}` : email.to; const toFull = email.domain ? `${email.to}!${email.domain}` : email.to;
return toFull === userFull; return toFull === userFull;
}); });
// Sent: emails where 'from!domain' matches current user
const sentEmails = emails.filter(email => { const sentEmails = emails.filter(email => {
if (!email.from) return false; if (!email.from) return false;
const fromFull = email.domain ? `${email.from}!${email.domain}` : email.from; const fromFull = email.domain ? `${email.from}!${email.domain}` : email.from;
return fromFull === userFull; return fromFull === userFull;
}); });
let html = ''; let currentFolder = 'inbox';
html += '<h3>Inbox</h3>'; let selectedEmail = null;
if (inboxEmails.length === 0) { function renderList() {
html += '<p>No emails.</p>'; const list = currentFolder === 'inbox' ? inboxEmails : sentEmails;
} else { let html = `<div class="email-list-section"><div class="email-list-title">${currentFolder === 'inbox' ? 'Inbox' : 'Sent'}</div>`;
html += inboxEmails.map(email => { if (list.length === 0) {
const fromDisplay = email.domain ? `${email.from}!${email.domain}` : email.from; html += '<p>No emails.</p>';
return ` } else {
<div class="email-item"> html += list.map((email, idx) => {
<strong>From:</strong> ${fromDisplay}<br> const display = currentFolder === 'inbox'
<strong>Subject:</strong> ${email.subject}<br> ? (email.domain ? `${email.from}!${email.domain}` : email.from)
<div>${email.body}</div> : (email.domain ? `${email.to}!${email.domain}` : email.to);
</div> const fromto = currentFolder === 'inbox' ? `From: ${display}` : `To: ${display}`;
`; return `<div class="email-list-item${selectedEmail === idx ? ' selected' : ''}" data-idx="${idx}">
}).join(''); <div class="subject">${email.subject}</div>
<div class="fromto">${fromto}</div>
</div>`;
}).join('');
}
html += '</div>';
document.getElementById('email-list').innerHTML = html;
// Add click listeners
document.querySelectorAll('.email-list-item').forEach(item => {
item.onclick = function() {
selectedEmail = parseInt(this.getAttribute('data-idx'));
renderList();
renderDetail();
};
});
} }
html += '<h3>Sent</h3>'; function renderDetail() {
if (sentEmails.length === 0) { const list = currentFolder === 'inbox' ? inboxEmails : sentEmails;
html += '<p>No sent emails.</p>'; if (selectedEmail == null || !list[selectedEmail]) {
} else { document.getElementById('email-detail').innerHTML = '';
html += sentEmails.map(email => { return;
const toDisplay = email.domain ? `${email.to}!${email.domain}` : email.to; }
return ` const email = list[selectedEmail];
<div class="email-item"> const fromDisplay = email.domain ? `${email.from}!${email.domain}` : email.from;
<strong>To:</strong> ${toDisplay}<br> const toDisplay = email.domain ? `${email.to}!${email.domain}` : email.to;
<strong>Subject:</strong> ${email.subject}<br> document.getElementById('email-detail').innerHTML = `
<div>${email.body}</div> <div class="email-detail-content">
<div class="email-detail-header">
<div><strong>From:</strong> ${fromDisplay}</div>
<div><strong>To:</strong> ${toDisplay}</div>
<div><strong>Subject:</strong> ${email.subject}</div>
</div> </div>
`; <div class="email-detail-body">${email.body}</div>
}).join(''); </div>
`;
} }
document.getElementById('inboxList').innerHTML = html; // Sidebar folder switching
document.getElementById('sidebar-inbox').onclick = function() {
currentFolder = 'inbox';
selectedEmail = null;
document.getElementById('sidebar-inbox').classList.add('active');
document.getElementById('sidebar-sent').classList.remove('active');
renderList();
renderDetail();
};
document.getElementById('sidebar-sent').onclick = function() {
currentFolder = 'sent';
selectedEmail = null;
document.getElementById('sidebar-inbox').classList.remove('active');
document.getElementById('sidebar-sent').classList.add('active');
renderList();
renderDetail();
};
renderList();
renderDetail();
}) })
.catch(err => { .catch(err => {
document.getElementById('inboxError').innerText = 'Failed to load inbox.'; document.getElementById('inboxError').innerText = 'Failed to load inbox.';
@@ -174,3 +207,56 @@ if (document.getElementById('sendForm')) {
} }
}; };
} }
// Compose Overlay logic
if (document.getElementById('openComposeBtn')) {
const overlay = document.getElementById('composeOverlay');
const openBtn = document.getElementById('openComposeBtn');
const closeBtn = document.getElementById('closeComposeBtn');
const form = document.getElementById('composeForm');
const errorP = document.getElementById('composeError');
openBtn.onclick = function() {
overlay.style.display = 'flex';
form.reset();
errorP.innerText = '';
};
closeBtn.onclick = function() {
overlay.style.display = 'none';
};
form.onsubmit = async function(e) {
e.preventDefault();
const { username, password } = getCredentials();
if (!username || !password) {
window.location.href = 'login.html';
return;
}
const toField = document.getElementById('composeTo').value.trim();
if (!/^\w+!.+/.test(toField)) {
errorP.innerText = 'Recipient must be in the format username!domain';
return;
}
let to = toField;
let domain = '';
if (toField.includes('!')) {
[to, domain] = toField.split('!');
}
const subject = document.getElementById('composeSubject').value;
const body = document.getElementById('composeBody').value;
const email = domain ? { from: username, to, domain, subject, body } : { from: username, to, subject, body };
const res = await fetch('http://localhost:8080/email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(username + ':' + password)
},
body: JSON.stringify(email)
});
if (res.ok) {
overlay.style.display = 'none';
location.reload();
} else {
const err = await res.text();
errorP.innerText = err;
}
};
}

215
style.css
View File

@@ -4,30 +4,168 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.container { .layout {
display: flex;
min-height: 100vh;
}
.sidebar {
width: 240px;
background: #e0e0e0; /* Changed from #222e3c to a light grey */
color: #222e3c;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 32px 0 0 0;
box-shadow: 2px 0 8px rgba(0,0,0,0.04);
}
.sidebar h2 {
color: #222e3c;
text-align: center;
margin-bottom: 32px;
font-size: 1.5em;
letter-spacing: 1px;
}
.sidebar nav ul {
list-style: none;
padding: 0;
margin: 0 0 24px 0;
}
.sidebar nav li {
margin: 0 0 8px 0;
}
.sidebar-btn {
width: 100%;
background: none;
border: none;
color: #222e3c;
padding: 14px 0;
font-size: 1.1em;
text-align: left;
padding-left: 32px;
cursor: pointer;
transition: background 0.2s;
}
.sidebar-btn.active, .sidebar-btn:hover {
background: #cccccc;
}
.compose-btn, .logout-btn {
margin: 12px 24px 0 24px;
padding: 12px 0;
border-radius: 4px;
border: none;
font-size: 1em;
font-weight: bold;
cursor: pointer;
}
.compose-btn {
background: #0077cc;
color: #fff;
}
.compose-btn:hover {
background: #005fa3;
}
.logout-btn {
background: #fff; background: #fff;
max-width: 400px; color: #222e3c;
margin: 40px auto; border: 1px solid #eee;
padding: 30px 40px; margin-bottom: 24px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
} }
h1, h2 { .logout-btn:hover {
text-align: center; background: #f4f4f4;
} }
nav { .main-content {
text-align: center; flex: 1;
margin-bottom: 20px; background: #fff;
padding: 40px 32px;
display: flex;
gap: 32px;
} }
nav a { #email-list {
margin: 0 10px; width: 340px;
border-right: 1px solid #eee;
padding-right: 24px;
overflow-y: auto;
max-height: 70vh;
}
.email-list-section {
margin-bottom: 32px;
}
.email-list-title {
font-size: 1.1em;
color: #0077cc; color: #0077cc;
text-decoration: none; margin-bottom: 8px;
} }
nav a:hover { .email-list-item {
text-decoration: underline; padding: 12px 10px;
border-radius: 4px;
margin-bottom: 6px;
cursor: pointer;
border: 1px solid transparent;
transition: background 0.15s, border 0.15s;
} }
input, textarea, button { .email-list-item.selected, .email-list-item:hover {
background: #f0f6fa;
border: 1px solid #0077cc;
}
.email-list-item .subject {
font-weight: bold;
color: #222e3c;
}
.email-list-item .fromto {
font-size: 0.95em;
color: #555;
}
.email-detail {
flex: 1;
min-width: 0;
padding-left: 24px;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.email-detail-content {
background: #f8fafc;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
}
.email-detail-header {
margin-bottom: 16px;
}
.email-detail-header strong {
color: #0077cc;
}
.email-detail-body {
margin-top: 16px;
white-space: pre-wrap;
color: #222e3c;
}
.error {
color: #d8000c;
text-align: center;
}
/* Modal overlay for compose */
.modal-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.35);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #fff;
padding: 32px 32px 24px 32px;
border-radius: 10px;
box-shadow: 0 4px 24px rgba(0,0,0,0.18);
min-width: 340px;
max-width: 95vw;
max-height: 90vh;
overflow-y: auto;
position: relative;
}
#composeForm input, #composeForm textarea, #composeForm button {
width: 100%; width: 100%;
margin: 8px 0; margin: 8px 0;
padding: 10px; padding: 10px;
@@ -35,30 +173,39 @@ input, textarea, button {
border-radius: 4px; border-radius: 4px;
box-sizing: border-box; box-sizing: border-box;
} }
button { #composeForm button[type="submit"] {
background: #0077cc; background: #0077cc;
color: #fff; color: #fff;
border: none; border: none;
cursor: pointer;
font-weight: bold; font-weight: bold;
margin-bottom: 0;
} }
button:hover { #composeForm button[type="submit"]:hover {
background: #005fa3; background: #005fa3;
} }
.error { #composeForm button[type="button"] {
color: #d8000c; background: #eee;
text-align: center; color: #222e3c;
border: none;
margin-top: 0;
} }
#inboxList { #composeForm button[type="button"]:hover {
margin-top: 20px; background: #ddd;
} }
.email-item { @media (max-width: 900px) {
border-bottom: 1px solid #eee; .main-content {
padding: 10px 0; flex-direction: column;
} padding: 24px 8px;
.email-item:last-child { }
border-bottom: none; #email-list {
} width: 100%;
.email-item strong { max-height: 200px;
color: #0077cc; border-right: none;
border-bottom: 1px solid #eee;
padding-right: 0;
padding-bottom: 16px;
}
.email-detail {
padding-left: 0;
}
} }