Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 285 additions & 0 deletions app/[locale]/student-activities/student-council/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import NotificationsPanel from '~/components/notifications/notifications-panel';
import Heading from '~/components/heading';
import ImageHeader from '~/components/image-header';
import GenericTable from '~/components/ui/generic-table';
import FICGroup from '~/components/fic-group';
import StudentGroup from '~/components/student-group';
import { getTranslations } from '~/i18n/translations';
import { db } from '~/server/db';

export const revalidate = 300;
export default async function StudentCouncil({
params: { locale },
}: {
params: { locale: string };
}) {
const text = await getTranslations(locale);

// Fetch all student council data with person details
const studentCouncilMembers = await db.query.studentCouncil.findMany({
with: {
person: true,
},
orderBy: (studentCouncil, { asc }) => [
asc(studentCouncil.category),
asc(studentCouncil.section),
],
});

// Filter members by category "institute functionaries"
const instituteFunctionaries = studentCouncilMembers.filter(
(member) => member.category === 'institute_functionaries'
);
const coreCommitteeMembers = studentCouncilMembers.filter(
(member) => member.category === 'core_committee'
);
const nominatedStudentsMembers = studentCouncilMembers.filter(
(member) => member.category === 'nominated_students'
);
const studentRepresentatives = studentCouncilMembers.filter(
(member) => member.category === 'students_representatives'
);

// Separate faculty and students from institute functionaries
const facultyMembers = instituteFunctionaries.filter(
(member) => member.person.type === 'faculty'
);

// Get faculty employee IDs
const facultyPersonIds = facultyMembers.map((m) => m.personId);
const facultyData =
facultyPersonIds.length > 0
? await db.query.faculty.findMany({
where: (faculty, { inArray }) =>
inArray(faculty.id, facultyPersonIds),
columns: { id: true, employeeId: true },
})
: [];

// Create map for faculty designations
const facultyDesignationMap = new Map(
facultyMembers.map((m) => [m.personId, m.section])
);

// Prepare data for components
const facultyGroupData = facultyData.map((f) => ({
employeeId: f.employeeId,
designation: facultyDesignationMap.get(f.id) ?? '',
}));
Comment on lines +48 to +68
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

Redundant DB work: this page queries faculty to compute facultyGroupData, and then FICGroup queries faculty again to render details. Consider changing FICGroup to accept personIds (or accept fully-hydrated faculty/person rows) so the faculty table is queried only once.

Copilot uses AI. Check for mistakes.

// Prepare data for core committee (students only)
const coreCommitteePersonIds = coreCommitteeMembers.map((m) => m.personId);
const coreCommitteeStudentData =
coreCommitteePersonIds.length > 0
? await db.query.students.findMany({
where: (students, { inArray }) =>
inArray(students.id, coreCommitteePersonIds),
columns: { id: true, rollNumber: true },
})
: [];

const coreCommitteeDesignationMap = new Map(
coreCommitteeMembers.map((m) => [m.personId, m.section])
);

const coreCommitteeGroupData = coreCommitteeStudentData.map((s) => ({
rollNumber: s.rollNumber,
designation: coreCommitteeDesignationMap.get(s.id) ?? undefined,
}));

Comment on lines +70 to +89
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

Redundant DB work: this page queries students to get roll numbers, and then StudentGroup queries students again by roll number to fetch person details. Consider updating StudentGroup to accept student/person IDs (or pre-fetched student+person data) so you don’t need the extra query per group.

Copilot uses AI. Check for mistakes.
// Prepare data for nominated students
const nominatedStudentsPersonIds = nominatedStudentsMembers.map(
(m) => m.personId
);
const nominatedStudentsData =
nominatedStudentsPersonIds.length > 0
? await db.query.students.findMany({
where: (students, { inArray }) =>
inArray(students.id, nominatedStudentsPersonIds),
columns: { id: true, rollNumber: true },
})
: [];

const nominatedStudentsDesignationMap = new Map(
nominatedStudentsMembers.map((m) => [m.personId, m.section])
);

const nominatedStudentsGroupData = nominatedStudentsData.map((s) => ({
rollNumber: s.rollNumber,
designation: nominatedStudentsDesignationMap.get(s.id) ?? undefined,
}));

// Prepare data for student representatives
const studentRepresentativesPersonIds = studentRepresentatives.map(
(m) => m.personId
);
const studentRepresentativesData =
studentRepresentativesPersonIds.length > 0
? await db.query.students.findMany({
where: (students, { inArray }) =>
inArray(students.id, studentRepresentativesPersonIds),
columns: { id: true, rollNumber: true },
})
: [];

// Get academic details for student representatives
const studentRepresentativesAcademicData =
studentRepresentativesPersonIds.length > 0
? await db.query.studentAcademicDetails.findMany({
where: (academicDetails, { inArray }) =>
inArray(academicDetails.id, studentRepresentativesPersonIds),
columns: {
id: true,
section: true,
currentSemester: true,
batch: true,
},
with: {
major: {
columns: { name: true, degree: true },
},
},
})
: [];

// Create maps for student representatives
const studentRepresentativesMap = new Map(
studentRepresentativesData.map((s) => [s.id, s])
);
const studentRepresentativesAcademicMap = new Map(
studentRepresentativesAcademicData.map((s) => [s.id, s])
);

return (
<>
<ImageHeader
title={text.StudentCouncil.title}
src="student-activities/thought-lab/welcome-bk-shivani.jpeg"
/>

<section className="container">
{/* Main Title with dual elephants */}
<Heading
glyphDirection="dual"
heading="h3"
text={text.StudentCouncil.title.toUpperCase()}
/>
{/* About Description */}
<p className="mb-6">{text.StudentCouncil.about}</p>
<section className="container my-10" id="notifications">
<Heading
glyphDirection="ltr"
heading="h3"
href="#notifications"
id="notifications"
text={text.Notifications.title.toUpperCase()}
/>
<NotificationsPanel
locale={locale}
category="student-activities"
showViewAll={true}
viewAllHref={`/${locale}/notifications?category=miscellaneous`}
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

NotificationsPanel is filtered to category="student-activities", but the "View all" link points to category=miscellaneous, which will show the wrong set of notifications on /notifications. Update viewAllHref to use category=student-activities (or omit the category param if you want the unfiltered notifications page).

Suggested change
viewAllHref={`/${locale}/notifications?category=miscellaneous`}
viewAllHref={`/${locale}/notifications?category=student-activities`}

Copilot uses AI. Check for mistakes.
/>
</section>
<Heading
glyphDirection="dual"
heading="h3"
text={text.StudentCouncil.headings.instituteFunctionaries.toUpperCase()}
/>

{/* Display Faculty Members */}
{facultyGroupData.length > 0 && (
<FICGroup facultyData={facultyGroupData} />
)}

<Heading
glyphDirection="dual"
heading="h3"
text={text.StudentCouncil.headings.coreCommittee.toUpperCase()}
className="mt-12"
/>

{/* Display Core Committee Students */}
{coreCommitteeGroupData.length > 0 ? (
<div className="mt-6">
<StudentGroup studentData={coreCommitteeGroupData} />
</div>
) : (
<p className="text-gray-500 mt-6 text-center">
No core committee members found.
</p>
)}
Comment on lines +208 to +211
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

The empty-state message is hardcoded in English (No core committee members found.). Since this is a localized route, this should come from i18n translations so it renders correctly in Hindi/other locales.

Copilot uses AI. Check for mistakes.

<Heading
glyphDirection="dual"
heading="h3"
text={text.StudentCouncil.headings.nominatedStudents.toUpperCase()}
className="mt-12"
/>

{/* Display Nominated Students */}
{nominatedStudentsGroupData.length > 0 ? (
<div className="mt-6">
<StudentGroup studentData={nominatedStudentsGroupData} />
</div>
) : (
<p className="text-gray-500 mt-6 text-center">
No nominated students found.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

This empty-state message is hardcoded in English (No nominated students found.). Please move it into the StudentCouncil translations so it’s localized per locale.

Suggested change
No nominated students found.
{text.StudentCouncil.emptyStates.nominatedStudents}

Copilot uses AI. Check for mistakes.
</p>
)}

<Heading
glyphDirection="dual"
heading="h3"
text={text.StudentCouncil.headings.studentsRepresentatives.toUpperCase()}
className="mt-12"
/>

{/* Display Student Representatives */}
<div className="mt-6">
{studentRepresentatives.length > 0 ? (
<GenericTable
headers={[
{ key: 'roll', label: text.StudentCouncil.tableHeaders.roll },
{ key: 'name', label: text.StudentCouncil.tableHeaders.name },
{
key: 'contact',
label: text.StudentCouncil.tableHeaders.contact,
},
{
key: 'branch',
label: text.StudentCouncil.tableHeaders.branch,
},
{ key: 'batch', label: text.StudentCouncil.tableHeaders.batch },
]}
tableData={studentRepresentatives.map((member) => {
const studentData = studentRepresentativesMap.get(
member.personId
);
const academicData = studentRepresentativesAcademicMap.get(
member.personId
);
const branchWithDegree = academicData?.major
? `${academicData.major.name} (${academicData.major.degree})`
: '-';
return {
roll: studentData?.rollNumber ?? '-',
name: member.person.name,
contact: member.person.telephone
? `${member.person.telephone}`.trim()
: '-',
branch: branchWithDegree,
batch: academicData?.batch ? `${academicData.batch}` : '-',
};
})}
/>
) : (
<p className="text-gray-500 text-center">
No student representatives found.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

This empty-state message is hardcoded in English (No student representatives found.). Please use i18n translation strings instead so the page remains fully localized.

Suggested change
No student representatives found.
{text.StudentCouncil.emptyStates.noStudentRepresentatives}

Copilot uses AI. Check for mistakes.
</p>
)}
</div>
</section>
</>
);
}
2 changes: 2 additions & 0 deletions i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
nccEn,
nssEn,
laboratoriesEn,
studentCouncilEn,
} from './translate';
import { Label } from '@radix-ui/react-label';
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

