Repository: centmarde/Performance-Monitoring-System
Pull request #107: Dev/marde
Author: login: centmarde
Created: 2025-05-09T10:05:52Z | Merged: 2025-05-09T10:09:26Z

This pull request introduces significant enhancements and fixes across multiple files in the project. The primary changes include the integration of dynamic topic handling for subjects, improvements to chart rendering and data visualization, and usability enhancements in the UI for better user interaction. Additionally, some unused code has been removed for better maintainability. Below are the most important changes grouped by theme:

### Dynamic Topic Handling and Data Management
* Added a `fetchTopicNames` function in `src/components/system/Review.vue` to dynamically fetch topic names based on the selected subject and integrate them into charts and chat responses. This ensures topic names are accurately reflected in the UI and chat system. [[1]](diffhunk://#diff-0100d6b30c73f84827835d3518e17bdd85a4c609751f5410214e18b4a715b274R103-R145) [[2]](diffhunk://#diff-0100d6b30c73f84827835d3518e17bdd85a4c609751f5410214e18b4a715b274R270-R341)
* Updated the `startChat` function in `src/composables/bootstrap.ts` to include topic names in the chat context, improving the relevance of chat responses.
* Modified `fetchRecords` in `src/pages/home/NewRecords.vue` to calculate `wwTotal` dynamically based on the number of topics available for the selected subject. [[1]](diffhunk://#diff-a000114da10b39a249aa5e1913177b93c49730fec7ccca40ce3f72455542a264R164-R183) [[2]](diffhunk://#diff-a000114da10b39a249aa5e1913177b93c49730fec7ccca40ce3f72455542a264L190-R205)

### Chart Rendering Enhancements
* Updated the `updateChart` function in `src/components/system/Review.vue` to dynamically populate chart labels and data with actual topic names and other performance metrics, improving the clarity of visualized data.
* Adjusted chart axis labels to rotate and scale dynamically for better readability.

### UI and Usability Improvements
* Added a "Go Back" button to `src/pages/home/Tracking.vue` for easier navigation. [[1]](diffhunk://#diff-1e85a873e7be033b7083bcf29cc28f9f06d7985933290fa1f3601a99559650b5L6-R22) [[2]](diffhunk://#diff-1e85a873e7be033b7083bcf29cc28f9f06d7985933290fa1f3601a99559650b5R38-R44)
* Implemented dynamic column width calculation in `src/pages/home/NewRecords.vue` and `src/pages/home/RecentRecords.vue` based on header text length, enhancing table usability. [[1]](diffhunk://#diff-a000114da10b39a249aa5e1913177b93c49730fec7ccca40ce3f72455542a264R280-R302) [[2]](diffhunk://#diff-b6983348d6c94b132a98fae8a7bc8474f18a966230d9b9a5b0181ebb38ccba7cL264-R300)

### Code Simplification and Cleanup
* Removed the unused `customHash` function from `src/stores/authUser.ts` and its associated usage, simplifying the codebase. [[1]](diffhunk://#diff-0217393bdf39cc0abe8df36f6b533303116656ea3a134efac97995d7866d0d5dL11-R11) [[2]](diffhunk://#diff-0217393bdf39cc0abe8df36f6b533303116656ea3a134efac97995d7866d0d5dL69-L75)

### Minor Fixes
* Adjusted the grade warning threshold in `src/pages/home/RecentRecords.vue` to correctly reflect failing grades.

These changes collectively improve the functionality, maintainability, and user experience of the application.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
	- Dynamic topic names are now fetched and displayed based on the selected subject throughout student review, records, and analysis features.
	- Chart labels and AI feedback use subject-specific topic names for improved clarity.
	- Added a "Go Back" button to the tracking page for easier navigation.

- **Improvements**
	- Written work totals and column widths now adapt to the actual number and length of topics for each subject.
	- Enhanced expand/collapse controls for topic columns in records views.
	- AI analysis feedback is now more tailored, referencing actual topic names and providing targeted improvement strategies.

- **Bug Fixes**
	- Corrected calculation of written work totals and improved UI consistency for column headers.

- **Chores**
	- Removed custom password hashing from user registration logic.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/src/components/system/Review.vue b/src/components/system/Review.vue
index 463ee7b..daedfc7 100644
--- a/src/components/system/Review.vue
+++ b/src/components/system/Review.vue
@@ -83,6 +83,7 @@ import { defineComponent, ref, onMounted, watch } from "vue";
 import * as echarts from "echarts";
 import { useGroqChat } from "@/composables/bootstrap";
 import { useRoute } from "vue-router";
+import axios from "axios";
 
 export default defineComponent({
   setup() {
@@ -99,13 +100,49 @@ export default defineComponent({
     const selectedQuarter = ref(null);
     const availableQuarters = ref([]);
     const subjectName = ref("");
+    const topicNames = ref([]);
 
     const { chatContent, startChat } = useGroqChat();
 
+    // Fetch topic names for a given subject
+    const fetchTopicNames = async (subjectName) => {
+      try {
+        const response = await axios.get('/data/topics.json');
+        const subjects = response.data;
+        
+        // Find the subject that matches
+        const subject = subjects.find(s => 
+          s.subject.toLowerCase() === subjectName.toLowerCase());
+        
+        if (subject && subject.topics) {
+          topicNames.value = subject.topics.slice(0, 5); // Get up to 5 topics
+          // Fill with empty strings if less than 5 topics
+          while (topicNames.value.length < 5) {
+            topicNames.value.push("");
+          }
+          return topicNames.value;
+        } else {
+          console.warn(`Subject "${subjectName}" not found in topics.json`);
+          return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+        }
+      } catch (error) {
+        console.error("Error fetching topic names:", error);
+        return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+      }
+    };
+
     // Add watch effect for studentRecord
-    watch(studentRecord, (newValue) => {
+    watch(studentRecord, async (newValue) => {
       if (newValue) {
         chatContent.value = ""; // Reset chat content
+        
+        // Start chat with topic names
+        if (subjectName.value) {
+          const topics = await fetchTopicNames(subjectName.value);
+          startChat(studentRecord.value, selectedStudent.value, topics);
+        } else {
+          startChat(studentRecord.value, selectedStudent.value);
+        }
       }
     });
 
@@ -221,7 +258,7 @@ export default defineComponent({
         const { data, error } = await supabase
           .from("records")
           .select(
-            "initial_grade, topic1, topic2, topic3, topic4, topic5, pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8, pt9, pt10, qa1"
+            " topic1, topic2, topic3, topic4, topic5, pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8, pt9, pt10, qa1, initial_grade"
           )
           .eq("student_id", student.id)
           .eq("class_record_id", classRecord.id)
@@ -230,46 +267,78 @@ export default defineComponent({
         if (!error) {
           studentRecord.value = data;
           isFailing.value = studentRecord.value.initial_grade < 75;
+          
+          if (selectedSubject.value && selectedSubject.value.title) {
+            subjectName.value = selectedSubject.value.title;
+            await fetchTopicNames(selectedSubject.value.title);
+          }
+          
           updateChart();
-          startChat(studentRecord.value, studentFullName);
         }
       }
     };
 
-    const updateChart = () => {
+    const updateChart = async () => {
       if (!studentRecord.value) return;
 
+      // Ensure we have topic names
+      if (subjectName.value && topicNames.value.length === 0) {
+        await fetchTopicNames(subjectName.value);
+      }
+
       const chartDom = document.getElementById("chart");
       const myChart = echarts.init(chartDom);
+      
+      // Prepare data for chart
+      const chartData = [];
+      const labels = [];
+      
+      // Add topics with their actual names
+      for (let i = 1; i <= 5; i++) {
+        const topicKey = `topic${i}`;
+        if (studentRecord.value[topicKey] !== undefined) {
+          chartData.push(studentRecord.value[topicKey]);
+          labels.push(topicNames.value[i-1] || `Topic ${i}`);
+        }
+      }
+      
+      // Add PT items
+      for (let i = 1; i <= 10; i++) {
+        const ptKey = `pt${i}`;
+        if (studentRecord.value[ptKey] !== undefined) {
+          chartData.push(studentRecord.value[ptKey]);
+          labels.push(`PT${i}`);
+        }
+      }
+      
+      // Add QA
+      if (studentRecord.value.qa1 !== undefined) {
+        chartData.push(studentRecord.value.qa1);
+        labels.push("QA");
+      }
+
       const option = {
         tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
         xAxis: {
           type: "category",
-          data: [
-            "TOPIC1",
-            "TOPIC2",
-            "TOPIC3",
-            "TOPIC4",
-            "TOPIC5",
-           
-            "PT1",
-            "PT2",
-            "PT3",
-            "PT4",
-            "PT5",
-            "PT6",
-            "PT7",
-            "PT8",
-            "PT9",
-            "PT10",
-            "QA1",
-          ],
+          data: labels,
+          axisLabel: {
+            interval: 0,
+            rotate: 45,
+            textStyle: {
+              fontSize: 10
+            }
+          }
+        },
+        yAxis: { 
+          type: "value",
+          min: 0,
+          max: 100
         },
-        yAxis: { type: "value" },
         series: [
           {
             type: "bar",
-            data: Object.values(studentRecord.value),
+            data: chartData,
             itemStyle: {
               color: (params) => {
                 const value = params.data;
@@ -302,9 +371,9 @@ export default defineComponent({
 
       // Get subject name from localStorage
       const storedSubjectName = localStorage.getItem("selectedSubjectName");
-      console.log(storedSubjectName);
       if (storedSubjectName) {
         subjectName.value = storedSubjectName;
+        await fetchTopicNames(storedSubjectName);
       }
 
       // Handle subject and quarter selection automatically
@@ -333,6 +402,7 @@ export default defineComponent({
       selectedQuarter,
       availableQuarters,
       subjectName,
+      topicNames,
     };
   },
 });
diff --git a/src/composables/bootstrap.ts b/src/composables/bootstrap.ts
index 509193b..a3bb1f5 100644
--- a/src/composables/bootstrap.ts
+++ b/src/composables/bootstrap.ts
@@ -25,24 +25,23 @@ function formatResponse(content: string): string {
 export function useGroqChat() {
   const chatContent = ref("");
 
-  async function startChat(studentRecord: any, studentName: string) {
+  async function startChat(studentRecord: any, studentName: string, topicNames: string[] = []) {
     const chatCompletion = await groq.chat.completions.create({
       messages: [
-        /* {
-          role: "assistant",
-          content:
-            "Hello there, teacher. It's great to see you. I trust you're having a wonderful day so far. Is there something specific you'd like to discuss or learn about today, or would you like me to suggest some topics we could explore together?",
-        }, */
         {
           role: "assistant",
           content:
-            "provide advice for the teacher to help the student improve their performance. dont provide a list in your responsne also shorten it in 500 words.",
+            "Analyze this student's performance data to identify which specific topics they are struggling with. Focus on the lowest scores and suggest targeted strategies to help improve in those specific areas. Use the actual topic names in your feedback, not just topic numbers. Keep your response under 500 words."
         },
         {
           role: "system",
           content: ` 
-          WW = written works, PT = personal task, and QA = quality assessment.
-          Also, remember the name of the student: ${studentName}. ${JSON.stringify(
+          The actual topic names for topic1 through topic5 are: ${JSON.stringify(topicNames)}.
+          WW = written works (these are the named topics), PT = personal task (pt1 through pt10), and QA = quality assessment (qa1).
+          Scores below 75 are considered failing, 75-80 are borderline, and above 80 are good.
+          Identify which specific named topics have the lowest scores and suggest concrete improvement strategies tailored to those topics.
+          Always refer to topics by their actual names (not as topic1, topic2, etc.) when providing advice.
+          Remember the name of the student: ${studentName}. ${JSON.stringify(
             studentRecord
           )}`,
         },
diff --git a/src/pages/home/DataEntry.vue b/src/pages/home/DataEntry.vue
index 2a6f19e..6460509 100644
--- a/src/pages/home/DataEntry.vue
+++ b/src/pages/home/DataEntry.vue
@@ -494,12 +494,16 @@ const saveClassRecord = async () => {
   if (!isNaN(sectionId) && !isNaN(classRecordId)) {
     await recordsStore.addRecordsForSection(
       Number(sectionId),
-      Number(classRecordId)
+      Number(classRecordId),
+      selectedSubject.value // Pass the subject title here
     );
   } else {
     console.error("Invalid section or class record ID");
   }
 
+  // Store selected subject name for later use
+  localStorage.setItem("selectedSubjectName", selectedSubject.value);
+
   classRecordDialog.value = false;
 
   toast.success("Subject added successfully.", {
diff --git a/src/pages/home/NewRecords.vue b/src/pages/home/NewRecords.vue
index ae18600..b491bef 100644
--- a/src/pages/home/NewRecords.vue
+++ b/src/pages/home/NewRecords.vue
@@ -161,10 +161,26 @@ const fetchRecords = async () => {
   }
 
   const gradeCalculations = await fetchGradeCalculations(classRecordId);
+  
+  // Get topic count for the selected subject
+  const subjectName = localStorage.getItem("selectedSubjectName");
+  const topics = await getTopicsForSubject(subjectName || "");
+  const topicCount = topics.length;
+  
   jsondata.value = records
     .map((record) => {
       const gradeCalculation =
         gradeCalculations.find((gc) => gc.student_id === record.id) || {};
+        
+      // Calculate wwTotal based on the number of available topics
+      let wwTotal = 0;
+      for (let i = 1; i <= Math.min(5, topicCount); i++) {
+        const topicKey = `topic${i}`;
+        if (record[topicKey] !== null && record[topicKey] !== undefined) {
+          wwTotal += record[topicKey];
+        }
+      }
+      
       const item = {
         id: record.student_id,
         name: record.students
@@ -175,7 +191,6 @@ const fetchRecords = async () => {
         topic3: record.topic3,
         topic4: record.topic4,
         topic5: record.topic5,
-
         pt1: record.pt1,
         pt2: record.pt2,
         pt3: record.pt3,
@@ -187,8 +202,7 @@ const fetchRecords = async () => {
         pt9: record.pt9,
         pt10: record.pt10,
         qa1: record.qa1,
-        wwTotal:
-              record.topic1 + record.topic2 + record.topic3 + record.topic4 + record.topic5,
+        wwTotal: wwTotal, // Use the calculated wwTotal
 
         ptTotal:
           record.pt1 +
@@ -263,12 +277,29 @@ const toggleColumnExpanded = (index) => {
   wwHeaders.value[index].expanded = !wwHeaders.value[index].expanded;
 };
 
+// Dynamically calculate column width based on header text length if expanded
 const getColumnWidth = (header) => {
-  return header.expanded ? '200px' : '120px';
+  if (header.expanded) {
+    // 12px per character, min 120px, max 400px
+    const base = 12;
+    const min = 120;
+    const max = 400;
+    const textLen = (header.text || "").length;
+    return Math.max(min, Math.min(max, textLen * base)) + "px";
+  }
+  return "120px";
 };
 
 const getColumnMinWidth = (header) => {
-  return header.expanded ? '200px' : '120px';
+  if (header.expanded) {
+    // Match getColumnWidth for consistency
+    const base = 12;
+    const min = 120;
+    const max = 400;
+    const textLen = (header.text || "").length;
+    return Math.max(min, Math.min(max, textLen * base)) + "px";
+  }
+  return "120px";
 };
 
 onMounted(async () => {
@@ -522,33 +553,34 @@ const getGradeClass = (grade) => {
                       position: 'relative'
                     }"
                   >
-                  <span
-  @click="editTopic(index)"
-  :title="wwHeaders[index].text || 'Topic name'"
-  style="
-    display: inline-block;
-    width: 90%;
-    background: transparent;
-    color: white;
-    border: 1px solid #00796b;
-    border-radius: 4px;
-    padding: 4px;
-    text-align: center;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    cursor: pointer;
-  "
->
-  {{ wwHeaders[index].text || 'Topic name' }}
-</span>
-                    <v-icon 
-                      class="expand-icon" 
-                      size="small" 
-                      @click="toggleColumnExpanded(index)"
+                    <span
+                      :title="header.text"
+                      style="
+                        display: inline-block;
+                        width: 90%;
+                        background: transparent;
+                        color: white;
+                        border: 1px solid #00796b;
+                        border-radius: 4px;
+                        padding: 4px;
+                        text-align: center;
+                        overflow: hidden;
+                        text-overflow: ellipsis;
+                        white-space: nowrap;
+                      "
                     >
-                      {{ header.expanded ? 'mdi-minus' : 'mdi-plus' }}
-                    </v-icon>
+                      {{ header.text}}
+                    </span>
+                    <template v-if="!header.disabled">
+                      <v-icon
+                        class="expand-icon"
+                        size="small"
+                        style="position: absolute; right: 4px; top: 50%; transform: translateY(-50%); cursor: pointer;"
+                        @click="toggleColumnExpanded(index)"
+                      >
+                        {{ header.expanded ? 'mdi-chevron-down' : 'mdi-chevron-right' }}
+                      </v-icon>
+                    </template>
                   </th>
                   <th
                     v-for="header in ptHeaders"
diff --git a/src/pages/home/RecentRecords.vue b/src/pages/home/RecentRecords.vue
index f1aad34..b82b0d8 100644
--- a/src/pages/home/RecentRecords.vue
+++ b/src/pages/home/RecentRecords.vue
@@ -261,16 +261,43 @@ const updateTopicHeaders = async () => {
   }
 };
 
-const toggleColumnExpanded = (index) => {
-  wwHeaders.value[index].expanded = !wwHeaders.value[index].expanded;
+const wwExpanded = ref(false);
+
+const toggleAllWwColumns = () => {
+  wwExpanded.value = !wwExpanded.value;
+  wwHeaders.value = wwHeaders.value.map(header => ({
+    ...header,
+    expanded: wwExpanded.value
+  }));
 };
 
+// Dynamically calculate column width based on header text length if expanded
 const getColumnWidth = (header) => {
-  return header.expanded ? "200px" : "120px";
+  if (header.expanded) {
+    // 12px per character, min 120px, max 400px
+    const base = 12;
+    const min = 120;
+    const max = 400;
+    const textLen = (header.text || "").length;
+    return Math.max(min, Math.min(max, textLen * base)) + "px";
+  }
+  return "120px";
 };
 
 const getColumnMinWidth = (header) => {
-  return header.expanded ? "200px" : "120px";
+  if (header.expanded) {
+    // Match getColumnWidth for consistency
+    const base = 12;
+    const min = 120;
+    const max = 400;
+    const textLen = (header.text || "").length;
+    return Math.max(min, Math.min(max, textLen * base)) + "px";
+  }
+  return "120px";
+};
+
+const toggleColumnExpanded = (index) => {
+  wwHeaders.value[index].expanded = !wwHeaders.value[index].expanded;
 };
 
 onMounted(async () => {
@@ -505,13 +532,16 @@ onMounted(async () => {
                     >
                       {{ header.text || "" }}
                     </span>
-                    <v-icon
-                      class="expand-icon"
-                      size="small"
-                      @click="toggleColumnExpanded(index)"
-                    >
-                      {{ header.expanded ? "mdi-minus" : "mdi-plus" }}
-                    </v-icon>
+                    <template v-if="!header.disabled">
+                      <v-icon
+                        class="expand-icon"
+                        size="small"
+                        style="position: absolute; right: 4px; top: 50%; transform: translateY(-50%); cursor: pointer;"
+                        @click="toggleColumnExpanded(index)"
+                      >
+                        {{ header.expanded ? "mdi-chevron-down" : "mdi-chevron-right" }}
+                      </v-icon>
+                    </template>
                   </th>
                   <th
                     v-for="header in ptHeaders"
@@ -927,7 +957,7 @@ onMounted(async () => {
                     <v-btn
                       v-if="
                         parseFloat(item.quarterly_grade) <= 80 &&
-                        parseFloat(item.quarterly_grade) >= 76
+                        parseFloat(item.quarterly_grade) >= 75
                       "
                       color="warning"
                       size="small"
diff --git a/src/pages/home/Tracking.vue b/src/pages/home/Tracking.vue
index 3978abe..58c46e7 100644
--- a/src/pages/home/Tracking.vue
+++ b/src/pages/home/Tracking.vue
@@ -3,12 +3,23 @@
     <template #content>
       <v-container>
         <v-row justify="start">
-          <v-col cols="auto">
+          <v-col cols="auto" class="w-100">
             <v-card class="pa-3 rounded-card glass-card">
-              <h4 class="font-weight-bold text-end">
-                <span class="mdi mdi-account-school"></span> Student's Missed
-                Activities Tracking
-              </h4>
+              <div class="d-flex justify-space-between align-center">
+                <h4 class="font-weight-bold mb-0">
+                  <span class="mdi mdi-account-school"></span> Student's Missed
+                  Activities Tracking
+                </h4>
+                <v-btn
+                  icon
+                  color="white"
+                  class="ml-2"
+                  @click="goBack"
+                  title="Go Back"
+                >
+                  <v-icon>mdi-arrow-left</v-icon>
+                </v-btn>
+              </div>
             </v-card>
           </v-col>
         </v-row>
@@ -24,6 +35,13 @@
 <script setup lang="ts">
 import { ref, computed } from "vue";
 import HomeLayout from "@/layouts/HomeLayout.vue";
+import { useRouter } from "vue-router";
+
+const router = useRouter();
+
+const goBack = () => {
+  router.go(-1);
+};
 </script>
 
 <style scoped>
diff --git a/src/stores/authUser.ts b/src/stores/authUser.ts
index 7bc2685..268288e 100644
--- a/src/stores/authUser.ts
+++ b/src/stores/authUser.ts
@@ -8,15 +8,7 @@ import { useToast } from "vue-toastification";
 const toast = useToast();
 
 // Custom hash function
-function customHash(password: string): string {
-  let hash = 0;
-  for (let i = 0; i < password.length; i++) {
-    const char = password.charCodeAt(i);
-    hash = (hash << 5) - hash + char;
-    hash |= 0; // Convert to 32bit integer
-  }
-  return `HashPassword${hash.toString()}`;
-}
+
 
 interface UserData {
   id?: string;
@@ -66,13 +58,12 @@ export const useAuthUserStore = defineStore("authUser", () => {
     const userId = signUpData.user.id;
 
     // Hash the password before inserting it into the database
-    const hashedPassword = customHash(password);
+   
 
     const { error: insertError } = await supabase.from("users").insert([
       {
         user_type: userType,
         email: email,
-        password: hashedPassword, // Use the hashed password
         user_id: userId,
       },
     ]);
diff --git a/src/stores/recordsStore.ts b/src/stores/recordsStore.ts
index a430921..0537a28 100644
--- a/src/stores/recordsStore.ts
+++ b/src/stores/recordsStore.ts
@@ -76,7 +76,8 @@ export const useRecordsStore = defineStore("recordsStore", () => {
 
   async function addRecordsForSection(
     sectionId: number,
-    classRecordId: number
+    classRecordId: number,
+    subjectTitle?: string // Add optional parameter for subject title
   ) {
     const { data: students, error } = await supabase
       .from("students")
@@ -88,22 +89,26 @@ export const useRecordsStore = defineStore("recordsStore", () => {
       return null;
     }
 
-    const subjectName = localStorage.getItem("selectedSubjectName");
+    // Use the provided subjectTitle or fall back to localStorage
+    const subjectName = subjectTitle || localStorage.getItem("selectedSubjectName");
     const subjectTopics = topicsData.find(
       (subject) => subject.subject === subjectName
     )?.topics || [];
+    
+    console.log(`Found ${subjectTopics.length} topics for subject ${subjectName}`);
 
     const getRandomScore = (min: number, max: number) =>
       Math.floor(Math.random() * (max - min + 1)) + min;
 
     const records: Record[] = students.map((student: any) => {
+      // Create a base record with all topic fields set to null initially
       const record: Record = {
         student_id: student.id,
-        topic1: subjectTopics[0] ? getRandomScore(50, 100) : null,
-        topic2: subjectTopics[1] ? getRandomScore(50, 100) : null,
-        topic3: subjectTopics[2] ? getRandomScore(50, 100) : null,
-        topic4: subjectTopics[3] ? getRandomScore(50, 100) : null,
-        topic5: subjectTopics[4] ? getRandomScore(50, 100) : null,
+        topic1: null,
+        topic2: null,
+        topic3: null,
+        topic4: null,
+        topic5: null,
         pt1: getRandomScore(50, 100),
         pt2: getRandomScore(50, 100),
         pt3: getRandomScore(70, 100),
@@ -118,12 +123,13 @@ export const useRecordsStore = defineStore("recordsStore", () => {
         class_record_id: classRecordId,
       };
 
-      // Remove null values for topics if less than 5
-      Object.keys(record).forEach((key) => {
-        if (key.startsWith("topic") && record[key as keyof Record] === null) {
-          delete record[key as keyof Record];
+      // Only populate topic fields based on the available topics
+      for (let i = 0; i < Math.min(5, subjectTopics.length); i++) {
+        const topicKey = `topic${i + 1}` as keyof Record;
+        if (subjectTopics[i]) {
+          record[topicKey] = getRandomScore(50, 100);
         }
-      });
+      }
 
       return record;
     });


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>src/components/system/Review.vue (1)</summary><blockquote>

`281-355`: _🛠️ Refactor suggestion_

**Potential memory leak: repeatedly calling `echarts.init` on the same DOM node**  

`updateChart` re-initialises ECharts every time without disposing the previous instance. This can leak canvases and listeners, especially when the watcher fires often.

```diff
const chartDom = document.getElementById("chart");
-const myChart = echarts.init(chartDom);
+let myChart = echarts.getInstanceByDom(chartDom);
+if (!myChart) {
+  myChart = echarts.init(chartDom);
+}
```

Dispose of the instance in `onUnmounted` if the component can be destroyed.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>src/stores/recordsStore.ts (1)</summary><blockquote>

`103-103`: **Consider adding proper type for student parameter**

The code uses `any` type for the student parameter, which bypasses TypeScript's type checking benefits.


```diff
- const records: Record[] = students.map((student: any) => {
+ const records: Record[] = students.map((student: { id: number }) => {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 103-103: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

</details>

</details>

</blockquote></details>
<details>
<summary>src/composables/bootstrap.ts (1)</summary><blockquote>

`28-28`: **Consider adding proper type for studentRecord parameter**

The code uses `any` type for the studentRecord parameter, which bypasses TypeScript's type checking benefits.


```diff
- async function startChat(studentRecord: any, studentName: string, topicNames: string[] = []) {
+ async function startChat(studentRecord: Record<string, number | null>, studentName: string, topicNames: string[] = []) {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 28-28: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

</details>

</details>

</blockquote></details>
<details>
<summary>src/pages/home/RecentRecords.vue (2)</summary><blockquote>

`274-297`: **Magic numbers in dynamic-width helpers – consider lifting to shared util**  

`getColumnWidth` and `getColumnMinWidth` both repeat the `(base, min, max)` constants. This duplication makes later tuning error-prone. A small refactor to a shared helper (or a closure that returns both width & minWidth) would keep them DRY.

```diff
-const base = 12;
-const min = 120;
-const max = 400;
+const COL_WIDTH = { base: 12, min: 120, max: 400 };
+const calcWidth = (textLen) =>
+  Math.max(COL_WIDTH.min, Math.min(COL_WIDTH.max, textLen * COL_WIDTH.base)) +
+  "px";
```

Not urgent, but worth tidying.

---

`535-545`: **Event bubbling on the expand icon may cause accidental column-sort / drag**  

Because the `<v-icon>` sits inside the `<th>` element, clicks could bubble and trigger unintended table-wide handlers (e.g., sorting if added later). Add `@click.stop` (or `@click.prevent`) to keep the event local:

```diff
-@click="toggleColumnExpanded(index)"
+@click.stop="toggleColumnExpanded(index)"
```

</blockquote></details>
<details>
<summary>src/pages/home/NewRecords.vue (1)</summary><blockquote>

`280-303`: **Duplicate column-width helpers – factor out to avoid divergence**  

`getColumnWidth` / `getColumnMinWidth` repeat the constants defined in `RecentRecords.vue`. Extract these into a composable (e.g., `useColumnWidth.ts`) and import in both places. This keeps UX consistent and simplifies future tweaks.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a6f3166303a21f39b75a4ab15e4f8ca2b04c867e and 0ab8672ce0dec8b0490ae078db6a1893e11151c8.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `src/components/system/Review.vue` (6 hunks)
* `src/composables/bootstrap.ts` (1 hunks)
* `src/pages/home/DataEntry.vue` (1 hunks)
* `src/pages/home/NewRecords.vue` (4 hunks)
* `src/pages/home/RecentRecords.vue` (3 hunks)
* `src/pages/home/Tracking.vue` (2 hunks)
* `src/stores/authUser.ts` (2 hunks)
* `src/stores/recordsStore.ts` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>src/stores/recordsStore.ts</summary>

[error] 103-103: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

</details>
<details>
<summary>src/composables/bootstrap.ts</summary>

[error] 28-28: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

</details>
<details>
<summary>src/pages/home/RecentRecords.vue</summary>

[error] 266-266: 'toggleAllWwColumns' is assigned a value but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (11)</summary><blockquote>

<details>
<summary>src/stores/authUser.ts (2)</summary>

`10-11`: **Removing custom password hashing is a positive change.**

Removing the custom hash function is generally a good practice when using authentication providers like Supabase that already handle password hashing securely. This eliminates potential issues with double-hashing and simplifies your authentication flow.

---

`60-61`: <details>
<summary>✅ Verification successful</summary>

**Security verification recommended.**

The removal of custom password hashing before database insertion is appropriate since Supabase Auth already handles password security. Your implementation correctly omits storing the password in your "users" table, which aligns with best practices for credential management.


To ensure this change doesn't introduce security issues, verify that:
1. The application relies solely on Supabase Auth for authentication
2. No other parts of the application expect passwords to be hashed with the removed function

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for any other references to custom password hashing in the codebase
rg -i "customHash|password.*hash|hash.*password" --type ts
```

Length of output: 146

---

🏁 Script executed:

```shell
#!/bin/bash
# Verify that Supabase Auth is used for all authentication flows
echo "Searching for Supabase Auth methods..."
rg -n "supabase\\.auth\\.(signUp|signIn|signOut|onAuthStateChange)" --type ts

# Check for any direct handling of plain-text passwords elsewhere
echo "Searching for direct password assignments or checks..."
rg -n "password\\s*[:=]" --type ts

# Look for any custom authentication modules or imports
echo "Searching for custom auth or hash imports..."
rg -n "customHash|bcrypt|argon2|pbkdf2" --type ts
```

Length of output: 1077

---



</details>

**Security Audit Complete: Supabase Auth Exclusively Manages Passwords**

All password handling is delegated to Supabase Auth, and no custom hashing remains in the codebase:

- Supabase Auth methods (`signUp`, `signInWithPassword`, `signOut`, `onAuthStateChange`) are used in `src/stores/authUser.ts` and `src/lib/supabase.ts`.
- No custom hash functions or libraries (`customHash`, `bcrypt`, `argon2`, `pbkdf2`) are referenced.
- There are no other instances of plain-text password processing beyond function parameters and the Supabase calls.

No further changes are required—Supabase Auth correctly secures credentials.

</details>
<details>
<summary>src/pages/home/Tracking.vue (2)</summary>

`6-22`: **Good navigation enhancement!**

The addition of a "Go Back" button with proper icon and tooltip improves the user experience by providing an intuitive way to navigate back to the previous page. The flex container layout is well implemented to maintain proper spacing between the title and the back button.

---

`38-44`: **Clean implementation of navigation logic**

The implementation of the navigation logic using Vue Router's `go(-1)` method is a standard and effective approach for history-based navigation.

</details>
<details>
<summary>src/pages/home/DataEntry.vue (2)</summary>

`497-499`: **Good integration with dynamic topic handling**

Passing the selected subject title to the `addRecordsForSection` function enables proper dynamic topic handling, which aligns well with the broader changes for subject-specific topic management.

---

`504-505`: **Effective state persistence approach**

Storing the selected subject name in localStorage is a good approach for maintaining context between different views and components that need access to the current subject.

</details>
<details>
<summary>src/stores/recordsStore.ts (3)</summary>

`79-81`: **Good API enhancement for flexibility**

Making the subject title an optional parameter improves the function's flexibility while maintaining backward compatibility.

---

`92-99`: **Effective subject selection logic with good fallback**

The approach to use the provided subject title or fall back to localStorage is robust. The logging statement helps with debugging and verifying that the correct number of topics are being found.

---

`104-132`: **Improved dynamic topic field handling**

The changes to initialize all topic fields to null first and then populate them based on available topics is a more robust approach that handles varying numbers of topics per subject. This is a significant improvement over the previous implementation.

</details>
<details>
<summary>src/composables/bootstrap.ts (2)</summary>

`33-35`: **Improved AI prompt for better student feedback**

The updated assistant message provides more specific instructions for analyzing student performance data, which will lead to more targeted and helpful feedback.

---

`38-44`: **Enhanced system message with topic context**

The addition of actual topic names and clarifying information about score thresholds greatly improves the context provided to the AI model. Having the AI refer to topics by their actual names rather than generic labels will make the feedback more relevant and easier for students to understand.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T10:09:06Z

Review comments:
diff_hunk: @@ -261,16 +261,43 @@ const updateTopicHeaders = async () => {
   }
 };
 
-const toggleColumnExpanded = (index) => {
-  wwHeaders.value[index].expanded = !wwHeaders.value[index].expanded;
+const wwExpanded = ref(false);
+
+const toggleAllWwColumns = () => {
+  wwExpanded.value = !wwExpanded.value;
+  wwHeaders.value = wwHeaders.value.map(header => ({
+    ...header,
+    expanded: wwExpanded.value
+  }));
 };
path: src/pages/home/RecentRecords.vue
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**`toggleAllWwColumns` is currently orphaned – either wire it up or drop it**  

You introduced a handy global-toggle helper but never call it from the template or elsewhere. As a result, ESLint rightfully marks it as “unused”. Either:

1. Add a master-expand icon / button in the header row and call `toggleAllWwColumns`, or  
2. Remove the function (and the `wwExpanded` ref) to keep the codebase clean.

```diff
-const wwExpanded = ref(false);
-const toggleAllWwColumns = () => {
-  wwExpanded.value = !wwExpanded.value;
-  wwHeaders.value = wwHeaders.value.map(header => ({
-    ...header,
-    expanded: wwExpanded.value
-  }));
-};
```

If you decide to keep the feature, please add the UI hook; otherwise delete the dead code.

---

I’ll verify whether `toggleAllWwColumns` (and its state `wwExpanded`) is ever referenced in the template or elsewhere:

---

🏁 Script executed:

```shell
#!/bin/bash
# Confirm the file exists
echo "Checking for RecentRecords.vue…"
git ls-files | grep -q 'src/pages/home/RecentRecords.vue' && echo "✔ Found RecentRecords.vue" || echo "✖ RecentRecords.vue not found"

# Search for any invocation of the helper
echo "Searching for toggleAllWwColumns usages…"
rg -n "toggleAllWwColumns" .

# Look for template bindings that might call the helper
echo "Searching for @click or other bindings…"
rg -n "@click.*toggleAllWwColumns" src/pages/home/RecentRecords.vue || echo "no @click bindings found"

# Check references to the expanded state
echo "Searching for wwExpanded references…"
rg -n "wwExpanded" src/pages/home/RecentRecords.vue || echo "no additional wwExpanded references"

# And double-check headers mapping state usage
echo "Searching for wwHeaders usages…"
rg -n "wwHeaders" src/pages/home/RecentRecords.vue || echo "no additional wwHeaders mappings"
```

Length of output: 1526

---



</details>

**Remove or wire up the orphaned `toggleAllWwColumns` helper**  

The `toggleAllWwColumns` function (and its `wwExpanded` ref) in `src/pages/home/RecentRecords.vue` is never invoked—ESLint correctly flags it as unused. You must either:

- **Add a UI hook:** e.g. a master-expand button in the header row with `@click="toggleAllWwColumns"`,  
- **Or remove the dead code** to keep the codebase clean.

Locations to address:  
- src/pages/home/RecentRecords.vue, lines 264–272

Suggested diff if you choose to drop it:

```diff
- const wwExpanded = ref(false);
- const toggleAllWwColumns = () => {
-   wwExpanded.value = !wwExpanded.value;
-   wwHeaders.value = wwHeaders.value.map(header => ({
-     ...header,
-     expanded: wwExpanded.value
-   }));
- };
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion

`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 266-266: 'toggleAllWwColumns' is assigned a value but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-09T10:09:05Z
author_association: NONE
diff_hunk: @@ -161,10 +161,26 @@ const fetchRecords = async () => {
   }
 
   const gradeCalculations = await fetchGradeCalculations(classRecordId);
+  
+  // Get topic count for the selected subject
+  const subjectName = localStorage.getItem("selectedSubjectName");
+  const topics = await getTopicsForSubject(subjectName || "");
+  const topicCount = topics.length;
+  
   jsondata.value = records
     .map((record) => {
       const gradeCalculation =
         gradeCalculations.find((gc) => gc.student_id === record.id) || {};
+        
+      // Calculate wwTotal based on the number of available topics
+      let wwTotal = 0;
+      for (let i = 1; i <= Math.min(5, topicCount); i++) {
+        const topicKey = `topic${i}`;
+        if (record[topicKey] !== null && record[topicKey] !== undefined) {
+          wwTotal += record[topicKey];
+        }
+      }
+      
path: src/pages/home/NewRecords.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`wwTotal` calculation silently ignores legacy topic fields when no topics are returned**  

If the subject’s topic list fails to load (network/file error) `topics.length` becomes `0`, and `wwTotal` is computed over an empty range – even though `topic1…topic5` scores may exist in the record. Prefer falling back to 5 to preserve existing behaviour:

```diff
-const topicCount = topics.length;
+const topicCount = topics.length || 5; // default to 5 legacy fields
```

This prevents unexpected zero totals when the topic service is unavailable.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
  // Get topic count for the selected subject
  const subjectName = localStorage.getItem("selectedSubjectName");
  const topics = await getTopicsForSubject(subjectName || "");
- const topicCount = topics.length;
+ const topicCount = topics.length || 5; // default to 5 legacy fields
  
  jsondata.value = records
    .map((record) => {
      const gradeCalculation =
        gradeCalculations.find((gc) => gc.student_id === record.id) || {};
        
      // Calculate wwTotal based on the number of available topics
      let wwTotal = 0;
      for (let i = 1; i <= Math.min(5, topicCount); i++) {
        const topicKey = `topic${i}`;
        if (record[topicKey] !== null && record[topicKey] !== undefined) {
          wwTotal += record[topicKey];
        }
      }
      
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-09T10:09:06Z
author_association: NONE
diff_hunk: @@ -99,13 +100,49 @@ export default defineComponent({
     const selectedQuarter = ref(null);
     const availableQuarters = ref([]);
     const subjectName = ref("");
+    const topicNames = ref([]);
 
     const { chatContent, startChat } = useGroqChat();
 
+    // Fetch topic names for a given subject
+    const fetchTopicNames = async (subjectName) => {
+      try {
+        const response = await axios.get('/data/topics.json');
+        const subjects = response.data;
+        
+        // Find the subject that matches
+        const subject = subjects.find(s => 
+          s.subject.toLowerCase() === subjectName.toLowerCase());
+        
+        if (subject && subject.topics) {
+          topicNames.value = subject.topics.slice(0, 5); // Get up to 5 topics
+          // Fill with empty strings if less than 5 topics
+          while (topicNames.value.length < 5) {
+            topicNames.value.push("");
+          }
+          return topicNames.value;
+        } else {
+          console.warn(`Subject "${subjectName}" not found in topics.json`);
+          return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+        }
+      } catch (error) {
+        console.error("Error fetching topic names:", error);
+        return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+      }
+    };
path: src/components/system/Review.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fallback path forgets to update `topicNames` state**  

When the subject is missing or the fetch fails you return a default array but do **not** assign it to `topicNames.value`, leaving the reactive variable empty and breaking `updateChart()`.

```diff
-  return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+  topicNames.value = ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+  return topicNames.value;
```

Apply the same change to the `catch` block.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
    // Fetch topic names for a given subject
    const fetchTopicNames = async (subjectName) => {
      try {
        const response = await axios.get('/data/topics.json');
        const subjects = response.data;
        
        // Find the subject that matches
        const subject = subjects.find(s => 
          s.subject.toLowerCase() === subjectName.toLowerCase());
        
        if (subject && subject.topics) {
          topicNames.value = subject.topics.slice(0, 5); // Get up to 5 topics
          // Fill with empty strings if less than 5 topics
          while (topicNames.value.length < 5) {
            topicNames.value.push("");
          }
          return topicNames.value;
        } else {
          console.warn(`Subject "${subjectName}" not found in topics.json`);
-          return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+          topicNames.value = ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+          return topicNames.value;
        }
      } catch (error) {
        console.error("Error fetching topic names:", error);
-        return ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+        topicNames.value = ["Topic 1", "Topic 2", "Topic 3", "Topic 4", "Topic 5"];
+        return topicNames.value;
      }
    };
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-09T10:09:06Z
author_association: NONE

PR comments:
user: login: vercel[bot]
created_at: 2025-05-09T10:05:56Z
body: [vc]: #gRO2mE0sn5LFwupfgPkKof0UcLVuwKIhLZCenEgNsCo=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJwZXJmb3JtYW5jZS1tb25pdG9yaW5nLXN5c3RlbSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJwZXJmb3JtYW5jZS1tb25pdG9yaW5nLXN5c3RlbS1naXQtZGV2LW1hcmRlLWNlbnRtYXJkZXMtcHJvamVjdHMudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vY2VudG1hcmRlcy1wcm9qZWN0cy9wZXJmb3JtYW5jZS1tb25pdG9yaW5nLXN5c3RlbS9DTVd6aHgyWEZmVHozRnlSQmZ2YjlMaGVMMnhBIiwicHJldmlld1VybCI6InBlcmZvcm1hbmNlLW1vbml0b3Jpbmctc3lzdGVtLWdpdC1kZXYtbWFyZGUtY2VudG1hcmRlcy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **performance-monitoring-system** | ✅ Ready ([Inspect](https://vercel.com/centmardes-projects/performance-monitoring-system/CMWzhx2XFfTz3FyRBfvb9LheL2xA)) | [Visit Preview](https://performance-monitoring-system-git-dev-marde-centmardes-projects.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/performance-monitoring-system-git-dev-marde-centmardes-projects.vercel.app?via=pr-comment-feedback-link) | May 9, 2025 10:05am |


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-09T10:05:58Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces dynamic handling of subject topics throughout the application. Topic names are now fetched from a JSON file and used for chart labeling, chat prompts, and record calculations, replacing static topic fields. UI components for data entry and record review are updated to reflect the variable number and names of topics per subject, and related store and composable functions are refactored accordingly.

## Changes

| File(s)                                                                                           | Change Summary                                                                                                                                                                                                                              |
|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `src/components/system/Review.vue`                                                                | Integrated dynamic fetching of topic names from a JSON file, updated chart and chat logic to use these names, and refactored related functions and watchers for reactivity and asynchronicity.                                              |
| `src/composables/bootstrap.ts`                                                                    | Updated `startChat` function to accept topic names, enhanced system and assistant prompts to use dynamic topic labels and provide targeted feedback.                                                  |
| `src/pages/home/DataEntry.vue`                                                                    | Modified record saving to store and persist the selected subject name in localStorage and pass it to the records store.                                                                              |
| `src/pages/home/NewRecords.vue`                                                                   | Made written work total and column widths dynamically adapt to the number and length of topics for the selected subject, improved topic header UI.                                                    |
| `src/pages/home/RecentRecords.vue`                                                                | Added global expand/collapse for topic columns, made column widths dynamic, updated expand/collapse icon logic, and adjusted AI Analysis button logic.                                                |
| `src/pages/home/Tracking.vue`                                                                     | Added a "Go Back" button to the header using Vue Router, improved card layout.                                                                                                                       |
| `src/stores/authUser.ts`                                                                          | Removed custom password hashing logic from user registration.                                                                                                                                        |
| `src/stores/recordsStore.ts`                                                                      | Refactored `addRecordsForSection` to accept an optional subject title, assign topic fields based on available topics, and log topic count for debugging.                                              |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant DataEntry
    participant RecordsStore
    participant Review
    participant TopicsJSON
    participant Chart
    participant Chat

    User->>DataEntry: Selects subject and enters data
    DataEntry->>RecordsStore: addRecordsForSection(sectionId, classRecordId, subjectTitle)
    RecordsStore->>TopicsJSON: Fetch topics for subjectTitle
    TopicsJSON-->>RecordsStore: Return topics array
    RecordsStore-->>DataEntry: Records created with topic fields

    User->>Review: Opens review for a student
    Review->>TopicsJSON: fetchTopicNames(subjectName)
    TopicsJSON-->>Review: Return topicNames
    Review->>Chart: updateChart(topicNames, studentScores)
    Review->>Chat: startChat(studentRecord, studentName, topicNames)
    Chat-->>Review: Chat initialized with dynamic topic context
```

## Possibly related PRs

- [centmarde/Performance-Monitoring-System#106](https://github.com/centmarde/Performance-Monitoring-System/pull/106): Introduced the `topics.json` data file and migrated from fixed "WW" fields to dynamic topic fields, laying the groundwork for this PR’s dynamic topic handling.

## Poem

> In the warren of code, new topics abound,  
> Dynamic and clever, they're fetched, not hard-found.  
> Charts now display them, chatbots discuss,  
> Columns expand gently, adapt just for us.  
> With every new subject, fresh names hop in—  
> A rabbit’s delight, let the reviews begin! 🐇✨

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> - CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> 
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=centmarde/Performance-Monitoring-System&utm_content=107):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE