diff --git a/src/components/dashboard/AdminDashboard.astro b/src/components/dashboard/AdminDashboard.astro new file mode 100644 index 0000000..e2c9caf --- /dev/null +++ b/src/components/dashboard/AdminDashboard.astro @@ -0,0 +1,183 @@ +--- +// Admin Dashboard Component +import { Authentication } from "../../scripts/pocketbase/Authentication"; +import { Get } from "../../scripts/pocketbase/Get"; + +const auth = Authentication.getInstance(); +const get = Get.getInstance(); + +// Fetch some basic stats for the admin dashboard +let userCount = 0; +let officerCount = 0; +let eventCount = 0; +let reimbursementCount = 0; + +try { + if (auth.isAuthenticated()) { + const userResponse = await get.getList("users", 1, 1); + userCount = userResponse.totalItems; + + const officerResponse = await get.getList("officers", 1, 1); + officerCount = officerResponse.totalItems; + + const eventResponse = await get.getList("events", 1, 1); + eventCount = eventResponse.totalItems; + + const reimbursementResponse = await get.getList("reimbursement", 1, 1); + reimbursementCount = reimbursementResponse.totalItems; + } +} catch (error) { + console.error("Error fetching admin dashboard data:", error); +} +--- + +
+
+

Administrator Dashboard

+ + +
+ +
+
+

Users

+

{userCount}

+

Total registered users

+
+
+ + +
+
+

Officers

+

{officerCount}

+

Active officers

+
+
+ + +
+
+

Events

+

{eventCount}

+

Total events

+
+
+ + +
+
+

Reimbursements

+

{reimbursementCount}

+

Total reimbursements

+
+
+
+ + +
+

Administrative Actions

+
+ + + + +
+
+ + +
+

Recent System Activity

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TimeUserActionDetails
Just nowAdminLoginAdministrator logged in
10 min agoSystemUpdateEvent request status changed
1 hour agoJane DoeCreateNew reimbursement request submitted
+
+
+
+
+ + + diff --git a/src/config/dashboard.yaml b/src/config/dashboard.yaml index 4c3487e..4cbcf14 100644 --- a/src/config/dashboard.yaml +++ b/src/config/dashboard.yaml @@ -65,6 +65,14 @@ sections: component: "SponsorAnalytics" class: "text-warning hover:text-warning-focus" + # Administrator Menu + adminDashboard: + title: "Admin Dashboard" + icon: "heroicons:shield-check" + role: "administrator" + component: "AdminDashboard" + class: "text-error hover:text-error-focus" + # Settings (accessible to all except sponsors) settings: title: "Settings" @@ -98,8 +106,8 @@ categories: admin: title: "Admin Menu" - sections: [] - role: "admin" + sections: ["adminDashboard"] + role: "administrator" sponsor: title: "Sponsor Portal" diff --git a/src/pages/dashboard.astro b/src/pages/dashboard.astro index 7680980..f46d3d7 100644 --- a/src/pages/dashboard.astro +++ b/src/pages/dashboard.astro @@ -1,9 +1,13 @@ --- import yaml from "js-yaml"; import { Icon } from "astro-icon/components"; -import { hasAccess, type OfficerStatus } from "../utils/roleAccess"; import fs from "node:fs"; import path from "node:path"; +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"; +import { OfficerTypes } from "../schemas/pocketbase/schema"; const title = "Dashboard"; @@ -13,17 +17,17 @@ const dashboardConfig = yaml.load(fs.readFileSync(configPath, "utf8")) as any; // Dynamically import all dashboard components const components = Object.fromEntries( - await Promise.all( - Object.values(dashboardConfig.sections) - .filter((section: any) => section.component) // Only process sections with components - .map(async (section: any) => { - const component = await import( - `../components/dashboard/${section.component}.astro` - ); - console.log(`Loaded component: ${section.component}`); // Debug log - return [section.component, component.default]; - }) - ) + await Promise.all( + Object.values(dashboardConfig.sections) + .filter((section: any) => section.component) // Only process sections with components + .map(async (section: any) => { + const component = await import( + `../components/dashboard/${section.component}.astro` + ); + console.log(`Loaded component: ${section.component}`); // Debug log + return [section.component, component.default]; + }), + ), ); console.log("Available components:", Object.keys(components)); // Debug log @@ -31,680 +35,696 @@ console.log("Available components:", Object.keys(components)); // Debug log - - - - {title} | IEEE UCSD - - - - -
- -
- + + +
+ +
+
+
+
+
+
+
+
+
+
+ + + + + + +
+ + + + + + +
+ +
+
+
+ +

IEEE UCSD

+
+
+
+ + +
+ +
+
+
+

Loading 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 ( + + ); + }, + ) + } +
+
+
+ + diff --git a/src/utils/roleAccess.ts b/src/utils/roleAccess.ts index 14d0d75..b9bf7cd 100644 --- a/src/utils/roleAccess.ts +++ b/src/utils/roleAccess.ts @@ -1,16 +1,37 @@ -export type OfficerStatus = "admin" | "executive" | "general" | "past" | "sponsor" | "none" | ""; +export type OfficerStatus = + | "administrator" + | "executive" + | "general" + | "honorary" + | "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 +export function hasAccess( + userRole: OfficerStatus, + requiredRole: OfficerStatus, +): boolean { + const roleHierarchy: RoleHierarchy = { + administrator: [ + "administrator", + "executive", + "general", + "honorary", + "past", + "sponsor", + "none", + "", + ], + executive: ["executive", "general", "honorary", "past", "none", ""], + general: ["general", "honorary", "past", "none", ""], + honorary: ["honorary", "none", ""], + past: ["past", "none", ""], + sponsor: ["sponsor"], // Sponsor can only access sponsor-specific content + none: ["none", ""], + "": [""], + }; + + return roleHierarchy[userRole]?.includes(requiredRole) || false; +}