Fix critical bug: cycle phase boundaries now scale with cycle length
CRITICAL BUG FIX: - Phase boundaries were hardcoded for 31-day cycle, breaking correct phase calculations for users with different cycle lengths (28, 35, etc.) - Added getPhaseBoundaries(cycleLength) function in cycle.ts - Updated getPhase() to accept cycleLength parameter (default 31) - Updated all callers (API routes, components) to pass cycleLength - Added 13 new tests for phase boundaries with 28, 31, and 35-day cycles ICS IMPROVEMENTS: - Fixed emojis to match calendar.md spec: 🩸🌱🌸🌙🌑 - Added CATEGORIES field for calendar app colors per spec: MENSTRUAL=Red, FOLLICULAR=Green, OVULATION=Pink, EARLY_LUTEAL=Yellow, LATE_LUTEAL=Orange - Added 5 new tests for CATEGORIES Updated IMPLEMENTATION_PLAN.md with discovered issues and test counts. 825 tests passing (up from 807) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,9 +34,10 @@ describe("MiniCalendar", () => {
|
||||
it("renders current cycle day and phase", () => {
|
||||
render(<MiniCalendar {...baseProps} />);
|
||||
|
||||
// Jan 15, 2026 with lastPeriod Jan 1 = Day 15 (OVULATION)
|
||||
// Jan 15, 2026 with lastPeriod Jan 1 = Day 15
|
||||
// For 28-day cycle: EARLY_LUTEAL starts at day 15 (28-13=15)
|
||||
expect(screen.getByText(/Day 15/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/OVULATION/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/EARLY_LUTEAL/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders compact day-of-week headers", () => {
|
||||
@@ -86,6 +87,8 @@ describe("MiniCalendar", () => {
|
||||
});
|
||||
|
||||
describe("phase colors", () => {
|
||||
// For 28-day cycle:
|
||||
// MENSTRUAL: 1-3, FOLLICULAR: 4-12, OVULATION: 13-14, EARLY_LUTEAL: 15-21, LATE_LUTEAL: 22-28
|
||||
it("applies menstrual phase color (blue) to days 1-3", () => {
|
||||
render(<MiniCalendar {...baseProps} />);
|
||||
|
||||
@@ -95,37 +98,37 @@ describe("MiniCalendar", () => {
|
||||
expect(day1).toHaveClass("bg-blue-100");
|
||||
});
|
||||
|
||||
it("applies follicular phase color (green) to days 4-14", () => {
|
||||
it("applies follicular phase color (green) to days 4-12", () => {
|
||||
render(<MiniCalendar {...baseProps} />);
|
||||
|
||||
// Day 5 is FOLLICULAR (bg-green-100)
|
||||
// Day 5 is FOLLICULAR (bg-green-100) for 28-day cycle
|
||||
const buttons = screen.getAllByRole("button");
|
||||
const day5 = buttons.find((btn) => btn.textContent === "5");
|
||||
expect(day5).toHaveClass("bg-green-100");
|
||||
});
|
||||
|
||||
it("applies ovulation phase color (purple) to days 15-16", () => {
|
||||
it("applies ovulation phase color (purple) to days 13-14", () => {
|
||||
render(<MiniCalendar {...baseProps} />);
|
||||
|
||||
// Day 15 is OVULATION (bg-purple-100)
|
||||
// Day 13 is OVULATION (bg-purple-100) for 28-day cycle
|
||||
const buttons = screen.getAllByRole("button");
|
||||
const day15 = buttons.find((btn) => btn.textContent === "15");
|
||||
expect(day15).toHaveClass("bg-purple-100");
|
||||
const day13 = buttons.find((btn) => btn.textContent === "13");
|
||||
expect(day13).toHaveClass("bg-purple-100");
|
||||
});
|
||||
|
||||
it("applies early luteal phase color (yellow) to days 17-24", () => {
|
||||
it("applies early luteal phase color (yellow) to days 15-21", () => {
|
||||
render(<MiniCalendar {...baseProps} />);
|
||||
|
||||
// Day 20 is EARLY_LUTEAL (bg-yellow-100)
|
||||
// Day 18 is EARLY_LUTEAL (bg-yellow-100) for 28-day cycle
|
||||
const buttons = screen.getAllByRole("button");
|
||||
const day20 = buttons.find((btn) => btn.textContent === "20");
|
||||
expect(day20).toHaveClass("bg-yellow-100");
|
||||
const day18 = buttons.find((btn) => btn.textContent === "18");
|
||||
expect(day18).toHaveClass("bg-yellow-100");
|
||||
});
|
||||
|
||||
it("applies late luteal phase color (red) to days 25-31", () => {
|
||||
it("applies late luteal phase color (red) to days 22-28", () => {
|
||||
render(<MiniCalendar {...baseProps} />);
|
||||
|
||||
// Day 25 is LATE_LUTEAL (bg-red-100)
|
||||
// Day 25 is LATE_LUTEAL (bg-red-100) for 28-day cycle
|
||||
const buttons = screen.getAllByRole("button");
|
||||
const day25 = buttons.find((btn) => btn.textContent === "25");
|
||||
expect(day25).toHaveClass("bg-red-100");
|
||||
|
||||
@@ -55,7 +55,7 @@ export function MiniCalendar({
|
||||
|
||||
// Calculate current cycle day and phase for the header
|
||||
const currentCycleDay = getCycleDay(lastPeriodDate, cycleLength, today);
|
||||
const currentPhase = getPhase(currentCycleDay);
|
||||
const currentPhase = getPhase(currentCycleDay, cycleLength);
|
||||
|
||||
const handlePreviousMonth = () => {
|
||||
if (displayMonth === 0) {
|
||||
@@ -154,7 +154,7 @@ export function MiniCalendar({
|
||||
}
|
||||
|
||||
const cycleDay = getCycleDay(lastPeriodDate, cycleLength, date);
|
||||
const phase = getPhase(cycleDay);
|
||||
const phase = getPhase(cycleDay, cycleLength);
|
||||
const isToday =
|
||||
date.getFullYear() === today.getFullYear() &&
|
||||
date.getMonth() === today.getMonth() &&
|
||||
|
||||
Reference in New Issue
Block a user