move settings around
This commit is contained in:
parent
eea944fcd2
commit
49e1b00586
4 changed files with 380 additions and 382 deletions
|
@ -85,41 +85,20 @@ interface AuthElements {
|
||||||
memberStatus: HTMLDivElement;
|
memberStatus: HTMLDivElement;
|
||||||
lastLogin: HTMLParagraphElement;
|
lastLogin: HTMLParagraphElement;
|
||||||
storeContent: HTMLDivElement;
|
storeContent: HTMLDivElement;
|
||||||
resumeUpload: HTMLInputElement;
|
|
||||||
resumeName: HTMLParagraphElement;
|
|
||||||
resumeDownload: HTMLAnchorElement;
|
|
||||||
deleteResume: HTMLButtonElement;
|
|
||||||
uploadStatus: HTMLParagraphElement;
|
|
||||||
resumeActions: HTMLDivElement;
|
|
||||||
memberIdInput: HTMLInputElement;
|
|
||||||
saveMemberId: HTMLButtonElement;
|
|
||||||
memberIdStatus: HTMLParagraphElement;
|
|
||||||
officerViewToggle: HTMLDivElement;
|
officerViewToggle: HTMLDivElement;
|
||||||
officerViewCheckbox: HTMLInputElement;
|
officerViewCheckbox: HTMLInputElement;
|
||||||
officerContent: HTMLDivElement;
|
officerContent: HTMLDivElement;
|
||||||
resumeList: HTMLTableSectionElement;
|
|
||||||
refreshResumes: HTMLButtonElement;
|
|
||||||
resumeSearch: HTMLInputElement;
|
|
||||||
searchResumes: HTMLButtonElement;
|
|
||||||
profileEditor: HTMLDialogElement;
|
profileEditor: HTMLDialogElement;
|
||||||
editorName: HTMLInputElement;
|
editorName: HTMLInputElement;
|
||||||
editorEmail: HTMLInputElement;
|
editorEmail: HTMLInputElement;
|
||||||
editorMemberId: HTMLInputElement;
|
|
||||||
editorPoints: HTMLInputElement;
|
editorPoints: HTMLInputElement;
|
||||||
editorResume: HTMLInputElement;
|
|
||||||
editorCurrentResume: HTMLParagraphElement;
|
|
||||||
saveProfileButton: HTMLButtonElement;
|
saveProfileButton: HTMLButtonElement;
|
||||||
sponsorViewToggle: HTMLDivElement;
|
sponsorViewToggle: HTMLDivElement;
|
||||||
pdfViewer: HTMLDialogElement;
|
|
||||||
pdfFrame: HTMLIFrameElement;
|
|
||||||
pdfTitle: HTMLHeadingElement;
|
|
||||||
pdfExternalLink: HTMLAnchorElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StoreAuth {
|
export class StoreAuth {
|
||||||
private pb: PocketBase;
|
private pb: PocketBase;
|
||||||
private elements: AuthElements & { loadingSkeleton: HTMLDivElement };
|
private elements: AuthElements & { loadingSkeleton: HTMLDivElement };
|
||||||
private isEditingMemberId: boolean = false;
|
|
||||||
private cachedUsers: any[] = [];
|
private cachedUsers: any[] = [];
|
||||||
private config = config;
|
private config = config;
|
||||||
|
|
||||||
|
@ -137,6 +116,43 @@ export class StoreAuth {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public method to handle login
|
||||||
|
public async handleLogin() {
|
||||||
|
try {
|
||||||
|
const authMethods = await this.pb.collection("users").listAuthMethods();
|
||||||
|
const oidcProvider = authMethods.oauth2?.providers?.find(
|
||||||
|
(p: { name: string }) => p.name === this.config.api.oauth2.providerName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!oidcProvider) {
|
||||||
|
throw new Error("OIDC provider not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("provider", JSON.stringify(oidcProvider));
|
||||||
|
const redirectUrl = window.location.origin + this.config.api.oauth2.redirectPath;
|
||||||
|
const authUrl = oidcProvider.authURL + encodeURIComponent(redirectUrl);
|
||||||
|
window.location.href = authUrl;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Authentication error:", err);
|
||||||
|
this.elements.userEmail.textContent = this.config.ui.messages.auth.loginError;
|
||||||
|
this.elements.userName.textContent = "Error";
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public method to update profile settings
|
||||||
|
public async updateProfileSettings(data: {
|
||||||
|
major?: string;
|
||||||
|
graduation_year?: string | number;
|
||||||
|
}) {
|
||||||
|
const user = this.pb.authStore.model;
|
||||||
|
if (!user?.id) {
|
||||||
|
throw new Error("User ID not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.pb.collection("users").update(user.id, data);
|
||||||
|
}
|
||||||
|
|
||||||
private getElements(): AuthElements & { loadingSkeleton: HTMLDivElement } {
|
private getElements(): AuthElements & { loadingSkeleton: HTMLDivElement } {
|
||||||
// Get all required elements
|
// Get all required elements
|
||||||
const loginButton = document.getElementById("contentLoginButton") as HTMLButtonElement;
|
const loginButton = document.getElementById("contentLoginButton") as HTMLButtonElement;
|
||||||
|
@ -148,47 +164,27 @@ export class StoreAuth {
|
||||||
const memberStatus = document.getElementById("memberStatus") as HTMLDivElement;
|
const memberStatus = document.getElementById("memberStatus") as HTMLDivElement;
|
||||||
const lastLogin = document.getElementById("lastLogin") as HTMLParagraphElement;
|
const lastLogin = document.getElementById("lastLogin") as HTMLParagraphElement;
|
||||||
const storeContent = document.getElementById("storeContent") as HTMLDivElement;
|
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 officerViewToggle = document.getElementById("officerViewToggle") as HTMLDivElement;
|
||||||
const officerViewCheckbox = officerViewToggle?.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
const officerViewCheckbox = officerViewToggle?.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||||
const officerContent = document.getElementById("officerContent") as HTMLDivElement;
|
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;
|
|
||||||
const profileEditor = document.getElementById("profileEditor") as HTMLDialogElement;
|
const profileEditor = document.getElementById("profileEditor") as HTMLDialogElement;
|
||||||
const editorName = document.getElementById("editorName") as HTMLInputElement;
|
const editorName = document.getElementById("editorName") as HTMLInputElement;
|
||||||
const editorEmail = document.getElementById("editorEmail") as HTMLInputElement;
|
const editorEmail = document.getElementById("editorEmail") as HTMLInputElement;
|
||||||
const editorMemberId = document.getElementById("editorMemberId") as HTMLInputElement;
|
|
||||||
const editorPoints = document.getElementById("editorPoints") 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;
|
const saveProfileButton = document.getElementById("saveProfileButton") as HTMLButtonElement;
|
||||||
const sponsorViewToggle = document.getElementById("sponsorViewToggle") as HTMLDivElement;
|
const sponsorViewToggle = document.getElementById("sponsorViewToggle") as HTMLDivElement;
|
||||||
const pdfViewer = document.getElementById("pdfViewer") as HTMLDialogElement;
|
|
||||||
const pdfFrame = document.getElementById("pdfFrame") as HTMLIFrameElement;
|
|
||||||
const pdfTitle = document.getElementById("pdfTitle") as HTMLHeadingElement;
|
|
||||||
const pdfExternalLink = document.getElementById("pdfExternalLink") as HTMLAnchorElement;
|
|
||||||
|
|
||||||
// Add CSS for loading state transitions
|
// Add CSS for loading state transitions
|
||||||
const style = document.createElement("style");
|
const style = document.createElement("style");
|
||||||
style.textContent = `
|
style.textContent = `
|
||||||
.loading-state {
|
.loading-state {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: opacity 0.3s ease-in-out;
|
transition: opacity 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
.content-ready {
|
.content-ready {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -201,35 +197,15 @@ export class StoreAuth {
|
||||||
memberStatus,
|
memberStatus,
|
||||||
lastLogin,
|
lastLogin,
|
||||||
storeContent,
|
storeContent,
|
||||||
resumeUpload,
|
|
||||||
resumeName,
|
|
||||||
resumeDownload,
|
|
||||||
deleteResume,
|
|
||||||
uploadStatus,
|
|
||||||
resumeActions,
|
|
||||||
memberIdInput,
|
|
||||||
saveMemberId,
|
|
||||||
memberIdStatus,
|
|
||||||
officerViewToggle,
|
officerViewToggle,
|
||||||
officerViewCheckbox,
|
officerViewCheckbox,
|
||||||
officerContent,
|
officerContent,
|
||||||
resumeList,
|
|
||||||
refreshResumes,
|
|
||||||
resumeSearch,
|
|
||||||
searchResumes,
|
|
||||||
profileEditor,
|
profileEditor,
|
||||||
editorName,
|
editorName,
|
||||||
editorEmail,
|
editorEmail,
|
||||||
editorMemberId,
|
|
||||||
editorPoints,
|
editorPoints,
|
||||||
editorResume,
|
|
||||||
editorCurrentResume,
|
|
||||||
saveProfileButton,
|
saveProfileButton,
|
||||||
sponsorViewToggle,
|
sponsorViewToggle
|
||||||
pdfViewer,
|
|
||||||
pdfFrame,
|
|
||||||
pdfTitle,
|
|
||||||
pdfExternalLink
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,20 +217,6 @@ export class StoreAuth {
|
||||||
this.elements.loginButton.addEventListener("click", () => this.handleLogin());
|
this.elements.loginButton.addEventListener("click", () => this.handleLogin());
|
||||||
this.elements.logoutButton.addEventListener("click", () => this.handleLogout());
|
this.elements.logoutButton.addEventListener("click", () => this.handleLogout());
|
||||||
|
|
||||||
// Resume upload event listener
|
|
||||||
this.elements.resumeUpload.addEventListener("change", (e) => {
|
|
||||||
const file = (e.target as HTMLInputElement).files?.[0];
|
|
||||||
if (file) {
|
|
||||||
this.handleResumeUpload(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Resume delete event listener
|
|
||||||
this.elements.deleteResume.addEventListener("click", () => this.handleResumeDelete());
|
|
||||||
|
|
||||||
// Member ID save event listener
|
|
||||||
this.elements.saveMemberId.addEventListener("click", () => this.handleMemberIdButton());
|
|
||||||
|
|
||||||
// Listen for auth state changes
|
// Listen for auth state changes
|
||||||
this.pb.authStore.onChange(() => {
|
this.pb.authStore.onChange(() => {
|
||||||
console.log("Auth state changed. IsValid:", this.pb.authStore.isValid);
|
console.log("Auth state changed. IsValid:", this.pb.authStore.isValid);
|
||||||
|
@ -276,11 +238,6 @@ export class StoreAuth {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.handleProfileSave();
|
this.handleProfileSave();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add resume view event listener
|
|
||||||
document.addEventListener('viewResume', ((e: CustomEvent) => {
|
|
||||||
this.handleResumeView(e.detail.url, e.detail.fileName);
|
|
||||||
}) as EventListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateUI() {
|
private async updateUI() {
|
||||||
|
@ -292,12 +249,6 @@ export class StoreAuth {
|
||||||
userEmail,
|
userEmail,
|
||||||
memberStatus,
|
memberStatus,
|
||||||
lastLogin,
|
lastLogin,
|
||||||
memberIdInput,
|
|
||||||
saveMemberId,
|
|
||||||
resumeUpload,
|
|
||||||
resumeName,
|
|
||||||
resumeDownload,
|
|
||||||
resumeActions,
|
|
||||||
loadingSkeleton,
|
loadingSkeleton,
|
||||||
officerViewToggle,
|
officerViewToggle,
|
||||||
sponsorViewToggle,
|
sponsorViewToggle,
|
||||||
|
@ -343,36 +294,11 @@ export class StoreAuth {
|
||||||
memberStatus.classList.add("badge-neutral");
|
memberStatus.classList.add("badge-neutral");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update member ID input and state
|
|
||||||
memberIdInput.value = user.member_id || "";
|
|
||||||
memberIdInput.disabled = false;
|
|
||||||
saveMemberId.disabled = false;
|
|
||||||
|
|
||||||
// Update last login
|
// Update last login
|
||||||
lastLogin.textContent = user.last_login
|
lastLogin.textContent = user.last_login
|
||||||
? new Date(user.last_login).toLocaleString()
|
? new Date(user.last_login).toLocaleString()
|
||||||
: this.config.ui.messages.auth.never;
|
: this.config.ui.messages.auth.never;
|
||||||
|
|
||||||
// Update resume section
|
|
||||||
resumeUpload.disabled = false;
|
|
||||||
if (user.resume && (!Array.isArray(user.resume) || user.resume.length > 0)) {
|
|
||||||
const resumeUrl = user.resume.toString();
|
|
||||||
const fileName = this.getFileNameFromUrl(resumeUrl);
|
|
||||||
resumeName.textContent = fileName;
|
|
||||||
const fullUrl = this.pb.files.getURL(user, resumeUrl);
|
|
||||||
resumeDownload.href = "#";
|
|
||||||
resumeDownload.onclick = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
this.handleResumeView(fullUrl, fileName);
|
|
||||||
};
|
|
||||||
resumeActions.style.display = "flex";
|
|
||||||
} else {
|
|
||||||
resumeName.textContent = "No resume uploaded";
|
|
||||||
resumeDownload.href = "#";
|
|
||||||
resumeDownload.onclick = null;
|
|
||||||
resumeActions.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show/hide view toggles
|
// Show/hide view toggles
|
||||||
officerViewToggle.style.display = isOfficer ? "block" : "none";
|
officerViewToggle.style.display = isOfficer ? "block" : "none";
|
||||||
sponsorViewToggle.style.display = isSponsor ? "block" : "none";
|
sponsorViewToggle.style.display = isSponsor ? "block" : "none";
|
||||||
|
@ -395,21 +321,6 @@ export class StoreAuth {
|
||||||
memberStatus.classList.add("badge-neutral");
|
memberStatus.classList.add("badge-neutral");
|
||||||
lastLogin.textContent = this.config.ui.messages.auth.never;
|
lastLogin.textContent = this.config.ui.messages.auth.never;
|
||||||
|
|
||||||
// Disable inputs
|
|
||||||
memberIdInput.disabled = true;
|
|
||||||
saveMemberId.disabled = true;
|
|
||||||
resumeUpload.disabled = true;
|
|
||||||
|
|
||||||
// Reset member ID
|
|
||||||
memberIdInput.value = "";
|
|
||||||
this.isEditingMemberId = false;
|
|
||||||
|
|
||||||
// Reset resume section
|
|
||||||
resumeName.textContent = "No resume uploaded";
|
|
||||||
resumeDownload.href = "#";
|
|
||||||
resumeDownload.onclick = null;
|
|
||||||
resumeActions.style.display = "none";
|
|
||||||
|
|
||||||
// Hide view toggles
|
// Hide view toggles
|
||||||
officerViewToggle.style.display = "none";
|
officerViewToggle.style.display = "none";
|
||||||
sponsorViewToggle.style.display = "none";
|
sponsorViewToggle.style.display = "none";
|
||||||
|
@ -423,148 +334,18 @@ export class StoreAuth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleLogin() {
|
|
||||||
try {
|
|
||||||
const authMethods = await this.pb.collection("users").listAuthMethods();
|
|
||||||
const oidcProvider = authMethods.oauth2?.providers?.find(
|
|
||||||
(p: { name: string }) => p.name === this.config.api.oauth2.providerName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!oidcProvider) {
|
|
||||||
throw new Error("OIDC provider not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem("provider", JSON.stringify(oidcProvider));
|
|
||||||
const redirectUrl = window.location.origin + this.config.api.oauth2.redirectPath;
|
|
||||||
const authUrl = oidcProvider.authURL + encodeURIComponent(redirectUrl);
|
|
||||||
window.location.href = authUrl;
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Authentication error:", err);
|
|
||||||
this.elements.userEmail.textContent = this.config.ui.messages.auth.loginError;
|
|
||||||
this.elements.userName.textContent = "Error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleLogout() {
|
private handleLogout() {
|
||||||
this.pb.authStore.clear();
|
this.pb.authStore.clear();
|
||||||
this.cachedUsers = [];
|
this.cachedUsers = [];
|
||||||
this.isEditingMemberId = false;
|
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFileNameFromUrl(url: string): string {
|
|
||||||
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 handleResumeView(url: string, fileName: string) {
|
|
||||||
const { pdfViewer, pdfFrame, pdfTitle, pdfExternalLink } = this.elements;
|
|
||||||
pdfFrame.src = url;
|
|
||||||
pdfTitle.textContent = fileName;
|
|
||||||
pdfExternalLink.href = url;
|
|
||||||
pdfViewer.showModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleResumeUpload(file: File) {
|
|
||||||
const { uploadStatus } = this.elements;
|
|
||||||
try {
|
|
||||||
const user = this.pb.authStore.model;
|
|
||||||
if (!user?.id) {
|
|
||||||
throw new Error("User ID not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("resume", file);
|
|
||||||
|
|
||||||
await this.pb.collection("users").update(user.id, formData);
|
|
||||||
uploadStatus.textContent = this.config.ui.messages.resume.success;
|
|
||||||
this.updateUI();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
uploadStatus.textContent = "";
|
|
||||||
}, this.config.ui.messages.resume.messageTimeout);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Resume upload error:", err);
|
|
||||||
uploadStatus.textContent = this.config.ui.messages.resume.error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleResumeDelete() {
|
|
||||||
const { uploadStatus } = this.elements;
|
|
||||||
try {
|
|
||||||
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 = this.config.ui.messages.resume.deleteSuccess;
|
|
||||||
this.updateUI();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
uploadStatus.textContent = "";
|
|
||||||
}, this.config.ui.messages.resume.messageTimeout);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Resume deletion error:", err);
|
|
||||||
uploadStatus.textContent = this.config.ui.messages.resume.deleteError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleMemberIdButton() {
|
|
||||||
const user = this.pb.authStore.model;
|
|
||||||
if (user?.member_id && !this.isEditingMemberId) {
|
|
||||||
this.isEditingMemberId = true;
|
|
||||||
this.updateUI();
|
|
||||||
} else {
|
|
||||||
await this.handleMemberIdSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleMemberIdSave() {
|
|
||||||
const { memberIdInput, memberIdStatus } = this.elements;
|
|
||||||
const memberId = memberIdInput.value.trim();
|
|
||||||
|
|
||||||
try {
|
|
||||||
memberIdStatus.textContent = this.config.ui.messages.memberId.saving;
|
|
||||||
|
|
||||||
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 = this.config.ui.messages.memberId.success;
|
|
||||||
this.isEditingMemberId = false;
|
|
||||||
this.updateUI();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
memberIdStatus.textContent = "";
|
|
||||||
}, this.config.ui.messages.memberId.messageTimeout);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("IEEE Member ID save error:", err);
|
|
||||||
memberIdStatus.textContent = this.config.ui.messages.memberId.error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleProfileSave() {
|
private async handleProfileSave() {
|
||||||
const {
|
const {
|
||||||
profileEditor,
|
profileEditor,
|
||||||
editorName,
|
editorName,
|
||||||
editorEmail,
|
editorEmail,
|
||||||
editorMemberId,
|
|
||||||
editorPoints,
|
editorPoints,
|
||||||
editorResume,
|
|
||||||
saveProfileButton,
|
saveProfileButton,
|
||||||
} = this.elements;
|
} = this.elements;
|
||||||
const userId = saveProfileButton.dataset.userId;
|
const userId = saveProfileButton.dataset.userId;
|
||||||
|
@ -575,23 +356,11 @@ export class StoreAuth {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// First get the current user data to check existing resume
|
|
||||||
const currentUser = await this.pb.collection("users").getOne(userId);
|
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("name", editorName.value);
|
formData.append("name", editorName.value);
|
||||||
formData.append("email", editorEmail.value);
|
formData.append("email", editorEmail.value);
|
||||||
formData.append("member_id", editorMemberId.value);
|
|
||||||
formData.append("points", editorPoints.value);
|
formData.append("points", editorPoints.value);
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.pb.collection("users").update(userId, formData);
|
await this.pb.collection("users").update(userId, formData);
|
||||||
profileEditor.close();
|
profileEditor.close();
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
|
|
|
@ -127,104 +127,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Member Details -->
|
|
||||||
<div class="space-y-4">
|
|
||||||
<!-- IEEE Member ID -->
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text flex items-center gap-2">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-4 w-4"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M10 2a1 1 0 00-1 1v1a1 1 0 002 0V3a1 1 0 00-1-1zM4 4h3a3 3 0 006 0h3a2 2 0 012 2v9a2 2 0 01-2 2H4a2 2 0 01-2-2V6a2 2 0 012-2zm2.5 7a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm2.45 4a2.5 2.5 0 10-4.9 0h4.9zM12 9a1 1 0 100 2h3a1 1 0 100-2h-3zm-1 4a1 1 0 011-1h2a1 1 0 110 2h-2a1 1 0 01-1-1z"
|
|
||||||
clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
IEEE Member ID
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<div class="join w-full">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="memberIdInput"
|
|
||||||
placeholder="Enter your IEEE Member ID"
|
|
||||||
class="join-item input input-bordered flex-1 disabled:bg-base-200 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
id="saveMemberId"
|
|
||||||
class="join-item btn disabled:bg-base-200 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed enabled:btn-primary hover:enabled:bg-primary-focus"
|
|
||||||
disabled>Save</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<label class="label">
|
|
||||||
<span id="memberIdStatus" class="label-text-alt"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Resume Upload -->
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text flex items-center gap-2">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-4 w-4"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z"
|
|
||||||
clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
Resume
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<div id="resumeSection" class="space-y-2">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<p id="resumeName" class="text-sm truncate flex-1">
|
|
||||||
No resume uploaded
|
|
||||||
</p>
|
|
||||||
<div id="resumeActions" class="flex gap-2">
|
|
||||||
<a
|
|
||||||
id="resumeDownload"
|
|
||||||
href="#"
|
|
||||||
target="_blank"
|
|
||||||
class="btn btn-ghost btn-xs">View</a
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
id="deleteResume"
|
|
||||||
class="btn btn-ghost btn-xs text-error"
|
|
||||||
>Delete</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="uploadSection">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="resumeUpload"
|
|
||||||
accept=".pdf,.doc,.docx"
|
|
||||||
class="file-input file-input-bordered file-input-sm w-full disabled:bg-base-200 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<label class="label">
|
|
||||||
<span id="uploadStatus" class="label-text-alt"
|
|
||||||
></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Auth Buttons -->
|
<!-- Auth Buttons -->
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<button
|
<button
|
||||||
id="contentLoginButton"
|
id="contentLoginButton"
|
||||||
class="login-button btn btn-primary w-full gap-2"
|
class="login-button btn btn-primary w-full gap-2 hidden"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -241,7 +148,7 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="contentLogoutButton"
|
id="contentLogoutButton"
|
||||||
class="logout-button btn btn-error w-full gap-2"
|
class="logout-button btn btn-error w-full gap-2 hidden"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -409,15 +316,55 @@
|
||||||
// Initialize auth and event check-in
|
// Initialize auth and event check-in
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
try {
|
try {
|
||||||
new StoreAuth();
|
const auth = new StoreAuth();
|
||||||
new EventCheckIn();
|
new EventCheckIn();
|
||||||
|
|
||||||
|
// Add login button event listener
|
||||||
|
const loginButtons = document.querySelectorAll(".login-button");
|
||||||
|
loginButtons.forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
const loadingSkeleton =
|
||||||
|
document.getElementById("loadingSkeleton");
|
||||||
|
const userInfo = document.getElementById("userInfo");
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
if (loadingSkeleton)
|
||||||
|
loadingSkeleton.style.display = "block";
|
||||||
|
if (userInfo) userInfo.classList.add("hidden");
|
||||||
|
|
||||||
|
// Call the handleLogin method
|
||||||
|
auth.handleLogin().catch((error) => {
|
||||||
|
console.error("Login error:", error);
|
||||||
|
// Show error message
|
||||||
|
if (loadingSkeleton)
|
||||||
|
loadingSkeleton.style.display = "none";
|
||||||
|
if (userInfo) {
|
||||||
|
const errorMessage = document.createElement("div");
|
||||||
|
errorMessage.className = "alert alert-error";
|
||||||
|
errorMessage.innerHTML = `
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span>Failed to sign in. Please try again.</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
userInfo.innerHTML = "";
|
||||||
|
userInfo.appendChild(errorMessage);
|
||||||
|
userInfo.classList.remove("hidden");
|
||||||
|
userInfo.style.opacity = "1";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Add error handling for failed initialization
|
// Add error handling for failed initialization
|
||||||
window.addEventListener("unhandledrejection", (event) => {
|
window.addEventListener("unhandledrejection", (event) => {
|
||||||
console.error("Profile loading error:", event.reason);
|
console.error("Profile loading error:", event.reason);
|
||||||
const userInfo = document.getElementById("userInfo");
|
const userInfo = document.getElementById("userInfo");
|
||||||
const loadingSkeleton =
|
const loadingSkeleton =
|
||||||
document.getElementById("loadingSkeleton");
|
document.getElementById("loadingSkeleton");
|
||||||
|
const memberDetails = document.getElementById("memberDetails");
|
||||||
const errorMessage = document.createElement("div");
|
const errorMessage = document.createElement("div");
|
||||||
|
|
||||||
errorMessage.className = "alert alert-error";
|
errorMessage.className = "alert alert-error";
|
||||||
|
@ -432,12 +379,32 @@
|
||||||
|
|
||||||
if (loadingSkeleton) loadingSkeleton.style.display = "none";
|
if (loadingSkeleton) loadingSkeleton.style.display = "none";
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
userInfo.innerHTML = "";
|
if (memberDetails) memberDetails.style.display = "none";
|
||||||
userInfo.appendChild(errorMessage);
|
userInfo.appendChild(errorMessage);
|
||||||
userInfo.classList.remove("hidden");
|
userInfo.classList.remove("hidden");
|
||||||
userInfo.style.opacity = "1";
|
userInfo.style.opacity = "1";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Check auth state and update UI accordingly
|
||||||
|
const authState = auth.getAuthState();
|
||||||
|
if (!authState.isValid || !authState.model) {
|
||||||
|
const userInfo = document.getElementById("userInfo");
|
||||||
|
const loadingSkeleton =
|
||||||
|
document.getElementById("loadingSkeleton");
|
||||||
|
const memberDetails = document.getElementById("memberDetails");
|
||||||
|
const contentLoginButton =
|
||||||
|
document.getElementById("contentLoginButton");
|
||||||
|
|
||||||
|
if (loadingSkeleton) loadingSkeleton.style.display = "none";
|
||||||
|
if (memberDetails) memberDetails.style.display = "none";
|
||||||
|
if (contentLoginButton)
|
||||||
|
contentLoginButton.classList.remove("hidden");
|
||||||
|
if (userInfo) {
|
||||||
|
userInfo.classList.remove("hidden");
|
||||||
|
userInfo.style.opacity = "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to initialize profile:", error);
|
console.error("Failed to initialize profile:", error);
|
||||||
}
|
}
|
||||||
|
@ -478,7 +445,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set member since date (you'll need to modify StoreAuth.ts to include this)
|
// Set member since date
|
||||||
if (memberSinceElement) {
|
if (memberSinceElement) {
|
||||||
const created = new Date(); // Replace with actual user creation date
|
const created = new Date(); // Replace with actual user creation date
|
||||||
memberSinceElement.textContent = created.toLocaleDateString();
|
memberSinceElement.textContent = created.toLocaleDateString();
|
||||||
|
|
172
src/components/profile/UserSettings.astro
Normal file
172
src/components/profile/UserSettings.astro
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div class="card bg-base-200 shadow-xl">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title text-2xl mb-4">Account Settings</h2>
|
||||||
|
|
||||||
|
<!-- IEEE Member ID -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">IEEE Member ID</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="memberIdInput"
|
||||||
|
placeholder="Enter your IEEE Member ID"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Major -->
|
||||||
|
<div class="form-control mt-4">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">Major</span>
|
||||||
|
</label>
|
||||||
|
<select id="majorSelect" class="select select-bordered w-full">
|
||||||
|
<option disabled selected>Select your major</option>
|
||||||
|
<option value="ECE"
|
||||||
|
>Electrical & Computer Engineering</option
|
||||||
|
>
|
||||||
|
<option value="CSE">Computer Science & Engineering</option>
|
||||||
|
<option value="MAE"
|
||||||
|
>Mechanical & Aerospace Engineering</option
|
||||||
|
>
|
||||||
|
<option value="BE">Bioengineering</option>
|
||||||
|
<option value="SE">Structural Engineering</option>
|
||||||
|
<option value="NANO">Nanoengineering</option>
|
||||||
|
<option value="CENG">Chemical Engineering</option>
|
||||||
|
<option value="OTHER">Other</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Expected Graduation Year -->
|
||||||
|
<div class="form-control mt-4">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">Expected Graduation Year</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="gradYearInput"
|
||||||
|
min="2024"
|
||||||
|
max="2030"
|
||||||
|
placeholder="Enter graduation year"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resume -->
|
||||||
|
<div class="form-control mt-4">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">Resume</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<p id="currentResume" class="text-sm opacity-70">
|
||||||
|
No resume uploaded
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="resumeUpload"
|
||||||
|
accept=".pdf,.doc,.docx"
|
||||||
|
class="file-input file-input-bordered file-input-sm w-full"
|
||||||
|
/>
|
||||||
|
<label class="label">
|
||||||
|
<span id="uploadStatus" class="label-text-alt"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Save Button -->
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button class="btn btn-primary" id="saveSettings">Save Changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { StoreAuth } from "../auth/StoreAuth";
|
||||||
|
const auth = new StoreAuth();
|
||||||
|
|
||||||
|
// Get form elements
|
||||||
|
const memberIdInput = document.getElementById(
|
||||||
|
"memberIdInput"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const majorSelect = document.getElementById(
|
||||||
|
"majorSelect"
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
const gradYearInput = document.getElementById(
|
||||||
|
"gradYearInput"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const resumeUpload = document.getElementById(
|
||||||
|
"resumeUpload"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const currentResume = document.getElementById("currentResume");
|
||||||
|
const uploadStatus = document.getElementById("uploadStatus");
|
||||||
|
const saveSettings = document.getElementById("saveSettings");
|
||||||
|
|
||||||
|
// Load current user data
|
||||||
|
const loadUserData = () => {
|
||||||
|
const authState = auth.getAuthState();
|
||||||
|
if (authState.isValid && authState.model) {
|
||||||
|
const user = authState.model;
|
||||||
|
|
||||||
|
// Set current values
|
||||||
|
if (memberIdInput) memberIdInput.value = user.member_id || "";
|
||||||
|
if (majorSelect) majorSelect.value = user.major || "";
|
||||||
|
if (gradYearInput) gradYearInput.value = user.graduation_year || "";
|
||||||
|
|
||||||
|
// Update resume display
|
||||||
|
if (currentResume && user.resume) {
|
||||||
|
const fileName = user.resume.toString().split("/").pop();
|
||||||
|
currentResume.textContent = fileName || "Resume uploaded";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle resume upload
|
||||||
|
if (resumeUpload && uploadStatus) {
|
||||||
|
resumeUpload.addEventListener("change", async (e) => {
|
||||||
|
const file = (e.target as HTMLInputElement).files?.[0];
|
||||||
|
if (file) {
|
||||||
|
uploadStatus.textContent = "Uploading...";
|
||||||
|
try {
|
||||||
|
await auth.handleResumeUpload(file);
|
||||||
|
uploadStatus.textContent = "Resume uploaded successfully";
|
||||||
|
if (currentResume) currentResume.textContent = file.name;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Resume upload error:", err);
|
||||||
|
uploadStatus.textContent = "Failed to upload resume";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle save settings
|
||||||
|
if (saveSettings) {
|
||||||
|
saveSettings.addEventListener("click", async () => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
member_id: memberIdInput?.value,
|
||||||
|
major: majorSelect?.value,
|
||||||
|
graduation_year: gradYearInput?.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
await auth.updateProfileSettings(data);
|
||||||
|
alert("Settings saved successfully");
|
||||||
|
loadUserData(); // Reload the data to show updated values
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to save settings:", err);
|
||||||
|
alert("Failed to save settings");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load initial data
|
||||||
|
loadUserData();
|
||||||
|
|
||||||
|
// Update when auth state changes
|
||||||
|
window.addEventListener("storage", (e) => {
|
||||||
|
if (e.key === "pocketbase_auth") {
|
||||||
|
loadUserData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -3,6 +3,7 @@ import Layout from "../layouts/Layout.astro";
|
||||||
import UserProfile from "../components/auth/UserProfile.astro";
|
import UserProfile from "../components/auth/UserProfile.astro";
|
||||||
import DefaultProfileView from "../components/profile/DefaultProfileView.astro";
|
import DefaultProfileView from "../components/profile/DefaultProfileView.astro";
|
||||||
import OfficerProfileView from "../components/profile/OfficerView.astro";
|
import OfficerProfileView from "../components/profile/OfficerView.astro";
|
||||||
|
import UserSettings from "../components/profile/UserSettings.astro";
|
||||||
const title = "User Profile";
|
const title = "User Profile";
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -56,6 +57,50 @@ const title = "User Profile";
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Not Authenticated State -->
|
||||||
|
<div id="notAuthenticatedState" class="hidden w-full">
|
||||||
|
<div class="card bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body items-center text-center">
|
||||||
|
<div class="mb-6">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-16 w-16 text-base-content/30"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h2 class="card-title text-2xl mb-2">
|
||||||
|
Sign in to Access Your Profile
|
||||||
|
</h2>
|
||||||
|
<p class="text-base-content/70 mb-6">
|
||||||
|
Please sign in with your IEEE UCSD account to view
|
||||||
|
and manage your profile.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
class="login-button btn btn-primary btn-lg gap-2"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
Sign in with IEEEUCSD SSO
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Main Content Grid -->
|
<!-- Main Content Grid -->
|
||||||
<div
|
<div
|
||||||
id="mainContent"
|
id="mainContent"
|
||||||
|
@ -217,6 +262,9 @@ const title = "User Profile";
|
||||||
<div id="defaultView">
|
<div id="defaultView">
|
||||||
<DefaultProfileView />
|
<DefaultProfileView />
|
||||||
</div>
|
</div>
|
||||||
|
<div id="settingsView" class="hidden">
|
||||||
|
<UserSettings />
|
||||||
|
</div>
|
||||||
<div id="officerView" class="hidden">
|
<div id="officerView" class="hidden">
|
||||||
<OfficerProfileView />
|
<OfficerProfileView />
|
||||||
</div>
|
</div>
|
||||||
|
@ -233,6 +281,9 @@ const title = "User Profile";
|
||||||
// Initialize page state
|
// Initialize page state
|
||||||
const pageLoadingState = document.getElementById("pageLoadingState");
|
const pageLoadingState = document.getElementById("pageLoadingState");
|
||||||
const pageErrorState = document.getElementById("pageErrorState");
|
const pageErrorState = document.getElementById("pageErrorState");
|
||||||
|
const notAuthenticatedState = document.getElementById(
|
||||||
|
"notAuthenticatedState"
|
||||||
|
);
|
||||||
const mainContent = document.getElementById("mainContent");
|
const mainContent = document.getElementById("mainContent");
|
||||||
const tabs = document.querySelectorAll(".tab");
|
const tabs = document.querySelectorAll(".tab");
|
||||||
const defaultView = document.getElementById("defaultView");
|
const defaultView = document.getElementById("defaultView");
|
||||||
|
@ -273,12 +324,18 @@ const title = "User Profile";
|
||||||
// Show loading state
|
// Show loading state
|
||||||
if (pageLoadingState) pageLoadingState.classList.remove("hidden");
|
if (pageLoadingState) pageLoadingState.classList.remove("hidden");
|
||||||
if (pageErrorState) pageErrorState.classList.add("hidden");
|
if (pageErrorState) pageErrorState.classList.add("hidden");
|
||||||
|
if (notAuthenticatedState)
|
||||||
|
notAuthenticatedState.classList.add("hidden");
|
||||||
if (mainContent) mainContent.classList.add("hidden");
|
if (mainContent) mainContent.classList.add("hidden");
|
||||||
|
|
||||||
// Check auth state
|
// Check auth state
|
||||||
const authState = auth.getAuthState();
|
const authState = auth.getAuthState();
|
||||||
if (!authState.isValid || !authState.model) {
|
if (!authState.isValid || !authState.model) {
|
||||||
throw new Error("User not authenticated");
|
// Show not authenticated state
|
||||||
|
if (pageLoadingState) pageLoadingState.classList.add("hidden");
|
||||||
|
if (notAuthenticatedState)
|
||||||
|
notAuthenticatedState.classList.remove("hidden");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = authState.model;
|
const user = authState.model;
|
||||||
|
@ -350,12 +407,26 @@ const title = "User Profile";
|
||||||
|
|
||||||
// Update content visibility
|
// Update content visibility
|
||||||
const tabId = (tab as HTMLElement).dataset.tab;
|
const tabId = (tab as HTMLElement).dataset.tab;
|
||||||
if (tabId === "events") {
|
const defaultView = document.getElementById("defaultView");
|
||||||
defaultView!.style.display = "block";
|
const settingsView = document.getElementById("settingsView");
|
||||||
officerView!.style.display = "none";
|
const officerView = document.getElementById("officerView");
|
||||||
} else if (tabId === "officer") {
|
|
||||||
defaultView!.style.display = "none";
|
if (defaultView && settingsView && officerView) {
|
||||||
officerView!.style.display = "block";
|
defaultView.classList.add("hidden");
|
||||||
|
settingsView.classList.add("hidden");
|
||||||
|
officerView.classList.add("hidden");
|
||||||
|
|
||||||
|
switch (tabId) {
|
||||||
|
case "events":
|
||||||
|
defaultView.classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
case "settings":
|
||||||
|
settingsView.classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
case "officer":
|
||||||
|
officerView.classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -389,6 +460,25 @@ const title = "User Profile";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add login button event listener
|
||||||
|
const loginButtons = document.querySelectorAll(".login-button");
|
||||||
|
loginButtons.forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
// Show loading state while authentication is in progress
|
||||||
|
if (pageLoadingState) pageLoadingState.classList.remove("hidden");
|
||||||
|
if (notAuthenticatedState)
|
||||||
|
notAuthenticatedState.classList.add("hidden");
|
||||||
|
|
||||||
|
// Call the handleLogin method from StoreAuth
|
||||||
|
auth.handleLogin().catch((error) => {
|
||||||
|
console.error("Login error:", error);
|
||||||
|
// Show error state if login fails
|
||||||
|
if (pageLoadingState) pageLoadingState.classList.add("hidden");
|
||||||
|
if (pageErrorState) pageErrorState.classList.remove("hidden");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
Loading…
Reference in a new issue