From d57c9ce1bcd2f112b3ce64533f87b063972d6ae7 Mon Sep 17 00:00:00 2001 From: DonVoo Date: Sun, 12 Jul 2026 23:59:25 +0200 Subject: [PATCH] Add email box top dropdown --- backend/00-router.go | 135 ++++++++++++++++++++++++++++++++----- frontend-js/dist/style.css | 18 +++++ 2 files changed, 136 insertions(+), 17 deletions(-) diff --git a/backend/00-router.go b/backend/00-router.go index 82fc8c2..4453680 100644 --- a/backend/00-router.go +++ b/backend/00-router.go @@ -27,6 +27,8 @@ func RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("/archives/delete", archiveDeleteHandler) mux.HandleFunc("/transfer", transferHandler) mux.HandleFunc("/transfer/", transferHandler) + mux.HandleFunc("/boxes/source", emailBoxHandler) + mux.HandleFunc("/boxes/target", emailBoxHandler) mux.HandleFunc("/migrate/run", migrateRunHandler) // TODO Codex: RunMigration im Hintergrund mux.HandleFunc("/migrate/status", migrateStatusHandler) // TODO Codex: jobs-Fortschritt (HTMX-Poll) @@ -50,21 +52,21 @@ func homeHandler(w http.ResponseWriter, r *http.Request) { // renderShell ist das Grundgeruest im Outlook-2013-Look: // -// [ Ribbon: Archiv-Boxen | Mail-Transfer | Einstellungen | Mail-Graveyard ] +// [ Ribbon: Email-Box | Mail-Transfer | Einstellungen | Mail-Graveyard ] // [ Archiv-mbox | Nachrichtenliste | Quelle/Target ] func renderShell(w http.ResponseWriter, active, readingPane string) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprintf(w, ` Mail-Graveyard - + - +
@@ -98,7 +100,7 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
bereit
-`, active, renderRibbonTransferMenu(""), renderInitialReadPane(readingPane)) +`, renderRibbonEmailBoxMenu("archive"), renderRibbonTransferMenu(""), renderInitialReadPane(readingPane)) } func accountsHandler(w http.ResponseWriter, r *http.Request) { @@ -205,6 +207,23 @@ func transferHandler(w http.ResponseWriter, r *http.Request) { renderTransferPage(w, transferModeBySlug(mode), accounts, archiveMailboxes(accounts)) } +func emailBoxHandler(w http.ResponseWriter, r *http.Request) { + accounts, err := ListAccounts() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + kind := strings.TrimPrefix(r.URL.Path, "/boxes/") + switch kind { + case "source": + renderEmailBoxPage(w, "source", "Quell-Postfach", uniqueAccountValues(accounts, "source")) + case "target": + renderEmailBoxPage(w, "target", "Ziel-Postfach", uniqueAccountValues(accounts, "target")) + default: + http.Redirect(w, r, "/view", http.StatusSeeOther) + } +} + func archiveCreateHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) @@ -312,14 +331,14 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account, fmt.Fprintf(w, ` Konten-Verwaltung - Mail-Graveyard - + - +
@@ -337,7 +356,7 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account, Einstellungen -
`, renderRibbonTransferMenu("")) +
`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu("")) if msg != "" { fmt.Fprintf(w, `
%s
`, html.EscapeString(msg)) } @@ -356,14 +375,14 @@ func renderArchivesPage(w http.ResponseWriter, archives []string, accounts []Acc fmt.Fprintf(w, ` Archiv-mbox Verwaltung - Mail-Graveyard - + - +
@@ -381,7 +400,7 @@ func renderArchivesPage(w http.ResponseWriter, archives []string, accounts []Acc Einstellungen -
`, renderRibbonTransferMenu("")) +
`, renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu("")) if msg != "" { fmt.Fprintf(w, `
%s
`, html.EscapeString(msg)) } @@ -423,14 +442,14 @@ func renderTransferPage(w http.ResponseWriter, mode transferMode, accounts []Acc fmt.Fprintf(w, ` %s - Mail-Graveyard - + - +
@@ -453,11 +472,65 @@ func renderTransferPage(w http.ResponseWriter, mode transferMode, accounts []Acc
bereit
`, - html.EscapeString(mode.Title), renderRibbonTransferMenu(mode.Slug), transferMenuItems(mode.Slug), + html.EscapeString(mode.Title), renderRibbonEmailBoxMenu(""), renderRibbonTransferMenu(mode.Slug), transferMenuItems(mode.Slug), html.EscapeString(mode.LeftTitle), renderTransferPicker(mode.LeftKind, accounts, archives), html.EscapeString(mode.RightTitle), renderTransferPicker(mode.RightKind, accounts, archives)) } +func renderEmailBoxPage(w http.ResponseWriter, active, title string, values []string) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprintf(w, ` + +%s - Mail-Graveyard + + + + +
+ + +
Mail-Graveyard
+
+ + +
+
+
%s
%s
+
Status
+
+
+
bereit
`, + html.EscapeString(title), renderRibbonEmailBoxMenu(active), renderRibbonTransferMenu(""), + html.EscapeString(title), renderEmailBoxList(values)) +} + +func renderEmailBoxList(values []string) string { + if len(values) == 0 { + return `
Keine Postfaecher eingetragen.
` + } + var b strings.Builder + b.WriteString(``) + return b.String() +} + func transferMenuItems(active string) string { items := []transferMode{ {Slug: "postfach-archiv", Title: "Postfach <--> Archiv-MBox"}, @@ -475,6 +548,34 @@ func transferMenuItems(active string) string { return b.String() } +func renderRibbonEmailBoxMenu(active string) string { + items := []transferMode{ + {Slug: "archive", Title: "Archiv-Postfach"}, + {Slug: "source", Title: "Quell-Postfach"}, + {Slug: "target", Title: "Ziel-Postfach"}, + } + paths := map[string]string{ + "archive": "/view", + "source": "/boxes/source", + "target": "/boxes/target", + } + buttonClass := "tab ribbon-menu-button" + if active != "" { + buttonClass += " active" + } + var b strings.Builder + fmt.Fprintf(&b, `
`, buttonClass) + for _, item := range items { + class := "ribbon-dropdown-item" + if item.Slug == active { + class += " active" + } + fmt.Fprintf(&b, `%s`, class, paths[item.Slug], html.EscapeString(item.Title)) + } + b.WriteString(`
`) + return b.String() +} + func renderRibbonTransferMenu(active string) string { items := []transferMode{ {Slug: "postfach-archiv", Title: "Postfach <--> Archiv-MBox"}, diff --git a/frontend-js/dist/style.css b/frontend-js/dist/style.css index 5250476..9a9a4bf 100644 --- a/frontend-js/dist/style.css +++ b/frontend-js/dist/style.css @@ -405,6 +405,9 @@ a.tree-node { display: block; color: inherit; text-decoration: none; } .transfer-page { background: #fff; } +.email-box-page { + background: #fff; +} .archive-page-layout { display: grid; grid-template-columns: minmax(520px, 1fr) minmax(360px, 460px); @@ -556,6 +559,21 @@ a.tree-node { display: block; color: inherit; text-decoration: none; } padding: 14px; border-bottom: 1px solid var(--ol-border); } +.email-box-list { + padding: 8px 0; +} +.email-box-row { + padding: 8px 14px; + border-bottom: 1px solid #ececec; + color: var(--ol-text); +} +.email-box-row:hover { + background: #eef6fd; +} +.email-box-detail { + padding: 18px; + color: var(--ol-muted); +} .muted { color: var(--ol-muted); } /* Statusleiste */