no more console logs

This commit is contained in:
chark1es 2025-03-08 22:23:30 -08:00
parent 16d9ec9e1d
commit 27bc2f4e70
32 changed files with 3752 additions and 4000 deletions

View file

@ -1,18 +1,22 @@
<script> <script>
const observer = new IntersectionObserver((entries) => { const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
entry.target.classList.add("in-view"); entry.target.classList.add("in-view");
entry.target.classList.remove("opacity-0"); entry.target.classList.remove("opacity-0");
console.log("Added 'in-view' class to:", entry.target); // console.log("Added 'in-view' class to:", entry.target);
} else { } else {
entry.target.classList.remove("in-view"); entry.target.classList.remove("in-view");
entry.target.classList.add("opacity-0"); entry.target.classList.add("opacity-0");
console.log("Removed 'in-view' class from:", entry.target); // console.log("Removed 'in-view' class from:", entry.target);
} }
}); });
}, { threshold: 0.2 }); },
{ threshold: 0.2 },
document.querySelectorAll("[data-inview]").forEach((el) => observer.observe(el)); );
</script>
document
.querySelectorAll("[data-inview]")
.forEach((el) => observer.observe(el));
</script>

View file

@ -5,151 +5,144 @@ import EventLoad from "./EventsSection/EventLoad";
--- ---
<div id="" class=""> <div id="" class="">
<div class="mb-4 sm:mb-6 px-4 sm:px-6"> <div class="mb-4 sm:mb-6 px-4 sm:px-6">
<h2 class="text-xl sm:text-2xl font-bold">Events</h2> <h2 class="text-xl sm:text-2xl font-bold">Events</h2>
<p class="opacity-70 text-sm sm:text-base"> <p class="opacity-70 text-sm sm:text-base">
View and manage your IEEE UCSD events View and manage your IEEE UCSD events
</p> </p>
</div>
<div
class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-4 sm:mb-6 px-4 sm:px-6"
>
<!-- Event Check-in Card -->
<div class="w-full">
<EventCheckIn client:load />
</div> </div>
<div <!-- Event Registration Card -->
class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-4 sm:mb-6 px-4 sm:px-6" <div class="w-full">
> <div
<!-- Event Check-in Card --> class="card bg-base-100 shadow-xl border border-base-200 opacity-50 cursor-not-allowed relative group h-full"
<div class="w-full"> >
<EventCheckIn client:load /> <div
class="absolute inset-0 bg-base-100 opacity-0 group-hover:opacity-90 transition-opacity duration-300 flex items-center justify-center z-10"
>
<span class="text-base-content font-medium text-sm sm:text-base"
>Coming Soon</span
>
</div> </div>
<div class="card-body p-4 sm:p-6">
<!-- Event Registration Card --> <h3 class="card-title text-base sm:text-lg mb-3 sm:mb-4">
<div class="w-full"> Event Registration
<div </h3>
class="card bg-base-100 shadow-xl border border-base-200 opacity-50 cursor-not-allowed relative group h-full" <div class="form-control w-full">
> <label class="label">
<div <span class="label-text text-sm sm:text-base"
class="absolute inset-0 bg-base-100 opacity-0 group-hover:opacity-90 transition-opacity duration-300 flex items-center justify-center z-10" >Select an event to register</span
> >
<span </label>
class="text-base-content font-medium text-sm sm:text-base" <div class="flex flex-col sm:flex-row gap-2">
>Coming Soon</span <select
> class="select select-bordered flex-1 text-sm sm:text-base h-10 min-h-[2.5rem] w-full"
</div> disabled
<div class="card-body p-4 sm:p-6"> >
<h3 class="card-title text-base sm:text-lg mb-3 sm:mb-4"> <option disabled selected>Pick an event</option>
Event Registration <option>Technical Workshop - Web Development</option>
</h3> <option>Professional Development Workshop</option>
<div class="form-control w-full"> <option>Social Event - Game Night</option>
<label class="label"> </select>
<span class="label-text text-sm sm:text-base" <button
>Select an event to register</span class="btn btn-primary text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-[100px]"
> disabled>Register</button
</label> >
<div class="flex flex-col sm:flex-row gap-2">
<select
class="select select-bordered flex-1 text-sm sm:text-base h-10 min-h-[2.5rem] w-full"
disabled
>
<option disabled selected>Pick an event</option>
<option
>Technical Workshop - Web Development</option
>
<option
>Professional Development Workshop</option
>
<option>Social Event - Game Night</option>
</select>
<button
class="btn btn-primary text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-[100px]"
disabled>Register</button
>
</div>
</div>
</div>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
</div>
<EventLoad client:load /> <EventLoad client:load />
</div> </div>
<!-- Event Details Modal --> <!-- Event Details Modal -->
<dialog id="eventDetailsModal" class="modal"> <dialog id="eventDetailsModal" class="modal">
<div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6"> <div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6">
<div class="flex justify-between items-center mb-3 sm:mb-4"> <div class="flex justify-between items-center mb-3 sm:mb-4">
<div class="flex items-center gap-2 sm:gap-3"> <div class="flex items-center gap-2 sm:gap-3">
<h3 class="font-bold text-base sm:text-lg" id="modalTitle"> <h3 class="font-bold text-base sm:text-lg" id="modalTitle">
Event Files Event Files
</h3> </h3>
<button <button
id="downloadAllBtn" id="downloadAllBtn"
class="btn btn-primary btn-sm gap-1 text-xs sm:text-sm" class="btn btn-primary btn-sm gap-1 text-xs sm:text-sm"
onclick="window.downloadAllFiles()" onclick="window.downloadAllFiles()"
> >
<iconify-icon <iconify-icon
icon="heroicons:arrow-down-tray-20-solid" icon="heroicons:arrow-down-tray-20-solid"
className="h-3 w-3 sm:h-4 sm:w-4"></iconify-icon> className="h-3 w-3 sm:h-4 sm:w-4"></iconify-icon>
Download All Download All
</button> </button>
</div> </div>
<button <button
class="btn btn-circle btn-ghost btn-sm sm:btn-md" class="btn btn-circle btn-ghost btn-sm sm:btn-md"
onclick="window.closeEventDetailsModal()" onclick="window.closeEventDetailsModal()"
> >
<iconify-icon <iconify-icon icon="heroicons:x-mark" className="h-4 w-4 sm:h-6 sm:w-6"
icon="heroicons:x-mark" ></iconify-icon>
className="h-4 w-4 sm:h-6 sm:w-6"></iconify-icon> </button>
</button>
</div>
<div id="filesContent" class="space-y-3 sm:space-y-4">
<!-- Files list will be populated here -->
</div>
</div> </div>
<form method="dialog" class="modal-backdrop">
<button onclick="window.closeEventDetailsModal()">close</button> <div id="filesContent" class="space-y-3 sm:space-y-4">
</form> <!-- Files list will be populated here -->
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button onclick="window.closeEventDetailsModal()">close</button>
</form>
</dialog> </dialog>
<!-- Universal File Preview Modal --> <!-- Universal File Preview Modal -->
<dialog id="filePreviewModal" class="modal"> <dialog id="filePreviewModal" class="modal">
<div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6"> <div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6">
<div class="flex justify-between items-center mb-3 sm:mb-4"> <div class="flex justify-between items-center mb-3 sm:mb-4">
<div class="flex items-center gap-2 sm:gap-3"> <div class="flex items-center gap-2 sm:gap-3">
<button <button
class="btn btn-ghost btn-sm text-xs sm:text-sm" class="btn btn-ghost btn-sm text-xs sm:text-sm"
onclick="window.closeFilePreviewEvents()">Close</button onclick="window.closeFilePreviewEvents()">Close</button
> >
<h3 <h3
class="font-bold text-base sm:text-lg truncate" class="font-bold text-base sm:text-lg truncate"
id="previewFileName" id="previewFileName"
> >
</h3> </h3>
</div> </div>
</div>
<div class="relative" id="previewContainer">
<div
id="previewLoadingSpinner"
class="absolute inset-0 flex items-center justify-center bg-base-200 bg-opacity-50 hidden"
>
<span class="loading loading-spinner loading-md sm:loading-lg"
></span>
</div>
<div id="previewContent" class="w-full">
<FilePreview client:load isModal={true} />
</div>
</div>
</div> </div>
<form method="dialog" class="modal-backdrop"> <div class="relative" id="previewContainer">
<button onclick="window.closeFilePreviewEvents()">close</button> <div
</form> id="previewLoadingSpinner"
class="absolute inset-0 flex items-center justify-center bg-base-200 bg-opacity-50 hidden"
>
<span class="loading loading-spinner loading-md sm:loading-lg"></span>
</div>
<div id="previewContent" class="w-full">
<FilePreview client:load isModal={true} />
</div>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button onclick="window.closeFilePreviewEvents()">close</button>
</form>
</dialog> </dialog>
<script> <script>
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import JSZip from "jszip"; import JSZip from "jszip";
// Add styles to the document // Add styles to the document
const style = document.createElement("style"); const style = document.createElement("style");
style.textContent = ` style.textContent = `
/* Custom styles for the event details modal */ /* Custom styles for the event details modal */
.event-details-grid { .event-details-grid {
display: grid; display: grid;
@ -165,234 +158,227 @@ import EventLoad from "./EventsSection/EventLoad";
/* Remove custom toast styles since we're using react-hot-toast */ /* Remove custom toast styles since we're using react-hot-toast */
`; `;
document.head.appendChild(style); document.head.appendChild(style);
// Add helper functions for file preview // Add helper functions for file preview
function getFileType(filename: string): string { function getFileType(filename: string): string {
const extension = filename.split(".").pop()?.toLowerCase(); const extension = filename.split(".").pop()?.toLowerCase();
const mimeTypes: { [key: string]: string } = { const mimeTypes: { [key: string]: string } = {
pdf: "application/pdf", pdf: "application/pdf",
jpg: "image/jpeg", jpg: "image/jpeg",
jpeg: "image/jpeg", jpeg: "image/jpeg",
png: "image/png", png: "image/png",
gif: "image/gif", gif: "image/gif",
mp4: "video/mp4", mp4: "video/mp4",
mp3: "audio/mpeg", mp3: "audio/mpeg",
txt: "text/plain", txt: "text/plain",
doc: "application/msword", doc: "application/msword",
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
xls: "application/vnd.ms-excel", xls: "application/vnd.ms-excel",
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
json: "application/json", json: "application/json",
}; };
return mimeTypes[extension || ""] || "application/octet-stream"; return mimeTypes[extension || ""] || "application/octet-stream";
}
// Universal file preview function for events section
window.previewFileEvents = function (url: string, filename: string) {
// console.log("previewFileEvents called with:", { url, filename });
// console.log("URL type:", typeof url, "URL length:", url?.length || 0);
// console.log(
// "Filename type:",
// typeof filename,
// "Filename length:",
// filename?.length || 0
// );
// Validate inputs
if (!url || typeof url !== "string") {
console.error("Invalid URL provided to previewFileEvents:", url);
toast.error("Cannot preview file: Invalid URL");
return;
} }
// Universal file preview function for events section if (!filename || typeof filename !== "string") {
window.previewFileEvents = function (url: string, filename: string) { console.error(
console.log("previewFileEvents called with:", { url, filename }); "Invalid filename provided to previewFileEvents:",
console.log("URL type:", typeof url, "URL length:", url?.length || 0); filename,
console.log( );
"Filename type:", toast.error("Cannot preview file: Invalid filename");
typeof filename, return;
"Filename length:", }
filename?.length || 0
);
// Validate inputs // Ensure URL is properly formatted
if (!url || typeof url !== "string") { if (!url.startsWith("http")) {
console.error("Invalid URL provided to previewFileEvents:", url); console.warn("URL doesn't start with http, attempting to fix:", url);
toast.error("Cannot preview file: Invalid URL"); if (url.startsWith("/")) {
return; url = `https://pocketbase.ieeeucsd.org${url}`;
} } else {
url = `https://pocketbase.ieeeucsd.org/${url}`;
}
// console.log("Fixed URL:", url);
}
if (!filename || typeof filename !== "string") { const modal = document.getElementById(
console.error( "filePreviewModal",
"Invalid filename provided to previewFileEvents:", ) as HTMLDialogElement;
filename const previewFileName = document.getElementById("previewFileName");
); const previewContent = document.getElementById("previewContent");
toast.error("Cannot preview file: Invalid filename"); const loadingSpinner = document.getElementById("previewLoadingSpinner");
return;
}
// Ensure URL is properly formatted if (modal && previewFileName && previewContent) {
if (!url.startsWith("http")) { // console.log("Found all required elements");
console.warn(
"URL doesn't start with http, attempting to fix:",
url
);
if (url.startsWith("/")) {
url = `https://pocketbase.ieeeucsd.org${url}`;
} else {
url = `https://pocketbase.ieeeucsd.org/${url}`;
}
console.log("Fixed URL:", url);
}
const modal = document.getElementById( // Show loading spinner
"filePreviewModal" if (loadingSpinner) {
) as HTMLDialogElement; loadingSpinner.classList.remove("hidden");
const previewFileName = document.getElementById("previewFileName"); }
const previewContent = document.getElementById("previewContent");
const loadingSpinner = document.getElementById("previewLoadingSpinner");
if (modal && previewFileName && previewContent) { // Update the filename display
console.log("Found all required elements"); previewFileName.textContent = filename;
// Show loading spinner // Show the modal
if (loadingSpinner) { modal.showModal();
loadingSpinner.classList.remove("hidden");
}
// Update the filename display // Test the URL with a fetch before dispatching the event
previewFileName.textContent = filename; fetch(url, { method: "HEAD" })
.then((response) => {
// Show the modal // console.log(
modal.showModal(); // "URL test response:",
// response.status,
// Test the URL with a fetch before dispatching the event // response.ok
fetch(url, { method: "HEAD" }) // );
.then((response) => { if (!response.ok) {
console.log( console.warn("URL might not be accessible:", url);
"URL test response:", toast(
response.status, "File might not be accessible. Attempting to preview anyway.",
response.ok {
);
if (!response.ok) {
console.warn("URL might not be accessible:", url);
toast(
"File might not be accessible. Attempting to preview anyway.",
{
icon: "⚠️",
style: {
borderRadius: "10px",
background: "#FFC107",
color: "#000",
},
}
);
}
})
.catch((err) => {
console.error("Error testing URL:", err);
})
.finally(() => {
// Dispatch state change event to update the FilePreview component
console.log(
"Dispatching filePreviewStateChange event with:",
{ url, filename }
);
window.dispatchEvent(
new CustomEvent("filePreviewStateChange", {
detail: { url, filename },
})
);
});
// Hide loading spinner after a short delay
setTimeout(() => {
if (loadingSpinner) {
loadingSpinner.classList.add("hidden");
}
}, 1000); // Increased delay to allow for URL testing
} else {
console.error("Missing required elements for file preview");
toast.error("Could not initialize file preview");
}
};
// Close file preview for events section
window.closeFilePreviewEvents = function () {
console.log("closeFilePreviewEvents called");
const modal = document.getElementById(
"filePreviewModal"
) as HTMLDialogElement;
const previewFileName = document.getElementById("previewFileName");
const previewContent = document.getElementById("previewContent");
const loadingSpinner = document.getElementById("previewLoadingSpinner");
if (loadingSpinner) {
loadingSpinner.classList.add("hidden");
}
if (modal && previewFileName && previewContent) {
console.log("Resetting preview and closing modal");
// First reset the preview state by dispatching an event with empty values
// This ensures the FilePreview component clears its internal state
window.dispatchEvent(
new CustomEvent("filePreviewStateChange", {
detail: { url: "", filename: "" },
})
);
// Reset the UI
previewFileName.textContent = "";
// Close the modal
modal.close();
console.log("File preview modal closed and state reset");
} else {
console.error("Could not find elements to close file preview");
}
};
// Update the showFilePreview function for events section
window.showFilePreviewEvents = function (file: {
url: string;
name: string;
}) {
console.log("showFilePreviewEvents called with:", file);
if (!file || !file.url || !file.name) {
console.error("Invalid file data:", file);
toast.error("Could not preview file: missing file information");
return;
}
window.previewFileEvents(file.url, file.name);
};
// Update the openDetailsModal function to use the events-specific preview
window.openDetailsModal = function (event: any) {
const modal = document.getElementById(
"eventDetailsModal"
) as HTMLDialogElement;
const filesContent = document.getElementById(
"filesContent"
) as HTMLDivElement;
// Check if event has ended
const eventEndDate = new Date(event.end_date);
const now = new Date();
if (eventEndDate > now) {
toast("Files are only available after the event has ended.", {
icon: "⚠️", icon: "⚠️",
style: { style: {
borderRadius: "10px", borderRadius: "10px",
background: "#FFC107", background: "#FFC107",
color: "#000", color: "#000",
}, },
}); },
return; );
}
})
.catch((err) => {
console.error("Error testing URL:", err);
})
.finally(() => {
// Dispatch state change event to update the FilePreview component
// console.log(
// "Dispatching filePreviewStateChange event with:",
// { url, filename }
// );
window.dispatchEvent(
new CustomEvent("filePreviewStateChange", {
detail: { url, filename },
}),
);
});
// Hide loading spinner after a short delay
setTimeout(() => {
if (loadingSpinner) {
loadingSpinner.classList.add("hidden");
} }
}, 1000); // Increased delay to allow for URL testing
} else {
console.error("Missing required elements for file preview");
toast.error("Could not initialize file preview");
}
};
// Reset state // Close file preview for events section
window.currentEventId = event.id; window.closeFilePreviewEvents = function () {
if (filesContent) filesContent.classList.remove("hidden"); // console.log("closeFilePreviewEvents called");
const modal = document.getElementById(
"filePreviewModal",
) as HTMLDialogElement;
const previewFileName = document.getElementById("previewFileName");
const previewContent = document.getElementById("previewContent");
const loadingSpinner = document.getElementById("previewLoadingSpinner");
// Populate files content if (loadingSpinner) {
if ( loadingSpinner.classList.add("hidden");
event.files && }
Array.isArray(event.files) &&
event.files.length > 0
) {
const baseUrl = "https://pocketbase.ieeeucsd.org";
const collectionId = "events";
const recordId = event.id;
filesContent.innerHTML = ` if (modal && previewFileName && previewContent) {
// console.log("Resetting preview and closing modal");
// First reset the preview state by dispatching an event with empty values
// This ensures the FilePreview component clears its internal state
window.dispatchEvent(
new CustomEvent("filePreviewStateChange", {
detail: { url: "", filename: "" },
}),
);
// Reset the UI
previewFileName.textContent = "";
// Close the modal
modal.close();
// console.log("File preview modal closed and state reset");
} else {
console.error("Could not find elements to close file preview");
}
};
// Update the showFilePreview function for events section
window.showFilePreviewEvents = function (file: {
url: string;
name: string;
}) {
// console.log("showFilePreviewEvents called with:", file);
if (!file || !file.url || !file.name) {
console.error("Invalid file data:", file);
toast.error("Could not preview file: missing file information");
return;
}
window.previewFileEvents(file.url, file.name);
};
// Update the openDetailsModal function to use the events-specific preview
window.openDetailsModal = function (event: any) {
const modal = document.getElementById(
"eventDetailsModal",
) as HTMLDialogElement;
const filesContent = document.getElementById(
"filesContent",
) as HTMLDivElement;
// Check if event has ended
const eventEndDate = new Date(event.end_date);
const now = new Date();
if (eventEndDate > now) {
toast("Files are only available after the event has ended.", {
icon: "⚠️",
style: {
borderRadius: "10px",
background: "#FFC107",
color: "#000",
},
});
return;
}
// Reset state
window.currentEventId = event.id;
if (filesContent) filesContent.classList.remove("hidden");
// Populate files content
if (event.files && Array.isArray(event.files) && event.files.length > 0) {
const baseUrl = "https://pocketbase.ieeeucsd.org";
const collectionId = "events";
const recordId = event.id;
filesContent.innerHTML = `
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table table-zebra w-full"> <table class="table table-zebra w-full">
<thead> <thead>
@ -403,16 +389,16 @@ import EventLoad from "./EventsSection/EventLoad";
</thead> </thead>
<tbody> <tbody>
${event.files ${event.files
.map((file: string) => { .map((file: string) => {
// Ensure the file URL is properly formatted // Ensure the file URL is properly formatted
const fileUrl = `${baseUrl}/api/files/${collectionId}/${recordId}/${file}`; const fileUrl = `${baseUrl}/api/files/${collectionId}/${recordId}/${file}`;
const fileType = getFileType(file); const fileType = getFileType(file);
// Properly escape the data for the onclick handler // Properly escape the data for the onclick handler
const fileData = { const fileData = {
url: fileUrl, url: fileUrl,
name: file, name: file,
}; };
return ` return `
<tr> <tr>
<td>${file}</td> <td>${file}</td>
<td class="text-right"> <td class="text-right">
@ -425,123 +411,123 @@ import EventLoad from "./EventsSection/EventLoad";
</td> </td>
</tr> </tr>
`; `;
}) })
.join("")} .join("")}
</tbody> </tbody>
</table> </table>
</div> </div>
`; `;
} else { } else {
filesContent.innerHTML = ` filesContent.innerHTML = `
<div class="text-center py-8 text-base-content/70"> <div class="text-center py-8 text-base-content/70">
<iconify-icon icon="heroicons:document-duplicate" className="h-12 w-12 mx-auto mb-4 opacity-50" /> <iconify-icon icon="heroicons:document-duplicate" className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>No files attached to this event</p> <p>No files attached to this event</p>
</div> </div>
`; `;
}
modal.showModal();
};
// Add downloadAllFiles function
window.downloadAllFiles = async function () {
const downloadBtn = document.getElementById(
"downloadAllBtn",
) as HTMLButtonElement;
if (!downloadBtn) return;
const originalBtnContent = downloadBtn.innerHTML;
try {
// Show loading state
downloadBtn.innerHTML =
'<span class="loading loading-spinner loading-xs"></span> Preparing...';
downloadBtn.disabled = true;
const zip = new JSZip();
// Get current event files
const baseUrl = "https://pocketbase.ieeeucsd.org";
const collectionId = "events";
const recordId = window.currentEventId;
// Get the current event from the window object
const eventDataId = `event_${window.currentEventId}`;
const event = window[eventDataId];
if (!event || !event.files || event.files.length === 0) {
throw new Error("No files available to download");
}
// Download each file and add to zip
const filePromises = event.files.map(async (filename: string) => {
const fileUrl = `${baseUrl}/api/files/${collectionId}/${recordId}/${filename}`;
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error(`Failed to download ${filename}`);
} }
const blob = await response.blob();
zip.file(filename, blob);
});
modal.showModal(); await Promise.all(filePromises);
};
// Add downloadAllFiles function // Generate and download zip
window.downloadAllFiles = async function () { const zipBlob = await zip.generateAsync({ type: "blob" });
const downloadBtn = document.getElementById( const downloadUrl = URL.createObjectURL(zipBlob);
"downloadAllBtn" const link = document.createElement("a");
) as HTMLButtonElement; link.href = downloadUrl;
if (!downloadBtn) return; link.download = `${event.event_name}_files.zip`;
const originalBtnContent = downloadBtn.innerHTML; document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(downloadUrl);
try { // Show success message
// Show loading state toast.success("Files downloaded successfully!");
downloadBtn.innerHTML = } catch (error: any) {
'<span class="loading loading-spinner loading-xs"></span> Preparing...'; console.error("Failed to download files:", error);
downloadBtn.disabled = true; toast.error(
error?.message || "Failed to download files. Please try again.",
);
} finally {
// Reset button state
downloadBtn.innerHTML = originalBtnContent;
downloadBtn.disabled = false;
}
};
const zip = new JSZip(); // Close event details modal
window.closeEventDetailsModal = function () {
const modal = document.getElementById(
"eventDetailsModal",
) as HTMLDialogElement;
const filesContent = document.getElementById("filesContent");
// Get current event files if (modal) {
const baseUrl = "https://pocketbase.ieeeucsd.org"; // Reset the files content
const collectionId = "events"; if (filesContent) {
const recordId = window.currentEventId; filesContent.innerHTML = "";
}
// Get the current event from the window object // Reset any other state if needed
const eventDataId = `event_${window.currentEventId}`; window.currentEventId = "";
const event = window[eventDataId];
if (!event || !event.files || event.files.length === 0) { // Close the modal
throw new Error("No files available to download"); modal.close();
} }
};
// Download each file and add to zip // Make helper functions available globally
const filePromises = event.files.map(async (filename: string) => { window.showFilePreview = window.showFilePreviewEvents;
const fileUrl = `${baseUrl}/api/files/${collectionId}/${recordId}/${filename}`; window.handlePreviewError = function () {
const response = await fetch(fileUrl); const previewContent = document.getElementById("previewContent");
if (!response.ok) { if (previewContent) {
throw new Error(`Failed to download ${filename}`); previewContent.innerHTML = `
}
const blob = await response.blob();
zip.file(filename, blob);
});
await Promise.all(filePromises);
// Generate and download zip
const zipBlob = await zip.generateAsync({ type: "blob" });
const downloadUrl = URL.createObjectURL(zipBlob);
const link = document.createElement("a");
link.href = downloadUrl;
link.download = `${event.event_name}_files.zip`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(downloadUrl);
// Show success message
toast.success("Files downloaded successfully!");
} catch (error: any) {
console.error("Failed to download files:", error);
toast.error(
error?.message || "Failed to download files. Please try again."
);
} finally {
// Reset button state
downloadBtn.innerHTML = originalBtnContent;
downloadBtn.disabled = false;
}
};
// Close event details modal
window.closeEventDetailsModal = function () {
const modal = document.getElementById(
"eventDetailsModal"
) as HTMLDialogElement;
const filesContent = document.getElementById("filesContent");
if (modal) {
// Reset the files content
if (filesContent) {
filesContent.innerHTML = "";
}
// Reset any other state if needed
window.currentEventId = "";
// Close the modal
modal.close();
}
};
// Make helper functions available globally
window.showFilePreview = window.showFilePreviewEvents;
window.handlePreviewError = function () {
const previewContent = document.getElementById("previewContent");
if (previewContent) {
previewContent.innerHTML = `
<div class="alert alert-error"> <div class="alert alert-error">
<Icon icon="heroicons:x-circle" className="h-6 w-6" /> <Icon icon="heroicons:x-circle" className="h-6 w-6" />
<span>Failed to load file preview</span> <span>Failed to load file preview</span>
</div> </div>
`; `;
} }
}; };
</script> </script>

View file

@ -249,8 +249,6 @@ const EventCheckIn = () => {
// Create the attendee record in PocketBase // Create the attendee record in PocketBase
const newAttendee = await update.create(Collections.EVENT_ATTENDEES, attendeeData); const newAttendee = await update.create(Collections.EVENT_ATTENDEES, attendeeData);
console.log("Successfully created attendance record");
// Update user's total points // Update user's total points
// First, get all the user's attendance records to calculate total points // First, get all the user's attendance records to calculate total points
const userAttendance = await get.getList<EventAttendee>( const userAttendance = await get.getList<EventAttendee>(
@ -267,7 +265,7 @@ const EventCheckIn = () => {
}); });
// Log the points update // Log the points update
console.log(`Updating user points to: ${totalPoints}`); // console.log(`Updating user points to: ${totalPoints}`);
// Update the user record with the new total points // Update the user record with the new total points
await update.updateFields(Collections.USERS, userId, { await update.updateFields(Collections.USERS, userId, {

View file

@ -74,9 +74,9 @@ const EventLoad = () => {
// Clear events table // Clear events table
if (db && db.events) { if (db && db.events) {
console.log("Clearing events cache..."); // console.log("Clearing events cache...");
await db.events.clear(); await db.events.clear();
console.log("Events cache cleared successfully"); // console.log("Events cache cleared successfully");
} }
// Reset sync timestamp for events by updating it to 0 // Reset sync timestamp for events by updating it to 0
@ -84,7 +84,7 @@ const EventLoad = () => {
const currentInfo = await dexieService.getLastSync(Collections.EVENTS); const currentInfo = await dexieService.getLastSync(Collections.EVENTS);
// Then update it with a timestamp of 0 (forcing a fresh sync) // Then update it with a timestamp of 0 (forcing a fresh sync)
await dexieService.updateLastSync(Collections.EVENTS); await dexieService.updateLastSync(Collections.EVENTS);
console.log("Events sync timestamp reset"); // console.log("Events sync timestamp reset");
// Reload events // Reload events
setLoading(true); setLoading(true);
@ -245,7 +245,7 @@ const EventLoad = () => {
const dataSync = DataSyncService.getInstance(); const dataSync = DataSyncService.getInstance();
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
console.log("Starting to load events..."); // console.log("Starting to load events...");
// Check if user is authenticated // Check if user is authenticated
if (!auth.isAuthenticated()) { if (!auth.isAuthenticated()) {
@ -255,7 +255,7 @@ const EventLoad = () => {
} }
// Force sync to ensure we have the latest data // Force sync to ensure we have the latest data
console.log("Syncing events collection..."); // console.log("Syncing events collection...");
let syncSuccess = false; let syncSuccess = false;
let retryCount = 0; let retryCount = 0;
const maxRetries = 3; const maxRetries = 3;
@ -263,13 +263,13 @@ const EventLoad = () => {
while (!syncSuccess && retryCount < maxRetries) { while (!syncSuccess && retryCount < maxRetries) {
try { try {
if (retryCount > 0) { if (retryCount > 0) {
console.log(`Retry attempt ${retryCount} of ${maxRetries}...`); // console.log(`Retry attempt ${retryCount} of ${maxRetries}...`);
// Add a small delay between retries // Add a small delay between retries
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount)); await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
} }
await dataSync.syncCollection(Collections.EVENTS, "published = true", "-start_date"); await dataSync.syncCollection(Collections.EVENTS, "published = true", "-start_date");
console.log("Events collection synced successfully"); // console.log("Events collection synced successfully");
syncSuccess = true; syncSuccess = true;
} catch (syncError) { } catch (syncError) {
retryCount++; retryCount++;
@ -282,7 +282,7 @@ const EventLoad = () => {
} }
// Get events from IndexedDB // Get events from IndexedDB
console.log("Fetching events from IndexedDB..."); // console.log("Fetching events from IndexedDB...");
const allEvents = await dataSync.getData<Event>( const allEvents = await dataSync.getData<Event>(
Collections.EVENTS, Collections.EVENTS,
false, // Don't force sync again false, // Don't force sync again
@ -290,27 +290,27 @@ const EventLoad = () => {
"-start_date" "-start_date"
); );
console.log(`Retrieved ${allEvents.length} events from IndexedDB`); // console.log(`Retrieved ${allEvents.length} events from IndexedDB`);
// Filter out invalid events // Filter out invalid events
const validEvents = allEvents.filter(event => isValidEvent(event)); const validEvents = allEvents.filter(event => isValidEvent(event));
console.log(`Filtered out ${allEvents.length - validEvents.length} invalid events`); // console.log(`Filtered out ${allEvents.length - validEvents.length} invalid events`);
// If no valid events found in IndexedDB, try fetching directly from PocketBase as fallback // If no valid events found in IndexedDB, try fetching directly from PocketBase as fallback
let eventsToProcess = validEvents; let eventsToProcess = validEvents;
if (allEvents.length === 0) { if (allEvents.length === 0) {
console.log("No events found in IndexedDB, trying direct PocketBase fetch..."); // console.log("No events found in IndexedDB, trying direct PocketBase fetch...");
try { try {
const pbEvents = await get.getAll<Event>( const pbEvents = await get.getAll<Event>(
Collections.EVENTS, Collections.EVENTS,
"published = true", "published = true",
"-start_date" "-start_date"
); );
console.log(`Retrieved ${pbEvents.length} events directly from PocketBase`); // console.log(`Retrieved ${pbEvents.length} events directly from PocketBase`);
// Filter out invalid events from PocketBase results // Filter out invalid events from PocketBase results
const validPbEvents = pbEvents.filter(event => isValidEvent(event)); const validPbEvents = pbEvents.filter(event => isValidEvent(event));
console.log(`Filtered out ${pbEvents.length - validPbEvents.length} invalid events from PocketBase`); // console.log(`Filtered out ${pbEvents.length - validPbEvents.length} invalid events from PocketBase`);
eventsToProcess = validPbEvents; eventsToProcess = validPbEvents;
@ -319,7 +319,7 @@ const EventLoad = () => {
const dexieService = DexieService.getInstance(); const dexieService = DexieService.getInstance();
const db = dexieService.getDB(); const db = dexieService.getDB();
if (db && db.events) { if (db && db.events) {
console.log(`Storing ${validPbEvents.length} valid PocketBase events in IndexedDB...`); // console.log(`Storing ${validPbEvents.length} valid PocketBase events in IndexedDB...`);
await db.events.bulkPut(validPbEvents); await db.events.bulkPut(validPbEvents);
} }
} }
@ -329,7 +329,7 @@ const EventLoad = () => {
} }
// Split events into upcoming, ongoing, and past based on start and end dates // Split events into upcoming, ongoing, and past based on start and end dates
console.log("Categorizing events..."); // console.log("Categorizing events...");
const now = new Date(); const now = new Date();
const { upcoming, ongoing, past } = eventsToProcess.reduce( const { upcoming, ongoing, past } = eventsToProcess.reduce(
(acc, event) => { (acc, event) => {
@ -382,7 +382,7 @@ const EventLoad = () => {
} }
); );
console.log(`Categorized events: ${upcoming.length} upcoming, ${ongoing.length} ongoing, ${past.length} past`); // console.log(`Categorized events: ${upcoming.length} upcoming, ${ongoing.length} ongoing, ${past.length} past`);
// Sort events // Sort events
upcoming.sort((a, b) => new Date(a.start_date).getTime() - new Date(b.start_date).getTime()); upcoming.sort((a, b) => new Date(a.start_date).getTime() - new Date(b.start_date).getTime());
@ -409,16 +409,16 @@ const EventLoad = () => {
// Try to load from IndexedDB only as a last resort // Try to load from IndexedDB only as a last resort
try { try {
console.log("Attempting to load events from IndexedDB only..."); // console.log("Attempting to load events from IndexedDB only...");
const dexieService = DexieService.getInstance(); const dexieService = DexieService.getInstance();
const db = dexieService.getDB(); const db = dexieService.getDB();
if (db && db.events) { if (db && db.events) {
const allCachedEvents = await db.events.filter(event => event.published === true).toArray(); const allCachedEvents = await db.events.filter(event => event.published === true).toArray();
console.log(`Found ${allCachedEvents.length} cached events in IndexedDB`); // console.log(`Found ${allCachedEvents.length} cached events in IndexedDB`);
// Filter out invalid events // Filter out invalid events
const cachedEvents = allCachedEvents.filter(event => isValidEvent(event)); const cachedEvents = allCachedEvents.filter(event => isValidEvent(event));
console.log(`Filtered out ${allCachedEvents.length - cachedEvents.length} invalid cached events`); // console.log(`Filtered out ${allCachedEvents.length - cachedEvents.length} invalid cached events`);
if (cachedEvents.length > 0) { if (cachedEvents.length > 0) {
// Process these events // Process these events
@ -458,7 +458,7 @@ const EventLoad = () => {
ongoing: ongoing.slice(0, 50), ongoing: ongoing.slice(0, 50),
past: past.slice(0, 50) past: past.slice(0, 50)
}); });
console.log("Successfully loaded events from cache"); // console.log("Successfully loaded events from cache");
} }
} }
} catch (cacheError) { } catch (cacheError) {

File diff suppressed because it is too large Load diff

View file

@ -287,7 +287,7 @@ export default function Attendees() {
const fetchEventData = async () => { const fetchEventData = async () => {
if (!eventId || !auth.isAuthenticated()) { if (!eventId || !auth.isAuthenticated()) {
if (!auth.isAuthenticated()) { if (!auth.isAuthenticated()) {
console.log('User not authenticated'); // console.log('User not authenticated');
setError('Authentication required'); setError('Authentication required');
} }
return; return;

View file

@ -631,7 +631,7 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
has_food: eventData.has_food || false has_food: eventData.has_food || false
}); });
console.log("Event data loaded successfully:", eventData); // console.log("Event data loaded successfully:", eventData);
} else { } else {
setEvent({ setEvent({
id: '', id: '',
@ -784,7 +784,7 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
// 1. Remove files marked for deletion // 1. Remove files marked for deletion
if (filesToDelete.size > 0) { if (filesToDelete.size > 0) {
console.log(`Removing ${filesToDelete.size} files from event ${event.id}`); // console.log(`Removing ${filesToDelete.size} files from event ${event.id}`);
currentFiles = currentFiles.filter(file => !filesToDelete.has(file)); currentFiles = currentFiles.filter(file => !filesToDelete.has(file));
// Update the files field first to remove deleted files // Update the files field first to remove deleted files
@ -797,7 +797,7 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
// 2. Add new files one by one to preserve existing ones // 2. Add new files one by one to preserve existing ones
if (selectedFiles.size > 0) { if (selectedFiles.size > 0) {
console.log(`Adding ${selectedFiles.size} new files to event ${event.id}`); // console.log(`Adding ${selectedFiles.size} new files to event ${event.id}`);
// Convert Map to array of File objects // Convert Map to array of File objects
const newFiles = Array.from(selectedFiles.values()); const newFiles = Array.from(selectedFiles.values());
@ -849,7 +849,7 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
// Then upload files if any // Then upload files if any
if (selectedFiles.size > 0 && newEvent?.id) { if (selectedFiles.size > 0 && newEvent?.id) {
console.log(`Adding ${selectedFiles.size} files to new event ${newEvent.id}`); // console.log(`Adding ${selectedFiles.size} files to new event ${newEvent.id}`);
// Convert Map to array of File objects // Convert Map to array of File objects
const newFiles = Array.from(selectedFiles.values()); const newFiles = Array.from(selectedFiles.values());

View file

@ -25,313 +25,295 @@ let error: string | null = null;
// This provides initial data for server-side rendering // This provides initial data for server-side rendering
// Client-side will use IndexedDB for data management // Client-side will use IndexedDB for data management
if (auth.isAuthenticated()) { if (auth.isAuthenticated()) {
try { try {
const userId = auth.getUserId(); const userId = auth.getUserId();
if (userId) { if (userId) {
userEventRequests = await get.getAll<EventRequest>( userEventRequests = await get.getAll<EventRequest>(
Collections.EVENT_REQUESTS, Collections.EVENT_REQUESTS,
`requested_user="${userId}"`, `requested_user="${userId}"`,
"-created" "-created",
); );
}
} catch (err) {
console.error("Failed to fetch user event requests:", err);
error = "Failed to load your event requests. Please try again later.";
} }
} catch (err) {
console.error("Failed to fetch user event requests:", err);
error = "Failed to load your event requests. Please try again later.";
}
} }
--- ---
<div class="w-full max-w-6xl mx-auto py-8 px-4"> <div class="w-full max-w-6xl mx-auto py-8 px-4">
<div class="mb-8"> <div class="mb-8">
<h1 class="text-3xl font-bold text-white mb-2">Event Request Form</h1> <h1 class="text-3xl font-bold text-white mb-2">Event Request Form</h1>
<p class="text-gray-300 mb-4"> <p class="text-gray-300 mb-4">
Submit your event request at least 6 weeks before your event. After Submit your event request at least 6 weeks before your event. After
submitting, please notify PR and/or Coordinators in the #-events submitting, please notify PR and/or Coordinators in the #-events Slack
Slack channel. channel.
</p> </p>
<div class="bg-base-300/50 p-4 rounded-lg text-sm text-gray-300"> <div class="bg-base-300/50 p-4 rounded-lg text-sm text-gray-300">
<p class="font-medium mb-2">This form includes sections for:</p> <p class="font-medium mb-2">This form includes sections for:</p>
<ul class="list-disc list-inside space-y-1 ml-2"> <ul class="list-disc list-inside space-y-1 ml-2">
<li>PR Materials (if needed)</li> <li>PR Materials (if needed)</li>
<li>Event Details</li> <li>Event Details</li>
<li>TAP Form Information</li> <li>TAP Form Information</li>
<li>AS Funding (if needed)</li> <li>AS Funding (if needed)</li>
</ul> </ul>
<p class="mt-3"> <p class="mt-3">
Your progress is automatically saved as you fill out the form. Your progress is automatically saved as you fill out the form.
</p> </p>
</div>
</div> </div>
</div>
<!-- Tabs --> <!-- Tabs -->
<div class="tabs tabs-boxed mb-6"> <div class="tabs tabs-boxed mb-6">
<a class="tab tab-lg tab-active" id="form-tab">Submit Event Request</a> <a class="tab tab-lg tab-active" id="form-tab">Submit Event Request</a>
<a class="tab tab-lg" id="submissions-tab">View Your Submissions</a> <a class="tab tab-lg" id="submissions-tab">View Your Submissions</a>
</div>
<!-- Form Tab Content -->
<div
id="form-content"
class="bg-base-200 rounded-lg shadow-xl overflow-hidden"
>
<div class="p-6">
<EventRequestForm client:load />
</div> </div>
</div>
<!-- Form Tab Content --> <!-- Submissions Tab Content -->
<div <div id="submissions-content" class="hidden">
id="form-content" <div class="bg-base-200 rounded-lg shadow-xl overflow-hidden p-6">
class="bg-base-200 rounded-lg shadow-xl overflow-hidden" <h2 class="text-2xl font-bold text-white mb-4">
> Your Event Request Submissions
<div class="p-6"> </h2>
<EventRequestForm client:load />
</div> {
</div> error && (
<div class="alert alert-error mb-6">
<!-- Submissions Tab Content --> <svg
<div id="submissions-content" class="hidden"> xmlns="http://www.w3.org/2000/svg"
<div class="bg-base-200 rounded-lg shadow-xl overflow-hidden p-6"> class="h-6 w-6 stroke-current shrink-0"
<h2 class="text-2xl font-bold text-white mb-4"> fill="none"
Your Event Request Submissions viewBox="0 0 24 24"
</h2> >
<path
{ stroke-linecap="round"
error && ( stroke-linejoin="round"
<div class="alert alert-error mb-6"> stroke-width="2"
<svg d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
xmlns="http://www.w3.org/2000/svg" />
class="h-6 w-6 stroke-current shrink-0" </svg>
fill="none" <span>{error}</span>
viewBox="0 0 24 24" </div>
> )
<path }
stroke-linecap="round"
stroke-linejoin="round" {
stroke-width="2" !error && (
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" <UserEventRequests client:load eventRequests={userEventRequests} />
/> )
</svg> }
<span>{error}</span>
</div>
)
}
{
!error && (
<UserEventRequests
client:load
eventRequests={userEventRequests}
/>
)
}
</div>
</div> </div>
</div>
</div> </div>
<style is:global> <style is:global>
/* Ensure the modal container is always visible */ /* Ensure the modal container is always visible */
#event-request-preview-modal-container { #event-request-preview-modal-container {
position: fixed !important; position: fixed !important;
top: 0 !important; top: 0 !important;
left: 0 !important; left: 0 !important;
width: 100vw !important; width: 100vw !important;
height: 100vh !important; height: 100vh !important;
z-index: 99999 !important; z-index: 99999 !important;
overflow: auto !important; overflow: auto !important;
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
} }
/* Style for the modal backdrop */ /* Style for the modal backdrop */
#event-request-preview-modal-container > div > div:first-child { #event-request-preview-modal-container > div > div:first-child {
position: fixed !important; position: fixed !important;
top: 0 !important; top: 0 !important;
left: 0 !important; left: 0 !important;
width: 100vw !important; width: 100vw !important;
height: 100vh !important; height: 100vh !important;
z-index: 99999 !important; z-index: 99999 !important;
background-color: rgba(0, 0, 0, 0.8) !important; background-color: rgba(0, 0, 0, 0.8) !important;
backdrop-filter: blur(8px) !important; backdrop-filter: blur(8px) !important;
display: flex !important; display: flex !important;
align-items: center !important; align-items: center !important;
justify-content: center !important; justify-content: center !important;
overflow: auto !important; overflow: auto !important;
} }
/* Style for the modal content */ /* Style for the modal content */
#event-request-preview-modal-container > div > div > div { #event-request-preview-modal-container > div > div > div {
z-index: 100000 !important; z-index: 100000 !important;
position: relative !important; position: relative !important;
max-width: 90vw !important; max-width: 90vw !important;
width: 100% !important; width: 100% !important;
max-height: 90vh !important; max-height: 90vh !important;
overflow: auto !important; overflow: auto !important;
margin: 2rem !important; margin: 2rem !important;
} }
</style> </style>
<!-- Add the modal component --> <!-- Add the modal component -->
<EventRequestFormPreviewModal client:load /> <EventRequestFormPreviewModal client:load />
<div class="dashboard-section hidden" id="eventRequestFormSection"> <div class="dashboard-section hidden" id="eventRequestFormSection">
<!-- ... existing code ... --> <!-- ... existing code ... -->
</div> </div>
<script is:inline> <script is:inline>
// Define the global function immediately to ensure it's available // Define the global function immediately to ensure it's available
window.showEventRequestFormPreview = function (formData) { window.showEventRequestFormPreview = function (formData) {
console.log( // console.log(
"Global showEventRequestFormPreview called with data", // "Global showEventRequestFormPreview called with data",
formData // formData
); // );
// Remove any elements that might be obstructing the view // Remove any elements that might be obstructing the view
const removeObstructions = () => { const removeObstructions = () => {
// Find any elements with high z-index that might be obstructing // Find any elements with high z-index that might be obstructing
document.querySelectorAll('[style*="z-index"]').forEach((el) => { document.querySelectorAll('[style*="z-index"]').forEach((el) => {
if ( if (
el.id !== "event-request-preview-modal-container" && el.id !== "event-request-preview-modal-container" &&
!el.closest("#event-request-preview-modal-container") !el.closest("#event-request-preview-modal-container")
) { ) {
// Store original z-index to restore later // Store original z-index to restore later
if (!el.dataset.originalZIndex) { if (!el.dataset.originalZIndex) {
el.dataset.originalZIndex = el.style.zIndex; el.dataset.originalZIndex = el.style.zIndex;
} }
// Temporarily lower z-index // Temporarily lower z-index
el.style.zIndex = "0"; el.style.zIndex = "0";
} }
}); });
};
// Create a custom event to trigger the preview
const event = new CustomEvent("showEventRequestPreviewModal", {
detail: { formData },
});
// Remove obstructions before showing modal
removeObstructions();
// Dispatch event to show modal
document.dispatchEvent(event);
console.log("showEventRequestPreviewModal event dispatched");
// Ensure modal container is visible
setTimeout(() => {
const modalContainer = document.getElementById(
"event-request-preview-modal-container"
);
if (modalContainer) {
modalContainer.style.zIndex = "99999";
modalContainer.style.position = "fixed";
modalContainer.style.top = "0";
modalContainer.style.left = "0";
modalContainer.style.width = "100vw";
modalContainer.style.height = "100vh";
modalContainer.style.overflow = "auto";
modalContainer.style.margin = "0";
modalContainer.style.padding = "0";
// Force body to allow scrolling
document.body.style.overflow = "auto";
// Ensure the modal content is properly sized
const modalContent =
modalContainer.querySelector("div > div > div");
if (modalContent) {
modalContent.style.maxWidth = "90vw";
modalContent.style.width = "100%";
modalContent.style.maxHeight = "90vh";
modalContent.style.overflow = "auto";
modalContent.style.margin = "2rem";
}
}
}, 100);
}; };
// Create a custom event to trigger the preview
const event = new CustomEvent("showEventRequestPreviewModal", {
detail: { formData },
});
// Remove obstructions before showing modal
removeObstructions();
// Dispatch event to show modal
document.dispatchEvent(event);
// console.log("showEventRequestPreviewModal event dispatched");
// Ensure modal container is visible
setTimeout(() => {
const modalContainer = document.getElementById(
"event-request-preview-modal-container",
);
if (modalContainer) {
modalContainer.style.zIndex = "99999";
modalContainer.style.position = "fixed";
modalContainer.style.top = "0";
modalContainer.style.left = "0";
modalContainer.style.width = "100vw";
modalContainer.style.height = "100vh";
modalContainer.style.overflow = "auto";
modalContainer.style.margin = "0";
modalContainer.style.padding = "0";
// Force body to allow scrolling
document.body.style.overflow = "auto";
// Ensure the modal content is properly sized
const modalContent = modalContainer.querySelector("div > div > div");
if (modalContent) {
modalContent.style.maxWidth = "90vw";
modalContent.style.width = "100%";
modalContent.style.maxHeight = "90vh";
modalContent.style.overflow = "auto";
modalContent.style.margin = "2rem";
}
}
}, 100);
};
</script> </script>
<script> <script>
// Import the DataSyncService for client-side use // Import the DataSyncService for client-side use
import { DataSyncService } from "../../scripts/database/DataSyncService"; import { DataSyncService } from "../../scripts/database/DataSyncService";
import { Collections } from "../../schemas/pocketbase/schema"; import { Collections } from "../../schemas/pocketbase/schema";
import { Authentication } from "../../scripts/pocketbase/Authentication"; import { Authentication } from "../../scripts/pocketbase/Authentication";
// Tab switching logic // Tab switching logic
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
// Initialize DataSyncService for client-side // Initialize DataSyncService for client-side
const dataSync = DataSyncService.getInstance(); const dataSync = DataSyncService.getInstance();
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
// Prefetch data into IndexedDB if authenticated // Prefetch data into IndexedDB if authenticated
if (auth.isAuthenticated()) { if (auth.isAuthenticated()) {
try { try {
const userId = auth.getUserId(); const userId = auth.getUserId();
if (userId) { if (userId) {
// Force sync to ensure we have the latest data // Force sync to ensure we have the latest data
await dataSync.syncCollection( await dataSync.syncCollection(
Collections.EVENT_REQUESTS, Collections.EVENT_REQUESTS,
`requested_user="${userId}"`, `requested_user="${userId}"`,
"-created" "-created",
); );
console.log( // console.log("Initial data sync complete for user event requests");
"Initial data sync complete for user event requests"
);
}
} catch (err) {
console.error("Error during initial data sync:", err);
}
} }
} catch (err) {
// console.error("Error during initial data sync:", err);
}
}
const formTab = document.getElementById("form-tab"); const formTab = document.getElementById("form-tab");
const submissionsTab = document.getElementById("submissions-tab"); const submissionsTab = document.getElementById("submissions-tab");
const formContent = document.getElementById("form-content"); const formContent = document.getElementById("form-content");
const submissionsContent = document.getElementById( const submissionsContent = document.getElementById("submissions-content");
"submissions-content"
);
// Function to switch tabs // Function to switch tabs
const switchTab = ( const switchTab = (
activeTab: HTMLElement, activeTab: HTMLElement,
activeContent: HTMLElement, activeContent: HTMLElement,
inactiveTab: HTMLElement, inactiveTab: HTMLElement,
inactiveContent: HTMLElement inactiveContent: HTMLElement,
) => { ) => {
// Update tab classes // Update tab classes
activeTab.classList.add("tab-active"); activeTab.classList.add("tab-active");
inactiveTab.classList.remove("tab-active"); inactiveTab.classList.remove("tab-active");
// Show/hide content // Show/hide content
activeContent.classList.remove("hidden"); activeContent.classList.remove("hidden");
inactiveContent.classList.add("hidden"); inactiveContent.classList.add("hidden");
// Dispatch event to refresh submissions when switching to submissions tab // Dispatch event to refresh submissions when switching to submissions tab
if (activeTab.id === "submissions-tab") { if (activeTab.id === "submissions-tab") {
// Dispatch a custom event that the UserEventRequests component listens for // Dispatch a custom event that the UserEventRequests component listens for
document.dispatchEvent(new CustomEvent("dashboardTabVisible")); document.dispatchEvent(new CustomEvent("dashboardTabVisible"));
} }
}; };
// Add click event listeners to tabs // Add click event listeners to tabs
formTab?.addEventListener("click", (e) => { formTab?.addEventListener("click", (e) => {
e.preventDefault(); e.preventDefault();
if (formContent && submissionsContent && submissionsTab) { if (formContent && submissionsContent && submissionsTab) {
switchTab( switchTab(formTab, formContent, submissionsTab, submissionsContent);
formTab, }
formContent,
submissionsTab,
submissionsContent
);
}
});
submissionsTab?.addEventListener("click", (e) => {
e.preventDefault();
if (formContent && submissionsContent && formTab) {
switchTab(
submissionsTab,
submissionsContent,
formTab,
formContent
);
}
});
// Listen for visibility changes
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
// Dispatch custom event that components can listen for
document.dispatchEvent(new CustomEvent("dashboardTabVisible"));
}
});
}); });
submissionsTab?.addEventListener("click", (e) => {
e.preventDefault();
if (formContent && submissionsContent && formTab) {
switchTab(submissionsTab, submissionsContent, formTab, formContent);
}
});
// Listen for visibility changes
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
// Dispatch custom event that components can listen for
document.dispatchEvent(new CustomEvent("dashboardTabVisible"));
}
});
});
</script> </script>

View file

@ -12,7 +12,7 @@ export const EventRequestFormPreviewModal: React.FC = () => {
// Function to handle showing the modal // Function to handle showing the modal
const showModal = (data: any) => { const showModal = (data: any) => {
console.log('showModal called with data', data); // console.log('showModal called with data', data);
setFormData(data); setFormData(data);
setIsOpen(true); setIsOpen(true);
}; };
@ -24,23 +24,23 @@ export const EventRequestFormPreviewModal: React.FC = () => {
// Define the global function // Define the global function
window.showEventRequestFormPreview = (data: any) => { window.showEventRequestFormPreview = (data: any) => {
console.log('Global showEventRequestFormPreview called with data', data); // console.log('Global showEventRequestFormPreview called with data', data);
showModal(data); showModal(data);
}; };
// Listen for the custom event as a fallback // Listen for the custom event as a fallback
const handleShowModal = (event: CustomEvent) => { const handleShowModal = (event: CustomEvent) => {
console.log('Received showEventRequestPreviewModal event', event.detail); // console.log('Received showEventRequestPreviewModal event', event.detail);
if (event.detail && event.detail.formData) { if (event.detail && event.detail.formData) {
showModal(event.detail.formData); showModal(event.detail.formData);
} else { } else {
console.error('Event detail or formData is missing', event.detail); // console.error('Event detail or formData is missing', event.detail);
} }
}; };
// Add event listener // Add event listener
document.addEventListener('showEventRequestPreviewModal', handleShowModal as EventListener); document.addEventListener('showEventRequestPreviewModal', handleShowModal as EventListener);
console.log('Event listener for showEventRequestPreviewModal added'); // console.log('Event listener for showEventRequestPreviewModal added');
// Clean up // Clean up
return () => { return () => {
@ -53,12 +53,12 @@ export const EventRequestFormPreviewModal: React.FC = () => {
} }
document.removeEventListener('showEventRequestPreviewModal', handleShowModal as EventListener); document.removeEventListener('showEventRequestPreviewModal', handleShowModal as EventListener);
console.log('Event listener for showEventRequestPreviewModal removed'); // console.log('Event listener for showEventRequestPreviewModal removed');
}; };
}, []); // Empty dependency array - only run once on mount }, []); // Empty dependency array - only run once on mount
const handleClose = () => { const handleClose = () => {
console.log('Modal closed'); // console.log('Modal closed');
setIsOpen(false); setIsOpen(false);
}; };
@ -122,7 +122,7 @@ const EventRequestFormPreview: React.FC<EventRequestFormPreviewProps> = ({
const parsedData = JSON.parse(savedData); const parsedData = JSON.parse(savedData);
setFormData(parsedData); setFormData(parsedData);
} catch (e) { } catch (e) {
console.error('Error parsing saved form data:', e); // console.error('Error parsing saved form data:', e);
} }
} }
setLoading(false); setLoading(false);

View file

@ -69,7 +69,7 @@ const UserEventRequests: React.FC<UserEventRequestsProps> = ({ eventRequests: in
// Listen for tab visibility changes and refresh data when tab becomes visible // Listen for tab visibility changes and refresh data when tab becomes visible
useEffect(() => { useEffect(() => {
const handleTabVisible = () => { const handleTabVisible = () => {
console.log("Tab became visible, refreshing event requests..."); // console.log("Tab became visible, refreshing event requests...");
refreshEventRequests(); refreshEventRequests();
}; };
@ -411,19 +411,19 @@ const UserEventRequests: React.FC<UserEventRequestsProps> = ({ eventRequests: in
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
console.log('Full Preview button clicked', selectedRequest); // console.log('Full Preview button clicked', selectedRequest);
try { try {
// Direct call to the global function // Direct call to the global function
if (typeof window.showEventRequestFormPreview === 'function') { if (typeof window.showEventRequestFormPreview === 'function') {
window.showEventRequestFormPreview(selectedRequest); window.showEventRequestFormPreview(selectedRequest);
} else { } else {
console.error('showEventRequestFormPreview is not a function', window.showEventRequestFormPreview); // console.log('Fallback: showEventRequestPreviewModal event dispatched');
// Fallback to event dispatch if function is not available // Fallback to event dispatch if function is not available
const event = new CustomEvent("showEventRequestPreviewModal", { const event = new CustomEvent("showEventRequestPreviewModal", {
detail: { formData: selectedRequest } detail: { formData: selectedRequest }
}); });
document.dispatchEvent(event); document.dispatchEvent(event);
console.log('Fallback: showEventRequestPreviewModal event dispatched'); // console.log('Fallback: showEventRequestPreviewModal event dispatched');
} }
} catch (error) { } catch (error) {
console.error('Error showing full preview:', error); console.error('Error showing full preview:', error);

View file

@ -37,7 +37,7 @@ try {
// Don't check authentication here - let the client component handle it // Don't check authentication here - let the client component handle it
// The server-side check is causing issues when the token is valid client-side but not server-side // The server-side check is causing issues when the token is valid client-side but not server-side
console.log("Fetching event requests in Astro component..."); // console.log("Fetching event requests in Astro component...");
// Expand the requested_user field to get user details // Expand the requested_user field to get user details
allEventRequests = await get allEventRequests = await get
.getAll<ExtendedEventRequest>(Collections.EVENT_REQUESTS, "", "-created", { .getAll<ExtendedEventRequest>(Collections.EVENT_REQUESTS, "", "-created", {
@ -49,9 +49,9 @@ try {
return []; return [];
}); });
console.log( // console.log(
`Fetched ${allEventRequests.length} event requests in Astro component`, // `Fetched ${allEventRequests.length} event requests in Astro component`,
); // );
// Process the event requests to add the requested_user_expand property // Process the event requests to add the requested_user_expand property
allEventRequests = allEventRequests.map((request) => { allEventRequests = allEventRequests.map((request) => {
@ -172,9 +172,9 @@ try {
"-created", "-created",
{ expand: "requested_user" }, { expand: "requested_user" },
); );
console.log("Initial data sync complete"); // console.log("Initial data sync complete");
} catch (err) { } catch (err) {
console.error("Error during initial data sync:", err); // console.error("Error during initial data sync:", err);
} }
// Check for error message in the UI // Check for error message in the UI
@ -183,9 +183,9 @@ try {
errorElement && errorElement &&
errorElement.textContent?.includes("Authentication error") errorElement.textContent?.includes("Authentication error")
) { ) {
console.log( // console.log(
"Authentication error detected in UI, redirecting to login...", // "Authentication error detected in UI, redirecting to login...",
); // );
// Redirect to login page after a short delay // Redirect to login page after a short delay
setTimeout(() => { setTimeout(() => {
window.location.href = "/login"; window.location.href = "/login";
@ -196,13 +196,13 @@ try {
// Also check if we have any event requests // Also check if we have any event requests
const tableContainer = document.querySelector(".event-table-container"); const tableContainer = document.querySelector(".event-table-container");
if (tableContainer) { if (tableContainer) {
console.log( // console.log(
"Event table container found, component should load normally", // "Event table container found, component should load normally",
); // );
} else { } else {
console.log( // console.log(
"Event table container not found, might be an issue with rendering", // "Event table container not found, might be an issue with rendering",
); // );
} }
}); });
</script> </script>

View file

@ -56,7 +56,7 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Don't check authentication here - try to fetch anyway // Don't check authentication here - try to fetch anyway
// The token might be valid for the API even if isAuthenticated() returns false // The token might be valid for the API even if isAuthenticated() returns false
console.log("Fetching event requests..."); // console.log("Fetching event requests...");
// Use DataSyncService to get data from IndexedDB with forced sync // Use DataSyncService to get data from IndexedDB with forced sync
const updatedRequests = await dataSync.getData<ExtendedEventRequest>( const updatedRequests = await dataSync.getData<ExtendedEventRequest>(
@ -67,12 +67,12 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
'requested_user' 'requested_user'
); );
console.log(`Fetched ${updatedRequests.length} event requests`); // console.log(`Fetched ${updatedRequests.length} event requests`);
setEventRequests(updatedRequests); setEventRequests(updatedRequests);
applyFilters(updatedRequests); applyFilters(updatedRequests);
} catch (error) { } catch (error) {
console.error('Error refreshing event requests:', error); // console.error('Error refreshing event requests:', error);
toast.error('Failed to refresh event requests'); toast.error('Failed to refresh event requests');
} finally { } finally {
setIsRefreshing(false); setIsRefreshing(false);
@ -169,7 +169,7 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
const eventRequest = eventRequests.find(req => req.id === id); const eventRequest = eventRequests.find(req => req.id === id);
const eventName = eventRequest?.name || 'Event'; const eventName = eventRequest?.name || 'Event';
console.error('Error updating status:', error); // console.error('Error updating status:', error);
toast.error(`Failed to update status for "${eventName}"`); toast.error(`Failed to update status for "${eventName}"`);
throw error; // Re-throw the error to be caught by the caller throw error; // Re-throw the error to be caught by the caller
} }
@ -242,7 +242,7 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Toast is now shown in updateEventRequestStatus // Toast is now shown in updateEventRequestStatus
closeUpdateModal(); closeUpdateModal();
} catch (error) { } catch (error) {
console.error('Error in handleUpdateStatus:', error); // console.error('Error in handleUpdateStatus:', error);
// Toast is now shown in updateEventRequestStatus // Toast is now shown in updateEventRequestStatus
// Keep modal open so user can try again // Keep modal open so user can try again
} }
@ -273,14 +273,14 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Check if we're authenticated // Check if we're authenticated
if (!auth.isAuthenticated()) { if (!auth.isAuthenticated()) {
console.log("Authentication check failed - attempting to continue anyway"); // console.log("Authentication check failed - attempting to continue anyway");
// Don't show error or redirect immediately - try to refresh first // Don't show error or redirect immediately - try to refresh first
try { try {
// Try to refresh event requests anyway - the token might be valid // Try to refresh event requests anyway - the token might be valid
await refreshEventRequests(); await refreshEventRequests();
} catch (err) { } catch (err) {
console.error("Failed to refresh after auth check:", err); // console.error("Failed to refresh after auth check:", err);
toast.error("Authentication error. Please log in again."); toast.error("Authentication error. Please log in again.");
// Only redirect if refresh fails // Only redirect if refresh fails
@ -289,7 +289,7 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
}, 2000); }, 2000);
} }
} else { } else {
console.log("Authentication check passed"); // console.log("Authentication check passed");
} }
}; };
@ -304,7 +304,7 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Listen for tab visibility changes and refresh data when tab becomes visible // Listen for tab visibility changes and refresh data when tab becomes visible
useEffect(() => { useEffect(() => {
const handleTabVisible = () => { const handleTabVisible = () => {
console.log("Tab became visible, refreshing event requests..."); // console.log("Tab became visible, refreshing event requests...");
refreshEventRequests(); refreshEventRequests();
}; };

View file

@ -49,7 +49,7 @@ export default function ShowProfileLogs() {
try { try {
setIsFetchingAll(true); setIsFetchingAll(true);
console.log("Fetching logs for user:", userId); // console.log("Fetching logs for user:", userId);
// Use DataSyncService to fetch logs // Use DataSyncService to fetch logs
const dataSync = DataSyncService.getInstance(); const dataSync = DataSyncService.getInstance();
@ -70,15 +70,15 @@ export default function ShowProfileLogs() {
"-created" "-created"
); );
console.log("Fetched logs:", fetchedLogs.length); // console.log("Fetched logs:", fetchedLogs.length);
if (fetchedLogs.length === 0) { if (fetchedLogs.length === 0) {
// If no logs found, try to fetch directly from PocketBase // If no logs found, try to fetch directly from PocketBase
console.log("No logs found in IndexedDB, trying direct fetch from PocketBase"); // console.log("No logs found in IndexedDB, trying direct fetch from PocketBase");
try { try {
const sendLog = SendLog.getInstance(); const sendLog = SendLog.getInstance();
const directLogs = await sendLog.getUserLogs(userId); const directLogs = await sendLog.getUserLogs(userId);
console.log("Direct fetch logs:", directLogs.length); // console.log("Direct fetch logs:", directLogs.length);
if (directLogs.length > 0) { if (directLogs.length > 0) {
setAllLogs(directLogs); setAllLogs(directLogs);
@ -90,7 +90,7 @@ export default function ShowProfileLogs() {
setTotalLogs(fetchedLogs.length); setTotalLogs(fetchedLogs.length);
} }
} catch (directError) { } catch (directError) {
console.error("Failed to fetch logs directly:", directError); // console.error("Failed to fetch logs directly:", directError);
setAllLogs(fetchedLogs); setAllLogs(fetchedLogs);
setTotalPages(Math.ceil(fetchedLogs.length / LOGS_PER_PAGE)); setTotalPages(Math.ceil(fetchedLogs.length / LOGS_PER_PAGE));
setTotalLogs(fetchedLogs.length); setTotalLogs(fetchedLogs.length);
@ -101,7 +101,7 @@ export default function ShowProfileLogs() {
setTotalLogs(fetchedLogs.length); setTotalLogs(fetchedLogs.length);
} }
} catch (error) { } catch (error) {
console.error("Failed to fetch logs:", error); // console.error("Failed to fetch logs:", error);
setError("Error loading activity"); setError("Error loading activity");
} finally { } finally {
setLoading(false); setLoading(false);
@ -134,7 +134,7 @@ export default function ShowProfileLogs() {
// Update displayed logs whenever filtered results change // Update displayed logs whenever filtered results change
useEffect(() => { useEffect(() => {
setLogs(filteredLogs); setLogs(filteredLogs);
console.log("Filtered logs updated:", filteredLogs.length, "logs"); // console.log("Filtered logs updated:", filteredLogs.length, "logs");
}, [filteredLogs]); }, [filteredLogs]);
// Debounced search handler // Debounced search handler
@ -178,12 +178,12 @@ export default function ShowProfileLogs() {
setTimeout(async () => { setTimeout(async () => {
// Check if logs were loaded // Check if logs were loaded
if (allLogs.length === 0) { if (allLogs.length === 0) {
console.log("No logs found after initial fetch, trying direct fetch"); // console.log("No logs found after initial fetch, trying direct fetch");
await directFetchLogs(); await directFetchLogs();
} }
}, 1000); }, 1000);
} catch (error) { } catch (error) {
console.error("Failed to load logs with retry:", error); // console.error("Failed to load logs with retry:", error);
} }
}; };
@ -200,14 +200,14 @@ export default function ShowProfileLogs() {
// Check if the logs collection exists and has any records // Check if the logs collection exists and has any records
const result = await pb.collection(Collections.LOGS).getList(1, 1); const result = await pb.collection(Collections.LOGS).getList(1, 1);
console.log("Logs collection check:", { // console.log("Logs collection check:", {
totalItems: result.totalItems, // totalItems: result.totalItems,
page: result.page, // page: result.page,
perPage: result.perPage, // perPage: result.perPage,
totalPages: result.totalPages // totalPages: result.totalPages
}); // });
} catch (error) { } catch (error) {
console.error("Failed to check logs collection:", error); // console.error("Failed to check logs collection:", error);
} }
}; };
@ -255,7 +255,7 @@ export default function ShowProfileLogs() {
return; return;
} }
console.log("Direct fetching logs for user:", userId); // console.log("Direct fetching logs for user:", userId);
// Fetch logs directly from PocketBase // Fetch logs directly from PocketBase
const result = await pb.collection(Collections.LOGS).getList<Log>(1, 100, { const result = await pb.collection(Collections.LOGS).getList<Log>(1, 100, {
@ -264,10 +264,10 @@ export default function ShowProfileLogs() {
expand: "user" expand: "user"
}); });
console.log("Direct fetch result:", { // console.log("Direct fetch result:", {
totalItems: result.totalItems, // totalItems: result.totalItems,
items: result.items.length // items: result.items.length
}); // });
if (result.items.length > 0) { if (result.items.length > 0) {
setAllLogs(result.items); setAllLogs(result.items);
@ -275,7 +275,7 @@ export default function ShowProfileLogs() {
setTotalLogs(result.items.length); setTotalLogs(result.items.length);
} }
} catch (error) { } catch (error) {
console.error("Failed to direct fetch logs:", error); // console.error("Failed to direct fetch logs:", error);
setError("Error loading activity"); setError("Error loading activity");
} finally { } finally {
setLoading(false); setLoading(false);
@ -315,13 +315,13 @@ export default function ShowProfileLogs() {
} }
// Debug logs // Debug logs
console.log("Render state:", { // console.log("Render state:", {
logsLength: logs.length, // logsLength: logs.length,
allLogsLength: allLogs.length, // allLogsLength: allLogs.length,
searchQuery, // searchQuery,
loading, // loading,
currentPage // currentPage
}); // });
if (allLogs.length === 0 && !searchQuery && !loading) { if (allLogs.length === 0 && !searchQuery && !loading) {
return ( return (
@ -348,10 +348,10 @@ export default function ShowProfileLogs() {
"Test log created for debugging", "Test log created for debugging",
userId userId
); );
console.log("Created test log"); // console.log("Created test log");
setTimeout(() => fetchLogs(true), 1000); setTimeout(() => fetchLogs(true), 1000);
} catch (error) { } catch (error) {
console.error("Failed to create test log:", error); // console.error("Failed to create test log:", error);
} }
}} }}
> >

View file

@ -13,14 +13,6 @@ const logtoEndpoint = import.meta.env.LOGTO_ENDPOINT;
const logtoTokenEndpoint = import.meta.env.LOGTO_TOKEN_ENDPOINT; const logtoTokenEndpoint = import.meta.env.LOGTO_TOKEN_ENDPOINT;
const logtoApiEndpoint = import.meta.env.LOGTO_API_ENDPOINT; const logtoApiEndpoint = import.meta.env.LOGTO_API_ENDPOINT;
// Log environment variables for debugging
console.log("Environment variables in Astro file:");
console.log("LOGTO_APP_ID:", logtoAppId);
console.log("LOGTO_APP_SECRET:", logtoAppSecret);
console.log("LOGTO_ENDPOINT:", logtoEndpoint);
console.log("LOGTO_TOKEN_ENDPOINT:", logtoTokenEndpoint);
console.log("LOGTO_API_ENDPOINT:", logtoApiEndpoint);
// Define fallback values if environment variables are not set // Define fallback values if environment variables are not set
const safeLogtoAppId = logtoAppId || "missing_app_id"; const safeLogtoAppId = logtoAppId || "missing_app_id";
const safeLogtoAppSecret = logtoAppSecret || "missing_app_secret"; const safeLogtoAppSecret = logtoAppSecret || "missing_app_secret";

View file

@ -162,7 +162,7 @@ export default function DisplaySettings() {
if (Object.keys(updateData).length > 0) { if (Object.keys(updateData).length > 0) {
await update.updateFields(Collections.USERS, userId, updateData); await update.updateFields(Collections.USERS, userId, updateData);
console.log('Initialized default display and accessibility settings'); // console.log('Initialized default display and accessibility settings');
} }
} catch (error) { } catch (error) {
console.error('Error initializing default settings:', error); console.error('Error initializing default settings:', error);

View file

@ -67,7 +67,7 @@ export default function NotificationSettings() {
{ notification_preferences: JSON.stringify(DEFAULT_NOTIFICATION_PREFERENCES) } { notification_preferences: JSON.stringify(DEFAULT_NOTIFICATION_PREFERENCES) }
); );
setPreferences(DEFAULT_NOTIFICATION_PREFERENCES); setPreferences(DEFAULT_NOTIFICATION_PREFERENCES);
console.log('Initialized default notification preferences'); // console.log('Initialized default notification preferences');
} catch (error) { } catch (error) {
console.error('Error initializing default notification preferences:', error); console.error('Error initializing default notification preferences:', error);
} }

View file

@ -45,29 +45,7 @@ export default function PasswordChangeSettings({
const logtoTokenEndpoint = envLogtoTokenEndpoint || propLogtoTokenEndpoint; const logtoTokenEndpoint = envLogtoTokenEndpoint || propLogtoTokenEndpoint;
const logtoApiEndpoint = envLogtoApiEndpoint || propLogtoApiEndpoint; const logtoApiEndpoint = envLogtoApiEndpoint || propLogtoApiEndpoint;
// Log props and environment variables for debugging
useEffect(() => {
console.log("PasswordChangeSettings props and env vars:");
console.log("Props - logtoAppId:", propLogtoAppId);
console.log("Props - logtoAppSecret:", propLogtoAppSecret);
console.log("Props - logtoEndpoint:", propLogtoEndpoint);
console.log("Props - logtoTokenEndpoint:", propLogtoTokenEndpoint);
console.log("Props - logtoApiEndpoint:", propLogtoApiEndpoint);
console.log("Env - LOGTO_APP_ID:", envLogtoAppId);
console.log("Env - LOGTO_APP_SECRET:", envLogtoAppSecret);
console.log("Env - LOGTO_ENDPOINT:", envLogtoEndpoint);
console.log("Env - LOGTO_TOKEN_ENDPOINT:", envLogtoTokenEndpoint);
console.log("Env - LOGTO_API_ENDPOINT:", envLogtoApiEndpoint);
console.log("Using - logtoAppId:", logtoAppId);
console.log("Using - logtoAppSecret:", logtoAppSecret);
console.log("Using - logtoEndpoint:", logtoEndpoint);
console.log("Using - logtoTokenEndpoint:", logtoTokenEndpoint);
console.log("Using - logtoApiEndpoint:", logtoApiEndpoint);
}, [
propLogtoAppId, propLogtoAppSecret, propLogtoEndpoint, propLogtoTokenEndpoint, propLogtoApiEndpoint,
envLogtoAppId, envLogtoAppSecret, envLogtoEndpoint, envLogtoTokenEndpoint, envLogtoApiEndpoint,
logtoAppId, logtoAppSecret, logtoEndpoint, logtoTokenEndpoint, logtoApiEndpoint
]);
// Get the user's Logto ID on component mount // Get the user's Logto ID on component mount
useEffect(() => { useEffect(() => {
@ -80,17 +58,17 @@ export default function PasswordChangeSettings({
return; return;
} }
console.log("Current user:", user); // console.log("Current user:", user);
const pb = auth.getPocketBase(); const pb = auth.getPocketBase();
try { try {
const externalAuthRecord = await pb.collection('_externalAuths').getFirstListItem(`recordRef="${user.id}" && provider="oidc"`); const externalAuthRecord = await pb.collection('_externalAuths').getFirstListItem(`recordRef="${user.id}" && provider="oidc"`);
console.log("Found external auth record:", externalAuthRecord); // console.log("Found external auth record:", externalAuthRecord);
const userId = externalAuthRecord.providerId; const userId = externalAuthRecord.providerId;
if (userId) { if (userId) {
setLogtoUserId(userId); setLogtoUserId(userId);
console.log("Set Logto user ID:", userId); // console.log("Set Logto user ID:", userId);
} else { } else {
console.error("No providerId found in external auth record"); console.error("No providerId found in external auth record");
toast.error("Could not determine your user ID. Please try again later or contact support."); toast.error("Could not determine your user ID. Please try again later or contact support.");
@ -153,7 +131,7 @@ export default function PasswordChangeSettings({
try { try {
const response = await fetch('/api/check-env'); const response = await fetch('/api/check-env');
const data = await response.json(); const data = await response.json();
console.log("Environment variables status:", data); // console.log("Environment variables status:", data);
// Check if all required environment variables are set // Check if all required environment variables are set
const { envStatus } = data; const { envStatus } = data;
@ -244,7 +222,7 @@ export default function PasswordChangeSettings({
const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document; const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
if (iframeDocument) { if (iframeDocument) {
const responseText = iframeDocument.body.innerText; const responseText = iframeDocument.body.innerText;
console.log("Response from iframe:", responseText); // console.log("Response from iframe:", responseText);
if (responseText) { if (responseText) {
try { try {
@ -308,7 +286,7 @@ export default function PasswordChangeSettings({
} else { } else {
// Use the fetch API with JSON // Use the fetch API with JSON
const endpoint = '/api/change-password'; const endpoint = '/api/change-password';
console.log(`Calling server-side API endpoint: ${endpoint}`); // console.log(`Calling server-side API endpoint: ${endpoint}`);
// Ensure we have the Logto user ID // Ensure we have the Logto user ID
if (!logtoUserId) { if (!logtoUserId) {
@ -317,13 +295,13 @@ export default function PasswordChangeSettings({
} }
// Log the values we're about to use // Log the values we're about to use
console.log("Values being used for API call:"); // console.log("Values being used for API call:");
console.log("- logtoUserId:", logtoUserId); // console.log("- logtoUserId:", logtoUserId);
console.log("- newPassword:", formData.newPassword ? "[PRESENT]" : "[MISSING]"); // console.log("- newPassword:", formData.newPassword ? "[PRESENT]" : "[MISSING]");
console.log("- logtoAppId:", logtoAppId); // console.log("- logtoAppId:", logtoAppId);
console.log("- logtoAppSecret:", logtoAppSecret ? "[PRESENT]" : "[MISSING]"); // console.log("- logtoAppSecret:", logtoAppSecret ? "[PRESENT]" : "[MISSING]");
console.log("- logtoTokenEndpoint:", logtoTokenEndpoint); // console.log("- logtoTokenEndpoint:", logtoTokenEndpoint);
console.log("- logtoApiEndpoint:", logtoApiEndpoint); // console.log("- logtoApiEndpoint:", logtoApiEndpoint);
// Prepare request data with explicit values (not relying on variable references that might be undefined) // Prepare request data with explicit values (not relying on variable references that might be undefined)
const requestData = { const requestData = {
@ -336,12 +314,12 @@ export default function PasswordChangeSettings({
logtoApiEndpoint: logtoApiEndpoint || logtoEndpoint logtoApiEndpoint: logtoApiEndpoint || logtoEndpoint
}; };
console.log("Request data:", { // console.log("Request data:", {
...requestData, // ...requestData,
currentPassword: "[REDACTED]", // currentPassword: "[REDACTED]",
newPassword: "[REDACTED]", // newPassword: "[REDACTED]",
logtoAppSecret: "[REDACTED]" // logtoAppSecret: "[REDACTED]"
}); // });
// Validate request data before sending // Validate request data before sending
if (!requestData.userId) { if (!requestData.userId) {
@ -368,7 +346,7 @@ export default function PasswordChangeSettings({
// Stringify the request data to ensure it's valid JSON // Stringify the request data to ensure it's valid JSON
const requestBody = JSON.stringify(requestData); const requestBody = JSON.stringify(requestData);
console.log("Request body (stringified):", requestBody); // console.log("Request body (stringified):", requestBody);
// Create a debug object to display in the UI // Create a debug object to display in the UI
const debugObj = { const debugObj = {
@ -394,13 +372,13 @@ export default function PasswordChangeSettings({
body: requestBody body: requestBody
}); });
console.log("Response status:", response.status); // console.log("Response status:", response.status);
// Process the response // Process the response
let result: any; let result: any;
try { try {
const responseText = await response.text(); const responseText = await response.text();
console.log("Raw response:", responseText); // console.log("Raw response:", responseText);
if (responseText) { if (responseText) {
result = JSON.parse(responseText); result = JSON.parse(responseText);
@ -408,7 +386,7 @@ export default function PasswordChangeSettings({
result = { success: false, message: 'Empty response from server' }; result = { success: false, message: 'Empty response from server' };
} }
console.log("API response:", result); // console.log("API response:", result);
// Add response to debug info // Add response to debug info
setDebugInfo((prev: any) => ({ setDebugInfo((prev: any) => ({
@ -470,13 +448,13 @@ export default function PasswordChangeSettings({
type="button" type="button"
className="btn btn-sm btn-warning" className="btn btn-sm btn-warning"
onClick={() => { onClick={() => {
console.log("Debug Info:"); // console.log("Debug Info:");
console.log("- logtoUserId:", logtoUserId); // console.log("- logtoUserId:", logtoUserId);
console.log("- Environment Variables:"); // console.log("- Environment Variables:");
console.log(" - LOGTO_APP_ID:", import.meta.env.LOGTO_APP_ID); // console.log(" - LOGTO_APP_ID:", import.meta.env.LOGTO_APP_ID);
console.log(" - LOGTO_ENDPOINT:", import.meta.env.LOGTO_ENDPOINT); // console.log(" - LOGTO_ENDPOINT:", import.meta.env.LOGTO_ENDPOINT);
console.log(" - LOGTO_TOKEN_ENDPOINT:", import.meta.env.LOGTO_TOKEN_ENDPOINT); // console.log(" - LOGTO_TOKEN_ENDPOINT:", import.meta.env.LOGTO_TOKEN_ENDPOINT);
console.log(" - LOGTO_API_ENDPOINT:", import.meta.env.LOGTO_API_ENDPOINT); // console.log(" - LOGTO_API_ENDPOINT:", import.meta.env.LOGTO_API_ENDPOINT);
toast.success("Debug info logged to console"); toast.success("Debug info logged to console");
}} }}

View file

@ -104,17 +104,17 @@ export default function ResumeSettings() {
const loadingToast = toast.loading('Uploading resume...'); const loadingToast = toast.loading('Uploading resume...');
// Log the file being uploaded for debugging // Log the file being uploaded for debugging
console.log('Uploading file:', { // console.log('Uploading file:', {
name: resumeFile.name, // name: resumeFile.name,
size: resumeFile.size, // size: resumeFile.size,
type: resumeFile.type // type: resumeFile.type
}); // });
let updatedUserData: User; let updatedUserData: User;
try { try {
// Use the FileManager to upload the file directly // Use the FileManager to upload the file directly
console.log('Using FileManager to upload resume file'); // console.log('Using FileManager to upload resume file');
// Upload the file using the FileManager's uploadFile method // Upload the file using the FileManager's uploadFile method
const result = await fileManager.uploadFile<User>( const result = await fileManager.uploadFile<User>(
@ -130,18 +130,18 @@ export default function ResumeSettings() {
throw new Error('Resume was not properly saved to the user record'); throw new Error('Resume was not properly saved to the user record');
} }
console.log('Resume upload successful:', result.resume); // console.log('Resume upload successful:', result.resume);
// Store the updated user data // Store the updated user data
updatedUserData = result; updatedUserData = result;
// Fetch the updated user record to ensure we have the latest data // Fetch the updated user record to ensure we have the latest data
const refreshedUser = await get.getOne<User>(Collections.USERS, user.id); const refreshedUser = await get.getOne<User>(Collections.USERS, user.id);
console.log('Refreshed user data:', refreshedUser); // console.log('Refreshed user data:', refreshedUser);
// Double check that the resume field is populated // Double check that the resume field is populated
if (!refreshedUser.resume) { if (!refreshedUser.resume) {
console.warn('Resume field is missing in the refreshed user data'); // console.warn('Resume field is missing in the refreshed user data');
} }
} catch (uploadError) { } catch (uploadError) {
console.error('Error in file upload process:', uploadError); console.error('Error in file upload process:', uploadError);
@ -173,7 +173,7 @@ export default function ResumeSettings() {
toast.success('Resume uploaded successfully'); toast.success('Resume uploaded successfully');
// Log the successful upload // Log the successful upload
console.log('Resume uploaded successfully:', updatedUserData.resume); // console.log('Resume uploaded successfully:', updatedUserData.resume);
// Dispatch a custom event to notify the dashboard about the resume upload // Dispatch a custom event to notify the dashboard about the resume upload
const event = new CustomEvent('resumeUploaded', { const event = new CustomEvent('resumeUploaded', {
@ -223,14 +223,14 @@ export default function ResumeSettings() {
const loadingToast = toast.loading('Deleting resume...'); const loadingToast = toast.loading('Deleting resume...');
// Log the deletion attempt // Log the deletion attempt
console.log('Attempting to delete resume for user:', user.id); // console.log('Attempting to delete resume for user:', user.id);
// Create a FormData with empty resume field to remove the file // Create a FormData with empty resume field to remove the file
const formData = new FormData(); const formData = new FormData();
formData.append('resume', ''); formData.append('resume', '');
try { try {
console.log('Using FileManager to delete resume file'); // console.log('Using FileManager to delete resume file');
// Use the FileManager's deleteFile method to remove the file // Use the FileManager's deleteFile method to remove the file
const result = await fileManager.deleteFile<User>( const result = await fileManager.deleteFile<User>(
@ -241,22 +241,22 @@ export default function ResumeSettings() {
// Verify the file was deleted // Verify the file was deleted
if (result.resume) { if (result.resume) {
console.warn('Resume field still exists after deletion attempt:', result.resume); // console.warn('Resume field still exists after deletion attempt:', result.resume);
toast.dismiss(loadingToast); toast.dismiss(loadingToast);
toast.error('Failed to completely remove the resume. Please try again.'); toast.error('Failed to completely remove the resume. Please try again.');
setUploading(false); setUploading(false);
return; return;
} }
console.log('Resume deletion successful for user:', user.id); // console.log('Resume deletion successful for user:', user.id);
// Fetch the updated user record to ensure we have the latest data // Fetch the updated user record to ensure we have the latest data
const refreshedUser = await get.getOne<User>(Collections.USERS, user.id); const refreshedUser = await get.getOne<User>(Collections.USERS, user.id);
console.log('Refreshed user data after deletion:', refreshedUser); // console.log('Refreshed user data after deletion:', refreshedUser);
// Double check that the resume field is empty // Double check that the resume field is empty
if (refreshedUser.resume) { if (refreshedUser.resume) {
console.warn('Resume field is still present in the refreshed user data:', refreshedUser.resume); // console.warn('Resume field is still present in the refreshed user data:', refreshedUser.resume);
} }
} catch (deleteError) { } catch (deleteError) {
console.error('Error in file deletion process:', deleteError); console.error('Error in file deletion process:', deleteError);
@ -275,7 +275,7 @@ export default function ResumeSettings() {
toast.success('Resume deleted successfully'); toast.success('Resume deleted successfully');
// Log the successful deletion // Log the successful deletion
console.log('Resume deleted successfully for user:', user.id); // console.log('Resume deleted successfully for user:', user.id);
// Dispatch a custom event to notify the dashboard about the resume deletion // Dispatch a custom event to notify the dashboard about the resume deletion
const event = new CustomEvent('resumeUploaded', { const event = new CustomEvent('resumeUploaded', {
@ -356,11 +356,11 @@ export default function ResumeSettings() {
const loadingToast = toast.loading('Replacing resume...'); const loadingToast = toast.loading('Replacing resume...');
// Log the file being uploaded for debugging // Log the file being uploaded for debugging
console.log('Replacing resume with file:', { // console.log('Replacing resume with file:', {
name: file.name, // name: file.name,
size: file.size, // size: file.size,
type: file.type // type: file.type
}); // });
// Create a FormData object for the file upload // Create a FormData object for the file upload
const formData = new FormData(); const formData = new FormData();

View file

@ -112,27 +112,27 @@ export default function ReimbursementList() {
const fileManager = FileManager.getInstance(); const fileManager = FileManager.getInstance();
useEffect(() => { useEffect(() => {
console.log('Component mounted'); // console.log('Component mounted');
fetchReimbursements(); fetchReimbursements();
}, []); }, []);
// Add effect to monitor requests state // Add effect to monitor requests state
useEffect(() => { useEffect(() => {
console.log('Requests state updated:', requests); // console.log('Requests state updated:', requests);
console.log('Number of requests:', requests.length); // console.log('Number of requests:', requests.length);
}, [requests]); }, [requests]);
// Add a useEffect to log preview URL and filename changes // Add a useEffect to log preview URL and filename changes
useEffect(() => { useEffect(() => {
console.log('Preview URL changed:', previewUrl); // console.log('Preview URL changed:', previewUrl);
console.log('Preview filename changed:', previewFilename); // console.log('Preview filename changed:', previewFilename);
}, [previewUrl, previewFilename]); }, [previewUrl, previewFilename]);
// Add a useEffect to log when the preview modal is shown/hidden // Add a useEffect to log when the preview modal is shown/hidden
useEffect(() => { useEffect(() => {
console.log('Show preview changed:', showPreview); // console.log('Show preview changed:', showPreview);
if (showPreview) { if (showPreview) {
console.log('Selected receipt:', selectedReceipt); // console.log('Selected receipt:', selectedReceipt);
} }
}, [showPreview, selectedReceipt]); }, [showPreview, selectedReceipt]);
@ -167,7 +167,7 @@ export default function ReimbursementList() {
'-created' '-created'
); );
console.log('Reimbursement records from IndexedDB:', reimbursementRecords); // console.log('Reimbursement records from IndexedDB:', reimbursementRecords);
// Process the records // Process the records
const processedRecords = reimbursementRecords.map(record => { const processedRecords = reimbursementRecords.map(record => {
@ -183,7 +183,7 @@ export default function ReimbursementList() {
auditNotes = record.audit_notes; auditNotes = record.audit_notes;
} }
} catch (e) { } catch (e) {
console.error('Error parsing audit notes:', e); // console.error('Error parsing audit notes:', e);
} }
} }
@ -217,7 +217,7 @@ export default function ReimbursementList() {
itemizedExpenses = receiptRecord.itemized_expenses as ItemizedExpense[]; itemizedExpenses = receiptRecord.itemized_expenses as ItemizedExpense[];
} }
} catch (e) { } catch (e) {
console.error('Error parsing itemized expenses:', e); // console.error('Error parsing itemized expenses:', e);
} }
} }
@ -241,13 +241,13 @@ export default function ReimbursementList() {
})); }));
} }
} catch (e) { } catch (e) {
console.error(`Error fetching receipt ${receiptId}:`, e); // console.error(`Error fetching receipt ${receiptId}:`, e);
} }
} }
} }
} }
} catch (err) { } catch (err) {
console.error('Error fetching reimbursements:', err); // console.error('Error fetching reimbursements:', err);
setError('Failed to load reimbursements. Please try again.'); setError('Failed to load reimbursements. Please try again.');
} finally { } finally {
setLoading(false); setLoading(false);
@ -256,7 +256,7 @@ export default function ReimbursementList() {
const handlePreviewFile = async (request: ReimbursementRequest, receiptId: string) => { const handlePreviewFile = async (request: ReimbursementRequest, receiptId: string) => {
try { try {
console.log('Previewing file for receipt ID:', receiptId); // console.log('Previewing file for receipt ID:', receiptId);
const pb = auth.getPocketBase(); const pb = auth.getPocketBase();
const fileManager = FileManager.getInstance(); const fileManager = FileManager.getInstance();
@ -265,13 +265,13 @@ export default function ReimbursementList() {
// Check if we already have the receipt details in our map // Check if we already have the receipt details in our map
if (receiptDetailsMap[receiptId]) { if (receiptDetailsMap[receiptId]) {
console.log('Using cached receipt details'); // console.log('Using cached receipt details');
// Use the cached receipt details // Use the cached receipt details
setSelectedReceipt(receiptDetailsMap[receiptId]); setSelectedReceipt(receiptDetailsMap[receiptId]);
// Check if the receipt has a file // Check if the receipt has a file
if (!receiptDetailsMap[receiptId].file) { if (!receiptDetailsMap[receiptId].file) {
console.error('Receipt has no file attached'); // console.error('Receipt has no file attached');
toast.error('This receipt has no file attached'); toast.error('This receipt has no file attached');
setPreviewUrl(''); setPreviewUrl('');
setPreviewFilename(''); setPreviewFilename('');
@ -280,7 +280,7 @@ export default function ReimbursementList() {
} }
// Get the file URL with token for protected files // Get the file URL with token for protected files
console.log('Getting file URL with token'); // console.log('Getting file URL with token');
const url = await fileManager.getFileUrlWithToken( const url = await fileManager.getFileUrlWithToken(
'receipts', 'receipts',
receiptId, receiptId,
@ -290,7 +290,7 @@ export default function ReimbursementList() {
// Check if the URL is empty // Check if the URL is empty
if (!url) { if (!url) {
console.error('Failed to get file URL: Empty URL returned'); // console.error('Failed to get file URL: Empty URL returned');
toast.error('Failed to load receipt: Could not generate file URL'); toast.error('Failed to load receipt: Could not generate file URL');
// Still show the preview modal but with empty URL to display the error message // Still show the preview modal but with empty URL to display the error message
setPreviewUrl(''); setPreviewUrl('');
@ -299,7 +299,7 @@ export default function ReimbursementList() {
return; return;
} }
console.log('Got URL:', url.substring(0, 50) + '...'); // console.log('Got URL:', url.substring(0, 50) + '...');
// Set the preview URL and filename // Set the preview URL and filename
setPreviewUrl(url); setPreviewUrl(url);
@ -309,28 +309,28 @@ export default function ReimbursementList() {
setShowPreview(true); setShowPreview(true);
// Log the current state // Log the current state
console.log('Current state after setting:', { // console.log('Current state after setting:', {
previewUrl: url, // previewUrl: url,
previewFilename: receiptDetailsMap[receiptId].file, // previewFilename: receiptDetailsMap[receiptId].file,
showPreview: true // showPreview: true
}); // });
return; return;
} }
// If not in the map, get the receipt record using its ID // If not in the map, get the receipt record using its ID
console.log('Fetching receipt details from server'); // console.log('Fetching receipt details from server');
const receiptRecord = await pb.collection('receipts').getOne(receiptId, { const receiptRecord = await pb.collection('receipts').getOne(receiptId, {
$autoCancel: false $autoCancel: false
}); });
if (receiptRecord) { if (receiptRecord) {
console.log('Receipt record found:', receiptRecord.id); // console.log('Receipt record found:', receiptRecord.id);
console.log('Receipt file:', receiptRecord.file); // console.log('Receipt file:', receiptRecord.file);
// Check if the receipt has a file // Check if the receipt has a file
if (!receiptRecord.file) { if (!receiptRecord.file) {
console.error('Receipt has no file attached'); // console.error('Receipt has no file attached');
toast.error('This receipt has no file attached'); toast.error('This receipt has no file attached');
setPreviewUrl(''); setPreviewUrl('');
setPreviewFilename(''); setPreviewFilename('');
@ -367,7 +367,7 @@ export default function ReimbursementList() {
setSelectedReceipt(receiptDetails); setSelectedReceipt(receiptDetails);
// Get the file URL with token for protected files // Get the file URL with token for protected files
console.log('Getting file URL with token for new receipt'); // console.log('Getting file URL with token for new receipt');
const url = await fileManager.getFileUrlWithToken( const url = await fileManager.getFileUrlWithToken(
'receipts', 'receipts',
receiptRecord.id, receiptRecord.id,
@ -377,7 +377,7 @@ export default function ReimbursementList() {
// Check if the URL is empty // Check if the URL is empty
if (!url) { if (!url) {
console.error('Failed to get file URL: Empty URL returned'); // console.error('Failed to get file URL: Empty URL returned');
toast.error('Failed to load receipt: Could not generate file URL'); toast.error('Failed to load receipt: Could not generate file URL');
// Still show the preview modal but with empty URL to display the error message // Still show the preview modal but with empty URL to display the error message
setPreviewUrl(''); setPreviewUrl('');
@ -386,7 +386,7 @@ export default function ReimbursementList() {
return; return;
} }
console.log('Got URL:', url.substring(0, 50) + '...'); // console.log('Got URL:', url.substring(0, 50) + '...');
// Set the preview URL and filename // Set the preview URL and filename
setPreviewUrl(url); setPreviewUrl(url);
@ -396,16 +396,16 @@ export default function ReimbursementList() {
setShowPreview(true); setShowPreview(true);
// Log the current state // Log the current state
console.log('Current state after setting:', { // console.log('Current state after setting:', {
previewUrl: url, // previewUrl: url,
previewFilename: receiptRecord.file, // previewFilename: receiptRecord.file,
showPreview: true // showPreview: true
}); // });
} else { } else {
throw new Error('Receipt not found'); throw new Error('Receipt not found');
} }
} catch (error) { } catch (error) {
console.error('Error loading receipt:', error); // console.error('Error loading receipt:', error);
toast.error('Failed to load receipt. Please try again.'); toast.error('Failed to load receipt. Please try again.');
// Show the preview modal with empty URL to display the error message // Show the preview modal with empty URL to display the error message
setPreviewUrl(''); setPreviewUrl('');
@ -423,7 +423,7 @@ export default function ReimbursementList() {
}; };
if (loading) { if (loading) {
console.log('Rendering loading state'); // console.log('Rendering loading state');
return ( return (
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
@ -437,7 +437,7 @@ export default function ReimbursementList() {
} }
if (error) { if (error) {
console.log('Rendering error state:', error); // console.log('Rendering error state:', error);
return ( return (
<motion.div <motion.div
initial={{ opacity: 0, y: -20 }} initial={{ opacity: 0, y: -20 }}
@ -450,8 +450,8 @@ export default function ReimbursementList() {
); );
} }
console.log('Rendering main component. Requests:', requests); // console.log('Rendering main component. Requests:', requests);
console.log('Requests length:', requests.length); // console.log('Requests length:', requests.length);
return ( return (
<> <>
@ -482,7 +482,7 @@ export default function ReimbursementList() {
> >
<AnimatePresence mode="popLayout"> <AnimatePresence mode="popLayout">
{requests.map((request, index) => { {requests.map((request, index) => {
console.log('Rendering request:', request); // console.log('Rendering request:', request);
return ( return (
<motion.div <motion.div
key={request.id} key={request.id}

View file

@ -134,7 +134,6 @@ export default function ReimbursementManagementPortal() {
} }
const records = await get.getAll<ExtendedReimbursement>('reimbursement', filter, sort); const records = await get.getAll<ExtendedReimbursement>('reimbursement', filter, sort);
console.log('Loaded reimbursements:', records);
// Load user data for submitters // Load user data for submitters
const userIds = new Set(records.map(r => r.submitted_by)); const userIds = new Set(records.map(r => r.submitted_by));
@ -165,11 +164,9 @@ export default function ReimbursementManagementPortal() {
// Load associated receipts // Load associated receipts
const receiptIds = enrichedRecords.flatMap(r => r.receipts || []); const receiptIds = enrichedRecords.flatMap(r => r.receipts || []);
console.log('Extracted receipt IDs:', receiptIds, 'from reimbursements:', enrichedRecords.map(r => ({ id: r.id, receipts: r.receipts })));
if (receiptIds.length > 0) { if (receiptIds.length > 0) {
try { try {
console.log('Attempting to load receipts with IDs:', receiptIds);
const receiptRecords = await Promise.all( const receiptRecords = await Promise.all(
receiptIds.map(async id => { receiptIds.map(async id => {
try { try {
@ -202,12 +199,10 @@ export default function ReimbursementManagementPortal() {
); );
const validReceipts = receiptRecords.filter((r): r is ExtendedReceipt => r !== null); const validReceipts = receiptRecords.filter((r): r is ExtendedReceipt => r !== null);
console.log('Successfully loaded receipt records:', validReceipts);
const receiptMap = Object.fromEntries( const receiptMap = Object.fromEntries(
validReceipts.map(receipt => [receipt.id, receipt]) validReceipts.map(receipt => [receipt.id, receipt])
); );
console.log('Created receipt map:', receiptMap);
setReceipts(receiptMap); setReceipts(receiptMap);
} catch (error: any) { } catch (error: any) {
console.error('Error loading receipts:', error); console.error('Error loading receipts:', error);
@ -219,7 +214,7 @@ export default function ReimbursementManagementPortal() {
toast.error('Failed to load receipts: ' + (error?.message || 'Unknown error')); toast.error('Failed to load receipts: ' + (error?.message || 'Unknown error'));
} }
} else { } else {
console.log('No receipt IDs found in reimbursements'); // console.log('No receipt IDs found in reimbursements');
setReceipts({}); setReceipts({});
} }
} catch (error) { } catch (error) {

View file

@ -29,7 +29,7 @@ const ImageWithFallback = ({ url, filename, onError }: ImageWithFallbackProps) =
try { try {
// Try to fetch the image as a blob and create an object URL // Try to fetch the image as a blob and create an object URL
console.log('Trying to fetch image as blob:', url); // console.log('Trying to fetch image as blob:', url);
const response = await fetch(url, { mode: 'cors' }); const response = await fetch(url, { mode: 'cors' });
if (!response.ok) { if (!response.ok) {
@ -38,7 +38,7 @@ const ImageWithFallback = ({ url, filename, onError }: ImageWithFallbackProps) =
const blob = await response.blob(); const blob = await response.blob();
const objectUrl = URL.createObjectURL(blob); const objectUrl = URL.createObjectURL(blob);
console.log('Created object URL:', objectUrl); // console.log('Created object URL:', objectUrl);
// Update the image source with the object URL // Update the image source with the object URL
setImgSrc(objectUrl); setImgSrc(objectUrl);
@ -48,10 +48,10 @@ const ImageWithFallback = ({ url, filename, onError }: ImageWithFallbackProps) =
onError('Failed to load image. This might be due to permission issues or the file may not exist.'); onError('Failed to load image. This might be due to permission issues or the file may not exist.');
// Log additional details // Log additional details
console.log('Image URL that failed:', url); // console.log('Image URL that failed:', url);
console.log('Current auth status:', // console.log('Current auth status:',
Authentication.getInstance().isAuthenticated() ? 'Authenticated' : 'Not authenticated' // Authentication.getInstance().isAuthenticated() ? 'Authenticated' : 'Not authenticated'
); // );
} }
}; };

View file

@ -76,26 +76,26 @@ const Calendar = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
const loadGapiAndListEvents = async () => { const loadGapiAndListEvents = async () => {
try { try {
console.log("Starting to load events..."); // console.log("Starting to load events...");
if (typeof window.gapi === "undefined") { if (typeof window.gapi === "undefined") {
console.log("Loading GAPI script..."); // console.log("Loading GAPI script...");
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const script = document.createElement("script"); const script = document.createElement("script");
script.src = "https://apis.google.com/js/api.js"; script.src = "https://apis.google.com/js/api.js";
document.body.appendChild(script); document.body.appendChild(script);
script.onload = () => { script.onload = () => {
console.log("GAPI script loaded"); // console.log("GAPI script loaded");
window.gapi.load("client", resolve); window.gapi.load("client", resolve);
}; };
script.onerror = () => { script.onerror = () => {
console.error("Failed to load GAPI script"); // console.error("Failed to load GAPI script");
reject(new Error("Failed to load the Google API script.")); reject(new Error("Failed to load the Google API script."));
}; };
}); });
} }
console.log("Initializing GAPI client..."); // console.log("Initializing GAPI client...");
await window.gapi.client.init({ await window.gapi.client.init({
apiKey: CALENDAR_API_KEY, apiKey: CALENDAR_API_KEY,
discoveryDocs: [ discoveryDocs: [
@ -115,7 +115,7 @@ const Calendar = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
0, 0,
); );
console.log("Fetching events..."); // console.log("Fetching events...");
const response = await window.gapi.client.calendar.events.list({ const response = await window.gapi.client.calendar.events.list({
calendarId: calendarId, calendarId: calendarId,
timeZone: userTimeZone, timeZone: userTimeZone,
@ -125,13 +125,13 @@ const Calendar = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
orderBy: "startTime", orderBy: "startTime",
}); });
console.log("Response received:", response); // console.log("Response received:", response);
if (response.result.items) { if (response.result.items) {
setEvents(response.result.items); setEvents(response.result.items);
} }
} catch (error) { } catch (error) {
console.error("Detailed Error: ", error); // console.error("Detailed Error: ", error);
setError(error.message || "Failed to load events"); setError(error.message || "Failed to load events");
} finally { } finally {
setLoading(false); setLoading(false);

View file

@ -48,16 +48,16 @@ const EventList = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
const loadGapiAndListEvents = async () => { const loadGapiAndListEvents = async () => {
try { try {
console.log("Starting to load events..."); // console.log("Starting to load events...");
if (typeof window.gapi === "undefined") { if (typeof window.gapi === "undefined") {
console.log("Loading GAPI script..."); // console.log("Loading GAPI script...");
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const script = document.createElement("script"); const script = document.createElement("script");
script.src = "https://apis.google.com/js/api.js"; script.src = "https://apis.google.com/js/api.js";
document.body.appendChild(script); document.body.appendChild(script);
script.onload = () => { script.onload = () => {
console.log("GAPI script loaded"); // console.log("GAPI script loaded");
window.gapi.load("client", resolve); window.gapi.load("client", resolve);
}; };
script.onerror = () => { script.onerror = () => {
@ -67,7 +67,7 @@ const EventList = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
}); });
} }
console.log("Initializing GAPI client..."); // console.log("Initializing GAPI client...");
await window.gapi.client.init({ await window.gapi.client.init({
apiKey: apiKey, apiKey: apiKey,
discoveryDocs: [ discoveryDocs: [
@ -75,7 +75,7 @@ const EventList = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
], ],
}); });
console.log("Fetching events..."); // console.log("Fetching events...");
const response = await window.gapi.client.calendar.events.list({ const response = await window.gapi.client.calendar.events.list({
calendarId: calendarId, calendarId: calendarId,
timeZone: userTimeZone, timeZone: userTimeZone,
@ -85,7 +85,7 @@ const EventList = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => {
orderBy: "startTime", orderBy: "startTime",
}); });
console.log("Response received:", response); // console.log("Response received:", response);
if (response.result.items) { if (response.result.items) {
setEvents(response.result.items); setEvents(response.result.items);

View file

@ -11,8 +11,6 @@ async function getLogtoAccessToken(
scope: string = "all", scope: string = "all",
): Promise<string | null> { ): Promise<string | null> {
try { try {
console.log("Attempting to get access token from Logto");
// Create Basic auth string // Create Basic auth string
const authString = Buffer.from(`${clientId}:${clientSecret}`).toString( const authString = Buffer.from(`${clientId}:${clientSecret}`).toString(
"base64", "base64",
@ -59,8 +57,6 @@ async function verifyUserPassword(
accessToken: string, accessToken: string,
): Promise<boolean> { ): Promise<boolean> {
try { try {
console.log("Verifying current password");
const response = await fetch( const response = await fetch(
`${logtoApiEndpoint}/api/users/${userId}/password/verify`, `${logtoApiEndpoint}/api/users/${userId}/password/verify`,
{ {
@ -75,7 +71,6 @@ async function verifyUserPassword(
// 204 means password matches, 422 means it doesn't match // 204 means password matches, 422 means it doesn't match
if (response.status === 204) { if (response.status === 204) {
console.log("Current password verified successfully");
return true; return true;
} }
@ -101,8 +96,6 @@ async function verifyUserPassword(
export const POST: APIRoute = async ({ request }) => { export const POST: APIRoute = async ({ request }) => {
try { try {
console.log("Received change password request");
// Parse request body // Parse request body
const contentType = request.headers.get("content-type"); const contentType = request.headers.get("content-type");
const rawBody = await request.text(); const rawBody = await request.text();
@ -161,6 +154,7 @@ export const POST: APIRoute = async ({ request }) => {
} }
// Get access token // Get access token
// console.log("Attempting to get access token from Logto");
const accessToken = await getLogtoAccessToken( const accessToken = await getLogtoAccessToken(
logtoTokenEndpoint, logtoTokenEndpoint,
logtoAppId, logtoAppId,
@ -181,6 +175,7 @@ export const POST: APIRoute = async ({ request }) => {
} }
// Verify current password before proceeding // Verify current password before proceeding
// console.log("Verifying current password");
const isPasswordValid = await verifyUserPassword( const isPasswordValid = await verifyUserPassword(
logtoApiEndpoint, logtoApiEndpoint,
userId, userId,
@ -202,6 +197,7 @@ export const POST: APIRoute = async ({ request }) => {
} }
// Change password using Logto Management API // Change password using Logto Management API
// console.log("Current password verified successfully");
const changePasswordResponse = await fetch( const changePasswordResponse = await fetch(
`${logtoApiEndpoint}/api/users/${userId}/password`, `${logtoApiEndpoint}/api/users/${userId}/password`,
{ {

File diff suppressed because it is too large Load diff

View file

@ -60,7 +60,7 @@ export class RedirectHandler {
{ emailVisibility: false }, { emailVisibility: false },
); );
console.log("Auth successful:", authData); // console.log("Auth successful:", authData);
this.contentEl.innerHTML = ` this.contentEl.innerHTML = `
<p class="text-3xl font-bold text-green-500 mb-4">Authentication Successful!</p> <p class="text-3xl font-bold text-green-500 mb-4">Authentication Successful!</p>
<p class="text-2xl font-medium">Initializing your data...</p> <p class="text-2xl font-medium">Initializing your data...</p>
@ -99,19 +99,15 @@ export class RedirectHandler {
private async initializeDataSync(): Promise<void> { private async initializeDataSync(): Promise<void> {
try { try {
// Dynamically import the AuthSyncService to avoid circular dependencies // Dynamically import the AuthSyncService to avoid circular dependencies
const { AuthSyncService } = await import('../database/AuthSyncService'); const { AuthSyncService } = await import("../database/AuthSyncService");
// Get the instance and trigger a full sync // Get the instance and trigger a full sync
const authSync = AuthSyncService.getInstance(); const authSync = AuthSyncService.getInstance();
const syncResult = await authSync.handleLogin(); const syncResult = await authSync.handleLogin();
if (syncResult) { // console.log('Initial data sync completed successfully');
console.log('Initial data sync completed successfully');
} else {
console.warn('Initial data sync completed with issues');
}
} catch (error) { } catch (error) {
console.error('Failed to initialize data sync:', error); console.error("Failed to initialize data sync:", error);
// Continue with login process even if sync fails // Continue with login process even if sync fails
} }
} }

View file

@ -78,7 +78,7 @@ export class AuthSyncService {
if (!isBrowser) return true; if (!isBrowser) return true;
if (this.isSyncing) { if (this.isSyncing) {
console.log("Sync already in progress, queueing login sync"); // console.log("Sync already in progress, queueing login sync");
if (this.syncPromise) { if (this.syncPromise) {
this.syncPromise = this.syncPromise.then(() => this.performLoginSync()); this.syncPromise = this.syncPromise.then(() => this.performLoginSync());
} else { } else {
@ -100,7 +100,7 @@ export class AuthSyncService {
if (!isBrowser) return; if (!isBrowser) return;
if (!this.auth.isAuthenticated()) { if (!this.auth.isAuthenticated()) {
console.log("Not authenticated, skipping login sync"); // console.log("Not authenticated, skipping login sync");
return; return;
} }
@ -108,7 +108,7 @@ export class AuthSyncService {
this.syncErrors = {}; this.syncErrors = {};
try { try {
console.log("Starting login sync process..."); // console.log("Starting login sync process...");
// Display sync notification if in browser environment // Display sync notification if in browser environment
this.showSyncNotification("Syncing your data..."); this.showSyncNotification("Syncing your data...");
@ -123,7 +123,7 @@ export class AuthSyncService {
); );
// Log the sync operation // Log the sync operation
console.log("User data synchronized on login"); // console.log("User data synchronized on login");
} }
// Sync all collections in parallel with conflict resolution // Sync all collections in parallel with conflict resolution
@ -131,9 +131,9 @@ export class AuthSyncService {
this.collectionsToSync.map(async (collection) => { this.collectionsToSync.map(async (collection) => {
try { try {
await this.dataSync.syncCollection(collection); await this.dataSync.syncCollection(collection);
console.log(`Successfully synced ${collection}`); // console.log(`Successfully synced ${collection}`);
} catch (error) { } catch (error) {
console.error(`Error syncing ${collection}:`, error); // console.error(`Error syncing ${collection}:`, error);
this.syncErrors[collection] = error as Error; this.syncErrors[collection] = error as Error;
} }
}), }),
@ -146,17 +146,17 @@ export class AuthSyncService {
const syncVerification = await this.verifySyncSuccess(); const syncVerification = await this.verifySyncSuccess();
if (syncVerification.success) { if (syncVerification.success) {
console.log("Login sync completed successfully"); // console.log("Login sync completed successfully");
this.showSyncNotification("Data sync complete!", "success"); this.showSyncNotification("Data sync complete!", "success");
} else { } else {
console.warn( // console.warn(
"Login sync completed with issues:", // "Login sync completed with issues:",
syncVerification.errors, // syncVerification.errors,
); // );
this.showSyncNotification("Some data could not be synced", "warning"); this.showSyncNotification("Some data could not be synced", "warning");
} }
} catch (error) { } catch (error) {
console.error("Error during login sync:", error); // console.error("Error during login sync:", error);
this.showSyncNotification("Failed to sync data", "error"); this.showSyncNotification("Failed to sync data", "error");
} finally { } finally {
this.isSyncing = false; this.isSyncing = false;
@ -180,7 +180,7 @@ export class AuthSyncService {
if (!isBrowser) return true; if (!isBrowser) return true;
if (this.isSyncing) { if (this.isSyncing) {
console.log("Sync already in progress, queueing logout cleanup"); // console.log("Sync already in progress, queueing logout cleanup");
this.syncQueue.push("logout"); this.syncQueue.push("logout");
return true; return true;
} }
@ -188,7 +188,7 @@ export class AuthSyncService {
this.isSyncing = true; this.isSyncing = true;
try { try {
console.log("Starting logout cleanup process..."); // console.log("Starting logout cleanup process...");
// Ensure any pending changes are synced before logout // Ensure any pending changes are synced before logout
await this.syncPendingChanges(); await this.syncPendingChanges();
@ -196,10 +196,10 @@ export class AuthSyncService {
// Clear all data from IndexedDB // Clear all data from IndexedDB
await this.dexieService.clearAllData(); await this.dexieService.clearAllData();
console.log("Logout cleanup completed successfully"); // console.log("Logout cleanup completed successfully");
return true; return true;
} catch (error) { } catch (error) {
console.error("Error during logout cleanup:", error); // console.error("Error during logout cleanup:", error);
return false; return false;
} finally { } finally {
this.isSyncing = false; this.isSyncing = false;
@ -224,7 +224,7 @@ export class AuthSyncService {
// This would be implemented if we had offline capabilities // This would be implemented if we had offline capabilities
// For now, we just log that we would sync pending changes // For now, we just log that we would sync pending changes
console.log("Checking for pending changes to sync before logout..."); // console.log("Checking for pending changes to sync before logout...");
// In a real implementation, this would sync any offline changes // In a real implementation, this would sync any offline changes
} }
@ -284,7 +284,7 @@ export class AuthSyncService {
window.toast(message, { type }); window.toast(message, { type });
} else { } else {
// Fallback to console // Fallback to console
console.log(`[${type.toUpperCase()}] ${message}`); // console.log(`[${type.toUpperCase()}] ${message}`);
} }
} }
@ -293,7 +293,7 @@ export class AuthSyncService {
*/ */
public async forceSyncAll(): Promise<boolean> { public async forceSyncAll(): Promise<boolean> {
if (this.isSyncing) { if (this.isSyncing) {
console.log("Sync already in progress, queueing full sync"); // console.log("Sync already in progress, queueing full sync");
this.syncQueue.push("login"); // Reuse login sync logic this.syncQueue.push("login"); // Reuse login sync logic
return true; return true;
} }

View file

@ -83,7 +83,7 @@ export class DataSyncService {
private async handleOnline(): Promise<void> { private async handleOnline(): Promise<void> {
if (!isBrowser) return; if (!isBrowser) return;
console.log("Device is online, syncing pending changes..."); // console.log("Device is online, syncing pending changes...");
this.offlineMode = false; this.offlineMode = false;
await this.syncOfflineChanges(); await this.syncOfflineChanges();
} }
@ -94,7 +94,7 @@ export class DataSyncService {
private handleOffline(): void { private handleOffline(): void {
if (!isBrowser) return; if (!isBrowser) return;
console.log("Device is offline, enabling offline mode..."); // console.log("Device is offline, enabling offline mode...");
this.offlineMode = true; this.offlineMode = true;
} }
@ -109,13 +109,13 @@ export class DataSyncService {
): Promise<T[]> { ): Promise<T[]> {
// Skip in non-browser environments // Skip in non-browser environments
if (!isBrowser) { if (!isBrowser) {
console.log(`Skipping sync for ${collection} in non-browser environment`); // console.log(`Skipping sync for ${collection} in non-browser environment`);
return []; return [];
} }
// Prevent multiple syncs of the same collection at the same time // Prevent multiple syncs of the same collection at the same time
if (this.syncInProgress[collection]) { if (this.syncInProgress[collection]) {
console.log(`Sync already in progress for ${collection}`); // console.log(`Sync already in progress for ${collection}`);
return []; return [];
} }
@ -124,19 +124,19 @@ export class DataSyncService {
try { try {
// Check if we're authenticated // Check if we're authenticated
if (!this.auth.isAuthenticated()) { if (!this.auth.isAuthenticated()) {
console.log(`Not authenticated, skipping sync for ${collection}`); // console.log(`Not authenticated, skipping sync for ${collection}`);
return []; return [];
} }
// Check if we're offline // Check if we're offline
if (this.offlineMode) { if (this.offlineMode) {
console.log(`Device is offline, using cached data for ${collection}`); // console.log(`Device is offline, using cached data for ${collection}`);
const db = this.dexieService.getDB(); const db = this.dexieService.getDB();
const table = this.getTableForCollection(collection); const table = this.getTableForCollection(collection);
return table ? ((await table.toArray()) as T[]) : []; return table ? ((await table.toArray()) as T[]) : [];
} }
console.log(`Syncing ${collection}...`); // console.log(`Syncing ${collection}...`);
// Normalize expand parameter to be an array of strings // Normalize expand parameter to be an array of strings
let normalizedExpand: string[] | undefined; let normalizedExpand: string[] | undefined;
@ -158,7 +158,7 @@ export class DataSyncService {
const items = await this.get.getAll<T>(collection, filter, sort, { const items = await this.get.getAll<T>(collection, filter, sort, {
expand: normalizedExpand, expand: normalizedExpand,
}); });
console.log(`Fetched ${items.length} items from ${collection}`); // console.log(`Fetched ${items.length} items from ${collection}`);
// Get the database table // Get the database table
const db = this.dexieService.getDB(); const db = this.dexieService.getDB();
@ -181,11 +181,11 @@ export class DataSyncService {
const existingItem = existingItemsMap.get(item.id); const existingItem = existingItemsMap.get(item.id);
// SECURITY FIX: Remove event_code from events before storing in IndexedDB // SECURITY FIX: Remove event_code from events before storing in IndexedDB
if (collection === Collections.EVENTS && 'event_code' in item) { if (collection === Collections.EVENTS && "event_code" in item) {
// Keep the event_code but ensure files array is properly handled // Keep the event_code but ensure files array is properly handled
if ('files' in item && Array.isArray((item as any).files)) { if ("files" in item && Array.isArray((item as any).files)) {
// Ensure files array is properly stored // Ensure files array is properly stored
console.log(`Event ${item.id} has ${(item as any).files.length} files`); // console.log(`Event ${item.id} has ${(item as any).files.length} files`);
} else { } else {
// Initialize empty files array if not present // Initialize empty files array if not present
(item as any).files = []; (item as any).files = [];
@ -232,18 +232,21 @@ export class DataSyncService {
// For events, ensure we handle the files field properly // For events, ensure we handle the files field properly
if (collection === Collections.EVENTS) { if (collection === Collections.EVENTS) {
// Ensure files array is properly handled // Ensure files array is properly handled
if ('files' in serverItem && Array.isArray((serverItem as any).files)) { if ("files" in serverItem && Array.isArray((serverItem as any).files)) {
console.log(`Server event ${serverItem.id} has ${(serverItem as any).files.length} files`); // console.log(`Server event ${serverItem.id} has ${(serverItem as any).files.length} files`);
} else { } else {
// Initialize empty files array if not present // Initialize empty files array if not present
(serverItem as any).files = []; (serverItem as any).files = [];
} }
// If local item has files but server doesn't, preserve local files // If local item has files but server doesn't, preserve local files
if ('files' in localItem && Array.isArray((localItem as any).files) && if (
(localItem as any).files.length > 0 && "files" in localItem &&
(!('files' in serverItem) || !(serverItem as any).files.length)) { Array.isArray((localItem as any).files) &&
console.log(`Preserving local files for event ${localItem.id}`); (localItem as any).files.length > 0 &&
(!("files" in serverItem) || !(serverItem as any).files.length)
) {
// console.log(`Preserving local files for event ${localItem.id}`);
(serverItem as any).files = (localItem as any).files; (serverItem as any).files = (localItem as any).files;
} }
} }
@ -255,9 +258,9 @@ export class DataSyncService {
); );
if (pendingChanges.length > 0) { if (pendingChanges.length > 0) {
console.log( // console.log(
`Found ${pendingChanges.length} pending changes for ${collection}:${localItem.id}`, // `Found ${pendingChanges.length} pending changes for ${collection}:${localItem.id}`,
); // );
// Server-wins strategy by default, but preserve local changes that haven't been synced // Server-wins strategy by default, but preserve local changes that haven't been synced
const mergedItem = { ...serverItem }; const mergedItem = { ...serverItem };
@ -268,12 +271,16 @@ export class DataSyncService {
// Apply each field change individually // Apply each field change individually
Object.entries(change.data).forEach(([key, value]) => { Object.entries(change.data).forEach(([key, value]) => {
// Special handling for files array // Special handling for files array
if (key === 'files' && Array.isArray(value)) { if (key === "files" && Array.isArray(value)) {
// Merge files arrays, removing duplicates // Merge files arrays, removing duplicates
const existingFiles = Array.isArray((mergedItem as any)[key]) ? (mergedItem as any)[key] : []; const existingFiles = Array.isArray((mergedItem as any)[key])
? (mergedItem as any)[key]
: [];
const newFiles = value as string[]; const newFiles = value as string[];
(mergedItem as any)[key] = [...new Set([...existingFiles, ...newFiles])]; (mergedItem as any)[key] = [
console.log(`Merged files for ${collection}:${localItem.id}`, (mergedItem as any)[key]); ...new Set([...existingFiles, ...newFiles]),
];
// console.log(`Merged files for ${collection}:${localItem.id}`, (mergedItem as any)[key]);
} else { } else {
(mergedItem as any)[key] = value; (mergedItem as any)[key] = value;
} }
@ -326,11 +333,11 @@ export class DataSyncService {
.toArray(); .toArray();
if (pendingChanges.length === 0) { if (pendingChanges.length === 0) {
console.log("No pending offline changes to sync"); // console.log("No pending offline changes to sync");
return true; return true;
} }
console.log(`Syncing ${pendingChanges.length} offline changes...`); // console.log(`Syncing ${pendingChanges.length} offline changes...`);
// Group changes by collection for more efficient processing // Group changes by collection for more efficient processing
const changesByCollection = pendingChanges.reduce( const changesByCollection = pendingChanges.reduce(
@ -411,9 +418,9 @@ export class DataSyncService {
}; };
const id = await this.offlineChangesTable.add(change as OfflineChange); const id = await this.offlineChangesTable.add(change as OfflineChange);
console.log( // console.log(
`Recorded offline change: ${operation} on ${collection}:${recordId}`, // `Recorded offline change: ${operation} on ${collection}:${recordId}`,
); // );
// Try to sync immediately if we're online // Try to sync immediately if we're online
if (!this.offlineMode) { if (!this.offlineMode) {
@ -500,7 +507,7 @@ export class DataSyncService {
// SECURITY FIX: Remove event_code from events before returning them // SECURITY FIX: Remove event_code from events before returning them
if (collection === Collections.EVENTS) { if (collection === Collections.EVENTS) {
data = data.map((item: any) => { data = data.map((item: any) => {
if ('event_code' in item) { if ("event_code" in item) {
const { event_code, ...rest } = item; const { event_code, ...rest } = item;
return rest; return rest;
} }
@ -538,17 +545,22 @@ export class DataSyncService {
// For events, ensure we handle the files field properly // For events, ensure we handle the files field properly
if (collection === Collections.EVENTS) { if (collection === Collections.EVENTS) {
// Ensure files array is properly handled // Ensure files array is properly handled
if (!('files' in pbItem) || !Array.isArray((pbItem as any).files)) { if (!("files" in pbItem) || !Array.isArray((pbItem as any).files)) {
(pbItem as any).files = []; (pbItem as any).files = [];
} }
// If we already have a local item with files, preserve them if server has none // If we already have a local item with files, preserve them if server has none
if (item && 'files' in item && Array.isArray((item as any).files) && if (
(item as any).files.length > 0 && !(pbItem as any).files.length) { item &&
console.log(`Preserving local files for event ${id}`); "files" in item &&
Array.isArray((item as any).files) &&
(item as any).files.length > 0 &&
!(pbItem as any).files.length
) {
// console.log(`Preserving local files for event ${id}`);
(pbItem as any).files = (item as any).files; (pbItem as any).files = (item as any).files;
} }
await table.put(pbItem); await table.put(pbItem);
item = pbItem; item = pbItem;
} else { } else {
@ -587,21 +599,21 @@ export class DataSyncService {
} }
// Special handling for files field in events // Special handling for files field in events
if (collection === Collections.EVENTS && 'files' in data) { if (collection === Collections.EVENTS && "files" in data) {
console.log(`Updating files for event ${id}`, (data as any).files); // console.log(`Updating files for event ${id}`, (data as any).files);
// Ensure files is an array // Ensure files is an array
if (!Array.isArray((data as any).files)) { if (!Array.isArray((data as any).files)) {
(data as any).files = []; (data as any).files = [];
} }
// If we're updating files, make sure we're not losing any // If we're updating files, make sure we're not losing any
if ('files' in currentItem && Array.isArray((currentItem as any).files)) { if ("files" in currentItem && Array.isArray((currentItem as any).files)) {
// Merge files arrays, removing duplicates // Merge files arrays, removing duplicates
const existingFiles = (currentItem as any).files as string[]; const existingFiles = (currentItem as any).files as string[];
const newFiles = (data as any).files as string[]; const newFiles = (data as any).files as string[];
(data as any).files = [...new Set([...existingFiles, ...newFiles])]; (data as any).files = [...new Set([...existingFiles, ...newFiles])];
console.log(`Merged files for event ${id}`, (data as any).files); // console.log(`Merged files for event ${id}`, (data as any).files);
} }
} }
@ -689,11 +701,14 @@ export class DataSyncService {
try { try {
// Store in localStorage instead of IndexedDB for security // Store in localStorage instead of IndexedDB for security
localStorage.setItem('pending_event_code', eventCode); localStorage.setItem("pending_event_code", eventCode);
localStorage.setItem('pending_event_code_timestamp', Date.now().toString()); localStorage.setItem(
console.log('Event code stored for offline check-in'); "pending_event_code_timestamp",
Date.now().toString(),
);
// console.log('Event code stored for offline check-in');
} catch (error) { } catch (error) {
console.error('Error storing event code:', error); console.error("Error storing event code:", error);
} }
} }
@ -704,11 +719,11 @@ export class DataSyncService {
if (!isBrowser) return; if (!isBrowser) return;
try { try {
localStorage.removeItem('pending_event_code'); localStorage.removeItem("pending_event_code");
localStorage.removeItem('pending_event_code_timestamp'); localStorage.removeItem("pending_event_code_timestamp");
console.log('Event code cleared'); // console.log('Event code cleared');
} catch (error) { } catch (error) {
console.error('Error clearing event code:', error); console.error("Error clearing event code:", error);
} }
} }
@ -716,21 +731,24 @@ export class DataSyncService {
* Get the stored event code from local storage * Get the stored event code from local storage
* @returns The stored event code, or null if none exists * @returns The stored event code, or null if none exists
*/ */
public async getStoredEventCode(): Promise<{ code: string; timestamp: number } | null> { public async getStoredEventCode(): Promise<{
code: string;
timestamp: number;
} | null> {
if (!isBrowser) return null; if (!isBrowser) return null;
try { try {
const code = localStorage.getItem('pending_event_code'); const code = localStorage.getItem("pending_event_code");
const timestamp = localStorage.getItem('pending_event_code_timestamp'); const timestamp = localStorage.getItem("pending_event_code_timestamp");
if (!code || !timestamp) return null; if (!code || !timestamp) return null;
return { return {
code, code,
timestamp: parseInt(timestamp) timestamp: parseInt(timestamp),
}; };
} catch (error) { } catch (error) {
console.error('Error getting stored event code:', error); console.error("Error getting stored event code:", error);
return null; return null;
} }
} }
@ -745,31 +763,31 @@ export class DataSyncService {
try { try {
const db = this.dexieService.getDB(); const db = this.dexieService.getDB();
const table = this.getTableForCollection(Collections.EVENTS); const table = this.getTableForCollection(Collections.EVENTS);
if (!table) { if (!table) {
console.error('Events table not found'); console.error("Events table not found");
return; return;
} }
// Get all events // Get all events
const events = await table.toArray(); const events = await table.toArray();
// Remove event_code from each event // Remove event_code from each event
const updatedEvents = events.map(event => { const updatedEvents = events.map((event) => {
if ('event_code' in event) { if ("event_code" in event) {
const { event_code, ...rest } = event; const { event_code, ...rest } = event;
return rest; return rest;
} }
return event; return event;
}); });
// Clear the table and add the updated events // Clear the table and add the updated events
await table.clear(); await table.clear();
await table.bulkAdd(updatedEvents); await table.bulkAdd(updatedEvents);
console.log('Successfully purged event codes from IndexedDB'); // console.log('Successfully purged event codes from IndexedDB');
} catch (error) { } catch (error) {
console.error('Error purging event codes:', error); console.error("Error purging event codes:", error);
} }
} }
} }

View file

@ -65,16 +65,17 @@ export class DashboardDatabase extends Dexie {
offlineChanges: offlineChanges:
"id, collection, recordId, operation, timestamp, synced, syncAttempts", "id, collection, recordId, operation, timestamp, synced, syncAttempts",
}); });
// Add version 3 with eventAttendees table and updated events table (no attendees field) // Add version 3 with eventAttendees table and updated events table (no attendees field)
this.version(3).stores({ this.version(3).stores({
events: "id, event_name, event_code, start_date, end_date, published", events: "id, event_name, event_code, start_date, end_date, published",
eventAttendees: "id, user, event, time_checked_in", eventAttendees: "id, user, event, time_checked_in",
}); });
// Add version 4 with files field in events table // Add version 4 with files field in events table
this.version(4).stores({ this.version(4).stores({
events: "id, event_name, event_code, start_date, end_date, published, files", events:
"id, event_name, event_code, start_date, end_date, published, files",
}); });
} }
@ -125,7 +126,7 @@ export class DexieService {
this.db.initialize(); this.db.initialize();
} else { } else {
// Use a mock database in non-browser environments // Use a mock database in non-browser environments
console.log("Running in Node.js environment, using mock database"); // console.log("Running in Node.js environment, using mock database");
this.db = new MockDashboardDatabase() as any; this.db = new MockDashboardDatabase() as any;
} }
} }

View file

@ -1,4 +1,4 @@
import { Authentication } from '../pocketbase/Authentication'; import { Authentication } from "../pocketbase/Authentication";
/** /**
* Initialize authentication synchronization * Initialize authentication synchronization
@ -9,27 +9,27 @@ export async function initAuthSync(): Promise<void> {
try { try {
// Get Authentication instance // Get Authentication instance
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
// This will trigger the lazy loading of AuthSyncService // This will trigger the lazy loading of AuthSyncService
// through the onAuthStateChange mechanism // through the onAuthStateChange mechanism
auth.onAuthStateChange(() => { auth.onAuthStateChange(() => {
console.log('Auth sync initialized and listening for auth state changes'); // console.log('Auth sync initialized and listening for auth state changes');
}); });
console.log('Auth sync initialization complete'); // console.log('Auth sync initialization complete');
} catch (error) { } catch (error) {
console.error('Failed to initialize auth sync:', error); console.error("Failed to initialize auth sync:", error);
} }
} }
// Export a function to manually trigger a full sync // Export a function to manually trigger a full sync
export async function forceFullSync(): Promise<boolean> { export async function forceFullSync(): Promise<boolean> {
try { try {
const { AuthSyncService } = await import('./AuthSyncService'); const { AuthSyncService } = await import("./AuthSyncService");
const authSync = AuthSyncService.getInstance(); const authSync = AuthSyncService.getInstance();
return await authSync.forceSyncAll(); return await authSync.forceSyncAll();
} catch (error) { } catch (error) {
console.error('Failed to force full sync:', error); console.error("Failed to force full sync:", error);
return false; return false;
} }
} }

View file

@ -87,20 +87,20 @@ export class Authentication {
try { try {
// Initialize AuthSyncService if needed (lazy loading) // Initialize AuthSyncService if needed (lazy loading)
await this.initAuthSyncService(); await this.initAuthSyncService();
// Get AuthSyncService instance // Get AuthSyncService instance
const { AuthSyncService } = await import('../database/AuthSyncService'); const { AuthSyncService } = await import("../database/AuthSyncService");
const authSync = AuthSyncService.getInstance(); const authSync = AuthSyncService.getInstance();
// Handle data cleanup before actual logout // Handle data cleanup before actual logout
await authSync.handleLogout(); await authSync.handleLogout();
// Clear auth store // Clear auth store
this.pb.authStore.clear(); this.pb.authStore.clear();
console.log('Logout completed successfully with data cleanup'); // console.log('Logout completed successfully with data cleanup');
} catch (error) { } catch (error) {
console.error('Error during logout:', error); console.error("Error during logout:", error);
// Fallback to basic logout if sync fails // Fallback to basic logout if sync fails
this.pb.authStore.clear(); this.pb.authStore.clear();
} }
@ -133,9 +133,12 @@ export class Authentication {
*/ */
public onAuthStateChange(callback: (isValid: boolean) => void): void { public onAuthStateChange(callback: (isValid: boolean) => void): void {
this.authChangeCallbacks.push(callback); this.authChangeCallbacks.push(callback);
// Initialize AuthSyncService when first callback is registered // Initialize AuthSyncService when first callback is registered
if (!this.authSyncServiceInitialized && this.authChangeCallbacks.length === 1) { if (
!this.authSyncServiceInitialized &&
this.authChangeCallbacks.length === 1
) {
this.initAuthSyncService(); this.initAuthSyncService();
} }
} }
@ -164,32 +167,32 @@ export class Authentication {
const isValid = this.pb.authStore.isValid; const isValid = this.pb.authStore.isValid;
this.authChangeCallbacks.forEach((callback) => callback(isValid)); this.authChangeCallbacks.forEach((callback) => callback(isValid));
} }
/** /**
* Initialize the AuthSyncService (lazy loading) * Initialize the AuthSyncService (lazy loading)
*/ */
private async initAuthSyncService(): Promise<void> { private async initAuthSyncService(): Promise<void> {
if (this.authSyncServiceInitialized) return; if (this.authSyncServiceInitialized) return;
try { try {
// Dynamically import AuthSyncService to avoid circular dependencies // Dynamically import AuthSyncService to avoid circular dependencies
const { AuthSyncService } = await import('../database/AuthSyncService'); const { AuthSyncService } = await import("../database/AuthSyncService");
// Initialize the service // Initialize the service
AuthSyncService.getInstance(); AuthSyncService.getInstance();
this.authSyncServiceInitialized = true; this.authSyncServiceInitialized = true;
console.log('AuthSyncService initialized successfully'); // console.log('AuthSyncService initialized successfully');
// If user is already authenticated, trigger initial sync // If user is already authenticated, trigger initial sync
if (this.isAuthenticated()) { if (this.isAuthenticated()) {
const authSync = AuthSyncService.getInstance(); const authSync = AuthSyncService.getInstance();
authSync.handleLogin().catch(err => { authSync.handleLogin().catch((err) => {
console.error('Error during initial data sync:', err); console.error("Error during initial data sync:", err);
}); });
} }
} catch (error) { } catch (error) {
console.error('Failed to initialize AuthSyncService:', error); console.error("Failed to initialize AuthSyncService:", error);
} }
} }
} }

View file

@ -3,7 +3,7 @@ import { Authentication } from "./Authentication";
export class FileManager { export class FileManager {
private auth: Authentication; private auth: Authentication;
private static instance: FileManager; private static instance: FileManager;
private static UNSUPPORTED_EXTENSIONS = ['afdesign', 'psd', 'ai', 'sketch']; private static UNSUPPORTED_EXTENSIONS = ["afdesign", "psd", "ai", "sketch"];
private constructor() { private constructor() {
this.auth = Authentication.getInstance(); this.auth = Authentication.getInstance();
@ -25,15 +25,18 @@ export class FileManager {
* @returns Object with validation result and reason if invalid * @returns Object with validation result and reason if invalid
*/ */
public validateFileType(file: File): { valid: boolean; reason?: string } { public validateFileType(file: File): { valid: boolean; reason?: string } {
const fileExtension = file.name.split('.').pop()?.toLowerCase(); const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (fileExtension && FileManager.UNSUPPORTED_EXTENSIONS.includes(fileExtension)) { if (
return { fileExtension &&
valid: false, FileManager.UNSUPPORTED_EXTENSIONS.includes(fileExtension)
reason: `File type .${fileExtension} is not supported. Please convert to PDF or image format.` ) {
return {
valid: false,
reason: `File type .${fileExtension} is not supported. Please convert to PDF or image format.`,
}; };
} }
return { valid: true }; return { valid: true };
} }
@ -51,7 +54,7 @@ export class FileManager {
recordId: string, recordId: string,
field: string, field: string,
file: File, file: File,
append: boolean = false append: boolean = false,
): Promise<T> { ): Promise<T> {
if (!this.auth.isAuthenticated()) { if (!this.auth.isAuthenticated()) {
throw new Error("User must be authenticated to upload files"); throw new Error("User must be authenticated to upload files");
@ -60,16 +63,18 @@ export class FileManager {
try { try {
this.auth.setUpdating(true); this.auth.setUpdating(true);
const pb = this.auth.getPocketBase(); const pb = this.auth.getPocketBase();
// Validate file size // Validate file size
const maxSize = 200 * 1024 * 1024; // 200MB const maxSize = 200 * 1024 * 1024; // 200MB
if (file.size > maxSize) { if (file.size > maxSize) {
throw new Error(`File size ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds 200MB limit`); throw new Error(
`File size ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds 200MB limit`,
);
} }
// Check for potentially problematic file types // Check for potentially problematic file types
const fileExtension = file.name.split('.').pop()?.toLowerCase(); const fileExtension = file.name.split(".").pop()?.toLowerCase();
// Validate file type // Validate file type
const validation = this.validateFileType(file); const validation = this.validateFileType(file);
if (!validation.valid) { if (!validation.valid) {
@ -77,49 +82,50 @@ export class FileManager {
} }
// Log upload attempt // Log upload attempt
console.log('Attempting file upload:', { // console.log('Attempting file upload:', {
name: file.name, // name: file.name,
size: file.size, // size: file.size,
type: file.type, // type: file.type,
extension: fileExtension, // extension: fileExtension,
collection: collectionName, // collection: collectionName,
recordId: recordId, // recordId: recordId,
field: field, // field: field,
append: append // append: append
}); // });
// Create FormData for the upload // Create FormData for the upload
const formData = new FormData(); const formData = new FormData();
// Use the + prefix for the field name if append is true // Use the + prefix for the field name if append is true
const fieldName = append ? `${field}+` : field; const fieldName = append ? `${field}+` : field;
// Get existing record to preserve existing files // Get existing record to preserve existing files
let existingRecord: any = null; let existingRecord: any = null;
let existingFiles: string[] = []; let existingFiles: string[] = [];
try { try {
if (recordId) { if (recordId) {
existingRecord = await pb.collection(collectionName).getOne(recordId); existingRecord = await pb.collection(collectionName).getOne(recordId);
existingFiles = existingRecord[field] || []; existingFiles = existingRecord[field] || [];
} }
} catch (error) { } catch (error) {
console.warn('Could not fetch existing record:', error); // console.warn('Could not fetch existing record:', error);
} }
// Check if the file already exists // Check if the file already exists
const fileExists = existingFiles.some(existingFile => const fileExists = existingFiles.some(
existingFile.toLowerCase() === file.name.toLowerCase() (existingFile) =>
existingFile.toLowerCase() === file.name.toLowerCase(),
); );
if (fileExists) { if (fileExists) {
console.warn(`File with name ${file.name} already exists. Renaming to avoid conflicts.`); // console.warn(`File with name ${file.name} already exists. Renaming to avoid conflicts.`);
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
const nameParts = file.name.split('.'); const nameParts = file.name.split(".");
const extension = nameParts.pop(); const extension = nameParts.pop();
const baseName = nameParts.join('.'); const baseName = nameParts.join(".");
const newFileName = `${baseName}_${timestamp}.${extension}`; const newFileName = `${baseName}_${timestamp}.${extension}`;
// Create a new file with the modified name // Create a new file with the modified name
const newFile = new File([file], newFileName, { type: file.type }); const newFile = new File([file], newFileName, { type: file.type });
formData.append(fieldName, newFile); formData.append(fieldName, newFile);
@ -128,60 +134,71 @@ export class FileManager {
} }
try { try {
const result = await pb.collection(collectionName).update<T>(recordId, formData); const result = await pb
console.log('Upload successful:', { .collection(collectionName)
result, .update<T>(recordId, formData);
fileInfo: { // console.log('Upload successful:', {
name: file.name, // result,
size: file.size, // fileInfo: {
type: file.type // name: file.name,
}, // size: file.size,
collection: collectionName, // type: file.type
recordId: recordId // },
}); // collection: collectionName,
// recordId: recordId
// });
// Verify the file was actually added to the record // Verify the file was actually added to the record
try { try {
const updatedRecord = await pb.collection(collectionName).getOne(recordId); const updatedRecord = await pb
console.log('Updated record files:', { .collection(collectionName)
files: updatedRecord.files, .getOne(recordId);
recordId: recordId // console.log('Updated record files:', {
}); // files: updatedRecord.files,
// recordId: recordId
// });
} catch (verifyError) { } catch (verifyError) {
console.warn('Could not verify file upload:', verifyError); // console.warn('Could not verify file upload:', verifyError);
} }
return result; return result;
} catch (pbError: any) { } catch (pbError: any) {
// Log detailed PocketBase error // Log detailed PocketBase error
console.error('PocketBase upload error:', { // console.error('PocketBase upload error:', {
status: pbError?.status, // status: pbError?.status,
response: pbError?.response, // response: pbError?.response,
data: pbError?.data, // data: pbError?.data,
message: pbError?.message // message: pbError?.message
}); // });
// More specific error message based on file type // More specific error message based on file type
if (fileExtension && FileManager.UNSUPPORTED_EXTENSIONS.includes(fileExtension)) { if (
throw new Error(`Upload failed: File type .${fileExtension} is not supported. Please convert to PDF or image format.`); fileExtension &&
FileManager.UNSUPPORTED_EXTENSIONS.includes(fileExtension)
) {
throw new Error(
`Upload failed: File type .${fileExtension} is not supported. Please convert to PDF or image format.`,
);
} }
throw new Error(`Upload failed: ${pbError?.message || 'Unknown PocketBase error'}`); throw new Error(
`Upload failed: ${pbError?.message || "Unknown PocketBase error"}`,
);
} }
} catch (err) { } catch (err) {
console.error(`Failed to upload file to ${collectionName}:`, { // console.error(`Failed to upload file to ${collectionName}:`, {
error: err, // error: err,
fileInfo: { // fileInfo: {
name: file.name, // name: file.name,
size: file.size, // size: file.size,
type: file.type // type: file.type
}, // },
auth: { // auth: {
isAuthenticated: this.auth.isAuthenticated(), // isAuthenticated: this.auth.isAuthenticated(),
userId: this.auth.getUserId() // userId: this.auth.getUserId()
} // }
}); // });
if (err instanceof Error) { if (err instanceof Error) {
throw err; throw err;
} }
@ -224,7 +241,7 @@ export class FileManager {
`File ${file.name} is too large. Maximum size is 50MB.`, `File ${file.name} is too large. Maximum size is 50MB.`,
); );
} }
// Validate file type // Validate file type
const validation = this.validateFileType(file); const validation = this.validateFileType(file);
if (!validation.valid) { if (!validation.valid) {
@ -241,7 +258,7 @@ export class FileManager {
.getOne<T>(recordId); .getOne<T>(recordId);
existingFiles = (record as any)[field] || []; existingFiles = (record as any)[field] || [];
} catch (error) { } catch (error) {
console.warn("Failed to fetch existing record:", error); // console.warn("Failed to fetch existing record:", error);
} }
} }
@ -260,7 +277,7 @@ export class FileManager {
processedFile = await this.compressImageIfNeeded(file, 50); // 50MB max size processedFile = await this.compressImageIfNeeded(file, 50); // 50MB max size
} }
} catch (error) { } catch (error) {
console.warn(`Failed to process file ${file.name}:`, error); // console.warn(`Failed to process file ${file.name}:`, error);
processedFile = file; // Use original file if processing fails processedFile = file; // Use original file if processing fails
} }
@ -298,7 +315,7 @@ export class FileManager {
.getOne<T>(recordId); .getOne<T>(recordId);
return finalRecord; return finalRecord;
} catch (err) { } catch (err) {
console.error(`Failed to upload files to ${collectionName}:`, err); // console.error(`Failed to upload files to ${collectionName}:`, err);
throw err; throw err;
} finally { } finally {
this.auth.setUpdating(false); this.auth.setUpdating(false);
@ -324,33 +341,33 @@ export class FileManager {
const record = await pb.collection(collectionName).getOne(recordId); const record = await pb.collection(collectionName).getOne(recordId);
existingFiles = record[field] || []; existingFiles = record[field] || [];
} catch (error) { } catch (error) {
console.warn("Failed to fetch existing record for duplicate check:", error); // console.warn("Failed to fetch existing record for duplicate check:", error);
} }
// Add new files, renaming duplicates if needed // Add new files, renaming duplicates if needed
for (const file of files) { for (const file of files) {
let fileToUpload = file; let fileToUpload = file;
// Check if filename already exists // Check if filename already exists
if (Array.isArray(existingFiles) && existingFiles.includes(file.name)) { if (Array.isArray(existingFiles) && existingFiles.includes(file.name)) {
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
const nameParts = file.name.split('.'); const nameParts = file.name.split(".");
const extension = nameParts.pop(); const extension = nameParts.pop();
const baseName = nameParts.join('.'); const baseName = nameParts.join(".");
const newFileName = `${baseName}_${timestamp}.${extension}`; const newFileName = `${baseName}_${timestamp}.${extension}`;
// Create a new file with the modified name // Create a new file with the modified name
fileToUpload = new File([file], newFileName, { type: file.type }); fileToUpload = new File([file], newFileName, { type: file.type });
console.log(`Renamed duplicate file from ${file.name} to ${newFileName}`); // console.log(`Renamed duplicate file from ${file.name} to ${newFileName}`);
} }
formData.append(field, fileToUpload); formData.append(field, fileToUpload);
} }
// Tell PocketBase to keep existing files // Tell PocketBase to keep existing files
if (existingFiles.length > 0) { if (existingFiles.length > 0) {
formData.append(`${field}@`, ''); // This tells PocketBase to keep existing files formData.append(`${field}@`, ""); // This tells PocketBase to keep existing files
} }
try { try {
@ -396,28 +413,28 @@ export class FileManager {
// Create FormData for the new files only // Create FormData for the new files only
const formData = new FormData(); const formData = new FormData();
// Tell PocketBase to keep existing files // Tell PocketBase to keep existing files
formData.append(`${field}@`, ''); formData.append(`${field}@`, "");
// Append new files, renaming if needed to avoid duplicates // Append new files, renaming if needed to avoid duplicates
for (const file of files) { for (const file of files) {
let fileToUpload = file; let fileToUpload = file;
// Check if filename already exists // Check if filename already exists
if (existingFilenames.has(file.name)) { if (existingFilenames.has(file.name)) {
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
const nameParts = file.name.split('.'); const nameParts = file.name.split(".");
const extension = nameParts.pop(); const extension = nameParts.pop();
const baseName = nameParts.join('.'); const baseName = nameParts.join(".");
const newFileName = `${baseName}_${timestamp}.${extension}`; const newFileName = `${baseName}_${timestamp}.${extension}`;
// Create a new file with the modified name // Create a new file with the modified name
fileToUpload = new File([file], newFileName, { type: file.type }); fileToUpload = new File([file], newFileName, { type: file.type });
console.log(`Renamed duplicate file from ${file.name} to ${newFileName}`); // console.log(`Renamed duplicate file from ${file.name} to ${newFileName}`);
} }
formData.append(field, fileToUpload); formData.append(field, fileToUpload);
} }
@ -426,7 +443,7 @@ export class FileManager {
.update<T>(recordId, formData); .update<T>(recordId, formData);
return result; return result;
} catch (err) { } catch (err) {
console.error(`Failed to append files to ${collectionName}:`, err); // console.error(`Failed to append files to ${collectionName}:`, err);
throw err; throw err;
} finally { } finally {
this.auth.setUpdating(false); this.auth.setUpdating(false);
@ -477,7 +494,7 @@ export class FileManager {
.update<T>(recordId, data); .update<T>(recordId, data);
return result; return result;
} catch (err) { } catch (err) {
console.error(`Failed to delete file from ${collectionName}:`, err); // console.error(`Failed to delete file from ${collectionName}:`, err);
throw err; throw err;
} finally { } finally {
this.auth.setUpdating(false); this.auth.setUpdating(false);
@ -512,7 +529,7 @@ export class FileManager {
const result = await response.blob(); const result = await response.blob();
return result; return result;
} catch (err) { } catch (err) {
console.error(`Failed to download file from ${collectionName}:`, err); // console.error(`Failed to download file from ${collectionName}:`, err);
throw err; throw err;
} finally { } finally {
this.auth.setUpdating(false); this.auth.setUpdating(false);
@ -552,7 +569,7 @@ export class FileManager {
return fileUrls; return fileUrls;
} catch (err) { } catch (err) {
console.error(`Failed to get files from ${collectionName}:`, err); // console.error(`Failed to get files from ${collectionName}:`, err);
throw err; throw err;
} finally { } finally {
this.auth.setUpdating(false); this.auth.setUpdating(false);
@ -643,22 +660,22 @@ export class FileManager {
public async getFileToken(): Promise<string> { public async getFileToken(): Promise<string> {
// Check authentication status // Check authentication status
if (!this.auth.isAuthenticated()) { if (!this.auth.isAuthenticated()) {
console.warn("User is not authenticated when trying to get file token"); // console.warn("User is not authenticated when trying to get file token");
// Try to refresh the auth if possible // Try to refresh the auth if possible
try { try {
const pb = this.auth.getPocketBase(); const pb = this.auth.getPocketBase();
if (pb.authStore.isValid) { if (pb.authStore.isValid) {
console.log( // console.log(
"Auth store is valid, but auth check failed. Trying to refresh token.", // "Auth store is valid, but auth check failed. Trying to refresh token.",
); // );
await pb.collection("users").authRefresh(); await pb.collection("users").authRefresh();
console.log("Auth refreshed successfully"); // console.log("Auth refreshed successfully");
} else { } else {
throw new Error("User must be authenticated to get a file token"); throw new Error("User must be authenticated to get a file token");
} }
} catch (refreshError) { } catch (refreshError) {
console.error("Failed to refresh authentication:", refreshError); // console.error("Failed to refresh authentication:", refreshError);
throw new Error("User must be authenticated to get a file token"); throw new Error("User must be authenticated to get a file token");
} }
} }
@ -668,19 +685,19 @@ export class FileManager {
const pb = this.auth.getPocketBase(); const pb = this.auth.getPocketBase();
// Log auth status // Log auth status
console.log("Auth status before getting token:", { // console.log("Auth status before getting token:", {
isValid: pb.authStore.isValid, // isValid: pb.authStore.isValid,
token: pb.authStore.token // token: pb.authStore.token
? pb.authStore.token.substring(0, 10) + "..." // ? pb.authStore.token.substring(0, 10) + "..."
: "none", // : "none",
model: pb.authStore.model ? pb.authStore.model.id : "none", // model: pb.authStore.model ? pb.authStore.model.id : "none",
}); // });
const result = await pb.files.getToken(); const result = await pb.files.getToken();
console.log("Got file token:", result.substring(0, 10) + "..."); // console.log("Got file token:", result.substring(0, 10) + "...");
return result; return result;
} catch (err) { } catch (err) {
console.error("Failed to get file token:", err); // console.error("Failed to get file token:", err);
throw err; throw err;
} finally { } finally {
this.auth.setUpdating(false); this.auth.setUpdating(false);
@ -705,25 +722,25 @@ export class FileManager {
// Check if filename is empty // Check if filename is empty
if (!filename) { if (!filename) {
console.error( // console.error(
`Empty filename provided for ${collectionName}/${recordId}`, // `Empty filename provided for ${collectionName}/${recordId}`,
); // );
return ""; return "";
} }
// Check if user is authenticated // Check if user is authenticated
if (!this.auth.isAuthenticated()) { if (!this.auth.isAuthenticated()) {
console.warn("User is not authenticated when trying to get file URL"); // console.warn("User is not authenticated when trying to get file URL");
} }
// Always try to use token for protected files // Always try to use token for protected files
if (useToken) { if (useToken) {
try { try {
console.log( // console.log(
`Getting file token for ${collectionName}/${recordId}/${filename}`, // `Getting file token for ${collectionName}/${recordId}/${filename}`,
); // );
const token = await this.getFileToken(); const token = await this.getFileToken();
console.log(`Got token: ${token.substring(0, 10)}...`); // console.log(`Got token: ${token.substring(0, 10)}...`);
// Make sure to pass the token as a query parameter // Make sure to pass the token as a query parameter
const url = pb.files.getURL( const url = pb.files.getURL(
@ -731,16 +748,16 @@ export class FileManager {
filename, filename,
{ token }, { token },
); );
console.log(`Generated URL with token: ${url.substring(0, 50)}...`); // console.log(`Generated URL with token: ${url.substring(0, 50)}...`);
return url; return url;
} catch (error) { } catch (error) {
console.error("Error getting file token:", error); // console.error("Error getting file token:", error);
// Fall back to URL without token // Fall back to URL without token
const url = pb.files.getURL( const url = pb.files.getURL(
{ id: recordId, collectionId: collectionName }, { id: recordId, collectionId: collectionName },
filename, filename,
); );
console.log(`Fallback URL without token: ${url.substring(0, 50)}...`); // console.log(`Fallback URL without token: ${url.substring(0, 50)}...`);
return url; return url;
} }
} }
@ -750,7 +767,7 @@ export class FileManager {
{ id: recordId, collectionId: collectionName }, { id: recordId, collectionId: collectionName },
filename, filename,
); );
console.log(`Generated URL without token: ${url.substring(0, 50)}...`); // console.log(`Generated URL without token: ${url.substring(0, 50)}...`);
return url; return url;
} }
} }