Label is imported but never used in this file, which will trigger lint/build errors. Please remove the unused import (or use it if it’s required).

Suggested change
import { Label } from '@radix-ui/react-label';

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -106,6 +107,7 @@ const text: Translations = {
NCC: nccEn,
NSS: nssEn,
Laboratories: laboratoriesEn,
StudentCouncil: studentCouncilEn,
};

export default text;
2 changes: 2 additions & 0 deletions i18n/hi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
nccHi,
nssHi,
laboratoriesHi,
studentCouncilHi,
} from './translate';

const text: Translations = {
Expand Down Expand Up @@ -105,5 +106,6 @@ const text: Translations = {
NCC: nccHi,
NSS: nssHi,
Laboratories: laboratoriesHi,
StudentCouncil: studentCouncilHi,
};
export default text;
1 change: 1 addition & 0 deletions i18n/translate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ export * from './tenders';
export * from './ncc';
export * from './nss';
export * from './laboratories';
export * from './student-council';
55 changes: 55 additions & 0 deletions i18n/translate/student-council.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Student council translations

export interface StudentCouncilTranslations {
title: string;
about: string;
headings: {
instituteFunctionaries: string;
coreCommittee: string;
nominatedStudents: string;
studentsRepresentatives: string;
};
tableHeaders: {
roll: string;
name: string;
contact: string;
branch: string;
batch: string;
};
}

export const studentCouncilEn: StudentCouncilTranslations = {
title: 'Student Council',
about: `The Student Council of NIT Kurukshetra acts as the democratic voice of the student fraternity, serving as a vital bridge between the administration and the learners. We are dedicated to ensuring student welfare, organizing grand fests, and upholding the institute's legacy. In reality, our primary job description involves running laps around the Admin Block chasing signatures for budget approvals that never come on time. We don’t just solve problems; we create WhatsApp groups to discuss them for three hours and achieve nothing. We are the chosen few who get to wear formal clothes while everyone else is in shorts, giving us the illusion of authority. We fight for your rights, but mostly we fight for extra food coupons during the fest. We are the Student Council: Overworked, under-slept, and powered entirely by tea and skipped lectures.`,
headings: {
instituteFunctionaries: 'Institute Functionaries',
coreCommittee: 'Core Committee',
nominatedStudents: 'Nominated Students',
studentsRepresentatives: `Student's Representatives`,
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

Heading text uses incorrect apostrophe/grammar: Student's Representatives implies a single student’s representatives. Consider changing this to Student Representatives or Students' Representatives.

Suggested change
studentsRepresentatives: `Student's Representatives`,
studentsRepresentatives: 'Student Representatives',

Copilot uses AI. Check for mistakes.
},
tableHeaders: {
roll: 'Roll Number',
name: 'Name',
contact: 'Contact No.',
branch: 'Branch & programme',
batch: 'Batch',
},
};

export const studentCouncilHi: StudentCouncilTranslations = {
title: 'छात्र परिषद',
about: `एनआईटी कुरुक्षेत्र की छात्र परिषद छात्र समुदाय की लोकतांत्रिक आवाज के रूप में कार्य करती है, प्रशासन और शिक्षार्थियों के बीच एक महत्वपूर्ण पुल के रूप में सेवा करती है। हम छात्र कल्याण सुनिश्चित करने, भव्य उत्सव आयोजित करने और संस्थान की विरासत को बनाए रखने के लिए समर्पित हैं। वास्तव में, हमारा प्राथमिक कार्य विवरण बजट अनुमोदन के लिए हस्ताक्षर के लिए प्रशासनिक ब्लॉक के चारों ओर दौड़ना है जो कभी समय पर नहीं आते। हम केवल समस्याओं को हल नहीं करते; हम उन्हें चर्चा करने के लिए तीन घंटे तक व्हाट्सएप समूह बनाते हैं और कुछ भी हासिल नहीं करते हैं। हम चुने हुए कुछ लोग हैं जो औपचारिक कपड़े पहनते हैं जबकि बाकी सभी शॉर्ट्स में होते हैं, जिससे हमें अधिकार का भ्रम होता है। हम आपके अधिकारों के लिए लड़ते हैं, लेकिन ज्यादातर हम उत्सव के दौरान अतिरिक्त भोजन कूपन के लिए लड़ते हैं। हम छात्र परिषद हैं: ओवरवर्केड, कम सोए हुए, और पूरी तरह से चाय और छोड़ी गई कक्षाओं से संचालित।`,
headings: {
instituteFunctionaries: 'संस्थान के अधिकारी',
coreCommittee: 'कोर समिति',
nominatedStudents: 'नॉमिनेटेड स्टूडेंट्स',
studentsRepresentatives: 'छात्र प्रतिनिधि',
},
tableHeaders: {
roll: 'रोल नंबर',
name: 'नाम',
contact: 'संपर्क नंबर',
branch: 'शाखा और कार्यक्रम',
batch: 'बैच',
},
};
3 changes: 3 additions & 0 deletions i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import type {
NCCTranslations,
NSSTranslations,
LaboratoriesTranslations,
StudentCouncilTranslations,
} from './translate';

export async function getTranslations(locale: string): Promise<Translations> {
Expand Down Expand Up @@ -111,6 +112,7 @@ export type {
TrainingAndPlacementTranslations,
NCCTranslations,
NSSTranslations,
StudentCouncilTranslations,
};

export interface Translations {
Expand Down Expand Up @@ -165,4 +167,5 @@ export interface Translations {
NCC: NCCTranslations;
NSS: NSSTranslations;
Laboratories: LaboratoriesTranslations;
StudentCouncil: StudentCouncilTranslations;
}