Feature/session wide algorithm#36
Conversation
… out employee days off throughout a session
…3, and 14 and maxing out days off from days 2-6 and 7-12
…layout for both assigning nightshifts and days off for employees
… into test/testData
📝 WalkthroughWalkthroughImplements full days-off and night-shift scheduling logic, adds Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
src/features/sessionWideAlgorithm.ts(1 hunks)src/types/sessionTypes.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/sessionWideAlgorithm.ts (1)
src/types/sessionTypes.ts (2)
Session(4-15)StaffSessionAttendee(39-45)
eshavignesh123
left a comment
There was a problem hiding this comment.
Hi Parth! Could you pull from main when you get a chance? We added a bunch of updates to sessionTypes, so refactoring your code based on that would be super helpful! We also added a new file, SessionScheduler.ts, to keep all our session-wide algorithms, so could you move your functions there as well? Thanks!
…, and used updated types to represent and store calculated days off and night shifts
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
src/features/scheduling/SessionScheduler.ts (1)
29-30: Use a named constant for milliseconds per day.The magic number
86400000(milliseconds in a day) reduces readability.Apply this diff:
+ const MS_PER_DAY = 86400000; const numDays = - Math.floor((endDate.getTime() - startDate.getTime()) / 86400000) + 1; + Math.floor((endDate.getTime() - startDate.getTime()) / MS_PER_DAY) + 1;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (1)
src/features/scheduling/SessionScheduler.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/scheduling/SessionScheduler.ts (1)
src/types/sessionTypes.ts (3)
SessionID(11-11)StaffAttendeeID(35-35)NightShiftID(49-49)
🔇 Additional comments (2)
src/features/scheduling/SessionScheduler.ts (2)
70-76: Boundary condition: date comparison at half-point may be ambiguous.Lines 71 and 75 use
<=and>to split the schedule at the half-point. IfhalfPointDateexactly matches a day off, it's counted in the first half. Verify this is the intended behavior.
136-145: Major: Program counselor consecutive days off eligibility logic is unclear and requires clarification.The logic at lines 137-145 checks whether a program counselor has any two consecutive days off anywhere during the session. However, the intent is ambiguous:
- Currently, the code creates one shift assignment per bunk for the entire session (not per night), making the consecutive days off check questionable in context.
- The logic returns
eligible = falseif the employee has ANY consecutive days off during the session, but it's unclear why consecutive days off (versus any days off) affects session-wide night shift eligibility.- The boundary checks (
prev <= endDate) are incomplete—they don't verify the dates fall within the session'sstartDatetoendDaterange.Given the architectural issue mentioned in your review (single-shift-per-bunk for entire session), this eligibility logic needs clarification alongside the overall night shift structure. Consider:
- Should night shifts be assigned per-night rather than per-session?
- If per-night, should eligibility check whether the counselor has a day off immediately before or after that specific night?
- If per-session, what is the business rule for consecutive days off affecting overall eligibility?
| const firstHalf = dayIndex <= Math.floor(numDays / 2); | ||
| const halfPointDate = new Date(startDate); | ||
| halfPointDate.setDate(startDate.getDate() + Math.floor(numDays / 2)); | ||
|
|
||
| const hasDayOffInFirstHalf = randomEmployee.daysOff.some( | ||
| (d) => new Date(d).getTime() <= halfPointDate.getTime() | ||
| ); | ||
|
|
||
| const hasDayOffInSecondHalf = randomEmployee.daysOff.some( | ||
| (d) => new Date(d).getTime() > halfPointDate.getTime() | ||
| ); | ||
|
|
||
| if ( | ||
| (firstHalf && hasDayOffInFirstHalf) || | ||
| (!firstHalf && hasDayOffInSecondHalf) | ||
| ) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Half-schedule logic may not guarantee one day off per half.
The current logic prevents assigning two days off in the same half but doesn't ensure both halves receive a day off. An employee could get both days off in one half if they're selected twice before the half switches, or end up with only one day off total.
Consider revising the algorithm to:
- First assign one day off in the first half to each employee, then
- Assign the second day off in the second half to each employee.
This ensures the constraint is met deterministically rather than relying on random chance.
🤖 Prompt for AI Agents
In src/features/scheduling/SessionScheduler.ts around lines 66–83 the current
check only blocks assigning a second day-off in the same half but doesn’t
guarantee each employee gets exactly one day-off in each half; change the
algorithm to a deterministic two-phase assignment: first iterate employees and
assign one day-off inside the first half (pick a random available date within
first half that doesn’t conflict with existing constraints), mark it, then
iterate again and assign the second day-off inside the second half (pick a
random available date within second half); if no valid date exists in the target
half, implement a clear fallback (e.g., try nearest valid date in that half or
log/fail the employee assignment) to preserve constraints and avoid leaving
employees with zero or two days in the same half.
…es, and fixed bugs in the assign nights off function such as not assigning 4 employees per bunk a night off
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (6)
src/features/scheduling/SessionScheduler.ts (6)
63-65: Random selection bug has been fixed.The previous critical issue where the code randomly selected from the full
employeesarray instead ofemployeeChoiceshas been corrected. The code now properly builds an array from the eligible employee set and selects randomly from it.
144-145: Typo has been corrected.The previous typo "progrma" has been fixed to
programCounselor.
159-159: Typos have been corrected.The previous typos "counsoler" and "choosen" have been fixed to "counselor" and "chosen".
74-91: Half-schedule logic still doesn't guarantee one day off per half.The algorithm prevents assigning two days off in the same half but doesn't ensure each employee receives exactly one day off in each half. An employee could end up with both days in one half (if selected before reaching that half's limit) or only one day total.
Consider a deterministic two-phase approach:
- First pass: Assign one day off in the first half to each employee
- Second pass: Assign one day off in the second half to each employee
This ensures the constraint is met reliably rather than depending on random selection order.
94-94: Input mutation creates side effects.The method directly mutates the
employeesparameter by pushing torandomEmployee.daysOff. This creates side effects that may surprise callers and complicates testing.Consider either:
- Clearly documenting this mutation behavior in a comment above the method, or
- Returning a map of employee IDs to assigned days off and letting the caller apply updates
111-168: Critical: Night shift assignment is still incomplete.The method creates only one
NightShiftIDobject per bunk for the entire session (lines 128-135) rather than creating shifts for each night. For a multi-day session, this leaves all nights except one without coverage.Expected behavior:
- Loop through each night of the session (use a date iterator from startDate to endDate)
- For each night, assign eligible counselors per bunk
- Rotate counselors across nights to distribute the workload
- Check day-off eligibility for the specific night being assigned
Current implementation would require substantial refactoring to assign shifts per night rather than per session.
🧹 Nitpick comments (3)
src/features/scheduling/SessionScheduler.ts (3)
3-8: Consider using Fisher-Yates for better performance.The current shuffle works correctly but has O(n log n) complexity due to sorting. For better performance with large employee lists, consider an in-place Fisher-Yates shuffle:
function shuffleArray<T>(arr: T[]): T[] { - return arr - .map(value => ({ value, sort: Math.random() })) - .sort((a, b) => a.sort - b.sort) - .map(({ value }) => value); + const shuffled = [...arr]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; }
146-154: Clarify or simplify the date boundary checks in eligibility logic.The eligibility check verifies that a program counselor doesn't have adjacent days off, but the
prev <= endDateandnext <= endDateconditions (lines 152-153) appear redundant. Ifemployee.daysOffonly contains dates within the session range, checking whether computed adjacent dates fall withinendDateshouldn't be necessary for correctness.Consider simplifying to:
const eligible = !employee.daysOff.some((day) => { const dayDate = new Date(day); const prev = new Date(dayDate); prev.setDate(prev.getDate() - 1); const next = new Date(dayDate); next.setDate(next.getDate() + 1); - return prev <= endDate && employee.daysOff.includes(toISODate(prev)) || - next <= endDate && employee.daysOff.includes(toISODate(next)); + return employee.daysOff.includes(toISODate(prev)) || + employee.daysOff.includes(toISODate(next)); });If the boundary check serves a specific purpose, please add a comment explaining why.
120-120: Consider hoistingtoISODatehelper to reduce duplication.The
toISODatehelper is defined insideassignNightShifts, butassignDaysOff(line 42) uses inlinetoISOString().split("T")[0]for the same purpose. Hoisting this helper to the module level would improve consistency and reduce duplication.+function toISODate(date: Date): string { + return date.toISOString().split("T")[0]; +} + export class SessionScheduler {Then update line 42 in
assignDaysOff:- const dateStr = currentDate.toISOString().split("T")[0]; + const dateStr = toISODate(currentDate);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
package.json(1 hunks)src/features/scheduling/FreeplayScheduler.ts(1 hunks)src/features/scheduling/SessionScheduler.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/scheduling/SessionScheduler.ts (1)
src/types/sessionTypes.ts (3)
SessionID(11-11)StaffAttendeeID(35-35)NightShiftID(49-49)
🔇 Additional comments (2)
src/features/scheduling/FreeplayScheduler.ts (1)
26-27: LGTM!The lint directive appropriately suppresses warnings for the unused parameter in this stub method. The explanatory comment provides helpful context.
package.json (1)
25-25: Verify or remove unusedsharpdependency
No imports or invocations ofsharpwere found in the codebase; remove it from package.json if it isn’t actually used.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/types/sessionTypes.ts (1)
62-62: LGTM! Consider adding a JSDoc comment for clarity.The addition of the
hasDaysOffboolean field toCommonSectionis well-typed and appropriately named. This flag appears to control whether the scheduling algorithm should assign days off during this section.Consider adding a brief JSDoc comment to clarify when this flag should be set:
+ /** + * Indicates whether staff/admin should have days off assigned during this section. + */ hasDaysOff: boolean;
nkanchinadam
left a comment
There was a problem hiding this comment.
Overall, a good structure of the functions and got overall logic down. Main things to change are:
- Use moment library instead of Date objects
- Don't make assumptions about the session length when assigning Days Off
- Make sure to assign all Staff & Admins
- Use helper functions to abstract out the logic & make it more readable
|
|
||
| const employeeChoices = new Set(employees); | ||
|
|
||
| const startDate = new Date(session.startDate); |
There was a problem hiding this comment.
instead of using the built-in JS Date class, which is annoying & difficult to work with, import the moment library and do all datetime calculations with that
| const currentDate = new Date(startDate); | ||
|
|
||
| for (let dayIndex = 1; dayIndex <= numDays; dayIndex++) { | ||
| const dateStr = currentDate.toISOString().split("T")[0]; |
There was a problem hiding this comment.
you could probably extract this logic into a helper function to make it more readable
| const dateStr = currentDate.toISOString().split("T")[0]; | ||
|
|
||
| // Skip restricted days (first, 13th, and 14th) | ||
| if (dayIndex === 1 || dayIndex === 13 || dayIndex === 14) { |
There was a problem hiding this comment.
Don't hardcode the days that shouldn't have anyone with a Day Off since they could change if Camp Starfish changes the length of the session they do, which they do throughout the year with shorter sessions as well as experimenting with different sessinon lengths. I realize that there's no real way to tell if a day should have anyone scheduled for a Day Off on it, so I just pushed a hasDaysOff field to the Section type in sessionTypes.ts.
| } | ||
|
|
||
| // Move to next day | ||
| currentDate.setDate(currentDate.getDate() + 1); |
There was a problem hiding this comment.
Since you're doing a for loop, you can just calculate the current date from the index of the loop instead of keeping track of it separately
| if (randomEmployee.daysOff.includes(dateStr)) continue; | ||
|
|
||
| // Checks if employee has a day off in the first half of schedule | ||
| const firstHalf = dayIndex <= Math.floor(numDays / 2); |
There was a problem hiding this comment.
Similar to earlier comment about not hardcoding sessions, the only rule about Days Off is that each staff & admin legally has to have 1 Day Off every 7 days of camp, so divide up the timeframe into weeks & round down for partial weeks and then assign Days Off for each week. It would probably also be helpful to create a helper function that handles a single week to simplify the logic.
|
|
||
| assignDaysOff(): SessionScheduler { return this; } | ||
| assignDaysOff(session: SessionID, employees: StaffAttendeeID[]): SessionScheduler { | ||
| const MAX_STAFF_OFF_PER_DAY = 2; |
There was a problem hiding this comment.
Unsure where the number 2 came from, but every staff & admin member needs to have a Day Off every week, so you can calculate the number off each day by doing (numStaff + numAdmin) / numDays
|
|
||
| // Keep track of attempts to avoid infinite loops | ||
| let attempts = 0; | ||
| const maxAttempts = numEmployees * 2; |
There was a problem hiding this comment.
I don't really get how assigning the staff & admin could fail, so modify the logic to ensure that everyone gets assigned since we don't the user to try generating a schedule only to encounter failures randomly
| ) { | ||
| attempts++; | ||
|
|
||
| const choices = Array.from(employeeChoices); |
There was a problem hiding this comment.
When choosing employees to assign, assign Program Counselors first and then everyone else since Program Counselors can't have a Day Off on a day where they're supposed to lead an activity. Try to assign them to Jamboree days since Program Counselor duties only apply to Bundles.
Additionally, try to ensure that of the people who have a Day Off on a given day, they're all from different bunks, so no bunk has too few counselors for the night.
| const shift: NightShiftID = { | ||
| id: `night-${bunkNumber}-${session.id}`, | ||
| sessionId: session.id, | ||
| [bunkNumber]: { |
There was a problem hiding this comment.
NightShiftID is a type intended for 1 night of and would contain the info for each bunk that night. [bunkId: number]: ... means that the type is a map where you can have multiple keys (the bunk numbers), so only create 1 of these objects per night and store the data in that single object.
|
|
||
| for (const employee of selected) { | ||
| // employee is a program counselor, check if they have day off before or after | ||
| if (employee.programCounselor) { |
There was a problem hiding this comment.
This condition doesn't only apply to Program Counselors, it applies to all staff. The only thing related to Program Counselors in these functions is assigning their Days Off to be during Jamborees.
…r being assigned off on jamboree days, assigning more evenly throughout bunks, and assigning a night shift object per night rather than per bunk
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/features/scheduling/SessionScheduler.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/scheduling/SessionScheduler.ts (1)
src/types/sessionTypes.ts (5)
SectionID(55-55)StaffAttendeeID(35-35)AdminAttendeeID(41-41)SessionID(11-11)NightShiftID(49-49)
| const NUM_WEEKS = Math.ceil(end.diff(start, "week", true)); | ||
| const NUM_DAYS = (end.diff(start, "day")) + 1 | ||
| const MAX_STAFF_OFF_PER_DAY = Math.ceil(employees.length / NUM_DAYS); | ||
| const employeesNeedingDayOff = new Set(employees); | ||
|
|
||
| let currDate = start; | ||
| // Need a future implementation for this date range to only include dates that allow for a day off. | ||
| const sessionDateRange: Set<Moment> = new Set(this.getDateRange(start, end)) | ||
|
|
||
| for (let i = 0; i < NUM_WEEKS; i++) { | ||
| if (currDate.clone().add(1, "week").isBefore(end)) { | ||
| this.assignDaysOffForWeek(currDate, currDate.clone().add(6, "day"), sessionDateRange, employees, employeesNeedingDayOff, MAX_STAFF_OFF_PER_DAY) | ||
| currDate.add(6, "day") | ||
| } | ||
| else { | ||
| this.assignDaysOffForWeek(currDate, end, sessionDateRange, employees, employeesNeedingDayOff, MAX_STAFF_OFF_PER_DAY) | ||
| currDate = end | ||
| } | ||
|
|
There was a problem hiding this comment.
Each counselor only receives a single day off for the entire session.
employeesNeedingDayOff is shared across the whole session and we delete counselors as soon as they get a day off. In a multi‑week session, anyone assigned in week one is never reconsidered, so they never get the legally required day off in later weeks. The per-day cap derived from employees.length / NUM_DAYS also means a 2‑week session with 10 counselors capped at 1 person/day can never schedule the 20 total days off that are required.
Reset the “needing a day off” pool each week, recompute the daily cap for that week, and advance the window without overlapping days so every counselor gets exactly one day off per week.
Apply this diff to correct the weekly scheduling:
- const NUM_WEEKS = Math.ceil(end.diff(start, "week", true));
- const NUM_DAYS = (end.diff(start, "day")) + 1
- const MAX_STAFF_OFF_PER_DAY = Math.ceil(employees.length / NUM_DAYS);
- const employeesNeedingDayOff = new Set(employees);
-
- let currDate = start;
- // Need a future implementation for this date range to only include dates that allow for a day off.
- const sessionDateRange: Set<Moment> = new Set(this.getDateRange(start, end))
-
- for (let i = 0; i < NUM_WEEKS; i++) {
- if (currDate.clone().add(1, "week").isBefore(end)) {
- this.assignDaysOffForWeek(currDate, currDate.clone().add(6, "day"), sessionDateRange, employees, employeesNeedingDayOff, MAX_STAFF_OFF_PER_DAY)
- currDate.add(6, "day")
- }
- else {
- this.assignDaysOffForWeek(currDate, end, sessionDateRange, employees, employeesNeedingDayOff, MAX_STAFF_OFF_PER_DAY)
- currDate = end
- }
-
- }
+ // Need a future implementation for this date range to only include dates that allow for a day off.
+ const sessionDateRange: Set<Moment> = new Set(this.getDateRange(start, end));
+
+ let weekStart = start.clone();
+ while (weekStart.isSameOrBefore(end)) {
+ const weekEnd = moment.min(weekStart.clone().add(6, "day"), end);
+ const daysInWeek = weekEnd.diff(weekStart, "day") + 1;
+ const maxStaffOffPerDay = Math.ceil(employees.length / daysInWeek);
+ const employeesNeedingDayOff = new Set(employees);
+
+ this.assignDaysOffForWeek(
+ weekStart,
+ weekEnd,
+ sessionDateRange,
[AUTO-TRUNCATED]
<!-- 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
// Need a future implementation for this date range to only include dates that allow for a day off.
const sessionDateRange: Set<Moment> = new Set(this.getDateRange(start, end));
let weekStart = start.clone();
while (weekStart.isSameOrBefore(end)) {
const weekEnd = moment.min(weekStart.clone().add(6, "day"), end);
const daysInWeek = weekEnd.diff(weekStart, "day") + 1;
const maxStaffOffPerDay = Math.ceil(employees.length / daysInWeek);
const employeesNeedingDayOff = new Set(employees);
this.assignDaysOffForWeek(
weekStart,
weekEnd,
sessionDateRange,
employees,
employeesNeedingDayOff,
maxStaffOffPerDay
);
weekStart = weekEnd.clone().add(1, "day");
}🤖 Prompt for AI Agents
In src/features/scheduling/SessionScheduler.ts around lines 208-226, the current
logic creates a single employeesNeedingDayOff set and a global
MAX_STAFF_OFF_PER_DAY for the whole session, causing counselors to get only one
day off for the entire session and preventing per-week quotas; change it to
iterate week-by-week: compute weekStart and weekEnd without overlapping (advance
by 7 days), build sessionDateRange once, but inside each week recompute
daysInWeek and maxStaffOffPerDay = ceil(employees.length / daysInWeek), create a
fresh employeesNeedingDayOff = new Set(employees) for that week, then call
assignDaysOffForWeek(weekStart, weekEnd, sessionDateRange, employees,
employeesNeedingDayOff, maxStaffOffPerDay) and advance to the next week until
weekStart > end.
… into test/testData
…mp-starfish into test/testData
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
test/emulatorData/firebase-export-metadata.json (1)
1-16: Emulator metadata structure looks correct.The Firebase emulator export metadata structure is appropriate for local development and testing. The versions specified align with standard Firebase emulator tooling.
Minor: Add a newline at the end of the file for better compliance with POSIX standards and to avoid potential issues with some text processing tools.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (8)
.vscode/sessions.json(1 hunks)src/app/LoginPage.tsx(1 hunks)src/features/scheduling/BunkJamboreeScheduler.ts(2 hunks)storage.rules(1 hunks)test/emulatorData/auth_export/accounts.json(1 hunks)test/emulatorData/auth_export/config.json(1 hunks)test/emulatorData/firebase-export-metadata.json(1 hunks)test/emulatorData/storage_export/buckets.json(1 hunks)
✅ Files skipped from review due to trivial changes (4)
- test/emulatorData/auth_export/config.json
- test/emulatorData/auth_export/accounts.json
- test/emulatorData/storage_export/buckets.json
- src/app/LoginPage.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/scheduling/BunkJamboreeScheduler.ts (1)
src/types/sessionTypes.ts (2)
SectionSchedule(72-75)BunkID(126-126)
🪛 GitHub Actions: Lint & Build on PR
src/features/scheduling/BunkJamboreeScheduler.ts
[error] 1-1: 'Bunk' is defined but never used. @typescript-eslint/no-unused-vars
🔇 Additional comments (2)
src/features/scheduling/BunkJamboreeScheduler.ts (1)
6-6: LGTM!The refactor from
Bunk[]toBunkID[]is consistent and correct. UsingBunkIDallows storing thesessionIdwith each bunk reference, which aligns with the type definition.Also applies to: 17-17
.vscode/sessions.json (1)
16-16: Verified: Emulator data directory is properly structured for import.The test/emulatorData directory contains all required Firebase emulator export files (firestore_export, auth_export, storage_export) with proper metadata. The structure is valid for import via
firebase emulators:start --import ./test/emulatorData.
| @@ -1,9 +1,9 @@ | |||
| import { AdminAttendeeID, SectionSchedule, SectionPreferences, Bunk } from "@/types/sessionTypes"; | |||
| import { AdminAttendeeID, SectionSchedule, SectionPreferences, Bunk, BunkID } from "@/types/sessionTypes"; | |||
There was a problem hiding this comment.
Remove unused import.
The Bunk import is no longer used after refactoring to BunkID[]. This is causing a pipeline failure.
Apply this diff to remove the unused import:
-import { AdminAttendeeID, SectionSchedule, SectionPreferences, Bunk, BunkID } from "@/types/sessionTypes";
+import { AdminAttendeeID, SectionSchedule, SectionPreferences, BunkID } from "@/types/sessionTypes";📝 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.
| import { AdminAttendeeID, SectionSchedule, SectionPreferences, Bunk, BunkID } from "@/types/sessionTypes"; | |
| import { AdminAttendeeID, SectionSchedule, SectionPreferences, BunkID } from "@/types/sessionTypes"; |
🧰 Tools
🪛 GitHub Actions: Lint & Build on PR
[error] 1-1: 'Bunk' is defined but never used. @typescript-eslint/no-unused-vars
🤖 Prompt for AI Agents
In src/features/scheduling/BunkJamboreeScheduler.ts around lines 1 to 1, the
import list includes an unused symbol `Bunk` which causes a pipeline failure;
remove `Bunk` from the import statement so the file only imports the actually
used types (e.g., AdminAttendeeID, SectionSchedule, SectionPreferences, BunkID).
| match /b/{bucket}/o { | ||
| match /{allPaths=**} { | ||
| allow read, write: if true; | ||
| allow read, write: if false; |
There was a problem hiding this comment.
🧩 Analysis chain
Critical: Verify that denying all storage access is intentional.
This change denies all read/write access to Firebase Storage, which will break any storage-dependent functionality in the application. This seems unrelated to the scheduling algorithm work described in the PR objectives.
If this is a temporary development/testing change, it should not be committed to the main branch. If intentional, please clarify the reasoning.
Consider reverting this change unless there's a specific reason to deny all storage access:
- allow read, write: if false;
+ allow read, write: if true;Or implement proper access control rules based on your security requirements instead of blanket denial.
I'll gather the necessary information to verify this review comment. Let me inspect the storage rules file and the context around this change.
🏁 Script executed:
#!/bin/bash
# Examine the storage.rules file to understand the full context
echo "=== Full storage.rules file ==="
cat storage.rules
echo -e "\n=== Git diff for storage.rules ==="
git diff storage.rules
echo -e "\n=== Git log for storage.rules (last 5 commits) ==="
git log --oneline -5 -- storage.rules
echo -e "\n=== Checking for any related documentation or comments ==="
rg -n "storage" --type-list | head -20Length of output: 1056
🏁 Script executed:
#!/bin/bash
# Search for any references to storage rules or access control in the codebase
echo "=== References to Firebase Storage or access control ==="
rg -i "storage" -B2 -A2 --type=md --type=json --type=ts --type=js -g '!node_modules' | head -50Length of output: 1891
Let me search for actual Firebase Storage usage in the codebase to understand if this rule change would genuinely break functionality.
🏁 Script executed:
#!/bin/bash
# Search for Firebase Storage method calls and operations
echo "=== Firebase Storage operations in application code ==="
rg -i "uploadBytes|download|getBytes|put|get\(|ref\(" -B2 -A2 --type=ts --type=js -g '!node_modules' | head -80
echo -e "\n=== Direct storage references ==="
rg "firebase\.storage|getStorage|ref\(|uploadFile|readFile" -B1 -A1 --type=ts --type=js -g '!node_modules'
echo -e "\n=== Check PR or commit context ==="
git show --stat 445ab62 | head -30Length of output: 7799
🏁 Script executed:
#!/bin/bash
# Look for README or documentation about this change
echo "=== Checking README or docs ==="
find . -maxdepth 2 -name "README*" -o -name "CHANGELOG*" -o -name "*.md" | head -10 | xargs grep -l "storage" 2>/dev/null || echo "No matches found in docs"
echo -e "\n=== Checking for TODOs or FIXMEs related to storage ==="
rg "TODO|FIXME" -i --type=ts --type=js --type=rules -B1 -A1 -g '!node_modules' | grep -i storage -B2 -A2Length of output: 130
Critical: Storage rules deny all access while application code actively uploads and downloads files.
Evidence shows the application uses Firebase Storage operations:
src/data/storage/fileOperations.tsimplementsuploadFile(),getFileURL(), andgetFileURLs()src/app/albums/[albumId]/AlbumPage.tsxcallsuploadFiles()to store album imagessrc/components/FileUploadModal.tsxprovides the UI for uploads
The current rule allow read, write: if false; will break all these operations. Revert this change and implement proper access control rules instead:
- allow read, write: if false;
+ allow read, write: if request.auth != null;Or use authentication-based rules as suggested by the commented code in the file referencing admin checks.
📝 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.
| allow read, write: if false; | |
| allow read, write: if request.auth != null; |
🤖 Prompt for AI Agents
In storage.rules around line 9, the rule "allow read, write: if false;" blocks
all storage operations and prevents the app from uploading/downloading files;
revert that blanket denial and replace it with authentication-based rules: allow
read/write when request.auth != null (or more specific checks) and, where
applicable, enforce ownership by matching request.auth.uid to a
metadata/filename or path segment (e.g., only allow writes to /users/{uid}/...
when request.auth.uid == uid) or check a custom admin claim for admin-only
actions; restore the previously commented admin-check logic if needed and ensure
public read-only content is explicitly allowed if required by the app.
a884ab9 to
974f469
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/features/scheduling/generation/SessionScheduler.ts`:
- Around line 55-69: In isJamboreeDay (private method in SessionScheduler)
remove the unused local variable dateStr that is declared as const dateStr =
this.toISODate(date); so that the function no longer defines an unused variable
and the linter error is resolved; simply delete that line and ensure the rest of
isJamboreeDay (including references to date, section.startDate, section.endDate
and section.type) remains unchanged.
- Around line 1-3: The import list in SessionScheduler.ts includes an unused
type NightShift which causes an ESLint unused import error; remove NightShift
from the import statement that currently reads "import { StaffAttendeeID,
AdminAttendeeID, NightShiftID, SessionID, SectionID, NightShift } from
"@/types/sessionTypes"; so update that import to exclude NightShift (leaving
StaffAttendeeID, AdminAttendeeID, NightShiftID, SessionID, SectionID) and run
lint/TS build to confirm there are no remaining references to the NightShift
symbol.
♻️ Duplicate comments (2)
src/features/scheduling/generation/SessionScheduler.ts (2)
30-38: Include the session end date ingetDateRange.The loop currently excludes
endDate, which drops the final day (and yields an empty range for single-day sessions). Also flipslettoconstto satisfy lint.✅ Proposed fix
- let fromDate = startDate - let toDate = endDate - let diff = toDate.diff(fromDate, "day") - let range = [] - for (let i = 0; i < diff; i++) { + const fromDate = startDate + const toDate = endDate + const diff = toDate.diff(fromDate, "day") + const range = [] + for (let i = 0; i <= diff; i++) { range.push(moment(startDate).add(i, "day")) }
204-227: Reset the day-off pool per week (not per session).
employeesNeedingDayOffandMAX_STAFF_OFF_PER_DAYare global for the whole session, so multi‑week sessions give each person only one day off total. The week windows also overlap becausecurrDate.add(6, "day")advances only 6 days. Recompute the pool and cap per week and advance by 7 days.✅ Proposed fix
- const NUM_WEEKS = Math.ceil(end.diff(start, "week", true)); - const NUM_DAYS = (end.diff(start, "day")) + 1 - const MAX_STAFF_OFF_PER_DAY = Math.ceil(employees.length / NUM_DAYS); - const employeesNeedingDayOff = new Set(employees); - - let currDate = start; // Need a future implementation for this date range to only include dates that allow for a day off. const sessionDateRange: Set<Moment> = new Set(this.getDateRange(start, end)) - - for (let i = 0; i < NUM_WEEKS; i++) { - if (currDate.clone().add(1, "week").isBefore(end)) { - this.assignDaysOffForWeek(currDate, currDate.clone().add(6, "day"), sessionDateRange, employees, employeesNeedingDayOff, MAX_STAFF_OFF_PER_DAY) - currDate.add(6, "day") - } - else { - this.assignDaysOffForWeek(currDate, end, sessionDateRange, employees, employeesNeedingDayOff, MAX_STAFF_OFF_PER_DAY) - currDate = end - } - - } + let weekStart = start.clone() + while (weekStart.isSameOrBefore(end)) { + const weekEnd = moment.min(weekStart.clone().add(6, "day"), end) + const daysInWeek = weekEnd.diff(weekStart, "day") + 1 + const maxStaffOffPerDay = Math.ceil(employees.length / daysInWeek) + const employeesNeedingDayOff = new Set(employees) + + this.assignDaysOffForWeek( + weekStart, + weekEnd, + sessionDateRange, + employees, + employeesNeedingDayOff, + maxStaffOffPerDay + ) + weekStart.add(7, "day") + }
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 Fix all issues with AI agents
In `@src/app/LoginPage.tsx`:
- Around line 37-45: The catch in generateTestData incorrectly sets a sign-in
message; change it to handle errors from generateSession by catching the error
(catch (err)) and calling setError with a message like "An error occurred while
generating test data. Please try again." and optionally log the actual error via
console.error(err) to aid debugging; update references in generateTestData and
ensure setError usage is consistent.
- Around line 81-88: Remove or hide the debug "Generate Test" button from the
production LoginPage: locate the button using the generateTestData function and
MicrosoftIcon in LoginPage.tsx and either delete it or wrap its JSX in a
condition that only renders in development (e.g., check process.env.NODE_ENV ===
'development'); if you keep it for dev use, replace the MicrosoftIcon with a
neutral dev icon and modify generateTestData to provide user-visible feedback
(toast or inline message) instead of only console.log, and ensure this
functionality is moved to an admin/dev-only route if it needs to exist
long-term.
In `@src/features/scheduling/generation/SessionScheduler.ts`:
- Line 125: The variable remainingWF in SessionScheduler.ts (declared as const
remainingWF = shuffledWF.filter(e => !selectedWFWindow1.includes(e) &&
!selectedWFWindow2.includes(e));) is unused; remove this declaration to fix the
pipeline by deleting the remainingWF assignment (or if its intent was to be used
later, instead integrate it where needed—e.g., replace any placeholder logic
that should reference remainingWF with this filtered array). Ensure references
to shuffledWF, selectedWFWindow1 and selectedWFWindow2 remain unchanged.
- Around line 114-116: The local variable otherEmployees created by
employees.filter(...) in SessionScheduler (the expression "const otherEmployees
= employees.filter(e => !('programCounselor' in e && e.programCounselor))") is
unused; remove that declaration or rename it to _otherEmployees if you intend to
keep it for future use to silence the linter/pipeline. Edit the
SessionScheduler.ts occurrence to either delete the const otherEmployees = ...
line or change its name to _otherEmployees so the variable is no longer flagged
as unused.
- Around line 138-146: The loop mutates the shared maxStaffOffPerDay causing it
to stay Infinity for subsequent employees; inside the window-1 and window-2
loops in SessionScheduler.ts, compute a per-employee local limit (e.g., let
perEmployeeMax = (dates.length === 1) ? Infinity : maxStaffOffPerDay) and pass
that perEmployeeMax into assignOneDayOff instead of mutating the outer
maxStaffOffPerDay; ensure you do the same pattern in the window2 loop so the
global cap isn't permanently changed.
- Around line 1-7: Remove the erroneous/unused imports in SessionScheduler.ts:
delete the faulty imports of da (from "@faker-js/faker/."), w (from
"@faker-js/faker/dist/airline-CLphikKp"), em (from "@mantine/core") and max
(from "moment"), and also remove the unused type NightShift from the import list
(keeping only used symbols like StaffAttendeeID, AdminAttendeeID, NightShiftID,
SessionID, SectionID, Moment, and moment); ensure no references to the removed
symbols remain elsewhere in SessionScheduler.ts and run the type-check/lint to
confirm pipeline errors are resolved.
In `@src/features/scheduling/test/generateEmulatorData.tsx`:
- Around line 495-513: The function parameter blockID is unused; either remove
it from the signature or rename it to _blockID to silence the unused-parameter
error — update generateBunkJamboActivities accordingly and apply the same change
to generateNonBunkJamboActivities so their signatures remain consistent with
callers/types.
- Around line 1-6: The import statement in generateEmulatorData.tsx includes
unused symbols causing pipeline failures; remove Freeplay and
SchedulingSectionID from the named import list (the import that currently lists
CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule,
BundleActivityWithAssignments, SectionPreferences, BunkID,
BunkJamboreeActivityWithAssignments, NonBunkJamboreeActivityWithAssignments,
Freeplay, PostID, SectionID, SessionID, NightShiftID, ProgramAreaID,
SchedulingSectionID, FreeplayID) so only used types remain imported.
- Line 739: The local variable campers assigned from
generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS) is unused; either remove the
declaration or rename it to _campers (prefix underscore) to silence the
unused-variable warning, or if generateCampers is being removed altogether,
delete this line; update the area around the generateCampers call in
generateEmulatorData.tsx so no unused symbol remains.
- Around line 349-352: The code sets schedule.alternatePeriodsOff["RH"] twice in
generateEmulatorData (variable schedule of type SectionSchedule<'BUNK-JAMBO'>);
remove the duplicate assignment so alternatePeriodsOff["RH"] is only initialized
once (leave a single schedule.alternatePeriodsOff["RH"] = [] and delete the
redundant repeated line).
- Around line 130-192: The function generateNonoLists is defined but never used,
causing linting and pipeline issues. To fix this, either remove the
generateNonoLists function if it is not needed, or export it to make it
available externally, or prefix its name with an underscore (e.g.,
_generateNonoLists) if you intend to keep it for future use. Apply the same fix
to other unused functions in the file such as generateYesyesLists and
generateBunks.
🧹 Nitpick comments (3)
src/features/scheduling/test/generateEmulatorData.tsx (2)
608-731: Remove large blocks of commented-out code.Over 120 lines of commented-out code should be removed. Version control preserves history if these implementations are needed later. Keeping them makes the file harder to maintain and navigate.
If these functions are planned for near-term use, consider keeping them but marking with
// TODO:comments explaining the plan, or moving to a separate file.
1-12: Consider renaming file from.tsxto.ts.This file contains no JSX/React components - it's pure TypeScript logic. Using
.tsxextension is misleading and may cause unnecessary JSX parsing overhead.src/features/scheduling/generation/SessionScheduler.ts (1)
186-186: Useconstwith clone pattern for clarity.ESLint flags this because
currDateis never reassigned. Whilemoment.add()mutates in place (solettechnically works), usingconstwith explicit reassignment is clearer:Proposed fix
- let currDate = startDate; + let currDate = startDate.clone(); // Create a night bunk object for each night while (currDate.isSameOrBefore(endDate)) { // ... shift logic ... this.nightShifts.push(shift); - currDate.add(1, "day"); + currDate = currDate.clone().add(1, "day"); }Or simply suppress the lint warning with a comment if mutation is intentional.
| const generateTestData = async () => { | ||
| try { | ||
| const data = await generateSession(); | ||
| console.log(data); | ||
| } | ||
| catch { | ||
| setError("An error occurred while trying to sign in. Please try again."); | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix the error message - it references sign-in instead of test data generation.
The catch block reuses the sign-in error message, which would be confusing to users/developers debugging test data generation failures.
Proposed fix
const generateTestData = async () => {
try {
const data = await generateSession();
console.log(data);
}
catch {
- setError("An error occurred while trying to sign in. Please try again.");
+ setError("An error occurred while generating test data. Please try again.");
}
}📝 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.
| const generateTestData = async () => { | |
| try { | |
| const data = await generateSession(); | |
| console.log(data); | |
| } | |
| catch { | |
| setError("An error occurred while trying to sign in. Please try again."); | |
| } | |
| } | |
| const generateTestData = async () => { | |
| try { | |
| const data = await generateSession(); | |
| console.log(data); | |
| } | |
| catch { | |
| setError("An error occurred while generating test data. Please try again."); | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@src/app/LoginPage.tsx` around lines 37 - 45, The catch in generateTestData
incorrectly sets a sign-in message; change it to handle errors from
generateSession by catching the error (catch (err)) and calling setError with a
message like "An error occurred while generating test data. Please try again."
and optionally log the actual error via console.error(err) to aid debugging;
update references in generateTestData and ensure setError usage is consistent.
| <button | ||
| onClick={generateTestData} | ||
| className="flex flex-row justify-around items-center w-5/6 max-w-[344px] bg-white | ||
| mt-5 py-4 px-12 rounded-full shadow-[0_4px_4px_-1px_rgba(0,0,0,0.2)] font-lato text-xl text-gray-600" | ||
| > | ||
| <Image src={MicrosoftIcon.src} alt="Microsoft" width={32} height={32} /> | ||
| Generate Test | ||
| </button> |
There was a problem hiding this comment.
Test button should not be on the production login page.
This "Generate Test" button appears to be debug/development tooling. Including it on the login page has several issues:
- It uses the Microsoft icon which is misleading
- Test data generation shouldn't be accessible to end users in production
- It only logs to console, providing no user feedback on success
Consider:
- Removing this button entirely and using a separate admin/dev route
- Conditionally rendering based on environment (e.g.,
process.env.NODE_ENV === 'development') - Using an appropriate icon if kept
Proposed fix (conditional rendering)
- <button
- onClick={generateTestData}
- className="flex flex-row justify-around items-center w-5/6 max-w-[344px] bg-white
- mt-5 py-4 px-12 rounded-full shadow-[0_4px_4px_-1px_rgba(0,0,0,0.2)] font-lato text-xl text-gray-600"
- >
- <Image src={MicrosoftIcon.src} alt="Microsoft" width={32} height={32} />
- Generate Test
- </button>
+ {process.env.NODE_ENV === 'development' && (
+ <button
+ onClick={generateTestData}
+ className="flex flex-row justify-around items-center w-5/6 max-w-[344px] bg-white
+ mt-5 py-4 px-12 rounded-full shadow-[0_4px_4px_-1px_rgba(0,0,0,0.2)] font-lato text-xl text-gray-600"
+ >
+ Generate Test Data
+ </button>
+ )}📝 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.
| <button | |
| onClick={generateTestData} | |
| className="flex flex-row justify-around items-center w-5/6 max-w-[344px] bg-white | |
| mt-5 py-4 px-12 rounded-full shadow-[0_4px_4px_-1px_rgba(0,0,0,0.2)] font-lato text-xl text-gray-600" | |
| > | |
| <Image src={MicrosoftIcon.src} alt="Microsoft" width={32} height={32} /> | |
| Generate Test | |
| </button> | |
| {process.env.NODE_ENV === 'development' && ( | |
| <button | |
| onClick={generateTestData} | |
| className="flex flex-row justify-around items-center w-5/6 max-w-[344px] bg-white | |
| mt-5 py-4 px-12 rounded-full shadow-[0_4px_4px_-1px_rgba(0,0,0,0.2)] font-lato text-xl text-gray-600" | |
| > | |
| Generate Test Data | |
| </button> | |
| )} |
🤖 Prompt for AI Agents
In `@src/app/LoginPage.tsx` around lines 81 - 88, Remove or hide the debug
"Generate Test" button from the production LoginPage: locate the button using
the generateTestData function and MicrosoftIcon in LoginPage.tsx and either
delete it or wrap its JSX in a condition that only renders in development (e.g.,
check process.env.NODE_ENV === 'development'); if you keep it for dev use,
replace the MicrosoftIcon with a neutral dev icon and modify generateTestData to
provide user-visible feedback (toast or inline message) instead of only
console.log, and ensure this functionality is moved to an admin/dev-only route
if it needs to exist long-term.
| import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments, | ||
| SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments,NonBunkJamboreeActivityWithAssignments, Freeplay, | ||
| PostID, SectionID, SessionID, NightShiftID, | ||
| ProgramAreaID, | ||
| SchedulingSectionID, | ||
| FreeplayID} from "@/types/sessionTypes"; |
There was a problem hiding this comment.
Remove unused imports to fix pipeline failures.
The pipeline is failing due to unused imports. Remove Freeplay and SchedulingSectionID from the import statement.
Proposed fix
-import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments,
- SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments,NonBunkJamboreeActivityWithAssignments, Freeplay,
- PostID, SectionID, SessionID, NightShiftID,
- ProgramAreaID,
- SchedulingSectionID,
- FreeplayID} from "@/types/sessionTypes";
+import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments,
+ SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments, NonBunkJamboreeActivityWithAssignments,
+ PostID, SectionID, SessionID, NightShiftID,
+ ProgramAreaID,
+ FreeplayID } from "@/types/sessionTypes";📝 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.
| import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments, | |
| SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments,NonBunkJamboreeActivityWithAssignments, Freeplay, | |
| PostID, SectionID, SessionID, NightShiftID, | |
| ProgramAreaID, | |
| SchedulingSectionID, | |
| FreeplayID} from "@/types/sessionTypes"; | |
| import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments, | |
| SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments, NonBunkJamboreeActivityWithAssignments, | |
| PostID, SectionID, SessionID, NightShiftID, | |
| ProgramAreaID, | |
| FreeplayID } from "@/types/sessionTypes"; |
🧰 Tools
🪛 ESLint
[error] 2-2: 'Freeplay' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 5-5: 'SchedulingSectionID' is defined but never used.
(@typescript-eslint/no-unused-vars)
🪛 GitHub Actions: Lint & Build on PR
[error] 2-2: ESLint: 'Freeplay' is defined but never used.
[error] 5-5: ESLint: 'SchedulingSectionID' is defined but never used.
🤖 Prompt for AI Agents
In `@src/features/scheduling/test/generateEmulatorData.tsx` around lines 1 - 6,
The import statement in generateEmulatorData.tsx includes unused symbols causing
pipeline failures; remove Freeplay and SchedulingSectionID from the named import
list (the import that currently lists CamperAttendeeID, StaffAttendeeID,
AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments,
SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments,
NonBunkJamboreeActivityWithAssignments, Freeplay, PostID, SectionID, SessionID,
NightShiftID, ProgramAreaID, SchedulingSectionID, FreeplayID) so only used types
remain imported.
| function generateNonoLists(campers: CamperAttendeeID[], staff: StaffAttendeeID[], admins: AdminAttendeeID[]) { | ||
| const allPeople = [...campers, ...staff, ...admins]; | ||
|
|
||
| for (const camper of campers) { | ||
|
|
||
| // Shuffle the array to pick random people | ||
| const shuffled = allPeople | ||
| .filter(p => p.id !== camper.id) // avoid themselves | ||
| .sort(() => Math.random() - 0.5); | ||
|
|
||
| // Pick 3 random people and save nonoList as an array of their IDs | ||
| const randomAmount = Math.floor(Math.random() * 4); | ||
| const selected = shuffled.slice(0, randomAmount); | ||
| camper.nonoList = selected.map(p => p.id); | ||
|
|
||
| // Add camper to each selected person's nonoList | ||
| for (const person of selected) { | ||
| if (!person.nonoList.includes(camper.id)) { | ||
| person.nonoList.push(camper.id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (const staffer of staff) { | ||
|
|
||
| // Shuffle the array to pick random people | ||
| const shuffled = allPeople | ||
| .filter(p => p.id !== staffer.id) // avoid themselves | ||
| .sort(() => Math.random() - 0.5); | ||
|
|
||
| // Pick 3 random people and save nonoList as an array of their IDs | ||
| const randomAmount = Math.floor(Math.random() * 4); | ||
| const selected = shuffled.slice(0, randomAmount); | ||
| staffer.nonoList = selected.map(p => p.id); | ||
|
|
||
| // Add camper to each selected person's nonoList | ||
| for (const person of selected) { | ||
| if (!person.nonoList.includes(staffer.id)) { | ||
| person.nonoList.push(staffer.id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (const admin of admins) { | ||
|
|
||
| // Shuffle the array to pick random people | ||
| const shuffled = allPeople | ||
| .filter(p => p.id !== admin.id) // avoid themselves | ||
| .sort(() => Math.random() - 0.5); | ||
|
|
||
| // Pick 3 random people and save nonoList as an array of their IDs | ||
| const randomAmount = Math.floor(Math.random() * 4); | ||
| const selected = shuffled.slice(0, randomAmount); | ||
| admin.nonoList = selected.map(p => p.id); | ||
|
|
||
| // Add camper to each selected person's nonoList | ||
| for (const person of selected) { | ||
| if (!person.nonoList.includes(admin.id)) { | ||
| person.nonoList.push(admin.id); | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Remove or export unused helper functions to fix pipeline failures.
generateNonoLists is defined but never used (only referenced in commented-out code). Either:
- Remove the function if not needed
- Export it for external use
- Prefix with underscore if intentionally kept for future use:
_generateNonoLists
The same applies to generateYesyesLists (line 194), generateBunks (line 224), and other unused functions flagged by ESLint.
🧰 Tools
🪛 ESLint
[error] 130-130: 'generateNonoLists' is defined but never used.
(@typescript-eslint/no-unused-vars)
🪛 GitHub Actions: Lint & Build on PR
[error] 130-130: ESLint: 'generateNonoLists' is defined but never used.
🤖 Prompt for AI Agents
In `@src/features/scheduling/test/generateEmulatorData.tsx` around lines 130 -
192, The function generateNonoLists is defined but never used, causing linting
and pipeline issues. To fix this, either remove the generateNonoLists function
if it is not needed, or export it to make it available externally, or prefix its
name with an underscore (e.g., _generateNonoLists) if you intend to keep it for
future use. Apply the same fix to other unused functions in the file such as
generateYesyesLists and generateBunks.
| const schedule: SectionSchedule<'BUNK-JAMBO'> = { blocks: {}, alternatePeriodsOff: {} }; | ||
| schedule.alternatePeriodsOff["RH"] = [] | ||
|
|
||
| schedule.alternatePeriodsOff["RH"] = [] |
There was a problem hiding this comment.
Remove duplicate assignment.
schedule.alternatePeriodsOff["RH"] is assigned twice consecutively. Remove the duplicate line.
Proposed fix
const schedule: SectionSchedule<'BUNK-JAMBO'> = { blocks: {}, alternatePeriodsOff: {} };
schedule.alternatePeriodsOff["RH"] = []
-
- schedule.alternatePeriodsOff["RH"] = []
for(let i = 0; i < blockIDs.length; i++)📝 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.
| const schedule: SectionSchedule<'BUNK-JAMBO'> = { blocks: {}, alternatePeriodsOff: {} }; | |
| schedule.alternatePeriodsOff["RH"] = [] | |
| schedule.alternatePeriodsOff["RH"] = [] | |
| const schedule: SectionSchedule<'BUNK-JAMBO'> = { blocks: {}, alternatePeriodsOff: {} }; | |
| schedule.alternatePeriodsOff["RH"] = [] |
🤖 Prompt for AI Agents
In `@src/features/scheduling/test/generateEmulatorData.tsx` around lines 349 -
352, The code sets schedule.alternatePeriodsOff["RH"] twice in
generateEmulatorData (variable schedule of type SectionSchedule<'BUNK-JAMBO'>);
remove the duplicate assignment so alternatePeriodsOff["RH"] is only initialized
once (leave a single schedule.alternatePeriodsOff["RH"] = [] and delete the
redundant repeated line).
| function generateBunkJamboActivities(totalActivities: number, blockID: string): BunkJamboreeActivityWithAssignments[] { | ||
| const activities: BunkJamboreeActivityWithAssignments[] = []; | ||
|
|
||
| for (let i = 0; i < totalActivities; i++) { | ||
| const name = faker.word.words(2); | ||
| const description = faker.lorem.sentence(); | ||
|
|
||
| activities.push({ | ||
| name, | ||
| description, | ||
| assignments: { | ||
| bunkNums: [], | ||
| adminIds: [] | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| return activities; | ||
| } |
There was a problem hiding this comment.
Remove or prefix unused blockID parameter to fix pipeline.
The blockID parameter is unused. Either remove it or prefix with underscore if kept for signature consistency.
Proposed fix
-function generateBunkJamboActivities(totalActivities: number, blockID: string): BunkJamboreeActivityWithAssignments[] {
+function generateBunkJamboActivities(totalActivities: number, _blockID: string): BunkJamboreeActivityWithAssignments[] {Apply the same fix to generateNonBunkJamboActivities at line 516.
📝 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.
| function generateBunkJamboActivities(totalActivities: number, blockID: string): BunkJamboreeActivityWithAssignments[] { | |
| const activities: BunkJamboreeActivityWithAssignments[] = []; | |
| for (let i = 0; i < totalActivities; i++) { | |
| const name = faker.word.words(2); | |
| const description = faker.lorem.sentence(); | |
| activities.push({ | |
| name, | |
| description, | |
| assignments: { | |
| bunkNums: [], | |
| adminIds: [] | |
| } | |
| }); | |
| } | |
| return activities; | |
| } | |
| function generateBunkJamboActivities(totalActivities: number, _blockID: string): BunkJamboreeActivityWithAssignments[] { | |
| const activities: BunkJamboreeActivityWithAssignments[] = []; | |
| for (let i = 0; i < totalActivities; i++) { | |
| const name = faker.word.words(2); | |
| const description = faker.lorem.sentence(); | |
| activities.push({ | |
| name, | |
| description, | |
| assignments: { | |
| bunkNums: [], | |
| adminIds: [] | |
| } | |
| }); | |
| } | |
| return activities; | |
| } |
🧰 Tools
🪛 ESLint
[error] 495-495: 'blockID' is defined but never used.
(@typescript-eslint/no-unused-vars)
🪛 GitHub Actions: Lint & Build on PR
[error] 495-495: ESLint: 'blockID' is defined but never used.
🤖 Prompt for AI Agents
In `@src/features/scheduling/test/generateEmulatorData.tsx` around lines 495 -
513, The function parameter blockID is unused; either remove it from the
signature or rename it to _blockID to silence the unused-parameter error —
update generateBunkJamboActivities accordingly and apply the same change to
generateNonBunkJamboActivities so their signatures remain consistent with
callers/types.
| const startDate = moment().startOf("month"); | ||
| const endDate = startDate.clone().add(2, "weeks"); | ||
|
|
||
| const campers: CamperAttendeeID[] = generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS); |
There was a problem hiding this comment.
Remove or use the campers variable to fix pipeline.
campers is assigned but never used since the code that would use it is commented out. Either remove the variable or prefix with underscore.
Proposed fix
- const campers: CamperAttendeeID[] = generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS);
+ // const campers: CamperAttendeeID[] = generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS);Or remove the line entirely if generateCampers is also being removed.
📝 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.
| const campers: CamperAttendeeID[] = generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS); | |
| // const campers: CamperAttendeeID[] = generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS); |
🧰 Tools
🪛 ESLint
[error] 739-739: 'campers' is assigned a value but never used.
(@typescript-eslint/no-unused-vars)
🪛 GitHub Actions: Lint & Build on PR
[error] 739-739: ESLint: 'campers' is assigned a value but never used.
🤖 Prompt for AI Agents
In `@src/features/scheduling/test/generateEmulatorData.tsx` at line 739, The local
variable campers assigned from generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS) is
unused; either remove the declaration or rename it to _campers (prefix
underscore) to silence the unused-variable warning, or if generateCampers is
being removed altogether, delete this line; update the area around the
generateCampers call in generateEmulatorData.tsx so no unused symbol remains.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/features/scheduling/generation/SessionScheduler.ts`:
- Around line 73-81: The current logic in SessionScheduler.ts skips bunk checks
when maxStaffOffPerDay === Infinity, which can let multiple employees from the
same bunk take the same day off; change the condition around the bunk check so
the bunkConflict check (using 'bunk' in employee, allEmployees, employee.bunk,
and employee.daysOff.includes(date)) always runs regardless of
maxStaffOffPerDay, and if a bunkConflict is detected either skip assigning that
day-off or implement a fallback assignment path that searches for an alternative
employee/date rather than bypassing the constraint entirely; ensure the code in
the loop that currently contains "if ('bunk' in employee && maxStaffOffPerDay
!== Infinity) { ... }" is refactored to always evaluate bunk constraints and
handle failures gracefully.
- Line 187: The variable currDate in SessionScheduler.ts is never reassigned so
change its declaration from let to const to satisfy the prefer-const rule; keep
using currDate with moment.add() (which mutates the moment instance) so no other
code changes are needed—update the declaration where currDate is defined (near
startDate usage) to const currDate.
♻️ Duplicate comments (2)
src/features/scheduling/generation/SessionScheduler.ts (2)
137-140: Use a local variable instead of mutatingmaxStaffOffPerDay.While the recalculation at line 131 resets the value each iteration, the mutation pattern is still error-prone. Using a local variable as suggested in a previous review would be cleaner.
Proposed fix
for (const employee of employees) { - maxStaffOffPerDay = Math.ceil(employees.length / window1.length); const dates = 'programCounselor' in employee && jamboreeWindow1.has(employee) ? window1.filter(d => this.isJamboreeISO(d, this.sections)) : window1; - if (dates.length === 1){ - // set maxStaffOffPerDay to Infinity - maxStaffOffPerDay = Infinity; - }; + const effectiveMax = dates.length === 1 + ? Infinity + : Math.ceil(employees.length / window1.length); - if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) { + if (!this.assignOneDayOff(employee, dates, dayCounts, employees, effectiveMax)) { console.warn(`Failed to assign window-1 day off for ${employee.id}`); } }Apply the same pattern to the window2 loop (lines 148-164).
1-7: Remove unused and erroneous imports to fix pipeline failures.The pipeline is still failing due to unused imports:
NightShift,da,w,em, andmax. These appear to be accidental auto-imports and must be removed.Proposed fix
-import { StaffAttendeeID, AdminAttendeeID, NightShiftID, SessionID, SectionID, NightShift } from "@/types/sessionTypes"; -import { da } from "@faker-js/faker/."; -import { w } from "@faker-js/faker/dist/airline-CLphikKp"; -import { em } from "@mantine/core"; -import { max } from "moment"; -import { Moment } from "moment"; -import moment from "moment"; +import { StaffAttendeeID, AdminAttendeeID, NightShiftID, SessionID, SectionID } from "@/types/sessionTypes"; +import moment, { Moment } from "moment";
🧹 Nitpick comments (3)
src/features/scheduling/generation/SessionScheduler.ts (3)
100-102: Hardcoded scheduling windows reduce flexibility.The windows are hardcoded to days 2-6 and 7-12 relative to session start, with a comment noting this is temporary. Consider deriving these windows dynamically from the session's actual duration to handle sessions of varying lengths.
203-207: Extract magic number for night shift staff count.The value
4for the number of employees selected per bunk per night is unexplained. Consider extracting this as a named constant for clarity and easier configuration.Proposed fix
+const NIGHT_SHIFT_STAFF_PER_BUNK = 4; + export class SessionScheduler { // ... existing code ... const shuffled = this.shuffleArray(employeesInBunk); - const selected = shuffled.slice(0, 4); + const selected = shuffled.slice(0, NIGHT_SHIFT_STAFF_PER_BUNK);
96-171: Note: Method mutates employee objects directly.
assignDaysOffmodifiesemployee.daysOffarrays in place rather than returning new data. This is a side-effect pattern that callers should be aware of. Consider documenting this behavior or returning a map of assignments for a more functional approach.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/features/scheduling/generation/SessionScheduler.ts`:
- Around line 200-227: The on‑duty split logic under‑allocates because it uses
"length + 1 < PER_SPLIT" and `selected` is never reassigned; change `selected`
to a const array and fix the condition to compare the current count directly
(e.g., use "shift[bunkNumber].counselorsOnDuty.length < PER_SPLIT") or
explicitly enforce a cap (introduce a MAX_ON_DUTY constant if the intent is to
"pick 4 employees") before pushing into shift[bunkNumber].counselorsOnDuty;
ensure fallback pushes go to shift[bunkNumber].nightBunkDuty, and keep all other
checks (daysOff via this.toISO(currDate), prevDate, nextDate) the same while
referencing employeesInBunk, PER_SPLIT, selected, and the shift[...] arrays to
locate the change.
♻️ Duplicate comments (7)
src/features/scheduling/generation/SessionScheduler.ts (2)
1-1: Remove unusedNightShiftimport to unblock lint.
NightShiftisn’t referenced in this file and fails the lint/build pipeline.🧹 Proposed fix
-import { StaffAttendeeID, AdminAttendeeID, NightShiftID, SessionID, SectionID, NightShift } from "@/types/sessionTypes"; +import { StaffAttendeeID, AdminAttendeeID, NightShiftID, SessionID, SectionID } from "@/types/sessionTypes";
68-77: Always enforce bunk conflicts, even when caps are lifted.When
maxStaffOffPerDayisInfinity, the bunk constraint is skipped, allowing same‑bunk counselors to take the same day off.🔒 Proposed fix
- if ('bunk' in employee && maxStaffOffPerDay !== Infinity) { + if ('bunk' in employee) { const bunkConflict = allEmployees.some(e => 'bunk' in e && e.bunk === employee.bunk && e.daysOff.includes(date) ); if (bunkConflict) continue; }src/features/scheduling/test/generateEmulatorData.tsx (5)
1-6: Remove unusedFreeplayandSchedulingSectionIDimports.Both are unused and currently fail lint.
🧹 Proposed fix
-import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments, - SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments,NonBunkJamboreeActivityWithAssignments, Freeplay, - PostID, SectionID, SessionID, NightShiftID, - ProgramAreaID, - SchedulingSectionID, - FreeplayID} from "@/types/sessionTypes"; +import { CamperAttendeeID, StaffAttendeeID, AdminAttendeeID, AgeGroup, SectionSchedule, BundleActivityWithAssignments, + SectionPreferences, BunkID, BunkJamboreeActivityWithAssignments, NonBunkJamboreeActivityWithAssignments, + PostID, SectionID, SessionID, NightShiftID, + ProgramAreaID, + FreeplayID } from "@/types/sessionTypes";
130-261: Unused list/bunk helpers trip lint.
generateNonoLists,generateYesyesLists, andgenerateBunksare never called (only referenced in commented code). Remove, export, or prefix with_to satisfy lint.
317-593: Unused schedule helpers + duplicatealternatePeriodsOffinit.
generateBundleBlockSchedule,generateBunkJamboBlockSchedule,generateNonBunkJamboBlockSchedule,generateFreeplayBlockSchedule,generateBlockIDs,generatePosts, andgeneratePrefsare unused and fail lint. Also,schedule.alternatePeriodsOff["RH"]is set twice ingenerateBunkJamboBlockSchedule.🧹 Proposed fix (duplicate init)
- schedule.alternatePeriodsOff["RH"] = [] - - schedule.alternatePeriodsOff["RH"] = [] + schedule.alternatePeriodsOff["RH"] = []
483-521: Silence unusedblockIDparameters.
blockIDisn’t used in either jamboree activity generator and fails lint.🧹 Proposed fix
-function generateBunkJamboActivities(totalActivities: number, blockID: string): BunkJamboreeActivityWithAssignments[] { +function generateBunkJamboActivities(totalActivities: number, _blockID: string): BunkJamboreeActivityWithAssignments[] { @@ -function generateNonBunkJamboActivities(totalActivities: number, blockID: string): NonBunkJamboreeActivityWithAssignments[] { +function generateNonBunkJamboActivities(totalActivities: number, _blockID: string): NonBunkJamboreeActivityWithAssignments[] {
727-729: Remove unusedcampersvariable ingenerateSession.
campersis unused and fails lint.🧹 Proposed fix
- const campers: CamperAttendeeID[] = generateCampers(TOTAL_CAMPERS, TOTAL_BUNKS);
| // choosing 4 employees | ||
| const employeesInBunk = employees.filter((e) => e.bunk === bunkNumber); | ||
| const PER_SPLIT = Math.ceil(employeesInBunk.length / 2); | ||
|
|
||
| if (employeesInBunk.length === 0) continue; | ||
| let selected: StaffAttendeeID[] = [] | ||
|
|
||
| for (const employee of employeesInBunk) { | ||
|
|
||
| if (employee.daysOff.includes(this.toISO(currDate))) { | ||
| continue; | ||
| } | ||
| const prevDate = currDate.clone().subtract(1, 'day'); | ||
| const nextDate = currDate.clone().add(1, 'day'); | ||
|
|
||
| const hasAdjacentDayOff = | ||
| employee.daysOff.includes(this.toISO(prevDate)) || | ||
| employee.daysOff.includes(this.toISO(nextDate)); | ||
|
|
||
| if (!hasAdjacentDayOff && shift[bunkNumber].counselorsOnDuty.length + 1 < PER_SPLIT ) { | ||
| shift[bunkNumber].counselorsOnDuty.push(employee.id); | ||
| selected.push(employee); | ||
| } | ||
|
|
||
| else { | ||
| shift[bunkNumber].nightBunkDuty.push(employee.id); | ||
| selected.push(employee); | ||
| } |
There was a problem hiding this comment.
Fix night‑shift split and make selected const.
length + 1 < PER_SPLIT under‑allocates on‑duty by one, and selected is never reassigned (lint failure). If the intent is “pick 4 employees,” add a cap or update the comment.
✅ Proposed fix
- let selected: StaffAttendeeID[] = []
+ const selected: StaffAttendeeID[] = []
@@
- if (!hasAdjacentDayOff && shift[bunkNumber].counselorsOnDuty.length + 1 < PER_SPLIT ) {
+ if (!hasAdjacentDayOff && shift[bunkNumber].counselorsOnDuty.length < PER_SPLIT) {
shift[bunkNumber].counselorsOnDuty.push(employee.id);
selected.push(employee);
}🧰 Tools
🪛 ESLint
[error] 205-205: 'selected' is never reassigned. Use 'const' instead.
(prefer-const)
🪛 GitHub Actions: Lint & Build on PR
[error] 205-205: 'selected' is never reassigned. Use 'const' instead. prefer-const
🤖 Prompt for AI Agents
In `@src/features/scheduling/generation/SessionScheduler.ts` around lines 200 -
227, The on‑duty split logic under‑allocates because it uses "length + 1 <
PER_SPLIT" and `selected` is never reassigned; change `selected` to a const
array and fix the condition to compare the current count directly (e.g., use
"shift[bunkNumber].counselorsOnDuty.length < PER_SPLIT") or explicitly enforce a
cap (introduce a MAX_ON_DUTY constant if the intent is to "pick 4 employees")
before pushing into shift[bunkNumber].counselorsOnDuty; ensure fallback pushes
go to shift[bunkNumber].nightBunkDuty, and keep all other checks (daysOff via
this.toISO(currDate), prevDate, nextDate) the same while referencing
employeesInBunk, PER_SPLIT, selected, and the shift[...] arrays to locate the
change.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/features/scheduling/generation/SessionScheduler.ts`:
- Around line 95-159: The fixed windows (window1/window2 created via
getDateStringsInRange) can extend past the session end causing out‑of‑range
dates and window2.length === 0 which leads to division by zero and Infinity for
maxStaffOffPerDay; clamp each window to the session end (e.g., compute end-bound
when calling getDateStringsInRange or trim the returned arrays against the
session end/last valid date) and skip a pass when a window is empty (if
window1.length===0 or window2.length===0, do not run the corresponding
for-loop), and also guard before computing maxStaffOffPerDay to avoid dividing
by zero (only compute Math.ceil(employees.length / window.length) when
window.length>0); apply the same guards to the jamboree filtering that uses
isJamboreeISO and to calls to assignOneDayOff so you never pass empty date
arrays.
♻️ Duplicate comments (2)
src/features/scheduling/generation/SessionScheduler.ts (2)
62-76: Always enforce the bunk constraint—even when using an Infinity cap.Right now the bunk check is skipped when
maxStaffOffPerDay === Infinity, which can schedule multiple counselors from the same bunk off on the same day and break coverage. This is the same risk previously flagged and still present.✅ Proposed fix
- if ('bunk' in employee && maxStaffOffPerDay !== Infinity) { + if ('bunk' in employee) { const bunkConflict = allEmployees.some(e => 'bunk' in e && e.bunk === employee.bunk && e.daysOff.includes(date) ); if (bunkConflict) continue; }
201-224: Fix on‑duty split off‑by‑one.
length + 1 < PER_SPLITunder‑allocates on‑duty staff. For example, whenPER_SPLITis 1, no one can ever be placed on duty. This was previously flagged and is still present.✅ Proposed fix
- if (!hasAdjacentDayOff && shift[bunkNumber].counselorsOnDuty.length + 1 < PER_SPLIT ) { + if (!hasAdjacentDayOff && shift[bunkNumber].counselorsOnDuty.length < PER_SPLIT) { shift[bunkNumber].counselorsOnDuty.push(employee.id); selected.push(employee); }
| // Define windows (days 2–6 and 7–12) (TEMP Set for default session. Need to change to handle multiple sessions) | ||
| const window1 = this.getDateStringsInRange(start.clone().add(1, "day"), start.clone().add(5, "day")); | ||
| const window2 = this.getDateStringsInRange(start.clone().add(6, "day"), start.clone().add(11, "day")); | ||
|
|
||
| const allDates = [...window1, ...window2]; | ||
| let maxStaffOffPerDay = Math.ceil(employees.length / allDates.length); | ||
|
|
||
| const dayCounts = new Map<string, number>(); | ||
| allDates.forEach(d => dayCounts.set(d, 0)); | ||
|
|
||
| // Separate program counselors | ||
| const nonWFProgramCounselors = employees.filter( | ||
| e => 'programCounselor' in e && e.programCounselor && e.programCounselor.id !== "WF" | ||
| ); | ||
| const WFProgramCounselors = employees.filter( | ||
| e => 'programCounselor' in e && e.programCounselor && e.programCounselor.id === "WF" | ||
| ); | ||
| // ---- Choose exact number of WF counselors for Jamboree ---- | ||
| const numWFWindow1 = Math.ceil(WFProgramCounselors.length/window1.length); // example: 3 WF on Jamboree in window1 | ||
| const numWFWindow2 = Math.ceil(WFProgramCounselors.length/window2.length); // example: 2 WF on Jamboree in window2 | ||
|
|
||
| const shuffledWF = this.shuffleArray(WFProgramCounselors); | ||
| const selectedWFWindow1 = shuffledWF.slice(0, numWFWindow1); | ||
| const selectedWFWindow2 = shuffledWF.slice(numWFWindow1, numWFWindow1 + numWFWindow2); | ||
|
|
||
| // ---- Build Jamboree sets ---- | ||
| const jamboreeWindow1 = new Set([...nonWFProgramCounselors, ...selectedWFWindow1]); | ||
| const jamboreeWindow2 = new Set([...nonWFProgramCounselors, ...selectedWFWindow2]); | ||
|
|
||
| // ---- PASS 1: window1 ---- | ||
| for (const employee of employees) { | ||
| maxStaffOffPerDay = Math.ceil(employees.length / window1.length); | ||
| const dates = | ||
| 'programCounselor' in employee && jamboreeWindow1.has(employee) | ||
| ? window1.filter(d => this.isJamboreeISO(d, this.sections)) | ||
| : window1; | ||
|
|
||
| if (dates.length === 1){ | ||
| // set maxStaffOffPerDay to Infinity | ||
| maxStaffOffPerDay = Infinity; | ||
| }; | ||
|
|
||
| if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) { | ||
| console.warn(`Failed to assign window-1 day off for ${employee.id}`); | ||
| } | ||
| } | ||
|
|
||
| // ---- PASS 2: window2 ---- | ||
| for (const employee of employees) { | ||
| maxStaffOffPerDay = Math.ceil(employees.length / window2.length); | ||
|
|
||
| const dates = | ||
| 'programCounselor' in employee && jamboreeWindow2.has(employee) | ||
| ? window2.filter(d => this.isJamboreeISO(d, this.sections)) | ||
| : window2; | ||
|
|
||
| if (dates.length === 1){ | ||
|
|
||
| maxStaffOffPerDay = Infinity; | ||
| }; | ||
|
|
||
| if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) { | ||
| console.warn(`Failed to assign window-2 day off for ${employee.id}`); | ||
| } | ||
| } |
There was a problem hiding this comment.
Clamp day‑off windows to session dates and guard empty windows.
The fixed windows (days 2–6, 7–12) can extend beyond the session end. For shorter sessions this creates out‑of‑range day‑offs and can also lead to window2.length === 0, which yields Math.ceil(employees.length / 0) = Infinity and noisy warnings.
✅ Proposed fix (clip windows + skip empty pass)
- // Define windows (days 2–6 and 7–12) (TEMP Set for default session. Need to change to handle multiple sessions)
- const window1 = this.getDateStringsInRange(start.clone().add(1, "day"), start.clone().add(5, "day"));
- const window2 = this.getDateStringsInRange(start.clone().add(6, "day"), start.clone().add(11, "day"));
+ // Define windows (days 2–6 and 7–12), clipped to session end
+ const sessionEnd = moment(session.endDate).subtract(1, "day");
+ const window1Start = start.clone().add(1, "day");
+ const window1End = moment.min(start.clone().add(5, "day"), sessionEnd);
+ const window1 = window1Start.isSameOrBefore(window1End)
+ ? this.getDateStringsInRange(window1Start, window1End)
+ : [];
+
+ const window2Start = start.clone().add(6, "day");
+ const window2End = moment.min(start.clone().add(11, "day"), sessionEnd);
+ const window2 = window2Start.isSameOrBefore(window2End)
+ ? this.getDateStringsInRange(window2Start, window2End)
+ : [];
@@
- for (const employee of employees) {
+ if (window1.length > 0) {
+ for (const employee of employees) {
maxStaffOffPerDay = Math.ceil(employees.length / window1.length);
@@
- if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) {
+ if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) {
console.warn(`Failed to assign window-1 day off for ${employee.id}`);
- }
+ }
+ }
}
@@
- for (const employee of employees) {
+ if (window2.length > 0) {
+ for (const employee of employees) {
maxStaffOffPerDay = Math.ceil(employees.length / window2.length);
@@
- if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) {
+ if (!this.assignOneDayOff(employee, dates, dayCounts, employees, maxStaffOffPerDay)) {
console.warn(`Failed to assign window-2 day off for ${employee.id}`);
- }
+ }
+ }
}🤖 Prompt for AI Agents
In `@src/features/scheduling/generation/SessionScheduler.ts` around lines 95 -
159, The fixed windows (window1/window2 created via getDateStringsInRange) can
extend past the session end causing out‑of‑range dates and window2.length === 0
which leads to division by zero and Infinity for maxStaffOffPerDay; clamp each
window to the session end (e.g., compute end-bound when calling
getDateStringsInRange or trim the returned arrays against the session end/last
valid date) and skip a pass when a window is empty (if window1.length===0 or
window2.length===0, do not run the corresponding for-loop), and also guard
before computing maxStaffOffPerDay to avoid dividing by zero (only compute
Math.ceil(employees.length / window.length) when window.length>0); apply the
same guards to the jamboree filtering that uses isJamboreeISO and to calls to
assignOneDayOff so you never pass empty date arrays.
Created the initial scheduling algorithm to assign days off and nights off for employees, and added extra fields to the StaffSessionAttendee type to store nights off and days off.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.