This commit is contained in:
Mateusz Gruszczyński committed 2026-09-25 15:31:55 +02:00
1 parent 90e3c399ea
commit 57ad26878b
12 files changed
+477 -31

No files matched your search

+97 -5
View File
@@ -46,20 +46,31 @@ const workspacePasswordForm = document.querySelector("#workspace-password-form")
const workspacePasswordInput = document.querySelector("#workspace-set-password");
const workspacePasswordError = document.querySelector("#workspace-password-error");
const workspaceSettingsForm = document.querySelector("#workspace-settings-form");
const workspaceSettingsButton = document.querySelector("#workspace-settings-button");
const archiveDeletedNotesToggle = document.querySelector("#archive-deleted-notes");
const workspaceSettingsStatus = document.querySelector("#workspace-settings-status");
const archivedNotesButton = document.querySelector("#archived-notes-button");
const archivedNotesDialog = document.querySelector("#archived-notes-dialog");
const archivedNotesList = document.querySelector("#archived-notes-list");
let notesPage = 1;
let notesSearchTimer;
const notesViewKey = `rustpad:workspace:${slug}:notes-view`;
let notesView = localStorage.getItem(notesViewKey) === "table" ? "table" : "grid";
let notesCache = [];
let archivedNotesCache = [];
function updateWorkspacePasswordControl() {
workspacePasswordForm.hidden = Boolean(info?.protected || !info?.can_set_password);
}
function updateWorkspaceSettingsControl() {
workspaceSettingsForm.hidden = !info?.can_manage_settings;
const canManage = Boolean(info?.can_manage_settings);
workspaceSettingsButton.hidden = !canManage;
archivedNotesButton.hidden = !canManage;
if (!canManage) {
workspaceSettingsForm.hidden = true;
workspaceSettingsButton.setAttribute("aria-expanded", "false");
}
archiveDeletedNotesToggle.checked = Boolean(info?.archive_deleted_notes);
}
@@ -165,6 +176,36 @@ function formatDate(value) {
const date = new Date(raw);
return Number.isNaN(date.getTime()) ? "—" : formatDateTime(date);
}
function renderArchivedNotes(notes) {
archivedNotesCache = Array.isArray(notes) ? notes : [];
if (!archivedNotesCache.length) {
archivedNotesList.innerHTML = `<p class="empty">${escapeHtml(t("workspace.archive.empty", {}, "No archived notes."))}</p>`;
return;
}
archivedNotesList.innerHTML = archivedNotesCache.map(note => {
const author = note.created_by || t("workspace.unknown", {}, "Unknown");
return `<article class="archived-note-card">
<div class="archived-note-card__copy">
<strong>${escapeHtml(note.title)}</strong>
<span>${escapeHtml(t("workspace.archive.archivedAt", { date: formatDate(note.deleted_at) }, `Archived: ${formatDate(note.deleted_at)}`))}</span>
<small>${escapeHtml(t("workspace.archive.createdBy", { author }, `Created by: ${author}`))}</small>
</div>
<button class="secondary-button inline-button" type="button" data-restore-archived-note="${escapeHtml(note.slug)}">${escapeHtml(t("workspace.archive.restore", {}, "Restore"))}</button>
</article>`;
}).join("");
}
async function loadArchivedNotes() {
archivedNotesList.innerHTML = `<p class="empty">${escapeHtml(t("common.loading", {}, "Loading…"))}</p>`;
try {
const notes = await api(`/api/workspaces/${encodeURIComponent(slug)}/archived-notes`);
renderArchivedNotes(notes);
} catch (error) {
archivedNotesList.innerHTML = `<p class="form-message error">${escapeHtml(error.message)}</p>`;
toast.danger(error.message, { title: t("workspace.archive.loadFailed", {}, "Could not load archived notes") });
}
}
function setNotesView(view) {
notesView = view === "table" ? "table" : "grid";
localStorage.setItem(notesViewKey, notesView);
@@ -374,6 +415,52 @@ workspacePasswordForm.addEventListener("submit", async event => {
submit.disabled = false;
}
});
workspaceSettingsButton.addEventListener("click", () => {
if (workspaceSettingsButton.hidden) return;
const willOpen = workspaceSettingsForm.hidden;
workspaceSettingsForm.hidden = !willOpen;
workspaceSettingsButton.setAttribute("aria-expanded", String(willOpen));
if (willOpen) {
workspaceSettingsStatus.textContent = "";
archiveDeletedNotesToggle.checked = Boolean(info?.archive_deleted_notes);
}
});
archivedNotesButton.addEventListener("click", () => {
if (!archivedNotesDialog.open) archivedNotesDialog.showModal();
void loadArchivedNotes();
});
document.querySelector("#close-archived-notes").addEventListener("click", () => archivedNotesDialog.close());
archivedNotesDialog.addEventListener("click", event => {
if (event.target === archivedNotesDialog) archivedNotesDialog.close();
});
archivedNotesList.addEventListener("click", async event => {
const button = event.target.closest("[data-restore-archived-note]");
if (!button) return;
const noteSlug = button.dataset.restoreArchivedNote;
const archivedNote = archivedNotesCache.find(note => note.slug === noteSlug);
const title = archivedNote?.title || noteSlug;
const confirmed = await askConfirm(
t("workspace.archive.restoreAsk", { title }, `Restore note “${title}” to this workspace?`),
{
title: t("workspace.archive.restoreTitle", {}, "Restore archived note"),
confirmText: t("workspace.archive.restore", {}, "Restore"),
},
);
if (!confirmed) return;
button.disabled = true;
try {
await api(`/api/workspaces/${encodeURIComponent(slug)}/archived-notes/${encodeURIComponent(noteSlug)}/restore`, { method: "POST" });
toast.success(
t("workspace.archive.restored", { title }, `The note "${title}" was restored.`),
{ title: t("workspace.archive.restoredTitle", {}, "Note restored") },
);
await Promise.all([loadArchivedNotes(), openWorkspace()]);
} catch (error) {
toast.danger(error.message, { title: t("workspace.archive.restoreFailed", {}, "Could not restore note") });
if (button.isConnected) button.disabled = false;
}
});
workspaceSettingsForm.addEventListener("submit", async event => {
event.preventDefault();
workspaceSettingsStatus.textContent = "";
@@ -387,12 +474,17 @@ workspaceSettingsForm.addEventListener("submit", async event => {
info = { ...info, ...updated };
updateWorkspaceSettingsControl();
workspaceSettingsStatus.textContent = info.archive_deleted_notes
? "Deleted notes will be archived in the database."
: "Deleted notes will be permanently removed.";
toast.success("Workspace deletion policy saved.", { title: "Workspace settings" });
? t("workspace.settings.archiveEnabled", {}, "Deleted notes will be archived in the database.")
: t("workspace.settings.archiveDisabled", {}, "Deleted notes will be permanently removed.");
workspaceSettingsForm.hidden = true;
workspaceSettingsButton.setAttribute("aria-expanded", "false");
toast.success(
t("workspace.settings.saved", {}, "Workspace deletion policy saved."),
{ title: t("workspace.settings.title", {}, "Workspace settings") },
);
} catch (error) {
workspaceSettingsStatus.textContent = error.message;
toast.danger(error.message, { title: "Could not save workspace settings" });
toast.danger(error.message, { title: t("workspace.settings.saveFailed", {}, "Could not save workspace settings") });
} finally {
submit.disabled = false;
}