Prevent accidental duplicate notes in workspace actions

This commit is contained in:
2026-03-28 21:31:02 +00:00
parent c4c5906daf
commit 21c8b4b4c7
+59 -10
View File
@@ -159,6 +159,7 @@
<div class="grid gap-3 md:grid-cols-2"> <div class="grid gap-3 md:grid-cols-2">
<select id="typeInput" class="field"> <select id="typeInput" class="field">
<option value="personal">personal</option> <option value="personal">personal</option>
<option value="general">general</option>
<option value="job">job</option> <option value="job">job</option>
<option value="manager">manager</option> <option value="manager">manager</option>
<option value="billing">billing</option> <option value="billing">billing</option>
@@ -188,6 +189,7 @@
</div> </div>
<div class="mt-4 flex flex-wrap gap-3"> <div class="mt-4 flex flex-wrap gap-3">
<button id="createBtn" class="btn btn-accent-emerald">Create Note</button> <button id="createBtn" class="btn btn-accent-emerald">Create Note</button>
<button id="copyBtn" class="btn btn-secondary" disabled>Copy Note</button>
<button id="saveBtn" class="btn btn-accent-indigo" disabled>Save Changes</button> <button id="saveBtn" class="btn btn-accent-indigo" disabled>Save Changes</button>
<button id="hideBtn" class="btn btn-secondary" disabled>Hide</button> <button id="hideBtn" class="btn btn-secondary" disabled>Hide</button>
<button id="deleteBtn" class="btn btn-danger" disabled>Soft Delete</button> <button id="deleteBtn" class="btn btn-danger" disabled>Soft Delete</button>
@@ -260,16 +262,42 @@
const saveBtn = document.getElementById('saveBtn'); const saveBtn = document.getElementById('saveBtn');
const hideBtn = document.getElementById('hideBtn'); const hideBtn = document.getElementById('hideBtn');
const deleteBtn = document.getElementById('deleteBtn'); const deleteBtn = document.getElementById('deleteBtn');
const createBtn = document.getElementById('createBtn');
const copyBtn = document.getElementById('copyBtn');
let recorderStream = null; let recorderStream = null;
let mediaRecorder = null; let mediaRecorder = null;
let recordChunks = []; let recordChunks = [];
let lastRecordedFile = null; let lastRecordedFile = null;
let actionInFlight = false;
function writeOutput(value) { function writeOutput(value) {
output.textContent = typeof value === 'string' ? value : JSON.stringify(value, null, 2); output.textContent = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
} }
function setActionBusy(isBusy) {
actionInFlight = isBusy;
const disabled = !!isBusy;
createBtn.disabled = disabled || !!selectedNote;
copyBtn.disabled = disabled || !selectedNote;
saveBtn.disabled = disabled || !selectedNote;
hideBtn.disabled = disabled || !selectedNote;
deleteBtn.disabled = disabled || !selectedNote;
}
async function runNoteAction(handler) {
if (actionInFlight) {
writeOutput('Please wait for the current action to finish.');
return;
}
setActionBusy(true);
try {
await handler();
} finally {
setActionBusy(false);
}
}
function escapeHtml(value) { function escapeHtml(value) {
return String(value || '') return String(value || '')
.replace(/&/g, '&amp;') .replace(/&/g, '&amp;')
@@ -747,9 +775,9 @@
lastRecordedFile = null; lastRecordedFile = null;
selectedAttachmentsStatus.textContent = 'Select a note to view attachments.'; selectedAttachmentsStatus.textContent = 'Select a note to view attachments.';
selectedAttachmentsList.innerHTML = ''; selectedAttachmentsList.innerHTML = '';
saveBtn.disabled = true; setActionBusy(false);
hideBtn.disabled = true; createBtn.disabled = false;
deleteBtn.disabled = true; copyBtn.disabled = true;
} }
function selectNote(note) { function selectNote(note) {
@@ -769,9 +797,9 @@
recordPreview.classList.add('hidden'); recordPreview.classList.add('hidden');
recordStatus.textContent = 'Ready'; recordStatus.textContent = 'Ready';
lastRecordedFile = null; lastRecordedFile = null;
saveBtn.disabled = false; setActionBusy(false);
hideBtn.disabled = false; createBtn.disabled = true;
deleteBtn.disabled = false; copyBtn.disabled = false;
hideBtn.textContent = toBoolFlag(note.hidden) ? 'Unhide' : 'Hide'; hideBtn.textContent = toBoolFlag(note.hidden) ? 'Unhide' : 'Hide';
} }
@@ -882,6 +910,9 @@
async function createNote() { async function createNote() {
await ensurePocketBaseClient(); await ensurePocketBaseClient();
ensureLoggedIn(); ensureLoggedIn();
if (selectedNote) {
throw new Error('A note is selected. Use Save Changes to update it or Copy Note to duplicate it intentionally.');
}
const payload = buildNotePayload(); const payload = buildNotePayload();
const record = await pb.collection(NOTES_COLLECTION).create(payload); const record = await pb.collection(NOTES_COLLECTION).create(payload);
const uploaded = await uploadPendingAttachments(record.id); const uploaded = await uploadPendingAttachments(record.id);
@@ -893,6 +924,23 @@
writeOutput({ message: 'Note created', id: record.id, attachmentsUploaded: uploaded.length }); writeOutput({ message: 'Note created', id: record.id, attachmentsUploaded: uploaded.length });
} }
async function copyNote() {
if (!selectedNote) throw new Error('Select a note first');
const sourceId = selectedNote.id;
await ensurePocketBaseClient();
ensureLoggedIn();
const payload = buildNotePayload();
payload.title = `${payload.title} (Copy)`;
const record = await pb.collection(NOTES_COLLECTION).create(payload);
const uploaded = await uploadPendingAttachments(record.id);
await loadNotes();
const refreshed = allNotes.find((n) => n.id === record.id) || record;
selectNote(refreshed);
renderList();
await renderSelectedAttachments();
writeOutput({ message: 'Note copied', sourceId, newId: record.id, attachmentsUploaded: uploaded.length });
}
async function saveNote() { async function saveNote() {
if (!selectedNote) throw new Error('Select a note first'); if (!selectedNote) throw new Error('Select a note first');
await ensurePocketBaseClient(); await ensurePocketBaseClient();
@@ -946,10 +994,11 @@
bind('loginBtn', loginPocketBase); bind('loginBtn', loginPocketBase);
bind('logoutBtn', logoutPocketBase); bind('logoutBtn', logoutPocketBase);
bind('refreshBtn', loadNotes); bind('refreshBtn', loadNotes);
bind('createBtn', createNote); bind('createBtn', () => runNoteAction(createNote));
bind('saveBtn', saveNote); bind('copyBtn', () => runNoteAction(copyNote));
bind('hideBtn', toggleHidden); bind('saveBtn', () => runNoteAction(saveNote));
bind('deleteBtn', softDelete); bind('hideBtn', () => runNoteAction(toggleHidden));
bind('deleteBtn', () => runNoteAction(softDelete));
document.getElementById('pickFilesBtn').addEventListener('click', () => { document.getElementById('pickFilesBtn').addEventListener('click', () => {
attachmentsInput.click(); attachmentsInput.click();