IEEE UCSD Store
-IEEE UCSD Store
+Member Management
+Name | +Member ID | +Points | +Resume | +Last Updated | +
---|
diff --git a/src/components/auth/StoreAuth.ts b/src/components/auth/StoreAuth.ts index 8e9afcc..2570132 100644 --- a/src/components/auth/StoreAuth.ts +++ b/src/components/auth/StoreAuth.ts @@ -1,46 +1,59 @@ import PocketBase from "pocketbase"; interface AuthElements { - loginButton: HTMLButtonElement; - logoutButton: HTMLButtonElement; - userInfo: HTMLDivElement; - userName: HTMLParagraphElement; - userEmail: HTMLParagraphElement; - memberStatus: HTMLDivElement; - lastLogin: HTMLParagraphElement; - storeContent: HTMLDivElement; - resumeUpload: HTMLInputElement; - resumeName: HTMLParagraphElement; - resumeDownload: HTMLAnchorElement; - deleteResume: HTMLButtonElement; - uploadStatus: HTMLParagraphElement; - resumeActions: HTMLDivElement; - memberIdInput: HTMLInputElement; - saveMemberId: HTMLButtonElement; - memberIdStatus: HTMLParagraphElement; + loginButton: HTMLButtonElement; + logoutButton: HTMLButtonElement; + userInfo: HTMLDivElement; + userName: HTMLParagraphElement; + userEmail: HTMLParagraphElement; + memberStatus: HTMLDivElement; + lastLogin: HTMLParagraphElement; + storeContent: HTMLDivElement; + resumeUpload: HTMLInputElement; + resumeName: HTMLParagraphElement; + resumeDownload: HTMLAnchorElement; + deleteResume: HTMLButtonElement; + uploadStatus: HTMLParagraphElement; + resumeActions: HTMLDivElement; + memberIdInput: HTMLInputElement; + saveMemberId: HTMLButtonElement; + memberIdStatus: HTMLParagraphElement; + officerViewToggle: HTMLDivElement; + officerViewCheckbox: HTMLInputElement; + officerContent: HTMLDivElement; + resumeList: HTMLTableSectionElement; + refreshResumes: HTMLButtonElement; + resumeSearch: HTMLInputElement; + searchResumes: HTMLButtonElement; } export class StoreAuth { - private pb: PocketBase; - private elements: AuthElements & { loadingSkeleton: HTMLDivElement }; - private isEditingMemberId: boolean = false; + private pb: PocketBase; + private elements: AuthElements & { loadingSkeleton: HTMLDivElement }; + private isEditingMemberId: boolean = false; - constructor() { - this.pb = new PocketBase("https://pocketbase.ieeeucsd.org"); - this.elements = this.getElements(); - this.init(); - } + constructor() { + this.pb = new PocketBase("https://pocketbase.ieeeucsd.org"); + this.elements = this.getElements(); + this.init(); + } - private getElements(): AuthElements & { loadingSkeleton: HTMLDivElement } { - // Fun typescript fixes - const loginButton = document.getElementById("loginButton") as HTMLButtonElement; - const logoutButton = document.getElementById("logoutButton") as HTMLButtonElement; - const userInfo = document.getElementById("userInfo") as HTMLDivElement; - const loadingSkeleton = document.getElementById("loadingSkeleton") as HTMLDivElement; - - // Add CSS for loading state transitions - const style = document.createElement('style'); - style.textContent = ` + private getElements(): AuthElements & { loadingSkeleton: HTMLDivElement } { + // Fun typescript fixes + const loginButton = document.getElementById( + "loginButton", + ) as HTMLButtonElement; + const logoutButton = document.getElementById( + "logoutButton", + ) as HTMLButtonElement; + const userInfo = document.getElementById("userInfo") as HTMLDivElement; + const loadingSkeleton = document.getElementById( + "loadingSkeleton", + ) as HTMLDivElement; + + // Add CSS for loading state transitions + const style = document.createElement("style"); + style.textContent = ` .loading-state { opacity: 0.5; transition: opacity 0.3s ease-in-out; @@ -49,334 +62,826 @@ export class StoreAuth { opacity: 1; } `; - document.head.appendChild(style); + document.head.appendChild(style); - const userName = document.getElementById("userName") as HTMLParagraphElement; - const userEmail = document.getElementById("userEmail") as HTMLParagraphElement; - const memberStatus = document.getElementById("memberStatus") as HTMLDivElement; - const lastLogin = document.getElementById("lastLogin") as HTMLParagraphElement; - const storeContent = document.getElementById("storeContent") as HTMLDivElement; - const resumeUpload = document.getElementById("resumeUpload") as HTMLInputElement; - const resumeName = document.getElementById("resumeName") as HTMLParagraphElement; - const resumeDownload = document.getElementById("resumeDownload") as HTMLAnchorElement; - const deleteResume = document.getElementById("deleteResume") as HTMLButtonElement; - const uploadStatus = document.getElementById("uploadStatus") as HTMLParagraphElement; - const resumeActions = document.getElementById("resumeActions") as HTMLDivElement; - const memberIdInput = document.getElementById("memberIdInput") as HTMLInputElement; - const saveMemberId = document.getElementById("saveMemberId") as HTMLButtonElement; - const memberIdStatus = document.getElementById("memberIdStatus") as HTMLParagraphElement; + const userName = document.getElementById( + "userName", + ) as HTMLParagraphElement; + const userEmail = document.getElementById( + "userEmail", + ) as HTMLParagraphElement; + const memberStatus = document.getElementById( + "memberStatus", + ) as HTMLDivElement; + const lastLogin = document.getElementById( + "lastLogin", + ) as HTMLParagraphElement; + const storeContent = document.getElementById( + "storeContent", + ) as HTMLDivElement; + const resumeUpload = document.getElementById( + "resumeUpload", + ) as HTMLInputElement; + const resumeName = document.getElementById( + "resumeName", + ) as HTMLParagraphElement; + const resumeDownload = document.getElementById( + "resumeDownload", + ) as HTMLAnchorElement; + const deleteResume = document.getElementById( + "deleteResume", + ) as HTMLButtonElement; + const uploadStatus = document.getElementById( + "uploadStatus", + ) as HTMLParagraphElement; + const resumeActions = document.getElementById( + "resumeActions", + ) as HTMLDivElement; + const memberIdInput = document.getElementById( + "memberIdInput", + ) as HTMLInputElement; + const saveMemberId = document.getElementById( + "saveMemberId", + ) as HTMLButtonElement; + const memberIdStatus = document.getElementById( + "memberIdStatus", + ) as HTMLParagraphElement; + const officerViewToggle = document.getElementById( + "officerViewToggle", + ) as HTMLDivElement; + const officerViewCheckbox = officerViewToggle?.querySelector( + 'input[type="checkbox"]', + ) as HTMLInputElement; + const officerContent = document.getElementById( + "officerContent", + ) as HTMLDivElement; + const resumeList = document.getElementById( + "resumeList", + ) as HTMLTableSectionElement; + const refreshResumes = document.getElementById( + "refreshResumes", + ) as HTMLButtonElement; + const resumeSearch = document.getElementById( + "resumeSearch", + ) as HTMLInputElement; + const searchResumes = document.getElementById( + "searchResumes", + ) as HTMLButtonElement; - if (!loginButton || !logoutButton || !userInfo || !storeContent || !userName || !userEmail || - !memberStatus || !lastLogin || !resumeUpload || !resumeName || !loadingSkeleton || - !resumeDownload || !deleteResume || !uploadStatus || !resumeActions || - !memberIdInput || !saveMemberId || !memberIdStatus) { - throw new Error("Required DOM elements not found"); - } - - return { - loginButton, logoutButton, userInfo, userName, userEmail, memberStatus, - lastLogin, storeContent, resumeUpload, resumeName, loadingSkeleton, - resumeDownload, deleteResume, uploadStatus, resumeActions, - memberIdInput, saveMemberId, memberIdStatus - }; + if ( + !loginButton || + !logoutButton || + !userInfo || + !storeContent || + !userName || + !userEmail || + !memberStatus || + !lastLogin || + !resumeUpload || + !resumeName || + !loadingSkeleton || + !resumeDownload || + !deleteResume || + !uploadStatus || + !resumeActions || + !memberIdInput || + !saveMemberId || + !memberIdStatus || + !officerViewToggle || + !officerViewCheckbox || + !officerContent || + !resumeList || + !refreshResumes || + !resumeSearch || + !searchResumes + ) { + throw new Error("Required DOM elements not found"); } - private updateMemberIdState() { - const { memberIdInput, saveMemberId } = this.elements; - const user = this.pb.authStore.model; + return { + loginButton, + logoutButton, + userInfo, + userName, + userEmail, + memberStatus, + lastLogin, + storeContent, + resumeUpload, + resumeName, + loadingSkeleton, + resumeDownload, + deleteResume, + uploadStatus, + resumeActions, + memberIdInput, + saveMemberId, + memberIdStatus, + officerViewToggle, + officerViewCheckbox, + officerContent, + resumeList, + refreshResumes, + resumeSearch, + searchResumes, + }; + } - if (user?.member_id && !this.isEditingMemberId) { - // Has member ID and not editing - show update button and disable input - memberIdInput.disabled = true; - memberIdInput.value = user.member_id; - saveMemberId.textContent = "Update"; - saveMemberId.classList.remove("btn-primary"); - saveMemberId.classList.add("btn-ghost"); - } else { - // No member ID or editing - show save button and enable input - memberIdInput.disabled = false; - saveMemberId.textContent = "Save"; - saveMemberId.classList.remove("btn-ghost"); - saveMemberId.classList.add("btn-primary"); - } + private updateMemberIdState() { + const { memberIdInput, saveMemberId } = this.elements; + const user = this.pb.authStore.model; + + if (user?.member_id && !this.isEditingMemberId) { + // Has member ID and not editing - show update button and disable input + memberIdInput.disabled = true; + memberIdInput.value = user.member_id; + saveMemberId.textContent = "Update"; + saveMemberId.classList.remove("btn-primary"); + saveMemberId.classList.add("btn-ghost"); + } else { + // No member ID or editing - show save button and enable input + memberIdInput.disabled = false; + saveMemberId.textContent = "Save"; + saveMemberId.classList.remove("btn-ghost"); + saveMemberId.classList.add("btn-primary"); } + } - private async updateUI() { - const { loginButton, logoutButton, userInfo, userName, userEmail, memberStatus, - lastLogin, storeContent, resumeName, resumeDownload, resumeActions, - memberIdInput, saveMemberId, resumeUpload, loadingSkeleton } = this.elements; + private async updateUI() { + const { + loginButton, + logoutButton, + userInfo, + userName, + userEmail, + memberStatus, + lastLogin, + storeContent, + resumeName, + resumeDownload, + resumeActions, + memberIdInput, + saveMemberId, + resumeUpload, + loadingSkeleton, + officerViewToggle, + officerContent, + } = this.elements; - // Hide buttons initially - loginButton.style.display = 'none'; - logoutButton.style.display = 'none'; + // Hide buttons initially + loginButton.style.display = "none"; + logoutButton.style.display = "none"; - if (this.pb.authStore.isValid && this.pb.authStore.model) { - // Update all the user information first - const user = this.pb.authStore.model; - userName.textContent = user.name || "Name not provided"; - userEmail.textContent = user.email || "Email not available"; - - // Update member status - if (user.verified) { - // Check and update member_type if not set - if (!user.member_type) { - try { - const isIeeeOfficer = user.email?.toLowerCase().endsWith('@ieeeucsd.org') || false; - const newMemberType = isIeeeOfficer ? "IEEE Officer" : "Regular Member"; - - await this.pb.collection("users").update(user.id, { - member_type: newMemberType - }); - - user.member_type = newMemberType; - } catch (err) { - console.error("Failed to update member type:", err); - } - } + if (this.pb.authStore.isValid && this.pb.authStore.model) { + // Update all the user information first + const user = this.pb.authStore.model; + userName.textContent = user.name || "Name not provided"; + userEmail.textContent = user.email || "Email not available"; - memberStatus.textContent = user.member_type || "Regular Member"; - memberStatus.classList.remove("badge-neutral", "badge-success", "badge-warning", "badge-info", "badge-error"); - - // Set color based on member type - if (user.member_type === "IEEE Administrator") { - memberStatus.classList.add("badge-warning"); // Red for administrators - } else if (user.member_type === "IEEE Officer") { - memberStatus.classList.add("badge-info"); // Blue for officers - } else { - memberStatus.classList.add("badge-neutral"); // Yellow for regular members - } - } else { - memberStatus.textContent = "Not Verified"; - memberStatus.classList.remove("badge-info", "badge-warning", "badge-success", "badge-error"); - memberStatus.classList.add("badge-neutral"); - } - - // Update member ID input and state - memberIdInput.value = user.member_id || ""; - this.updateMemberIdState(); - - // Update last login - const lastLoginDate = user.last_login ? new Date(user.last_login).toLocaleString() : "Never"; - lastLogin.textContent = lastLoginDate; - - // Update resume section - if (user.resume && (!Array.isArray(user.resume) || user.resume.length > 0)) { - const resumeUrl = user.resume.toString(); - resumeName.textContent = this.getFileNameFromUrl(resumeUrl); - resumeDownload.href = this.pb.files.getURL(user, resumeUrl); - resumeActions.style.display = 'flex'; - } else { - resumeName.textContent = "No resume uploaded"; - resumeDownload.href = "#"; - resumeActions.style.display = 'none'; - } - - // After everything is updated, show the content - loadingSkeleton.style.display = 'none'; - userInfo.classList.remove('hidden'); - // Use a small delay to ensure the transition works - setTimeout(() => { - userInfo.style.opacity = '1'; - }, 50); - - logoutButton.style.display = 'block'; - } else { - // Update for logged out state - userName.textContent = "Not signed in"; - userEmail.textContent = "Not signed in"; - memberStatus.textContent = "Not verified"; - memberStatus.classList.remove("badge-info", "badge-warning", "badge-success", "badge-error"); - memberStatus.classList.add("badge-neutral"); - lastLogin.textContent = "Never"; - - // Reset member ID - memberIdInput.value = ""; - memberIdInput.disabled = true; - this.isEditingMemberId = false; - this.updateMemberIdState(); - - // Reset resume section - resumeName.textContent = "No resume uploaded"; - resumeDownload.href = "#"; - resumeActions.style.display = 'none'; - - // After everything is updated, show the content - loadingSkeleton.style.display = 'none'; - userInfo.classList.remove('hidden'); - // Use a small delay to ensure the transition works - setTimeout(() => { - userInfo.style.opacity = '1'; - }, 50); - - loginButton.style.display = 'block'; - } - } - - private getFileNameFromUrl(url: string): string { - const parts = url.split("/"); - return parts[parts.length - 1]; - } - - private async handleMemberIdButton() { - const user = this.pb.authStore.model; - - if (user?.member_id && !this.isEditingMemberId) { - // If we have a member ID and we're not editing, switch to edit mode - this.isEditingMemberId = true; - this.updateMemberIdState(); - } else { - // If we're editing or don't have a member ID, try to save - await this.handleMemberIdSave(); - } - } - - private async handleMemberIdSave() { - const { memberIdInput, memberIdStatus } = this.elements; - const memberId = memberIdInput.value.trim(); - - try { - memberIdStatus.textContent = "Saving member ID..."; - - const user = this.pb.authStore.model; - if (!user?.id) { - throw new Error("User ID not found"); - } + // Update member status + if (user.verified) { + // Check and update member_type if not set + if (!user.member_type) { + try { + const isIeeeOfficer = + user.email?.toLowerCase().endsWith("@ieeeucsd.org") || false; + const newMemberType = isIeeeOfficer + ? "IEEE Officer" + : "Regular Member"; await this.pb.collection("users").update(user.id, { - member_id: memberId + member_type: newMemberType, }); - - memberIdStatus.textContent = "IEEE Member ID saved successfully!"; - this.isEditingMemberId = false; - this.updateUI(); - - // Clear the status message after a delay - setTimeout(() => { - memberIdStatus.textContent = ""; - }, 3000); - } catch (err: any) { - console.error("IEEE Member ID save error:", err); - memberIdStatus.textContent = "Failed to save IEEE Member ID. Please try again."; + + user.member_type = newMemberType; + } catch (err) { + console.error("Failed to update member type:", err); + } } - } - private async handleResumeUpload(file: File) { - const { uploadStatus } = this.elements; - - try { - uploadStatus.textContent = "Uploading resume..."; - - const formData = new FormData(); - formData.append("resume", file); + memberStatus.textContent = user.member_type || "Regular Member"; + memberStatus.classList.remove( + "badge-neutral", + "badge-success", + "badge-warning", + "badge-info", + "badge-error", + ); - const user = this.pb.authStore.model; - if (!user?.id) { - throw new Error("User ID not found"); - } - - await this.pb.collection("users").update(user.id, formData); - - uploadStatus.textContent = "Resume uploaded successfully!"; - this.updateUI(); - - // Clear the file input - this.elements.resumeUpload.value = ""; - - // Clear the status message after a delay - setTimeout(() => { - uploadStatus.textContent = ""; - }, 3000); - } catch (err: any) { - console.error("Resume upload error:", err); - uploadStatus.textContent = "Failed to upload resume. Please try again."; + // Set color based on member type + if (user.member_type === "IEEE Administrator") { + memberStatus.classList.add("badge-warning"); // Red for administrators + } else if (user.member_type === "IEEE Officer") { + memberStatus.classList.add("badge-info"); // Blue for officers + } else { + memberStatus.classList.add("badge-neutral"); // Yellow for regular members } + } else { + memberStatus.textContent = "Not Verified"; + memberStatus.classList.remove( + "badge-info", + "badge-warning", + "badge-success", + "badge-error", + ); + memberStatus.classList.add("badge-neutral"); + } + + // Update member ID input and state + memberIdInput.value = user.member_id || ""; + this.updateMemberIdState(); + + // Update last login + const lastLoginDate = user.last_login + ? new Date(user.last_login).toLocaleString() + : "Never"; + lastLogin.textContent = lastLoginDate; + + // Update resume section + if ( + user.resume && + (!Array.isArray(user.resume) || user.resume.length > 0) + ) { + const resumeUrl = user.resume.toString(); + resumeName.textContent = this.getFileNameFromUrl(resumeUrl); + resumeDownload.href = this.pb.files.getURL(user, resumeUrl); + resumeActions.style.display = "flex"; + } else { + resumeName.textContent = "No resume uploaded"; + resumeDownload.href = "#"; + resumeActions.style.display = "none"; + } + + // Handle officer view toggle visibility and data loading + const isOfficer = [ + "IEEE Officer", + "IEEE Administrator", + "IEEE Events", + ].includes(user.member_type || ""); + + officerViewToggle.style.display = isOfficer ? "block" : "none"; + + // If user is an officer, preload the table data + if (isOfficer) { + await this.fetchUserResumes(); + } + + // After everything is updated, show the content + loadingSkeleton.style.display = "none"; + userInfo.classList.remove("hidden"); + // Use a small delay to ensure the transition works + setTimeout(() => { + userInfo.style.opacity = "1"; + }, 50); + + logoutButton.style.display = "block"; + } else { + // Update for logged out state + userName.textContent = "Not signed in"; + userEmail.textContent = "Not signed in"; + memberStatus.textContent = "Not verified"; + memberStatus.classList.remove( + "badge-info", + "badge-warning", + "badge-success", + "badge-error", + ); + memberStatus.classList.add("badge-neutral"); + lastLogin.textContent = "Never"; + + // Reset member ID + memberIdInput.value = ""; + memberIdInput.disabled = true; + this.isEditingMemberId = false; + this.updateMemberIdState(); + + // Reset resume section + resumeName.textContent = "No resume uploaded"; + resumeDownload.href = "#"; + resumeActions.style.display = "none"; + + // After everything is updated, show the content + loadingSkeleton.style.display = "none"; + userInfo.classList.remove("hidden"); + // Use a small delay to ensure the transition works + setTimeout(() => { + userInfo.style.opacity = "1"; + }, 50); + + loginButton.style.display = "block"; + officerViewToggle.style.display = "none"; } + } - private async handleResumeDelete() { - const { uploadStatus } = this.elements; - - try { - uploadStatus.textContent = "Deleting resume..."; - - const user = this.pb.authStore.model; - if (!user?.id) { - throw new Error("User ID not found"); - } + private getFileNameFromUrl(url: string): string { + const parts = url.split("/"); + return parts[parts.length - 1]; + } - await this.pb.collection("users").update(user.id, { - "resume": null - }); - - uploadStatus.textContent = "Resume deleted successfully!"; - this.updateUI(); - - // Clear the status message after a delay - setTimeout(() => { - uploadStatus.textContent = ""; - }, 3000); - } catch (err: any) { - console.error("Resume deletion error:", err); - uploadStatus.textContent = "Failed to delete resume. Please try again."; + private async handleMemberIdButton() { + const user = this.pb.authStore.model; + + if (user?.member_id && !this.isEditingMemberId) { + // If we have a member ID and we're not editing, switch to edit mode + this.isEditingMemberId = true; + this.updateMemberIdState(); + } else { + // If we're editing or don't have a member ID, try to save + await this.handleMemberIdSave(); + } + } + + private async handleMemberIdSave() { + const { memberIdInput, memberIdStatus } = this.elements; + const memberId = memberIdInput.value.trim(); + + try { + memberIdStatus.textContent = "Saving member ID..."; + + const user = this.pb.authStore.model; + if (!user?.id) { + throw new Error("User ID not found"); + } + + await this.pb.collection("users").update(user.id, { + member_id: memberId, + }); + + memberIdStatus.textContent = "IEEE Member ID saved successfully!"; + this.isEditingMemberId = false; + this.updateUI(); + + // Clear the status message after a delay + setTimeout(() => { + memberIdStatus.textContent = ""; + }, 3000); + } catch (err: any) { + console.error("IEEE Member ID save error:", err); + memberIdStatus.textContent = + "Failed to save IEEE Member ID. Please try again."; + } + } + + private async handleResumeUpload(file: File) { + const { uploadStatus } = this.elements; + + try { + uploadStatus.textContent = "Uploading resume..."; + + const formData = new FormData(); + formData.append("resume", file); + + const user = this.pb.authStore.model; + if (!user?.id) { + throw new Error("User ID not found"); + } + + await this.pb.collection("users").update(user.id, formData); + + uploadStatus.textContent = "Resume uploaded successfully!"; + this.updateUI(); + + // Clear the file input + this.elements.resumeUpload.value = ""; + + // Clear the status message after a delay + setTimeout(() => { + uploadStatus.textContent = ""; + }, 3000); + } catch (err: any) { + console.error("Resume upload error:", err); + uploadStatus.textContent = "Failed to upload resume. Please try again."; + } + } + + private async handleResumeDelete() { + const { uploadStatus } = this.elements; + + try { + uploadStatus.textContent = "Deleting resume..."; + + const user = this.pb.authStore.model; + if (!user?.id) { + throw new Error("User ID not found"); + } + + await this.pb.collection("users").update(user.id, { + resume: null, + }); + + uploadStatus.textContent = "Resume deleted successfully!"; + this.updateUI(); + + // Clear the status message after a delay + setTimeout(() => { + uploadStatus.textContent = ""; + }, 3000); + } catch (err: any) { + console.error("Resume deletion error:", err); + uploadStatus.textContent = "Failed to delete resume. Please try again."; + } + } + + private async handleLogin() { + console.log("Starting OAuth2 authentication..."); + try { + const authMethods = await this.pb.collection("users").listAuthMethods(); + const oidcProvider = authMethods.oauth2?.providers?.find( + (p: { name: string }) => p.name === "oidc", + ); + + if (!oidcProvider) { + throw new Error("OIDC provider not found"); + } + + // Store provider info for the redirect page + localStorage.setItem("provider", JSON.stringify(oidcProvider)); + + // Redirect to the authorization URL + const redirectUrl = window.location.origin + "/oauth2-redirect"; + const authUrl = oidcProvider.authURL + encodeURIComponent(redirectUrl); + window.location.href = authUrl; + } catch (err: any) { + console.error("Authentication error:", err); + this.elements.userEmail.textContent = "Failed to start authentication"; + this.elements.userName.textContent = "Error"; + } + } + + private handleLogout() { + this.pb.authStore.clear(); + this.updateUI(); + } + + private async fetchUserResumes(searchQuery: string = "") { + try { + let filter = 'resume != ""'; + if (searchQuery) { + const terms = searchQuery + .toLowerCase() + .split(" ") + .filter((term) => term.length > 0); + if (terms.length > 0) { + const searchConditions = terms + .map( + (term) => + `(name ?~ "${term}" || email ?~ "${term}" || member_id ?~ "${term}")`, + ) + .join(" && "); + filter += ` && (${searchConditions})`; } - } + } - private async handleLogin() { - console.log("Starting OAuth2 authentication..."); - try { - const authMethods = await this.pb.collection("users").listAuthMethods(); - const oidcProvider = authMethods.oauth2?.providers?.find( - (p: { name: string }) => p.name === "oidc" - ); + const records = await this.pb.collection("users").getList(1, 50, { + filter, + sort: "-updated", + fields: "id,name,email,member_id,resume,updated,points", + }); - if (!oidcProvider) { - throw new Error("OIDC provider not found"); - } + const { resumeList } = this.elements; + const fragment = document.createDocumentFragment(); - // Store provider info for the redirect page - localStorage.setItem("provider", JSON.stringify(oidcProvider)); + if (records.items.length === 0) { + const row = document.createElement("tr"); + row.innerHTML = ` +