v0.2.85
This commit is contained in:
1 parent
90e3c399ea
commit
57ad26878b
12 files changed
+477
-31
No files matched your search
+97
-5
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user