neue Archivfunktion und Optimierung an Artikelbearbeitung

This commit is contained in:
2026-08-09 18:24:04 +02:00
parent dd51532533
commit b80e2d5a6c
23 changed files with 1185 additions and 77 deletions

View File

@@ -10,14 +10,32 @@ import { set, state } from "../store.js";
let openMenuId = null;
// Welche Zeile wird gerade umbenannt
let renamingId = null;
// Ist das Archiv aufgeklappt?
let archiveOpen = false;
const ROLE_LABEL = { owner: "Eigentümer", editor: "Bearbeiter", viewer: "Nur lesen" };
function formatDate(iso) {
if (!iso) return "";
return new Date(iso).toLocaleDateString("de-DE", {
day: "2-digit", month: "2-digit", year: "numeric",
});
}
export async function listsView(root, { openList, onLogout, settings }) {
let banner = null;
let archived = [];
async function reload() {
set({ lists: await get("/api/lists") });
// Zwei Abfragen statt einer mit Filterung im Browser: Das Archiv
// wird selten geöffnet und kann mit der Zeit groß werden.
const [active, old] = await Promise.all([
get("/api/lists?archived=false"),
get("/api/lists?archived=true"),
]);
archived = old;
set({ lists: active });
}
function say(message, kind = "error") {
@@ -102,25 +120,43 @@ export async function listsView(root, { openList, onLogout, settings }) {
openMenuId === entry.id
? el("div.list-menu", {},
isOwner
? el("div.menu-actions", {},
el("button", {
// Archivieren wirkt nur für die eigene Übersicht - deshalb
// steht es allen Mitgliedern offen, nicht nur dem Eigentümer.
el("p.sub", {},
entry.archived
? `Seit ${formatDate(entry.archived_at)} in deinem Archiv.`
: "Archivieren nimmt die Liste aus deiner Übersicht " +
"für die anderen Mitglieder bleibt sie sichtbar."),
entry.archived && entry.delete_due_at
? el("p.lead.warn", {},
`Wird am ${formatDate(entry.delete_due_at)} automatisch ` +
"gelöscht, wenn du sie nicht zurückholst.")
: null,
el("div.menu-actions", {},
el("button", {
type: "button",
onclick: () => guarded(() =>
post(`/api/lists/${entry.id}/${entry.archived ? "un" : ""}archive`)),
}, entry.archived ? "Aus dem Archiv holen" : "Archivieren"),
isOwner
? el("button", {
type: "button",
onclick: () => {
openMenuId = null;
renamingId = entry.id;
render();
},
}, "Umbenennen"),
el("button.danger", {
type: "button",
onclick: () => remove(entry),
}, "Löschen"))
: el("div.menu-actions", {},
el("button.danger", {
type: "button",
onclick: () => leave(entry),
}, "Liste verlassen")))
}, "Umbenennen")
: null),
isOwner
? el("button.danger.wide", {
type: "button",
onclick: () => remove(entry),
}, "Löschen")
: el("button.danger.wide", {
type: "button",
onclick: () => leave(entry),
}, "Liste verlassen"))
: null
);
}
@@ -220,9 +256,31 @@ export async function listsView(root, { openList, onLogout, settings }) {
el("h1", {}, "Meine Listen"),
state.lists.length
? el("ul.lists", {}, state.lists.map(row))
: el("p.empty", {}, "Noch keine Liste vorhanden."),
: el("p.empty", {},
archived.length
? "Alle Listen liegen im Archiv."
: "Noch keine Liste vorhanden."),
el("label", { htmlFor: "new-list" }, "Neue Liste"),
createRow())
createRow()),
archived.length
? el("section.card.archive", {},
el("button.archive-head", {
type: "button",
"aria-expanded": archiveOpen ? "true" : "false",
onclick: () => { archiveOpen = !archiveOpen; render(); },
},
el("span", {}, `Archiv (${archived.length})`),
el("span.chev", {}, archiveOpen ? "▾" : "▸")),
archiveOpen
? el("div", {},
el("p.lead", {},
"Listen, die du weggeräumt hast oder die du länger " +
"nicht geöffnet hast. Sie sind nicht gelöscht ein " +
"Klick holt sie zurück."),
el("ul.lists", {}, archived.map(row)))
: null)
: null
);
}