Description
addAttendance(lectureId, studentId) in src/utils/storage.js (line 203) unconditionally pushes a new { studentId, timestamp } entry into the session's attendance array every time it's called — there is no check for whether that studentId has already been recorded for the lecture. It's called from StudentFeedback.jsx inside the "join lecture" button handler (line 314), with no guard against being triggered more than once for the same student (e.g. double-click, page refresh and re-join, or browser back/forward into the join screen again).
Steps to Reproduce
- Go to: a student feedback link for an active lecture (
/feedback/:code or similar).
- Enter a name/roll/studentId and click the join/continue button that triggers
addAttendance.
- Navigate back (browser back button) to the join screen, or refresh and re-join with the same generated
studentId (persisted via getOrCreateStudentId in localStorage).
- Click join again.
- Check the lecture's attendance count on the Dashboard/Analytics page.
Expected Outcome
A student is counted once in attendance for a given lecture, regardless of how many times they pass through the join flow.
Actual Outcome
Each join click appends a new attendance record for the same studentId, so the same student is counted multiple times, inflating attendance totals and skewing any analytics built on top of getAttendance().
Expected Behavior
addAttendance should check whether studentId is already present in the lecture's attendance array and skip the push (or update the existing entry's timestamp) if so — mirroring the existing duplicate-prevention pattern already used for feedback via hasStudentSubmitted.
Actual Behavior
No deduplication exists; every call is an unconditional push.
Screenshots
N/A — this is a data-integrity bug, not a visual one. Reproducible by inspecting lecturePulse_sessions in DevTools → Application → Local Storage before and after a repeat join.
Environment
- Browser: Any (Chrome 125, Firefox 127, Safari)
- OS: Cross-platform
- Node.js version: 20.x
Affected Page / Component
Additional Context
Suggested fix, mirroring the existing hasStudentSubmitted pattern:
```js
export const addAttendance = (lectureId, studentId) => {
try {
const allSessions = JSON.parse(localStorage.getItem("lecturePulse_sessions") || "[]");
const updatedSessions = allSessions.map(s => {
if (s.id === lectureId) {
const attendance = s.attendance ? [...s.attendance] : [];
const alreadyPresent = attendance.some(a => a.studentId === studentId);
if (!alreadyPresent) {
attendance.push({ studentId, timestamp: new Date().toISOString() });
}
return { ...s, attendance };
}
return s;
});
localStorage.setItem("lecturePulse_sessions", JSON.stringify(updatedSessions));
} catch (error) {
console.error("Error adding attendance", error);
}
};
```
Note: studentId can be null for fully anonymous joins (entry.studentId: attendeeStudentId.trim() || null in StudentFeedback.jsx), so the fix should also decide whether multiple null/anonymous entries are intentional (likely yes) versus named/roll-identified students (should be deduplicated).
/assign
Description
addAttendance(lectureId, studentId)insrc/utils/storage.js(line 203) unconditionally pushes a new{ studentId, timestamp }entry into the session'sattendancearray every time it's called — there is no check for whether thatstudentIdhas already been recorded for the lecture. It's called fromStudentFeedback.jsxinside the "join lecture" button handler (line 314), with no guard against being triggered more than once for the same student (e.g. double-click, page refresh and re-join, or browser back/forward into the join screen again).Steps to Reproduce
/feedback/:codeor similar).addAttendance.studentId(persisted viagetOrCreateStudentIdinlocalStorage).Expected Outcome
A student is counted once in attendance for a given lecture, regardless of how many times they pass through the join flow.
Actual Outcome
Each join click appends a new attendance record for the same
studentId, so the same student is counted multiple times, inflating attendance totals and skewing any analytics built on top ofgetAttendance().Expected Behavior
addAttendanceshould check whetherstudentIdis already present in the lecture'sattendancearray and skip the push (or update the existing entry's timestamp) if so — mirroring the existing duplicate-prevention pattern already used for feedback viahasStudentSubmitted.Actual Behavior
No deduplication exists; every call is an unconditional
push.Screenshots
N/A — this is a data-integrity bug, not a visual one. Reproducible by inspecting
lecturePulse_sessionsin DevTools → Application → Local Storage before and after a repeat join.Environment
Affected Page / Component
src/utils/storage.js—addAttendance()Additional Context
Suggested fix, mirroring the existing
hasStudentSubmittedpattern:```js
export const addAttendance = (lectureId, studentId) => {
try {
const allSessions = JSON.parse(localStorage.getItem("lecturePulse_sessions") || "[]");
const updatedSessions = allSessions.map(s => {
if (s.id === lectureId) {
const attendance = s.attendance ? [...s.attendance] : [];
const alreadyPresent = attendance.some(a => a.studentId === studentId);
if (!alreadyPresent) {
attendance.push({ studentId, timestamp: new Date().toISOString() });
}
return { ...s, attendance };
}
return s;
});
localStorage.setItem("lecturePulse_sessions", JSON.stringify(updatedSessions));
} catch (error) {
console.error("Error adding attendance", error);
}
};
```
Note:
studentIdcan benullfor fully anonymous joins (entry.studentId: attendeeStudentId.trim() || nullinStudentFeedback.jsx), so the fix should also decide whether multiplenull/anonymous entries are intentional (likely yes) versus named/roll-identified students (should be deduplicated)./assign