@@ -257,12 +330,32 @@ const title = "Dashboard";
-
-
-
-
-
-
+
+ {
+ Object.entries(dashboardConfig.sections).map(
+ ([sectionKey, section]: [string, any]) => {
+ // Skip if no component is defined
+ if (!section.component) return null;
+
+ const Component =
+ components[section.component];
+ return (
+
+
+
+ );
+ }
+ )
+ }
@@ -274,6 +367,7 @@ const title = "Dashboard";
import { Authentication } from "../scripts/pocketbase/Authentication";
import { Get } from "../scripts/pocketbase/Get";
import { SendLog } from "../scripts/pocketbase/SendLog";
+ import { hasAccess, type OfficerStatus } from "../utils/roleAccess";
const auth = Authentication.getInstance();
const get = Get.getInstance();
@@ -293,29 +387,132 @@ const title = "Dashboard";
const userName = document.getElementById("userName");
const userRole = document.getElementById("userRole");
- // Display user profile information
- const updateUserProfile = async (user: any) => {
+ // Function to update section visibility based on role
+ const updateSectionVisibility = (officerStatus: OfficerStatus) => {
+ // Special handling for sponsor role
+ if (officerStatus === "sponsor") {
+ // Hide all sections first
+ document
+ .querySelectorAll("[data-role-required]")
+ .forEach((element) => {
+ element.classList.add("hidden");
+ });
+
+ // Only show sponsor sections
+ document
+ .querySelectorAll('[data-role-required="sponsor"]')
+ .forEach((element) => {
+ element.classList.remove("hidden");
+ });
+ return;
+ }
+
+ // For non-sponsor roles, handle normally
+ document.querySelectorAll("[data-role-required]").forEach((element) => {
+ const requiredRole = element.getAttribute(
+ "data-role-required"
+ ) as OfficerStatus;
+
+ // Skip elements that don't have a role requirement
+ if (!requiredRole || requiredRole === "none") {
+ element.classList.remove("hidden");
+ return;
+ }
+
+ // Check if user has permission for this role
+ const hasPermission = hasAccess(officerStatus, requiredRole);
+
+ // Only show elements if user has permission
+ element.classList.toggle("hidden", !hasPermission);
+ });
+ };
+
+ // Handle navigation
+ const handleNavigation = () => {
+ const navButtons = document.querySelectorAll(".dashboard-nav-btn");
+ const sections = document.querySelectorAll(".dashboard-section");
+ const mainContentDiv = document.getElementById("mainContent");
+
+ // Ensure mainContent is visible
+ if (mainContentDiv) {
+ mainContentDiv.classList.remove("hidden");
+ }
+
+ navButtons.forEach((button) => {
+ button.addEventListener("click", () => {
+ const sectionKey = button.getAttribute("data-section");
+
+ // Handle logout button
+ if (sectionKey === "logout") {
+ auth.logout();
+ window.location.reload();
+ return;
+ }
+
+ // Remove active class from all buttons
+ navButtons.forEach((btn) => {
+ btn.classList.remove("active", "bg-base-200");
+ });
+
+ // Add active class to clicked button
+ button.classList.add("active", "bg-base-200");
+
+ // Hide all sections
+ sections.forEach((section) => {
+ section.classList.add("hidden");
+ });
+
+ // Show selected section
+ const sectionId = `${sectionKey}Section`;
+ const targetSection = document.getElementById(sectionId);
+ if (targetSection) {
+ targetSection.classList.remove("hidden");
+ console.log(`Showing section: ${sectionId}`); // Debug log
+ }
+
+ // Close mobile sidebar if needed
+ if (window.innerWidth < 1024 && sidebar) {
+ sidebar.classList.add("-translate-x-full");
+ document.body.classList.remove("overflow-hidden");
+ const overlay = document.getElementById("sidebarOverlay");
+ overlay?.remove();
+ }
+ });
+ });
+ };
+
+ // Display user profile information and handle role-based access
+ const updateUserProfile = async (user: { id: string }) => {
if (!user) return;
try {
- // Get user information including member type
const extendedUser = await get.getOne("users", user.id, {
- fields: ["id", "name", "member_type", "expand.member_type"],
+ fields: [
+ "id",
+ "name",
+ "member_type",
+ "officer_status",
+ "expand.member_type",
+ ],
});
- // Update display elements
const displayName = extendedUser.name || "Unknown User";
const displayRole = extendedUser.member_type || "Member";
+ const officerStatus = (extendedUser.officer_status ||
+ "") as OfficerStatus;
const initials = (extendedUser.name || "U")
.split(" ")
.map((n: string) => n[0])
.join("")
.toUpperCase();
- // Update elements
+ // Update profile display
if (userName) userName.textContent = displayName;
if (userRole) userRole.textContent = displayRole;
if (userInitials) userInitials.textContent = initials;
+
+ // Update section visibility based on role
+ updateSectionVisibility(officerStatus);
} catch (error) {
console.error("Error fetching user profile:", error);
const fallbackValues = {
@@ -324,15 +521,16 @@ const title = "Dashboard";
initials: "?",
};
- // Update elements with fallback values
if (userName) userName.textContent = fallbackValues.name;
if (userRole) userRole.textContent = fallbackValues.role;
if (userInitials)
userInitials.textContent = fallbackValues.initials;
+
+ updateSectionVisibility("" as OfficerStatus);
}
};
- // Mobile sidebar toggle with overlay
+ // Mobile sidebar toggle
const mobileSidebarToggle = document.getElementById("mobileSidebarToggle");
if (mobileSidebarToggle && sidebar) {
const toggleSidebar = () => {
@@ -341,13 +539,11 @@ const title = "Dashboard";
if (isOpen) {
sidebar.classList.add("-translate-x-full");
document.body.classList.remove("overflow-hidden");
- // Remove overlay if it exists
const overlay = document.getElementById("sidebarOverlay");
overlay?.remove();
} else {
sidebar.classList.remove("-translate-x-full");
document.body.classList.add("overflow-hidden");
- // Add overlay
const overlay = document.createElement("div");
overlay.id = "sidebarOverlay";
overlay.className =
@@ -360,51 +556,6 @@ const title = "Dashboard";
mobileSidebarToggle.addEventListener("click", toggleSidebar);
}
- // Close sidebar on window resize if screen becomes larger
- window.addEventListener("resize", () => {
- if (window.innerWidth >= 1024) {
- const overlay = document.getElementById("sidebarOverlay");
- if (overlay) {
- overlay.remove();
- document.body.classList.remove("overflow-hidden");
- }
- if (sidebar) {
- sidebar.classList.remove("-translate-x-full");
- }
- }
- });
-
- // Handle navigation
- const handleNavigation = () => {
- const navButtons = document.querySelectorAll(".dashboard-nav-btn");
- const sections = document.querySelectorAll(".dashboard-section");
-
- navButtons.forEach((button) => {
- button.addEventListener("click", () => {
- // Remove active class from all buttons
- navButtons.forEach((btn) =>
- btn.classList.remove("active", "bg-base-200")
- );
- // Add active class to clicked button
- button.classList.add("active", "bg-base-200");
-
- // Hide all sections
- sections.forEach((section) => section.classList.add("hidden"));
- // Show selected section
- const sectionId = `${button.getAttribute("data-section")}Section`;
- document.getElementById(sectionId)?.classList.remove("hidden");
-
- // Close sidebar and cleanup overlay on mobile
- if (window.innerWidth < 1024 && sidebar) {
- sidebar.classList.add("-translate-x-full");
- document.body.classList.remove("overflow-hidden");
- const overlay = document.getElementById("sidebarOverlay");
- overlay?.remove();
- }
- });
- });
- };
-
// Initialize page
const initializePage = async () => {
try {
@@ -412,23 +563,98 @@ const title = "Dashboard";
if (pageErrorState) pageErrorState.classList.add("hidden");
if (notAuthenticatedState)
notAuthenticatedState.classList.add("hidden");
- if (mainContent) mainContent.classList.add("hidden");
+
+ // Show loading states
+ const userProfileSkeleton = document.getElementById(
+ "userProfileSkeleton"
+ );
+ const userProfileSignedOut = document.getElementById(
+ "userProfileSignedOut"
+ );
+ const userProfileSummary =
+ document.getElementById("userProfileSummary");
+ const menuLoadingSkeleton = document.getElementById(
+ "menuLoadingSkeleton"
+ );
+ const actualMenu = document.getElementById("actualMenu");
+
+ if (userProfileSkeleton)
+ userProfileSkeleton.classList.remove("hidden");
+ if (userProfileSummary) userProfileSummary.classList.add("hidden");
+ if (userProfileSignedOut)
+ userProfileSignedOut.classList.add("hidden");
+ if (menuLoadingSkeleton)
+ menuLoadingSkeleton.classList.remove("hidden");
+ if (actualMenu) actualMenu.classList.add("hidden");
// Check authentication
if (!auth.isAuthenticated()) {
if (pageLoadingState) pageLoadingState.classList.add("hidden");
if (notAuthenticatedState)
notAuthenticatedState.classList.remove("hidden");
+ if (userProfileSkeleton)
+ userProfileSkeleton.classList.add("hidden");
+ if (userProfileSignedOut)
+ userProfileSignedOut.classList.remove("hidden");
return;
}
const user = auth.getCurrentUser();
await updateUserProfile(user);
+ // Show actual profile and hide skeleton
+ if (userProfileSkeleton)
+ userProfileSkeleton.classList.add("hidden");
+ if (userProfileSummary)
+ userProfileSummary.classList.remove("hidden");
+
+ // Hide all sections first
+ document
+ .querySelectorAll(".dashboard-section")
+ .forEach((section) => {
+ section.classList.add("hidden");
+ });
+
+ // Show appropriate default section based on role
+ const extendedUser = await get.getOne("users", user.id, {
+ fields: ["officer_status"],
+ });
+ const officerStatus = (extendedUser.officer_status ||
+ "") as OfficerStatus;
+
+ let defaultSection;
+ let defaultButton;
+
+ if (officerStatus === "sponsor") {
+ defaultSection = document.getElementById(
+ "sponsorDashboardSection"
+ );
+ defaultButton = document.querySelector(
+ '[data-section="sponsorDashboard"]'
+ );
+ } else {
+ defaultSection = document.getElementById("profileSection");
+ defaultButton = document.querySelector(
+ '[data-section="profile"]'
+ );
+ }
+
+ if (defaultSection) {
+ defaultSection.classList.remove("hidden");
+ }
+ if (defaultButton) {
+ defaultButton.classList.add("active", "bg-base-200");
+ }
+
// Initialize navigation
handleNavigation();
- // Show main content
+ // Show actual menu and hide skeleton
+ if (menuLoadingSkeleton)
+ menuLoadingSkeleton.classList.add("hidden");
+ if (actualMenu) actualMenu.classList.remove("hidden");
+
+ // Show main content and hide loading
if (mainContent) mainContent.classList.remove("hidden");
if (pageLoadingState) pageLoadingState.classList.add("hidden");
} catch (error) {
@@ -466,21 +692,19 @@ const title = "Dashboard";
// Handle responsive sidebar
if (sidebar) {
- // Hide sidebar by default on mobile
if (window.innerWidth < 1024) {
sidebar.classList.add("-translate-x-full");
}
- // Add transition class
- sidebar.classList.add(
- "transition-transform",
- "duration-300",
- "ease-in-out",
- "lg:translate-x-0",
- "fixed",
- "lg:relative",
- "h-full",
- "z-50"
- );
+ window.addEventListener("resize", () => {
+ if (window.innerWidth >= 1024) {
+ const overlay = document.getElementById("sidebarOverlay");
+ if (overlay) {
+ overlay.remove();
+ document.body.classList.remove("overflow-hidden");
+ }
+ sidebar.classList.remove("-translate-x-full");
+ }
+ });
}
diff --git a/src/utils/roleAccess.ts b/src/utils/roleAccess.ts
new file mode 100644
index 0000000..14d0d75
--- /dev/null
+++ b/src/utils/roleAccess.ts
@@ -0,0 +1,16 @@
+export type OfficerStatus = "admin" | "executive" | "general" | "past" | "sponsor" | "none" | "";
+type RoleHierarchy = Record
;
+
+export function hasAccess(userRole: OfficerStatus, requiredRole: OfficerStatus): boolean {
+ const roleHierarchy: RoleHierarchy = {
+ "admin": ["admin", "sponsor", "executive", "general", "past", "none", ""],
+ "executive": ["executive", "general", "past", "none", ""],
+ "general": ["general", "past", "none", ""],
+ "past": ["past", "none", ""],
+ "sponsor": ["sponsor"], // Sponsor can only access sponsor-specific content
+ "none": ["none", ""],
+ "": [""]
+ };
+
+ return roleHierarchy[userRole]?.includes(requiredRole) || false;
+}
\ No newline at end of file