Fix spec compliance gaps in email and dashboard
Some checks failed
CI / quality (push) Failing after 28s
Deploy / deploy (push) Successful in 2m39s

- Email subject now follows spec format: PhaseFlow: [STATUS] - Day [cycleDay] ([phase])
- Daily email includes seed switch alert on day 15 (using getSeedSwitchAlert)
- Data panel HRV status now color-coded: green=Balanced, red=Unbalanced, gray=Unknown
- Data panel shows progress bar for week intensity vs phase limit with color thresholds

Adds 13 new tests (990 total).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-12 23:20:18 +00:00
parent 0ea8e2f2b5
commit d613417e47
6 changed files with 179 additions and 15 deletions

View File

@@ -55,11 +55,11 @@ describe("sendDailyEmail", () => {
ketoGuidance: "No - exit keto, need carbs for ovulation",
};
it("sends email with correct subject line", async () => {
it("sends email with correct subject line per spec", async () => {
await sendDailyEmail(sampleData);
expect(mockSend).toHaveBeenCalledWith(
expect.objectContaining({
subject: "Today's Training: 💪 TRAIN",
subject: "PhaseFlow: 💪 TRAIN - Day 15 (OVULATION)",
}),
);
});
@@ -126,6 +126,23 @@ describe("sendDailyEmail", () => {
const call = mockSend.mock.calls[0][0];
expect(call.text).toContain("Auto-generated by PhaseFlow");
});
it("includes seed switch alert on day 15", async () => {
// sampleData already has cycleDay: 15
await sendDailyEmail(sampleData);
const call = mockSend.mock.calls[0][0];
expect(call.text).toContain("🌱 SWITCH TODAY! Start Sesame + Sunflower");
});
it("does not include seed switch alert on other days", async () => {
const day10Data: DailyEmailData = {
...sampleData,
cycleDay: 10,
};
await sendDailyEmail(day10Data);
const call = mockSend.mock.calls[0][0];
expect(call.text).not.toContain("SWITCH TODAY");
});
});
describe("sendPeriodConfirmationEmail", () => {

View File

@@ -4,6 +4,7 @@ import { Resend } from "resend";
import { logger } from "@/lib/logger";
import { emailSentTotal } from "@/lib/metrics";
import { getSeedSwitchAlert } from "@/lib/nutrition";
const resend = new Resend(process.env.RESEND_API_KEY);
@@ -33,7 +34,12 @@ export async function sendDailyEmail(
data: DailyEmailData,
userId?: string,
): Promise<void> {
const subject = `Today's Training: ${data.decision.icon} ${data.decision.status}`;
// Subject format per spec: PhaseFlow: [STATUS] - Day [cycleDay] ([phase])
const subject = `PhaseFlow: ${data.decision.icon} ${data.decision.status} - Day ${data.cycleDay} (${data.phase})`;
// Check for seed switch alert on day 15
const seedSwitchAlert = getSeedSwitchAlert(data.cycleDay);
const seedSwitchSection = seedSwitchAlert ? `\n\n${seedSwitchAlert}` : "";
const body = `Good morning!
@@ -52,7 +58,7 @@ ${data.decision.icon} ${data.decision.reason}
🌱 SEEDS: ${data.seeds}
🍽️ MACROS: ${data.carbRange}
🥑 KETO: ${data.ketoGuidance}
🥑 KETO: ${data.ketoGuidance}${seedSwitchSection}
---
Auto-generated by PhaseFlow`;