Add welcome tab and icon actions

This commit is contained in:
DonVoo 2026-07-13 01:55:08 +02:00
parent b475b03b9e
commit 8e9fb55710
3 changed files with 145 additions and 48 deletions

View file

@ -49,12 +49,52 @@ func RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("/view/forward", forwardHandler) mux.HandleFunc("/view/forward", forwardHandler)
} }
// homeHandler rendert die Outlook-2013-Oberflaeche (Dreispalter). Der linke
// Baum, die Nachrichtenliste und der Lesebereich werden per HTMX nachgeladen.
func homeHandler(w http.ResponseWriter, r *http.Request) { func homeHandler(w http.ResponseWriter, r *http.Request) {
// "active" ist die CSS-Klasse fuer den hervorgehobenen Reiter. Wenn Codex renderStartPage(w, r)
// weitere Seiten baut, jeweils den passenden Reiter aktiv setzen. }
renderShell(w, "active", `<div class="ef-empty">Konto links waehlen oder unter „Postfaecher" anlegen.</div>`)
func renderStartPage(w http.ResponseWriter, r *http.Request) {
user := CurrentUser(r)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013">
<header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs">
%s
%s
</nav>
%s
</header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item active" href="/">Start</a>
<a class="backstage-item" href="/view">Archiv-Boxen</a>
<a class="backstage-item" href="/view">Import/Export</a>
<a class="backstage-item" href="/accounts">Konten-Verwaltung</a>
<a class="backstage-item" href="/archives">Archiv-mbox Verwaltung</a>
<a class="backstage-item" href="/transfer/postfach-archiv">Mail-Transfer</a>
<a class="backstage-item" href="/users">Benutzerverwaltung</a>
<a class="backstage-item" href="/logout">Logout</a>
</aside>
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
<main class="start-page">
<section class="welcome-panel">
<div class="welcome-kicker">Willkommen</div>
<h1>Mail-Graveyard</h1>
<p>Angemeldet als %s. Waehle eine Email-Box zum Sichten, starte einen Mail-Transfer oder pflege Benutzer und Postfaecher.</p>
<div class="welcome-actions">
<a class="btn" href="/view">Archiv-Postfach oeffnen</a>
<a class="btn secondary" href="/transfer/postfach-archiv">Mail-Transfer</a>
<a class="btn secondary" href="/users">Einstellungen</a>
</div>
</section>
</main>
<footer class="statusbar"><span>bereit</span></footer>
</body></html>`, renderRibbonEmailBoxMenu("home"), renderRibbonTransferMenu(""), renderRibbonActions(false), html.EscapeString(userDisplayLabel(user)))
} }
// renderShell ist das Grundgeruest im Outlook-2013-Look: // renderShell ist das Grundgeruest im Outlook-2013-Look:
@ -66,18 +106,17 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Mail-Graveyard</title> <title>Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item active" href="/view">Archiv-Boxen</a> <a class="backstage-item active" href="/view">Archiv-Boxen</a>
@ -108,7 +147,7 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
</div> </div>
<footer class="statusbar"><span id="mig-status" <footer class="statusbar"><span id="mig-status"
hx-get="/migrate/status" hx-trigger="every 3s" hx-swap="innerHTML">bereit</span></footer> hx-get="/migrate/status" hx-trigger="every 3s" hx-swap="innerHTML">bereit</span></footer>
</body></html>`, renderRibbonEmailBoxMenu("archive"), renderRibbonTransferMenu(""), renderInitialReadPane(readingPane)) </body></html>`, renderRibbonEmailBoxMenu("archive"), renderRibbonTransferMenu(""), renderRibbonActions(false), renderInitialReadPane(readingPane))
} }
func accountsHandler(w http.ResponseWriter, r *http.Request) { func accountsHandler(w http.ResponseWriter, r *http.Request) {
@ -511,18 +550,17 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Konten-Verwaltung - Mail-Graveyard</title> <title>Konten-Verwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a> <a class="backstage-item" href="/view">Archiv-Boxen</a>
@ -537,7 +575,7 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
<a class="backstage-item" href="/logout">Logout</a> <a class="backstage-item" href="/logout">Logout</a>
</aside> </aside>
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button> <button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
<main class="accounts-page">`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu("")) <main class="accounts-page">`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu(""), renderRibbonActions(false))
if msg != "" { if msg != "" {
fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg)) fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg))
} }
@ -556,18 +594,17 @@ func renderArchivesPage(w http.ResponseWriter, archives []string, accounts []Acc
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title> <title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a> <a class="backstage-item" href="/view">Archiv-Boxen</a>
@ -582,7 +619,7 @@ func renderArchivesPage(w http.ResponseWriter, archives []string, accounts []Acc
<a class="backstage-item" href="/logout">Logout</a> <a class="backstage-item" href="/logout">Logout</a>
</aside> </aside>
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button> <button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
<main class="accounts-page archive-page">`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu("")) <main class="accounts-page archive-page">`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu(""), renderRibbonActions(false))
if msg != "" { if msg != "" {
fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg)) fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg))
} }
@ -604,18 +641,17 @@ func renderUsersPage(w http.ResponseWriter, r *http.Request, users []AppUser, ed
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Benutzerverwaltung - Mail-Graveyard</title> <title>Benutzerverwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab active" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a> <a class="backstage-item" href="/view">Archiv-Boxen</a>
@ -630,7 +666,7 @@ func renderUsersPage(w http.ResponseWriter, r *http.Request, users []AppUser, ed
<a class="backstage-item" href="/logout">Logout</a> <a class="backstage-item" href="/logout">Logout</a>
</aside> </aside>
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button> <button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
<main class="accounts-page users-page">`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu("")) <main class="accounts-page users-page">`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu(""), renderRibbonActions(true))
if msg != "" { if msg != "" {
fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg)) fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg))
} }
@ -674,18 +710,17 @@ func renderTransferPage(w http.ResponseWriter, mode transferMode, accounts []Acc
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>%s - Mail-Graveyard</title> <title>%s - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a> <a class="backstage-item" href="/view">Archiv-Boxen</a>
@ -705,7 +740,7 @@ func renderTransferPage(w http.ResponseWriter, mode transferMode, accounts []Acc
</section> </section>
</main> </main>
<footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`, <footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`,
html.EscapeString(mode.Title), renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu(mode.Slug), transferMenuItems(mode.Slug), html.EscapeString(mode.Title), renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu(mode.Slug), renderRibbonActions(false), transferMenuItems(mode.Slug),
html.EscapeString(mode.LeftTitle), renderTransferPicker(mode.LeftKind, accounts, archives), html.EscapeString(mode.LeftTitle), renderTransferPicker(mode.LeftKind, accounts, archives),
html.EscapeString(mode.RightTitle), renderTransferPicker(mode.RightKind, accounts, archives)) html.EscapeString(mode.RightTitle), renderTransferPicker(mode.RightKind, accounts, archives))
} }
@ -715,18 +750,17 @@ func renderSourceEmailBoxPage(w http.ResponseWriter, accounts []Account) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quell-Postfach - Mail-Graveyard</title> <title>Quell-Postfach - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a> <a class="backstage-item" href="/view">Archiv-Boxen</a>
@ -749,7 +783,7 @@ func renderSourceEmailBoxPage(w http.ResponseWriter, accounts []Account) {
<section class="pane pane-read"><div class="ef-empty">Nachricht waehlen.</div></section> <section class="pane pane-read"><div class="ef-empty">Nachricht waehlen.</div></section>
</div> </div>
<footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`, <footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`,
renderRibbonEmailBoxMenu("source"), renderRibbonTransferMenu(""), renderEmailBoxSourcePane(accounts)) renderRibbonEmailBoxMenu("source"), renderRibbonTransferMenu(""), renderRibbonActions(false), renderEmailBoxSourcePane(accounts))
} }
func renderTargetEmailBoxPage(w http.ResponseWriter, accounts []Account) { func renderTargetEmailBoxPage(w http.ResponseWriter, accounts []Account) {
@ -757,18 +791,17 @@ func renderTargetEmailBoxPage(w http.ResponseWriter, accounts []Account) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Ziel-Postfach - Mail-Graveyard</title> <title>Ziel-Postfach - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"> <link rel="stylesheet" href="/static/style.css?v=20260712-20">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-19" defer></script></head> <script src="/static/app.js?v=20260712-20" defer></script></head>
<body class="ol2013"> <body class="ol2013">
<header class="ribbon"> <header class="ribbon">
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button> <button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
<nav class="ribbon-tabs"> <nav class="ribbon-tabs">
%s %s
%s %s
<a class="tab" href="/users">Einstellungen</a>
</nav> </nav>
<div class="ribbon-title">Mail-Graveyard</div><a class="ribbon-logout" href="/logout">Logout</a> %s
</header> </header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue"> <aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a> <a class="backstage-item" href="/view">Archiv-Boxen</a>
@ -791,7 +824,7 @@ func renderTargetEmailBoxPage(w http.ResponseWriter, accounts []Account) {
<aside class="pane pane-tree">%s</aside> <aside class="pane pane-tree">%s</aside>
</div> </div>
<footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`, <footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`,
renderRibbonEmailBoxMenu("target"), renderRibbonTransferMenu(""), renderEmailBoxTargetPane(accounts)) renderRibbonEmailBoxMenu("target"), renderRibbonTransferMenu(""), renderRibbonActions(false), renderEmailBoxTargetPane(accounts))
} }
func renderEmailBoxSourcePane(accounts []Account) string { func renderEmailBoxSourcePane(accounts []Account) string {
@ -890,9 +923,18 @@ func renderRibbonEmailBoxMenu(active string) string {
} }
buttonClass := "tab ribbon-menu-button" buttonClass := "tab ribbon-menu-button"
if active != "" { if active != "" {
buttonClass += " active" if active == "home" {
buttonClass = "tab ribbon-menu-button"
} else {
buttonClass += " active"
}
} }
var b strings.Builder var b strings.Builder
homeClass := "tab app-tab"
if active == "home" {
homeClass += " active"
}
fmt.Fprintf(&b, `<a class="%s" href="/">Mail-Graveyard</a>`, homeClass)
fmt.Fprintf(&b, `<div class="ribbon-menu"><button class="%s" type="button" aria-expanded="false" data-ribbon-menu-toggle>Email-Box</button><div class="ribbon-dropdown">`, buttonClass) fmt.Fprintf(&b, `<div class="ribbon-menu"><button class="%s" type="button" aria-expanded="false" data-ribbon-menu-toggle>Email-Box</button><div class="ribbon-dropdown">`, buttonClass)
for _, item := range items { for _, item := range items {
class := "ribbon-dropdown-item" class := "ribbon-dropdown-item"
@ -905,6 +947,14 @@ func renderRibbonEmailBoxMenu(active string) string {
return b.String() return b.String()
} }
func renderRibbonActions(settingsActive bool) string {
settingsClass := "ribbon-icon"
if settingsActive {
settingsClass += " active"
}
return fmt.Sprintf(`<div class="ribbon-spacer"></div><a class="%s" href="/users" title="Einstellungen" aria-label="Einstellungen">&#9881;</a><a class="ribbon-icon" href="/logout" title="Logout" aria-label="Logout">&#x23FB;</a>`, settingsClass)
}
func renderRibbonTransferMenu(active string) string { func renderRibbonTransferMenu(active string) string {
items := []transferMode{ items := []transferMode{
{Slug: "postfach-archiv", Title: "Postfach <--> Archiv-MBox"}, {Slug: "postfach-archiv", Title: "Postfach <--> Archiv-MBox"},

View file

@ -239,7 +239,7 @@ func renderLoginPage(w http.ResponseWriter, errMsg string) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Login - Mail-Graveyard</title> <title>Login - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"></head> <link rel="stylesheet" href="/static/style.css?v=20260712-20"></head>
<body class="ol2013 login-body"> <body class="ol2013 login-body">
<main class="login-card"> <main class="login-card">
<div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div> <div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div>
@ -259,7 +259,7 @@ func renderForgotPasswordPage(w http.ResponseWriter, msg string) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Passwort vergessen - Mail-Graveyard</title> <title>Passwort vergessen - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"></head> <link rel="stylesheet" href="/static/style.css?v=20260712-20"></head>
<body class="ol2013 login-body"> <body class="ol2013 login-body">
<main class="login-card"> <main class="login-card">
<div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div> <div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div>
@ -278,7 +278,7 @@ func renderResetPasswordPage(w http.ResponseWriter, token, errMsg string) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>Passwort setzen - Mail-Graveyard</title> <title>Passwort setzen - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-19"></head> <link rel="stylesheet" href="/static/style.css?v=20260712-20"></head>
<body class="ol2013 login-body"> <body class="ol2013 login-body">
<main class="login-card"> <main class="login-card">
<div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div> <div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div>

View file

@ -69,14 +69,21 @@ body.ol2013 {
position: relative; position: relative;
z-index: 20; z-index: 20;
} }
.ribbon-title { margin-left: auto; font-weight: 700; font-size: 15px; letter-spacing: .2px; } .ribbon-spacer { margin-left: auto; }
.ribbon-logout { .ribbon-icon {
width: 32px;
height: 32px;
color: #fff; color: #fff;
text-decoration: none; text-decoration: none;
padding: 6px 10px; display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(255, 255, 255, .35); border: 1px solid rgba(255, 255, 255, .35);
font-size: 17px;
line-height: 1;
} }
.ribbon-logout:hover { .ribbon-icon:hover,
.ribbon-icon.active {
background: var(--ol-blue-dark); background: var(--ol-blue-dark);
} }
.ribbon-tabs { display: flex; gap: 2px; height: 100%; } .ribbon-tabs { display: flex; gap: 2px; height: 100%; }
@ -108,6 +115,9 @@ body.ol2013 {
} }
.tab:hover { background: var(--ol-blue-dark); color: #fff; } .tab:hover { background: var(--ol-blue-dark); color: #fff; }
.tab.active { background: #fff; color: var(--ol-blue); border-bottom-color: #fff; } .tab.active { background: #fff; color: var(--ol-blue); border-bottom-color: #fff; }
.tab.app-tab {
font-weight: 700;
}
.ribbon-menu { .ribbon-menu {
position: relative; position: relative;
height: 100%; height: 100%;
@ -635,6 +645,43 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
} }
.muted { color: var(--ol-muted); } .muted { color: var(--ol-muted); }
.start-page {
flex: 1 1 auto;
display: grid;
place-items: center;
padding: 28px;
background: #fff;
}
.welcome-panel {
width: min(760px, 100%);
border: 1px solid var(--ol-border);
background: #fff;
padding: 32px;
box-shadow: 0 10px 24px rgba(0, 0, 0, .08);
}
.welcome-kicker {
color: var(--ol-blue-dark);
font-weight: 700;
margin-bottom: 8px;
}
.welcome-panel h1 {
margin: 0 0 12px;
font-size: 30px;
font-weight: 600;
}
.welcome-panel p {
margin: 0;
color: var(--ol-muted);
font-size: 15px;
line-height: 1.5;
}
.welcome-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 22px;
}
/* Statusleiste */ /* Statusleiste */
.statusbar { .statusbar {
flex: 0 0 auto; flex: 0 0 auto;