Skip to content

Added a new Current Tenure page#220

Open
Kr-Utkarsh-01 wants to merge 3 commits intoOpenLake:mainfrom
Kr-Utkarsh-01:tenure_page_clean_final
Open

Added a new Current Tenure page#220
Kr-Utkarsh-01 wants to merge 3 commits intoOpenLake:mainfrom
Kr-Utkarsh-01:tenure_page_clean_final

Conversation

@Kr-Utkarsh-01
Copy link

@Kr-Utkarsh-01 Kr-Utkarsh-01 commented Jan 26, 2026


name: " Pull Request"
about: Propose and submit changes to the project for review
title: "PR: [Added a new Current Tenure page to track the tenure of current POR holders]"

Related Issue


Changes Introduced

  • Added: Added a new Tenure page to track the tenure of the current POR holders

Why This Change?

  • Problem: There is currently no centralized, public-facing page where students can view the current holders of Positions of Responsibility (PORs). As a result, identifying office-bearers across clubs, councils, and committees is unclear and fragmented.
  • Solution: This PR introduces a new public-facing page that lists all current student representatives holding PORs. The page is structured to clearly show which positions are held within each club, council, and committee.
  • Impact: Improves transparency and accessibility by making it easy for all students to identify, verify, and contact current office-bearers, strengthening communication and engagement across student bodies.

Screenshots / Video (if applicable)

image

Summary by CodeRabbit

  • New Features
    • Added Tenure section allowing users to view current position holders organized by organizational units
    • Implemented search functionality to filter position holders by organization, member name, or position title
    • Added navigation menu items to access tenure information across all user roles

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Jan 26, 2026

@Kr-Utkarsh-01 is attempting to deploy a commit to the openlake's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Walkthrough

Introduces a new tenure display feature: backend endpoint /api/por/current retrieves and groups active position holders by organizational unit, while frontend adds TenurePage component with search filtering, navigation integration, and dashboard routing for all user roles.

Changes

Cohort / File(s) Summary
Backend API Route
backend/index.js, backend/routes/por.js
Mounts new /api/por route module; implements GET /api/por/current endpoint that queries active PositionHolder records, populates user/position/unit details, validates nested fields, groups results by unit name, and returns structured response with unit info and member cards (holder_id, user, position_title, tenure_year). Includes server error handling.
Frontend Navigation & Dashboard Config
frontend/src/config/dashboardComponents.js, frontend/src/config/navbarConfig.js
Registers TenurePage in dashboard component map; adds "tenure" navigation item (Users icon) to navbar for PRESIDENT, CLUB_COORDINATOR, and STUDENT roles.
Frontend Page & Route Integration
frontend/src/pages/TenurePage.jsx, frontend/src/routes/StudentRoutes.js
Creates TenurePage React component that fetches tenure data on mount, implements search/filter by organization name/member name/position, manages loading/error states with retry action, renders responsive grid of organization and member cards with avatars. Adds /tenure protected route in StudentRoutes.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant TenurePage as TenurePage Component
    participant API as Backend API
    participant DB as Database

    User->>TenurePage: Opens /tenure page
    TenurePage->>TenurePage: Mount component, set loading state
    TenurePage->>API: GET /api/por/current
    API->>DB: Query active PositionHolder records
    DB-->>API: Return records
    API->>API: Populate user, position, unit details
    API->>API: Validate nested fields & group by unit
    API-->>TenurePage: Return grouped tenure data
    TenurePage->>TenurePage: Store data in state, set loading false
    TenurePage->>User: Render organizations & member cards
    User->>TenurePage: Enter search query
    TenurePage->>TenurePage: Filter data (org name/member/position)
    TenurePage->>User: Update display with filtered results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Feature/announcements frontend #217: Modifies the same frontend configuration files (dashboardComponents.js and navbarConfig.js) to register new page components and navigation items, following an identical integration pattern.

Suggested reviewers

  • harshitap1305

Poem

