From 36840716734ff5a5ebc26483966430dcd17e371d Mon Sep 17 00:00:00 2001 From: chark1es Date: Mon, 27 Jan 2025 16:46:23 -0800 Subject: [PATCH] allows editing profiles --- src/components/auth/StoreAuth.ts | 394 +++++++++++++------------- src/components/auth/UserProfile.astro | 76 +++++ src/pages/online-store.astro | 2 +- 3 files changed, 280 insertions(+), 192 deletions(-) diff --git a/src/components/auth/StoreAuth.ts b/src/components/auth/StoreAuth.ts index 2570132..21e43ad 100644 --- a/src/components/auth/StoreAuth.ts +++ b/src/components/auth/StoreAuth.ts @@ -25,6 +25,14 @@ interface AuthElements { refreshResumes: HTMLButtonElement; resumeSearch: HTMLInputElement; searchResumes: HTMLButtonElement; + profileEditor: HTMLDialogElement; + editorName: HTMLInputElement; + editorEmail: HTMLInputElement; + editorMemberId: HTMLInputElement; + editorPoints: HTMLInputElement; + editorResume: HTMLInputElement; + editorCurrentResume: HTMLParagraphElement; + saveProfileButton: HTMLButtonElement; } export class StoreAuth { @@ -128,6 +136,31 @@ export class StoreAuth { "searchResumes", ) as HTMLButtonElement; + const profileEditor = document.getElementById( + "profileEditor", + ) as HTMLDialogElement; + const editorName = document.getElementById( + "editorName", + ) as HTMLInputElement; + const editorEmail = document.getElementById( + "editorEmail", + ) as HTMLInputElement; + const editorMemberId = document.getElementById( + "editorMemberId", + ) as HTMLInputElement; + const editorPoints = document.getElementById( + "editorPoints", + ) as HTMLInputElement; + const editorResume = document.getElementById( + "editorResume", + ) as HTMLInputElement; + const editorCurrentResume = document.getElementById( + "editorCurrentResume", + ) as HTMLParagraphElement; + const saveProfileButton = document.getElementById( + "saveProfileButton", + ) as HTMLButtonElement; + if ( !loginButton || !logoutButton || @@ -153,7 +186,15 @@ export class StoreAuth { !resumeList || !refreshResumes || !resumeSearch || - !searchResumes + !searchResumes || + !profileEditor || + !editorName || + !editorEmail || + !editorMemberId || + !editorPoints || + !editorResume || + !editorCurrentResume || + !saveProfileButton ) { throw new Error("Required DOM elements not found"); } @@ -184,6 +225,14 @@ export class StoreAuth { refreshResumes, resumeSearch, searchResumes, + profileEditor, + editorName, + editorEmail, + editorMemberId, + editorPoints, + editorResume, + editorCurrentResume, + saveProfileButton, }; } @@ -374,8 +423,13 @@ export class StoreAuth { } private getFileNameFromUrl(url: string): string { - const parts = url.split("/"); - return parts[parts.length - 1]; + try { + const urlObj = new URL(url); + const pathParts = urlObj.pathname.split("/"); + return decodeURIComponent(pathParts[pathParts.length - 1]); + } catch (e) { + return url.split("/").pop() || "Unknown File"; + } } private async handleMemberIdButton() { @@ -436,6 +490,15 @@ export class StoreAuth { throw new Error("User ID not found"); } + // Get current user data first + const currentUser = await this.pb.collection("users").getOne(user.id); + + // Keep existing data + formData.append("name", currentUser.name || ""); + formData.append("email", currentUser.email || ""); + formData.append("member_id", currentUser.member_id || ""); + formData.append("points", currentUser.points?.toString() || "0"); + await this.pb.collection("users").update(user.id, formData); uploadStatus.textContent = "Resume uploaded successfully!"; @@ -515,7 +578,7 @@ export class StoreAuth { private async fetchUserResumes(searchQuery: string = "") { try { - let filter = 'resume != ""'; + let filter = ""; // Remove the resume filter to show all users if (searchQuery) { const terms = searchQuery .toLowerCase() @@ -528,16 +591,20 @@ export class StoreAuth { `(name ?~ "${term}" || email ?~ "${term}" || member_id ?~ "${term}")`, ) .join(" && "); - filter += ` && (${searchConditions})`; + filter = searchConditions; // Only apply search conditions } } const records = await this.pb.collection("users").getList(1, 50, { filter, sort: "-updated", - fields: "id,name,email,member_id,resume,updated,points", + fields: + "id,name,email,member_id,resume,points,collectionId,collectionName", + expand: "resume", }); + console.log("Fetched records:", records.items); // Debug log + const { resumeList } = this.elements; const fragment = document.createDocumentFragment(); @@ -545,16 +612,19 @@ export class StoreAuth { const row = document.createElement("tr"); row.innerHTML = ` - ${searchQuery ? "No users found matching your search." : "No resumes uploaded yet."} + ${searchQuery ? "No users found matching your search." : "No users found."} `; fragment.appendChild(row); } else { records.items.forEach((user) => { const row = document.createElement("tr"); - const resumeUrl = user.resume - ? this.pb.files.getURL(user, user.resume) - : null; + const resumeUrl = + user.resume && user.resume !== "" + ? this.pb.files.getURL(user, user.resume.toString()) + : null; + + console.log("User resume:", user.resume, "Resume URL:", resumeUrl); // Debug log row.innerHTML = ` @@ -563,40 +633,7 @@ export class StoreAuth {
${user.name || "N/A"}
${user.email || "N/A"}
ID: ${user.member_id || "N/A"}
-
-
- Points: -
- ${user.points || 0} - -
- -
-
+
Points: ${user.points || 0}
${ resumeUrl @@ -607,9 +644,12 @@ export class StoreAuth { ` : 'No resume' } - - ${new Date(user.updated).toLocaleDateString()} - +
@@ -618,37 +658,7 @@ export class StoreAuth { ${user.email || "N/A"} ${user.member_id || "N/A"} - -
- ${user.points || 0} - -
- - + ${user.points || 0} ${ resumeUrl @@ -660,7 +670,14 @@ export class StoreAuth { : 'No resume' } - ${new Date(user.updated).toLocaleDateString()} + + + `; fragment.appendChild(row); @@ -670,8 +687,16 @@ export class StoreAuth { resumeList.innerHTML = ""; resumeList.appendChild(fragment); - // Setup event listeners for the points editing functionality - this.setupPointsEventListeners(); + // Setup edit profile event listeners + const editButtons = resumeList.querySelectorAll(".edit-profile"); + editButtons.forEach((button) => { + button.addEventListener("click", () => { + const userId = (button as HTMLButtonElement).dataset.userId; + if (userId) { + this.handleProfileEdit(userId); + } + }); + }); } catch (err) { console.error("Failed to fetch user resumes:", err); const { resumeList } = this.elements; @@ -685,131 +710,102 @@ export class StoreAuth { } } - private async updateUserPoints(userId: string, points: number) { + private async handleProfileEdit(userId: string) { try { - await this.pb.collection("users").update(userId, { - points: points, - }); + const user = await this.pb.collection("users").getOne(userId); + const { + profileEditor, + editorName, + editorEmail, + editorMemberId, + editorPoints, + editorCurrentResume, + saveProfileButton, + } = this.elements; - // Update the display after successful update - const displayElement = document.querySelector( - `.points-display-${userId}`, - ) as HTMLDivElement; - const editElement = document.querySelector( - `.points-edit-${userId}`, - ) as HTMLDivElement; - if (displayElement && editElement) { - const pointsSpan = displayElement.querySelector("span"); - if (pointsSpan) { - pointsSpan.textContent = points.toString(); - } - displayElement.classList.remove("hidden"); - editElement.classList.add("hidden"); + // Populate the form + editorName.value = user.name || ""; + editorEmail.value = user.email || ""; + editorMemberId.value = user.member_id || ""; + editorPoints.value = user.points?.toString() || "0"; + + // Update resume display + if (user.resume) { + const resumeUrl = this.pb.files.getURL(user, user.resume.toString()); + const fileName = this.getFileNameFromUrl(resumeUrl); + editorCurrentResume.textContent = `Current resume: ${fileName}`; + editorCurrentResume.classList.remove("opacity-70"); + } else { + editorCurrentResume.textContent = "No resume uploaded"; + editorCurrentResume.classList.add("opacity-70"); } + + // Store the user ID for saving + saveProfileButton.dataset.userId = userId; + + // Show the dialog + profileEditor.showModal(); } catch (err) { - console.error("Failed to update points:", err); + console.error("Failed to load user for editing:", err); } } - private setupPointsEventListeners() { - // Use event delegation for all points-related actions - const { resumeList } = this.elements; + private async handleProfileSave() { + const { + profileEditor, + editorName, + editorEmail, + editorMemberId, + editorPoints, + editorResume, + saveProfileButton, + } = this.elements; + const userId = saveProfileButton.dataset.userId; - resumeList.addEventListener("click", async (e) => { - console.log("Click event triggered"); - const target = e.target as HTMLElement; - const button = target.closest("button"); - console.log("Button found:", button); - if (!button) return; + if (!userId) { + console.error("No user ID found for saving"); + return; + } - const userId = button.dataset.userId; - console.log("User ID:", userId); - if (!userId) return; + try { + // First get the current user data to check existing resume + const currentUser = await this.pb.collection("users").getOne(userId); - // Handle edit button click - if (button.classList.contains("edit-points")) { - console.log("Edit points button clicked"); - const row = button.closest("tr"); - if (!row) return; + const formData = new FormData(); + formData.append("name", editorName.value); + formData.append("email", editorEmail.value); + formData.append("member_id", editorMemberId.value); + formData.append("points", editorPoints.value); - const displayElement = row.querySelector( - `.points-display-${userId}`, - ) as HTMLDivElement; - const editElement = row.querySelector( - `.points-edit-${userId}`, - ) as HTMLDivElement; - - console.log("Display element:", displayElement); - console.log("Edit element:", editElement); - - if (displayElement && editElement) { - const currentPoints = - displayElement.querySelector("span")?.textContent; - console.log("Current points:", currentPoints); - const input = editElement.querySelector("input") as HTMLInputElement; - console.log("Input element:", input); - if (input && currentPoints) { - input.value = currentPoints; - } - - displayElement.classList.add("hidden"); - editElement.classList.remove("hidden"); - } + // Only append resume if a new file is selected + if (editorResume.files && editorResume.files.length > 0) { + formData.append("resume", editorResume.files[0]); + } else if (currentUser.resume) { + // If no new file but there's an existing resume, keep it + formData.append("resume", currentUser.resume); } - // Handle confirm button click - if (button.classList.contains("confirm-points")) { - const row = button.closest("tr"); - if (!row) return; + // Log the form data for debugging + console.log("Form data being sent:", { + name: editorName.value, + email: editorEmail.value, + member_id: editorMemberId.value, + points: editorPoints.value, + hasNewResume: editorResume.files && editorResume.files.length > 0, + hasExistingResume: !!currentUser.resume, + }); - const input = row.querySelector( - `input[data-user-id="${userId}"]`, - ) as HTMLInputElement; - if (input) { - const points = parseInt(input.value) || 0; - await this.updateUserPoints(userId, points); + const updatedUser = await this.pb + .collection("users") + .update(userId, formData); + console.log("Update response:", updatedUser); - const displayElement = row.querySelector( - `.points-display-${userId}`, - ) as HTMLDivElement; - const editElement = row.querySelector( - `.points-edit-${userId}`, - ) as HTMLDivElement; - - if (displayElement && editElement) { - displayElement.classList.remove("hidden"); - editElement.classList.add("hidden"); - } - } - } - - // Handle cancel button click - if (button.classList.contains("cancel-points")) { - const row = button.closest("tr"); - if (!row) return; - - const displayElement = row.querySelector( - `.points-display-${userId}`, - ) as HTMLDivElement; - const editElement = row.querySelector( - `.points-edit-${userId}`, - ) as HTMLDivElement; - const input = row.querySelector( - `input[data-user-id="${userId}"]`, - ) as HTMLInputElement; - const currentPoints = - displayElement?.querySelector("span")?.textContent; - - if (input && currentPoints) { - input.value = currentPoints; - } - - if (displayElement && editElement) { - displayElement.classList.remove("hidden"); - editElement.classList.add("hidden"); - } - } - }); + // Close the dialog and refresh the table + profileEditor.close(); + this.fetchUserResumes(); + } catch (err) { + console.error("Failed to save user profile:", err); + } } private init() { @@ -883,5 +879,21 @@ export class StoreAuth { console.log("Auth state changed. IsValid:", this.pb.authStore.isValid); this.updateUI(); }); + + // Profile editor event listeners + const { profileEditor, saveProfileButton } = this.elements; + + // Close dialog when clicking outside + profileEditor.addEventListener("click", (e) => { + if (e.target === profileEditor) { + profileEditor.close(); + } + }); + + // Save profile button + saveProfileButton.addEventListener("click", (e) => { + e.preventDefault(); + this.handleProfileSave(); + }); } } diff --git a/src/components/auth/UserProfile.astro b/src/components/auth/UserProfile.astro index b129a15..3394574 100644 --- a/src/components/auth/UserProfile.astro +++ b/src/components/auth/UserProfile.astro @@ -171,6 +171,76 @@ + + + + + + diff --git a/src/pages/online-store.astro b/src/pages/online-store.astro index 58e46f0..68dc235 100644 --- a/src/pages/online-store.astro +++ b/src/pages/online-store.astro @@ -103,7 +103,7 @@ const title = "IEEE Store"; Member ID Points Resume - Last Updated + Actions