mirror of
https://github.com/ghostersk/gowebmail.git
synced 2026-04-17 08:36:01 +01:00
520 lines
35 KiB
HTML
520 lines
35 KiB
HTML
{{template "base" .}}
|
||
{{define "title"}}GoWebMail{{end}}
|
||
{{define "body_class"}}app-page{{end}}
|
||
|
||
{{define "body"}}
|
||
<div class="app" id="app-root" data-mob-view="list">
|
||
<!-- Mobile top bar (hidden on desktop) -->
|
||
<div class="mob-topbar" id="mob-topbar">
|
||
<button class="mob-nav-btn" id="mob-nav-btn" onclick="mobShowNav()" title="Menu">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
|
||
</button>
|
||
<button class="mob-back-btn" id="mob-back-btn" onclick="mobBack()" title="Back" style="display:none">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||
</button>
|
||
<span class="mob-title" id="mob-title">GoWebMail</span>
|
||
<button class="compose-btn" onclick="openCompose()" style="margin-left:auto;padding:5px 10px;font-size:11px">+ New</button>
|
||
<button class="compose-btn" onclick="window.open('/compose','_blank')" style="padding:5px 8px;font-size:11px" title="Compose in new tab">↗</button>
|
||
</div>
|
||
|
||
<!-- Sidebar -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-header">
|
||
<div class="logo">
|
||
<div class="logo-icon"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></div>
|
||
<span class="logo-text"><a href="/">GoWebMail</a></span>
|
||
</div>
|
||
<div style="position:relative;display:inline-flex">
|
||
<button class="compose-btn" onclick="openCompose()" style="border-radius:6px 0 0 6px">+ New</button>
|
||
<button class="compose-btn" onclick="toggleComposeDropdown(event)" style="border-radius:0 6px 6px 0;border-left:1px solid rgba(255,255,255,.25);padding:6px 7px" title="More options">
|
||
<svg viewBox="0 0 24 24" width="10" height="10" fill="white"><path d="M7 10l5 5 5-5z"/></svg>
|
||
</button>
|
||
<div id="compose-dropdown" style="display:none;position:absolute;top:100%;left:0;margin-top:4px;background:var(--surface);border:1px solid var(--border2);border-radius:7px;box-shadow:0 4px 16px rgba(0,0,0,.2);z-index:200;min-width:200px;overflow:hidden">
|
||
<div class="ctx-item" onclick="openCompose();closeComposeDropdown()">✉ New message</div>
|
||
<div class="ctx-item" onclick="window.open('/compose','_blank');closeComposeDropdown()">↗ New message in new tab</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-section">
|
||
<div class="nav-item active" id="nav-unified" onclick="selectFolder('unified','Unified Inbox')">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||
Unified Inbox
|
||
<span class="unread-badge" id="unread-total" style="display:none"></span>
|
||
</div>
|
||
<div class="nav-item" id="nav-starred" onclick="selectFolder('starred','Starred')">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
|
||
Starred
|
||
</div>
|
||
<div class="nav-item" id="nav-contacts" onclick="showContacts()">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 0H4v2h16V0zM0 4v18h24V4H0zm22 16H2V6h20v14zM12 11c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3zm-6 6c0-2.21 2.69-4 6-4s6 1.79 6 4H6z"/></svg>
|
||
Contacts
|
||
</div>
|
||
<div class="nav-item" id="nav-calendar" onclick="showCalendar()">
|
||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z"/></svg>
|
||
Calendar
|
||
</div>
|
||
<div id="folders-by-account"></div>
|
||
</div>
|
||
|
||
<div class="sidebar-footer">
|
||
<div class="user-info">
|
||
<span class="user-name" id="user-display">...</span>
|
||
<a href="/admin" id="admin-link" style="display:none;font-size:11px;color:var(--accent);text-decoration:none">Server Administration</a>
|
||
</div>
|
||
<div class="footer-actions">
|
||
<button class="icon-btn" id="accounts-btn" onclick="toggleAccountsMenu(event)" title="Manage accounts">
|
||
<svg viewBox="0 0 24 24"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
|
||
</button>
|
||
<button class="icon-btn" onclick="openSettings()" title="Settings">
|
||
<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
|
||
</button>
|
||
<button class="icon-btn" onclick="doLogout()" title="Sign out">
|
||
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
<!-- Mobile sidebar backdrop -->
|
||
<div class="mob-sidebar-backdrop" id="mob-sidebar-backdrop" onclick="mobCloseNav()"></div>
|
||
|
||
<!-- Message list -->
|
||
<div class="message-list-panel">
|
||
<div class="panel-header">
|
||
<span class="panel-title" id="panel-title">Unified Inbox</span>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<span class="panel-count" id="panel-count"></span>
|
||
<div class="filter-dropdown" id="filter-dropdown">
|
||
<button class="filter-dropdown-btn" id="filter-dropdown-btn" title="Filter & sort" onclick="var m=document.getElementById('filter-dropdown-menu');m.style.display=m.style.display==='block'?'none':'block';event.stopPropagation()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>
|
||
<span id="filter-label">Filter</span>
|
||
</button>
|
||
<div class="filter-dropdown-menu" id="filter-dropdown-menu" style="display:none">
|
||
<div class="filter-opt" id="fopt-default" onclick="goMailSetFilter('default');event.stopPropagation()">✓ Default order</div>
|
||
<div class="filter-sep-line"></div>
|
||
<div class="filter-opt" id="fopt-unread" onclick="goMailSetFilter('unread');event.stopPropagation()">○ Unread only</div>
|
||
<div class="filter-opt" id="fopt-attachment" onclick="goMailSetFilter('attachment');event.stopPropagation()">○ 📎 Has attachment</div>
|
||
<div class="filter-sep-line"></div>
|
||
<div class="filter-opt" id="fopt-date-desc" onclick="goMailSetFilter('date-desc');event.stopPropagation()">○ Newest first</div>
|
||
<div class="filter-opt" id="fopt-date-asc" onclick="goMailSetFilter('date-asc');event.stopPropagation()">○ Oldest first</div>
|
||
<div class="filter-opt" id="fopt-size-desc" onclick="goMailSetFilter('size-desc');event.stopPropagation()">○ Largest first</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="search-bar">
|
||
<div class="search-wrap">
|
||
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
|
||
<input class="search-input" type="text" id="search-input" placeholder="Search emails..." oninput="handleSearch(this.value)">
|
||
</div>
|
||
</div>
|
||
<div class="message-list" id="message-list">
|
||
<div class="spinner" style="margin-top:60px"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Message detail -->
|
||
<main class="message-detail" id="message-detail">
|
||
<div class="no-message">
|
||
<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||
<h3>Select a message</h3>
|
||
<p>Choose a message from the list to read it</p>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- ── Contacts panel ──────────────────────────────────────────────────── -->
|
||
<div id="contacts-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden;background:var(--bg)">
|
||
<div class="panel-header" style="padding:14px 18px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;flex-shrink:0">
|
||
<span style="font-family:'DM Serif Display',serif;font-size:17px;flex:1">Contacts</span>
|
||
<input id="contacts-search" type="search" placeholder="Search contacts…" oninput="filterContacts(this.value)"
|
||
style="padding:5px 10px;border:1px solid var(--border2);border-radius:6px;background:var(--surface3);color:var(--text);font-size:13px;width:200px">
|
||
<button class="btn-secondary" onclick="openContactForm()" style="font-size:12px">+ New Contact</button>
|
||
</div>
|
||
<div id="contacts-list" style="flex:1;overflow-y:auto;padding:12px"></div>
|
||
</div>
|
||
|
||
<!-- ── Calendar panel ──────────────────────────────────────────────────── -->
|
||
<div id="calendar-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden;background:var(--bg)">
|
||
<div style="padding:12px 18px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;flex-shrink:0">
|
||
<button class="icon-btn" onclick="calNav(-1)" title="Previous">‹</button>
|
||
<span id="cal-title" style="font-family:'DM Serif Display',serif;font-size:17px;min-width:200px;text-align:center"></span>
|
||
<button class="icon-btn" onclick="calNav(1)" title="Next">›</button>
|
||
<button class="btn-secondary" onclick="calGoToday()" style="font-size:12px;margin-left:4px">Today</button>
|
||
<div style="margin-left:auto;display:flex;gap:4px">
|
||
<button class="btn-secondary" id="cal-btn-month" onclick="calSetView('month')" style="font-size:12px">Month</button>
|
||
<button class="btn-secondary" id="cal-btn-week" onclick="calSetView('week')" style="font-size:12px">Week</button>
|
||
<button class="btn-secondary" onclick="openEventForm()" style="font-size:12px;background:var(--accent);color:white;border-color:var(--accent)">+ Event</button>
|
||
<button class="icon-btn" onclick="showCalDAVSettings()" title="CalDAV / sharing">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="cal-grid" style="flex:1;overflow-y:auto"></div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- ── Contact form modal ──────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="contact-modal">
|
||
<div class="modal" style="max-width:480px">
|
||
<h2 id="contact-modal-title">New Contact</h2>
|
||
<div class="modal-field"><label>Name</label><input id="cf-name" type="text" placeholder="Full name"></div>
|
||
<div class="modal-field"><label>Email</label><input id="cf-email" type="email" placeholder="email@example.com"></div>
|
||
<div class="modal-field"><label>Phone</label><input id="cf-phone" type="tel" placeholder="+1 555 000 0000"></div>
|
||
<div class="modal-field"><label>Company</label><input id="cf-company" type="text" placeholder="Company name"></div>
|
||
<div class="modal-field"><label>Notes</label><textarea id="cf-notes" rows="3" style="width:100%;resize:vertical;padding:8px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px"></textarea></div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('contact-modal')">Cancel</button>
|
||
<button id="cf-delete-btn" class="btn-secondary" style="color:var(--danger);display:none" onclick="deleteContact()">Delete</button>
|
||
<button class="modal-submit" onclick="saveContact()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Event form modal ──────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="event-modal">
|
||
<div class="modal" style="max-width:520px">
|
||
<h2 id="event-modal-title">New Event</h2>
|
||
<div class="modal-field"><label>Title</label><input id="ev-title" type="text" placeholder="Event title"></div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label>Start</label><input id="ev-start" type="datetime-local"></div>
|
||
<div class="modal-field"><label>End</label><input id="ev-end" type="datetime-local"></div>
|
||
</div>
|
||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:8px">
|
||
<input id="ev-allday" type="checkbox" style="width:auto">
|
||
<label for="ev-allday" style="font-weight:normal;color:var(--text2)">All day</label>
|
||
</div>
|
||
<div class="modal-field"><label>Location</label><input id="ev-location" type="text" placeholder="Location or video link"></div>
|
||
<div class="modal-field"><label>Description</label><textarea id="ev-desc" rows="3" style="width:100%;resize:vertical;padding:8px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px"></textarea></div>
|
||
<div class="modal-field"><label>Color</label>
|
||
<div style="display:flex;gap:6px" id="ev-colors">
|
||
<span data-color="#0078D4" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#0078D4;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#EA4335" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#EA4335;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#34A853" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#34A853;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#FBBC04" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#FBBC04;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#9C27B0" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#9C27B0;cursor:pointer;border:2px solid transparent"></span>
|
||
<span data-color="#FF6D00" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#FF6D00;cursor:pointer;border:2px solid transparent"></span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('event-modal')">Cancel</button>
|
||
<button id="ev-delete-btn" class="btn-secondary" style="color:var(--danger);display:none" onclick="deleteEvent()">Delete</button>
|
||
<button class="modal-submit" onclick="saveEvent()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── CalDAV settings modal ──────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="caldav-modal">
|
||
<div class="modal" style="max-width:560px">
|
||
<h2>CalDAV / Calendar Sharing</h2>
|
||
<p style="font-size:13px;color:var(--text2);margin-bottom:14px">
|
||
Subscribe to your GoWebMail calendar from any CalDAV client (Apple Calendar, Thunderbird, etc.) using a token URL. Tokens give read-only calendar access — no password needed.
|
||
</p>
|
||
<div id="caldav-tokens-list" style="margin-bottom:14px"></div>
|
||
<div style="display:flex;gap:8px;align-items:center">
|
||
<input id="caldav-label" type="text" placeholder="Token label (e.g. iPhone)" style="flex:1;padding:7px 10px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-size:13px">
|
||
<button class="btn-secondary" onclick="createCalDAVToken()" style="white-space:nowrap">Generate Token</button>
|
||
</div>
|
||
<div class="modal-actions" style="margin-top:16px">
|
||
<button class="modal-cancel" onclick="closeModal('caldav-modal')">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Accounts submenu popup ──────────────────────────────────────────────── -->
|
||
<div class="accounts-popup" id="accounts-popup">
|
||
<div class="accounts-popup-inner">
|
||
<div class="accounts-popup-header">
|
||
<span>Accounts</span>
|
||
<button class="icon-btn" onclick="closeAccountsMenu()" style="margin:-4px -4px -4px 0">
|
||
<svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||
</button>
|
||
</div>
|
||
<div id="accounts-popup-list"></div>
|
||
<button class="accounts-add-btn" onclick="closeAccountsMenu();openAddAccountModal()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||
Connect new account
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="accounts-popup-backdrop" id="accounts-popup-backdrop" onclick="closeAccountsMenu()"></div>
|
||
|
||
<!-- ── Draggable Compose dialog ───────────────────────────────────────────── -->
|
||
<div class="compose-dialog" id="compose-dialog">
|
||
<div class="compose-dialog-header" id="compose-drag-handle">
|
||
<span class="compose-title" id="compose-title">New Message</span>
|
||
<div style="display:flex;align-items:center;gap:2px">
|
||
<button class="compose-close" onclick="minimizeCompose()" title="Minimise">–</button>
|
||
<button class="compose-close" onclick="closeCompose()" title="Close">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="compose-body-wrap" id="compose-body-wrap">
|
||
<div class="compose-field"><label>From</label><select id="compose-from"></select></div>
|
||
<div class="compose-field compose-tag-field"><label>To</label><div id="compose-to" class="tag-container"></div></div>
|
||
<div class="compose-field compose-tag-field" id="cc-row" style="display:none"><label>CC</label><div id="compose-cc-tags" class="tag-container"></div></div>
|
||
<div class="compose-field compose-tag-field" id="bcc-row" style="display:none"><label>BCC</label><div id="compose-bcc-tags" class="tag-container"></div></div>
|
||
<div class="compose-field"><label>Subject</label><input type="text" id="compose-subject" oninput="S.draftDirty=true"></div>
|
||
<div class="compose-toolbar">
|
||
<button class="fmt-btn" title="Bold" onclick="execFmt('bold')"><b>B</b></button>
|
||
<button class="fmt-btn" title="Italic" onclick="execFmt('italic')"><i>I</i></button>
|
||
<button class="fmt-btn" title="Underline" onclick="execFmt('underline')"><u>U</u></button>
|
||
<span class="fmt-sep"></span>
|
||
<button class="fmt-btn" title="Bullets" onclick="execFmt('insertUnorderedList')">•—</button>
|
||
<button class="fmt-btn" title="Numbers" onclick="execFmt('insertOrderedList')">1—</button>
|
||
<span class="fmt-sep"></span>
|
||
<button class="fmt-btn" title="Link" onclick="insertLink()">🔗</button>
|
||
<button class="fmt-btn" title="Clear format" onclick="execFmt('removeFormat')">T⃗</button>
|
||
</div>
|
||
<div id="compose-editor" contenteditable="true" class="compose-editor" placeholder="Write your message..."></div>
|
||
<div id="compose-attach-list" class="compose-attach-list"></div>
|
||
<div class="compose-footer">
|
||
<button class="send-btn" id="send-btn" onclick="sendMessage()">Send</button>
|
||
<div style="display:flex;gap:6px;margin-left:4px">
|
||
<button class="btn-secondary" style="font-size:12px" onclick="showCCRow()">+CC</button>
|
||
<button class="btn-secondary" style="font-size:12px" onclick="showBCCRow()">+BCC</button>
|
||
<button class="btn-secondary" style="font-size:12px" onclick="triggerAttach()">📎 Attach</button>
|
||
<button class="btn-secondary" style="font-size:12px" onclick="saveDraft()">✎ Draft</button>
|
||
</div>
|
||
<input type="file" id="compose-attach-input" multiple style="display:none" onchange="handleAttachFiles(this)">
|
||
</div>
|
||
</div>
|
||
<div class="compose-resize" data-dir="e"></div>
|
||
<div class="compose-resize" data-dir="s"></div>
|
||
<div class="compose-resize" data-dir="se"></div>
|
||
<div class="compose-resize" data-dir="w"></div>
|
||
<div class="compose-resize" data-dir="sw"></div>
|
||
<div class="compose-resize" data-dir="n"></div>
|
||
<div class="compose-resize" data-dir="ne"></div>
|
||
<div class="compose-resize" data-dir="nw"></div>
|
||
</div>
|
||
<!-- Minimised pill (shown when user clicks – on header) -->
|
||
<div class="compose-minimised" id="compose-minimised" onclick="restoreCompose()">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||
<span id="compose-minimised-label">New Message</span>
|
||
</div>
|
||
|
||
<!-- ── Inline confirm (replaces browser confirm()) ───────────────────────── -->
|
||
<div class="inline-confirm" id="inline-confirm">
|
||
<p id="inline-confirm-msg" style="margin:0 0 14px;font-size:13px;line-height:1.5"></p>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||
<button class="btn-secondary" style="font-size:12px" id="inline-confirm-cancel">Cancel</button>
|
||
<button class="btn-danger" style="font-size:12px" id="inline-confirm-ok">Confirm</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Add Account Modal ──────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="add-account-modal">
|
||
<div class="modal">
|
||
<h2>Connect an account</h2>
|
||
<p>Connect Gmail or Outlook via OAuth, or any email via IMAP/SMTP.</p>
|
||
<div class="provider-btns">
|
||
<button class="provider-btn" id="btn-gmail" onclick="connectOAuth('gmail')">
|
||
<svg viewBox="0 0 24 24" width="20" height="20"><path fill="#EA4335" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#4285F4" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
|
||
Gmail
|
||
</button>
|
||
<button class="provider-btn" id="btn-outlook" onclick="connectOAuth('outlook')">
|
||
<!-- Microsoft 365 icon -->
|
||
<svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
||
<path fill="#EA3E23" d="M11.4 4H4v7.4h7.4V4z"/>
|
||
<path fill="#0364B8" d="M11.4 12.6H4V20h7.4v-7.4z"/>
|
||
<path fill="#0078D4" d="M20 4h-7.4v7.4H20V4z"/>
|
||
<path fill="#28A8E8" d="M20 12.6h-7.4V20H20v-7.4z"/>
|
||
</svg>
|
||
Microsoft 365
|
||
</button>
|
||
<button class="provider-btn" id="btn-outlook-personal" onclick="connectOAuth('outlook_personal')">
|
||
<!-- Outlook icon (blue envelope) -->
|
||
<svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
||
<rect width="24" height="24" rx="3" fill="#0078D4"/>
|
||
<path fill="white" d="M6 7h12v10H6z" opacity=".2"/>
|
||
<path fill="white" d="M6 7l6 5 6-5H6zm0 1.5V17h12V8.5l-6 5-6-5z"/>
|
||
</svg>
|
||
Outlook Personal
|
||
</button>
|
||
</div>
|
||
<div class="modal-divider"><span>or add IMAP account</span></div>
|
||
<div class="modal-field"><label>Email Address</label>
|
||
<div style="display:flex;gap:8px;flex:1">
|
||
<input type="email" id="imap-email" placeholder="you@example.com" style="flex:1">
|
||
<button class="btn-secondary" id="detect-btn" onclick="detectMailSettings()" style="white-space:nowrap;font-size:12px">Auto-detect</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-field"><label>Display Name</label><input type="text" id="imap-name" placeholder="Your Name"></div>
|
||
<div class="modal-field"><label>Password / App Password</label><input type="password" id="imap-password"></div>
|
||
<div style="font-size:11px;color:var(--muted);padding:0 0 8px;line-height:1.6">
|
||
Common ports — IMAP: <strong>993</strong> TLS/SSL, <strong>143</strong> STARTTLS/Plain ·
|
||
SMTP: <strong>587</strong> STARTTLS, <strong>465</strong> TLS/SSL, <strong>25</strong> Plain
|
||
</div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label>IMAP Host</label><input type="text" id="imap-host" placeholder="imap.example.com"></div>
|
||
<div class="modal-field"><label>IMAP Port</label><input type="number" id="imap-port" value="993"></div>
|
||
</div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label>SMTP Host</label><input type="text" id="smtp-host" placeholder="smtp.example.com"></div>
|
||
<div class="modal-field"><label>SMTP Port</label><input type="number" id="smtp-port" value="587"></div>
|
||
</div>
|
||
<div class="test-result" id="test-result"></div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('add-account-modal')">Cancel</button>
|
||
<button class="btn-secondary" onclick="testNewConnection()" id="test-btn">Test Connection</button>
|
||
<button class="modal-submit" onclick="addIMAPAccount()" id="save-acct-btn">Connect</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Edit Account Modal ─────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="edit-account-modal">
|
||
<div class="modal">
|
||
<h2>Account Settings</h2>
|
||
<p id="edit-account-email" style="font-weight:500;color:var(--text);margin-bottom:16px"></p>
|
||
<input type="hidden" id="edit-account-id">
|
||
<div class="modal-field"><label>Display Name</label><input type="text" id="edit-name"></div>
|
||
|
||
<!-- OAuth reconnect — shown only for gmail/outlook accounts -->
|
||
<div id="edit-oauth-section" style="display:none">
|
||
<div id="edit-oauth-expired-warning" style="display:none;background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.35);border-radius:8px;padding:10px 14px;margin-bottom:10px;font-size:13px;color:#f87171">
|
||
⚠️ Access token has expired — sync and send will fail until you reconnect.
|
||
</div>
|
||
<div style="background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin-bottom:4px">
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:10px">This account connects via <strong id="edit-oauth-provider-label"></strong> OAuth. To update permissions or fix an expired token, reconnect below.</div>
|
||
<button class="btn-secondary" id="edit-oauth-reconnect-btn" style="width:100%">🔗 Reconnect with <span id="edit-oauth-provider-label-btn"></span></button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- IMAP/SMTP credentials (hidden for OAuth accounts) -->
|
||
<div id="edit-creds-section">
|
||
<div class="modal-field"><label>New Password (leave blank to keep current)</label><input type="password" id="edit-password"></div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label>IMAP Host</label><input type="text" id="edit-imap-host"></div>
|
||
<div class="modal-field"><label>IMAP Port</label><input type="number" id="edit-imap-port"></div>
|
||
</div>
|
||
<div class="modal-row">
|
||
<div class="modal-field"><label>SMTP Host</label><input type="text" id="edit-smtp-host"></div>
|
||
<div class="modal-field"><label>SMTP Port</label><input type="number" id="edit-smtp-port"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group-title" style="margin:16px 0 8px">Sync Settings</div>
|
||
<div class="modal-field">
|
||
<label>Email history to sync</label>
|
||
<select id="edit-sync-mode" onchange="toggleSyncDaysField()" style="padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;outline:none">
|
||
<option value="preset-30">Last 1 month</option>
|
||
<option value="preset-90">Last 3 months</option>
|
||
<option value="preset-180">Last 6 months</option>
|
||
<option value="preset-365">Last 1 year</option>
|
||
<option value="preset-730">Last 2 years</option>
|
||
<option value="preset-1825">Last 5 years</option>
|
||
<option value="all" selected>All emails (full mailbox)</option>
|
||
<option value="days">Custom (days)</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-row" id="edit-sync-days-row" style="display:none">
|
||
<div class="modal-field"><label>Custom days to fetch</label><input type="number" id="edit-sync-days" value="30" min="1" max="36500"></div>
|
||
</div>
|
||
<div id="edit-conn-result" class="test-result" style="display:none"></div>
|
||
<div id="edit-last-error" style="display:none" class="alert error"></div>
|
||
<div class="settings-group-title" style="margin:16px 0 8px">Hidden Folders</div>
|
||
<div id="edit-hidden-folders" style="font-size:12px;color:var(--muted)">Loading…</div>
|
||
<div class="modal-actions">
|
||
<button class="modal-cancel" onclick="closeModal('edit-account-modal')">Cancel</button>
|
||
<button class="btn-secondary" id="edit-test-btn" onclick="testEditConnection()">Test Connection</button>
|
||
<button class="modal-submit" onclick="saveAccountEdit()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Settings Modal ─────────────────────────────────────────────────────── -->
|
||
<div class="modal-overlay" id="settings-modal">
|
||
<div class="modal" style="width:540px;max-height:90vh;overflow-y:auto">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:22px">
|
||
<h2 style="margin-bottom:0">Settings</h2>
|
||
<button onclick="closeModal('settings-modal')" class="icon-btn"><svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Profile</div>
|
||
<div class="modal-field">
|
||
<label>Username</label>
|
||
<div style="display:flex;gap:8px">
|
||
<input type="text" id="profile-username" placeholder="New username" style="flex:1">
|
||
<button class="btn-primary" onclick="updateProfile('username')">Save</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-field">
|
||
<label>Email Address</label>
|
||
<div style="display:flex;gap:8px">
|
||
<input type="email" id="profile-email" placeholder="New email address" style="flex:1">
|
||
<button class="btn-primary" onclick="updateProfile('email')">Save</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-field">
|
||
<label>Current Password <span style="color:var(--muted);font-size:11px">(required to confirm changes)</span></label>
|
||
<input type="password" id="profile-confirm-pw" placeholder="Enter your current password">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Email Sync</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">How often to automatically check all your accounts for new mail.</div>
|
||
<div style="display:flex;gap:10px;align-items:center">
|
||
<select id="sync-interval-select" style="flex:1;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
|
||
<option value="0">Manual only</option>
|
||
<option value="1">Every 1 minute</option>
|
||
<option value="5">Every 5 minutes</option>
|
||
<option value="10">Every 10 minutes</option>
|
||
<option value="15">Every 15 minutes (default)</option>
|
||
<option value="30">Every 30 minutes</option>
|
||
<option value="60">Every 60 minutes</option>
|
||
</select>
|
||
<button class="btn-primary" onclick="saveSyncInterval()">Save</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">Change Password</div>
|
||
<div class="modal-field"><label>Current Password</label><input type="password" id="cur-pw"></div>
|
||
<div class="modal-field"><label>New Password</label><input type="password" id="new-pw" placeholder="Min. 8 characters"></div>
|
||
<button class="btn-primary" onclick="changePassword()">Update Password</button>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title" style="display:flex;align-items:center;gap:10px">
|
||
Two-Factor Authentication <span id="mfa-badge"></span>
|
||
</div>
|
||
<div id="mfa-panel">Loading...</div>
|
||
</div>
|
||
|
||
<div class="settings-group">
|
||
<div class="settings-group-title">IP Access Rules</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:14px">
|
||
Control which IP addresses can access your account. This overrides global brute-force settings for your account only.
|
||
</div>
|
||
<div class="modal-field">
|
||
<label>Mode</label>
|
||
<select id="ip-rule-mode" onchange="toggleIPRuleHelp()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
|
||
<option value="disabled">Disabled — use global settings</option>
|
||
<option value="brute_skip">Skip brute-force check — listed IPs bypass lockout</option>
|
||
<option value="allow_only">Allow only — only listed IPs can log in</option>
|
||
</select>
|
||
</div>
|
||
<div id="ip-rule-help" style="font-size:12px;color:var(--muted);margin-bottom:10px;display:none"></div>
|
||
<div class="modal-field" id="ip-rule-list-field">
|
||
<label>Allowed IPs <span style="color:var(--muted);font-size:11px">(comma-separated)</span></label>
|
||
<input type="text" id="ip-rule-list" placeholder="e.g. 192.168.1.10, 10.0.0.5">
|
||
</div>
|
||
<button class="btn-primary" onclick="saveIPRules()">Save IP Rules</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Context menu -->
|
||
<div class="ctx-menu" id="ctx-menu"></div>
|
||
<div class="toast-container" id="toast-container"></div>
|
||
{{end}}
|
||
|
||
{{define "scripts"}}
|
||
<script src="/static/js/app.js?v=58"></script>
|
||
<script src="/static/js/contacts_calendar.js?v=58"></script>
|
||
{{end}}
|