appearence overhall
This commit is contained in:
38
inbox.html
38
inbox.html
@@ -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>
|
||||
|
||||
@@ -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
146
main.js
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
213
style.css
213
style.css
@@ -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;
|
||||
}
|
||||
.email-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.email-item strong {
|
||||
color: #0077cc;
|
||||
padding-right: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.email-detail {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user