fix file deletion
This commit is contained in:
parent
da6f5a3cfc
commit
36614817cb
1 changed files with 122 additions and 84 deletions
|
@ -80,6 +80,7 @@ declare global {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
openEditModal: (event?: any) => void;
|
openEditModal: (event?: any) => void;
|
||||||
deleteFile: (eventId: string, filename: string) => void;
|
deleteFile: (eventId: string, filename: string) => void;
|
||||||
|
undoDeleteFile: (eventId: string, filename: string) => void;
|
||||||
previewFile: (url: string, filename: string) => void;
|
previewFile: (url: string, filename: string) => void;
|
||||||
openDetailsModal: (event: Event) => void;
|
openDetailsModal: (event: Event) => void;
|
||||||
showFilePreview: (file: {
|
showFilePreview: (file: {
|
||||||
|
@ -923,6 +924,7 @@ declare global {
|
||||||
|
|
||||||
// Add file storage
|
// Add file storage
|
||||||
const selectedFileStorage = new Map<string, File>();
|
const selectedFileStorage = new Map<string, File>();
|
||||||
|
const filesToDelete = new Set<string>(); // Add storage for files to delete
|
||||||
|
|
||||||
interface AttendeeEntry {
|
interface AttendeeEntry {
|
||||||
user_id: string;
|
user_id: string;
|
||||||
|
@ -2021,7 +2023,7 @@ declare global {
|
||||||
return files
|
return files
|
||||||
.map(
|
.map(
|
||||||
(filename) => `
|
(filename) => `
|
||||||
<div class="flex items-center justify-between p-2 bg-base-200 rounded-lg">
|
<div class="flex items-center justify-between p-2 bg-base-200 rounded-lg${filesToDelete.has(filename) ? " opacity-50" : ""}" data-filename="${filename}">
|
||||||
<span class="truncate">${filename}</span>
|
<span class="truncate">${filename}</span>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button type="button" class="btn btn-ghost btn-xs" onclick="window.previewFile('${fileManager.getFileUrl("events", eventId, filename)}', '${filename}')">
|
<button type="button" class="btn btn-ghost btn-xs" onclick="window.previewFile('${fileManager.getFileUrl("events", eventId, filename)}', '${filename}')">
|
||||||
|
@ -2030,11 +2032,17 @@ declare global {
|
||||||
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/>
|
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-ghost btn-xs text-error" onclick="window.deleteFile('${eventId}', '${filename}')">
|
<div class="text-error">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
${
|
||||||
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
filesToDelete.has(filename)
|
||||||
</svg>
|
? `<button type="button" class="btn btn-ghost btn-xs" onclick="window.undoDeleteFile('${eventId}', '${filename}')">Undo</button>`
|
||||||
</button>
|
: `<button type="button" class="btn btn-ghost btn-xs text-error" onclick="window.deleteFile('${eventId}', '${filename}')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
||||||
|
</svg>
|
||||||
|
</button>`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
@ -2141,10 +2149,8 @@ declare global {
|
||||||
// Immediately disable buttons and show loading state
|
// Immediately disable buttons and show loading state
|
||||||
submitButton.disabled = true;
|
submitButton.disabled = true;
|
||||||
cancelButton.disabled = true;
|
cancelButton.disabled = true;
|
||||||
submitButton.innerHTML = `
|
submitButton.classList.add("btn-disabled");
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
submitButton.innerHTML = `<span class="loading loading-spinner"></span>`;
|
||||||
Saving...
|
|
||||||
`;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
@ -2179,22 +2185,49 @@ declare global {
|
||||||
try {
|
try {
|
||||||
if (eventId) {
|
if (eventId) {
|
||||||
// Update existing event
|
// Update existing event
|
||||||
submitButton.innerHTML = `
|
submitButton.innerHTML = `<span class="loading loading-spinner"></span>`;
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
|
||||||
Updating event...
|
// Get current event data to process file changes
|
||||||
`;
|
const pb = auth.getPocketBase();
|
||||||
|
const currentEvent = await pb
|
||||||
|
.collection("events")
|
||||||
|
.getOne(eventId);
|
||||||
|
const currentFiles = currentEvent.files || [];
|
||||||
|
|
||||||
|
// Filter out files marked for deletion
|
||||||
|
const remainingFiles = currentFiles.filter(
|
||||||
|
(filename: string) => !filesToDelete.has(filename)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update event with remaining files
|
||||||
|
const eventDataWithFiles = {
|
||||||
|
...eventData,
|
||||||
|
files: remainingFiles,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the event data first
|
||||||
updatedEvent = await update.updateFields(
|
updatedEvent = await update.updateFields(
|
||||||
"events",
|
"events",
|
||||||
eventId,
|
eventId,
|
||||||
eventData
|
eventDataWithFiles
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle file uploads if any
|
// Process file deletions
|
||||||
|
for (const filename of filesToDelete) {
|
||||||
|
await fileManager.deleteFile(
|
||||||
|
"events",
|
||||||
|
eventId,
|
||||||
|
filename
|
||||||
|
);
|
||||||
|
await sendLog.send(
|
||||||
|
"delete",
|
||||||
|
"event_file",
|
||||||
|
`Deleted file ${filename} from event ${eventData.event_name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then handle new file uploads if any
|
||||||
if (selectedFiles.length > 0) {
|
if (selectedFiles.length > 0) {
|
||||||
submitButton.innerHTML = `
|
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
|
||||||
Uploading files (0/${selectedFiles.length})...
|
|
||||||
`;
|
|
||||||
await fileManager.appendFiles(
|
await fileManager.appendFiles(
|
||||||
"events",
|
"events",
|
||||||
eventId,
|
eventId,
|
||||||
|
@ -2210,10 +2243,7 @@ declare global {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Create new event
|
// Create new event
|
||||||
submitButton.innerHTML = `
|
submitButton.innerHTML = `<span class="loading loading-spinner"></span>`;
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
|
||||||
Creating event...
|
|
||||||
`;
|
|
||||||
const pb = auth.getPocketBase();
|
const pb = auth.getPocketBase();
|
||||||
const newEvent = await pb
|
const newEvent = await pb
|
||||||
.collection("events")
|
.collection("events")
|
||||||
|
@ -2221,10 +2251,6 @@ declare global {
|
||||||
|
|
||||||
// Handle file uploads if any
|
// Handle file uploads if any
|
||||||
if (selectedFiles.length > 0) {
|
if (selectedFiles.length > 0) {
|
||||||
submitButton.innerHTML = `
|
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
|
||||||
Uploading files (0/${selectedFiles.length})...
|
|
||||||
`;
|
|
||||||
await fileManager.uploadFiles(
|
await fileManager.uploadFiles(
|
||||||
"events",
|
"events",
|
||||||
newEvent.id,
|
newEvent.id,
|
||||||
|
@ -2241,13 +2267,12 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show success state briefly
|
// Show success state briefly
|
||||||
|
submitButton.classList.remove("btn-disabled");
|
||||||
|
submitButton.classList.add("btn-success");
|
||||||
submitButton.innerHTML = `
|
submitButton.innerHTML = `
|
||||||
<div class="flex items-center gap-2 text-success">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
||||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
</svg>
|
||||||
</svg>
|
|
||||||
<span>Saved!</span>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
@ -2262,7 +2287,7 @@ declare global {
|
||||||
await refreshCache(); // Refresh the cache
|
await refreshCache(); // Refresh the cache
|
||||||
await fetchEvents(); // Update the UI
|
await fetchEvents(); // Update the UI
|
||||||
|
|
||||||
// Clear form inputs
|
// Clear form inputs and storage
|
||||||
const formFileInput = document.getElementById(
|
const formFileInput = document.getElementById(
|
||||||
"editEventFiles"
|
"editEventFiles"
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
|
@ -2270,85 +2295,98 @@ declare global {
|
||||||
if (formFileInput) formFileInput.value = "";
|
if (formFileInput) formFileInput.value = "";
|
||||||
if (newFiles) newFiles.innerHTML = "";
|
if (newFiles) newFiles.innerHTML = "";
|
||||||
|
|
||||||
// Clear storage after successful upload
|
// Clear storages after successful save
|
||||||
selectedFileStorage.clear();
|
selectedFileStorage.clear();
|
||||||
|
filesToDelete.clear();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error; // Re-throw to be caught by outer try-catch
|
throw error;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save event:", error);
|
console.error("Failed to save event:", error);
|
||||||
// Show error message in the button with icon
|
submitButton.classList.remove("btn-disabled");
|
||||||
|
submitButton.classList.add("btn-error");
|
||||||
submitButton.innerHTML = `
|
submitButton.innerHTML = `
|
||||||
<div class="flex items-center gap-2 text-error">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
||||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
<span>Failed</span>
|
`;
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
// Show detailed error to user
|
|
||||||
alert("Failed to save event. Please try again.");
|
alert("Failed to save event. Please try again.");
|
||||||
} finally {
|
} finally {
|
||||||
// Reset button state
|
|
||||||
submitButton.disabled = false;
|
submitButton.disabled = false;
|
||||||
cancelButton.disabled = false;
|
cancelButton.disabled = false;
|
||||||
|
submitButton.classList.remove(
|
||||||
|
"btn-disabled",
|
||||||
|
"btn-success",
|
||||||
|
"btn-error"
|
||||||
|
);
|
||||||
submitButton.innerHTML = originalText;
|
submitButton.innerHTML = originalText;
|
||||||
window.hideLoading?.();
|
window.hideLoading?.();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear storage when modal is closed
|
// Clear both storages when modal is closed
|
||||||
document.getElementById("editEventModal")?.addEventListener("close", () => {
|
document.getElementById("editEventModal")?.addEventListener("close", () => {
|
||||||
selectedFileStorage.clear();
|
selectedFileStorage.clear();
|
||||||
|
filesToDelete.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add delete file handler
|
// Add delete file handler
|
||||||
window.deleteFile = async function (eventId: string, filename: string) {
|
window.deleteFile = async function (eventId: string, filename: string) {
|
||||||
if (!confirm("Are you sure you want to delete this file?")) return;
|
if (!confirm("Are you sure you want to remove this file?")) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.showLoading?.();
|
// Add file to deletion set
|
||||||
const pb = auth.getPocketBase();
|
filesToDelete.add(filename);
|
||||||
|
|
||||||
// Get current event data
|
// Update the UI to show file as pending deletion
|
||||||
const event = await pb.collection("events").getOne(eventId);
|
|
||||||
|
|
||||||
// Filter out the file to delete
|
|
||||||
const updatedFiles = event.files.filter(
|
|
||||||
(f: string) => f !== filename
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update the event with the new files array
|
|
||||||
await pb.collection("events").update(eventId, {
|
|
||||||
files: updatedFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
await sendLog.send(
|
|
||||||
"delete",
|
|
||||||
"event_file",
|
|
||||||
`Deleted file ${filename} from event ${event.event_name}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Refresh the current files display
|
|
||||||
const currentFiles = document.getElementById("currentFiles");
|
const currentFiles = document.getElementById("currentFiles");
|
||||||
if (currentFiles && updatedFiles.length > 0) {
|
if (currentFiles) {
|
||||||
currentFiles.innerHTML = updateFilePreviewButtons(
|
const fileElement = currentFiles.querySelector(
|
||||||
updatedFiles,
|
`[data-filename="${filename}"]`
|
||||||
eventId
|
|
||||||
);
|
);
|
||||||
} else if (currentFiles) {
|
if (fileElement) {
|
||||||
currentFiles.innerHTML = `
|
fileElement.classList.add("opacity-50");
|
||||||
<div class="text-center py-4 text-base-content/70">
|
const deleteButton =
|
||||||
<p>No files attached</p>
|
fileElement.querySelector(".text-error");
|
||||||
</div>
|
if (deleteButton) {
|
||||||
`;
|
deleteButton.innerHTML = `
|
||||||
|
<button type="button" class="btn btn-ghost btn-xs" onclick="window.undoDeleteFile('${eventId}', '${filename}')">
|
||||||
|
Undo
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete file:", error);
|
console.error("Failed to stage file deletion:", error);
|
||||||
alert("Failed to delete file. Please try again.");
|
alert("Failed to stage file deletion. Please try again.");
|
||||||
} finally {
|
}
|
||||||
window.hideLoading?.();
|
};
|
||||||
|
|
||||||
|
// Add undo delete function
|
||||||
|
window.undoDeleteFile = function (eventId: string, filename: string) {
|
||||||
|
filesToDelete.delete(filename);
|
||||||
|
|
||||||
|
// Update the UI to show file as restored
|
||||||
|
const currentFiles = document.getElementById("currentFiles");
|
||||||
|
if (currentFiles) {
|
||||||
|
const fileElement = currentFiles.querySelector(
|
||||||
|
`[data-filename="${filename}"]`
|
||||||
|
);
|
||||||
|
if (fileElement) {
|
||||||
|
fileElement.classList.remove("opacity-50");
|
||||||
|
const undoButton = fileElement.querySelector(".text-error");
|
||||||
|
if (undoButton) {
|
||||||
|
undoButton.innerHTML = `
|
||||||
|
<button type="button" class="btn btn-ghost btn-xs text-error" onclick="window.deleteFile('${eventId}', '${filename}')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue