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>
<meta charset="UTF-8">
<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">
</head>
<body>
<div class="container">
<h2>Inbox</h2>
<button onclick="logout()">Logout</button>
<button onclick="location.href='send.html'">Compose</button>
<div id="inboxList"></div>
<div class="layout">
<aside class="sidebar">
<h2>bang</h2>
<nav>
<ul>
<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 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>
</body>
</html>

View File

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

146
main.js
View File

@@ -59,7 +59,7 @@ if (document.getElementById('loginForm')) {
}
// Inbox
if (document.getElementById('inboxList')) {
if (document.getElementById('email-list')) {
const { username, password } = getCredentials();
if (!username || !password) {
window.location.href = 'login.html';
@@ -70,8 +70,6 @@ if (document.getElementById('inboxList')) {
.then(res => res.json())
.then(emails => {
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;
for (const email of emails) {
if (email.to === username && email.domain) {
@@ -84,50 +82,85 @@ if (document.getElementById('inboxList')) {
}
}
const userFull = userDomain ? `${username}!${userDomain}` : username;
// Inbox: emails where 'to!domain' matches current user
const inboxEmails = emails.filter(email => {
if (!email.to) return false;
const toFull = email.domain ? `${email.to}!${email.domain}` : email.to;
return toFull === userFull;
});
// Sent: emails where 'from!domain' matches current user
const sentEmails = emails.filter(email => {
if (!email.from) return false;
const fromFull = email.domain ? `${email.from}!${email.domain}` : email.from;
return fromFull === userFull;
});
let html = '';
html += '<h3>Inbox</h3>';
if (inboxEmails.length === 0) {
let currentFolder = 'inbox';
let selectedEmail = null;
function renderList() {
const list = currentFolder === 'inbox' ? inboxEmails : sentEmails;
let html = `<div class="email-list-section"><div class="email-list-title">${currentFolder === 'inbox' ? 'Inbox' : 'Sent'}</div>`;
if (list.length === 0) {
html += '<p>No emails.</p>';
} else {
html += inboxEmails.map(email => {
html += list.map((email, idx) => {
const display = currentFolder === 'inbox'
? (email.domain ? `${email.from}!${email.domain}` : email.from)
: (email.domain ? `${email.to}!${email.domain}` : email.to);
const fromto = currentFolder === 'inbox' ? `From: ${display}` : `To: ${display}`;
return `<div class="email-list-item${selectedEmail === idx ? ' selected' : ''}" data-idx="${idx}">
<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();
};
});
}
function renderDetail() {
const list = currentFolder === 'inbox' ? inboxEmails : sentEmails;
if (selectedEmail == null || !list[selectedEmail]) {
document.getElementById('email-detail').innerHTML = '';
return;
}
const email = list[selectedEmail];
const fromDisplay = email.domain ? `${email.from}!${email.domain}` : email.from;
return `
<div class="email-item">
<strong>From:</strong> ${fromDisplay}<br>
<strong>Subject:</strong> ${email.subject}<br>
<div>${email.body}</div>
</div>
`;
}).join('');
}
html += '<h3>Sent</h3>';
if (sentEmails.length === 0) {
html += '<p>No sent emails.</p>';
} else {
html += sentEmails.map(email => {
const toDisplay = email.domain ? `${email.to}!${email.domain}` : email.to;
return `
<div class="email-item">
<strong>To:</strong> ${toDisplay}<br>
<strong>Subject:</strong> ${email.subject}<br>
<div>${email.body}</div>
document.getElementById('email-detail').innerHTML = `
<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 class="email-detail-body">${email.body}</div>
</div>
`;
}).join('');
}
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 => {
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;
}
};
}

209
style.css
View File

@@ -4,30 +4,168 @@ body {
margin: 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;
max-width: 400px;
margin: 40px auto;
padding: 30px 40px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
color: #222e3c;
border: 1px solid #eee;
margin-bottom: 24px;
}
h1, h2 {
text-align: center;
.logout-btn:hover {
background: #f4f4f4;
}
nav {
text-align: center;
margin-bottom: 20px;
.main-content {
flex: 1;
background: #fff;
padding: 40px 32px;
display: flex;
gap: 32px;
}
nav a {
margin: 0 10px;
#email-list {
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;
text-decoration: none;
margin-bottom: 8px;
}
nav a:hover {
text-decoration: underline;
.email-list-item {
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%;
margin: 8px 0;
padding: 10px;
@@ -35,30 +173,39 @@ input, textarea, button {
border-radius: 4px;
box-sizing: border-box;
}
button {
#composeForm button[type="submit"] {
background: #0077cc;
color: #fff;
border: none;
cursor: pointer;
font-weight: bold;
margin-bottom: 0;
}
button:hover {
#composeForm button[type="submit"]:hover {
background: #005fa3;
}
.error {
color: #d8000c;
text-align: center;
#composeForm button[type="button"] {
background: #eee;
color: #222e3c;
border: none;
margin-top: 0;
}
#inboxList {
margin-top: 20px;
#composeForm button[type="button"]:hover {
background: #ddd;
}
.email-item {
@media (max-width: 900px) {
.main-content {
flex-direction: column;
padding: 24px 8px;
}
#email-list {
width: 100%;
max-height: 200px;
border-right: none;
border-bottom: 1px solid #eee;
padding: 10px 0;
padding-right: 0;
padding-bottom: 16px;
}
.email-item:last-child {
border-bottom: none;
.email-detail {
padding-left: 0;
}
.email-item strong {
color: #0077cc;
}