🐰 A tenure page hops into view,
Where officers and roles shine through!
Grouped by club with cards so neat,
Search and filter—what a treat!
Students now know who leads the way! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Added a new Current Tenure page' clearly and concisely summarizes the main change in the pull request.
Description check ✅ Passed The PR description covers the key required sections: Related Issue, Changes Introduced, Why This Change (Problem/Solution/Impact), and includes a screenshot.
Linked Issues check ✅ Passed The PR fully implements all acceptance criteria from issue #116: backend GET /api/por/current endpoint with proper data population and grouping, frontend Tenure page with grouped display, individual POR holder cards, and search/filter functionality.
Out of Scope Changes check ✅ Passed All code changes are directly related to implementing the Tenure page feature. Changes include backend routes, frontend pages, dashboard/navbar configuration, and student routes—all within scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@frontend/src/pages/TenurePage.jsx`:
- Line 138: The list item uses an unstable key fallback (key={member.holder_id
|| Math.random()}), which causes remounts; replace the Math.random() fallback
with a stable identifier such as a deterministic property on the member object
(e.g., member.id or member.uuid) or derive a stable key from existing fields
(e.g., `${member.holder_id}-${member.someOtherField}`), ensuring the key for
each rendered element in the TenurePage list is constant across renders and
referencing the member.holder_id usage to locate the change.
🧹 Nitpick comments (7)
backend/routes/por.js (3)

7-20: Consider adding pagination for scalability.

The query fetches all active position holders without pagination. As the dataset grows, this could impact response times and memory usage.

♻️ Suggested approach with pagination
-router.get("/current", async (req, res) => {
+router.get("/current", async (req, res) => {
+  const page = parseInt(req.query.page) || 1;
+  const limit = parseInt(req.query.limit) || 100;
+  const skip = (page - 1) * limit;
+
   try {
     const activeHolders = await PositionHolder.find({ status: "active" })
+      .skip(skip)
+      .limit(limit)
       .populate({

33-41: Potential grouping collision if two units share the same name.

Using unit.name as the grouping key could merge members from different units if they have identical names. Consider using unit._id.toString() as the key and including the name in unit_info.

♻️ Suggested fix
       const unit = holder.position_id.unit_id;
-      const unitName = unit.name;
+      const unitKey = unit._id.toString();

-      if (!groupedData[unitName]) {
-        groupedData[unitName] = {
+      if (!groupedData[unitKey]) {
+        groupedData[unitKey] = {
           unit_info: unit,
           members: [],
         };
       }

-      groupedData[unitName].members.push({
+      groupedData[unitKey].members.push({

54-54: Avoid leaking internal error details in production.

Exposing error.message in the API response could reveal sensitive information about the server's internal state or database structure.

♻️ Suggested fix
-    res.status(500).json({ message: "Server Error", error: error.message });
+    res.status(500).json({ message: "Server Error" });
frontend/src/pages/TenurePage.jsx (3)

15-17: Remove console.log statements before merging.

Debug logging on lines 15, 17, and 27 should be removed or replaced with a conditional debug flag for production builds.

♻️ Suggested fix
     const fetchTenureData = async () => {
       try {
-        console.log("TenurePage: Fetching tenure data...");
         const response = await api.get("/api/por/current");
-        console.log("TenurePage: API Response:", response);

         if (response.data) {
       } catch (err) {
-        console.error("TenurePage: Error fetching tenure data:", err);
         const msg =

Also applies to: 27-27


45-71: Consider memoizing the filtered data.

The filteredData calculation runs on every render. With useMemo, it only recalculates when safeData or searchTerm changes.

♻️ Suggested optimization
+import React, { useEffect, useState, useMemo } from "react";
-import React, { useEffect, useState } from "react";
-  const filteredData = Object.entries(safeData).reduce(
+  const filteredData = useMemo(() => Object.entries(safeData).reduce(
     (acc, [orgName, data]) => {
       // ... existing logic
     },
     {},
-  );
+  ), [safeData, searchTerm]);

103-108: Consider a softer retry mechanism.

Using window.location.reload() triggers a full page reload. A cleaner approach would re-invoke the fetch function without losing other page state.

♻️ Suggested approach
+  const fetchTenureData = async () => {
+    setLoading(true);
+    setError(null);
+    try {
+      const response = await api.get("/api/por/current");
+      setTenureData(response.data || {});
+    } catch (err) {
+      const msg = err.response?.data?.message || err.message || "Failed to load tenure data.";
+      setError(`${msg}. Please ensure the backend server is running on the correct port.`);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    fetchTenureData();
+  }, []);

Then use onClick={fetchTenureData} for the retry button.

frontend/src/config/navbarConfig.js (1)

27-27: Consider using a distinct icon for Tenure.

The "Tenure" navigation item uses the same Users icon as "Clubs" (organization), which could cause visual confusion. Consider using Briefcase (already used in TenurePage.jsx) for consistency and distinction.

♻️ Suggested change

Update the import:

 import {
   Home,
   Users,
   Calendar,
   Trophy,
   ClipboardList,
   Star,
   User,
   MessageSquare,
   UserPlus,
   Plus,
   Award,
   Megaphone,
+  Briefcase,
 } from "lucide-react";

Then update each tenure entry:

-  { key: "tenure", label: "Tenure", icon: Users },
+  { key: "tenure", label: "Tenure", icon: Briefcase },

Also applies to: 42-42, 61-61, 77-77

{data.members && data.members.length > 0 ? (
data.members.map((member) => (
<div
key={member.holder_id || Math.random()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid using Math.random() as a React key.

Using Math.random() as a fallback key causes React to remount the component on every render, breaking reconciliation and potentially causing performance issues or state loss. Use a stable identifier instead.

♻️ Suggested fix
                   data.members.map((member) => (
                     <div
-                      key={member.holder_id || Math.random()}
+                      key={member.holder_id || `${orgName}-${member.position_title}-${member.user?.personal_info?.name}`}
                       className="flex flex-col items-center p-4 bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-shadow duration-200"
                     >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
key={member.holder_id || Math.random()}
key={member.holder_id || `${orgName}-${member.position_title}-${member.user?.personal_info?.name}`}
🤖 Prompt for AI Agents
In `@frontend/src/pages/TenurePage.jsx` at line 138, The list item uses an
unstable key fallback (key={member.holder_id || Math.random()}), which causes
remounts; replace the Math.random() fallback with a stable identifier such as a
deterministic property on the member object (e.g., member.id or member.uuid) or
derive a stable key from existing fields (e.g.,
`${member.holder_id}-${member.someOtherField}`), ensuring the key for each
rendered element in the TenurePage list is constant across renders and
referencing the member.holder_id usage to locate the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FOSSOVERFLOW-25] feat: Create Page to Display Current Tenure (POR Holders)

1 participant