diff --git a/frontend/src/components/dashboard/CalendarView.test.tsx b/frontend/src/components/dashboard/CalendarView.test.tsx
new file mode 100644
index 00000000..bfd11d7a
--- /dev/null
+++ b/frontend/src/components/dashboard/CalendarView.test.tsx
@@ -0,0 +1,81 @@
+import { describe, it, expect } from "vitest";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { CalendarView } from "./CalendarView";
+
+describe("CalendarView", () => {
+ it("disables the Today button when already viewing the current month and enables it when navigating away", () => {
+ render();
+
+ const todayButton = screen.getByRole("button", { name: "Today" });
+ expect(todayButton).toBeDisabled();
+
+ const nextMonthButton = screen.getByRole("button", { name: "Next month" });
+ fireEvent.click(nextMonthButton);
+
+ expect(todayButton).not.toBeDisabled();
+ });
+
+ it("resets the displayed month and year to the current system date when clicking Today", () => {
+ render();
+
+ const now = new Date();
+ const expectedLabel = now.toLocaleString("default", {
+ month: "long",
+ year: "numeric",
+ });
+
+ const monthHeading = screen.getByTestId("calendar-current-month");
+ expect(monthHeading).toHaveTextContent(expectedLabel);
+
+ const nextMonthButton = screen.getByRole("button", { name: "Next month" });
+ fireEvent.click(nextMonthButton);
+ fireEvent.click(nextMonthButton);
+ fireEvent.click(nextMonthButton);
+
+ const todayButton = screen.getByRole("button", { name: "Today" });
+ expect(todayButton).not.toBeDisabled();
+
+ fireEvent.click(todayButton);
+
+ expect(monthHeading).toHaveTextContent(expectedLabel);
+ expect(todayButton).toBeDisabled();
+ });
+
+ it("highlights the current day cell with an accent border ring and a Today pill badge", () => {
+ render();
+
+ const todayCell = screen.getByTestId("calendar-today-cell");
+ expect(todayCell).toBeInTheDocument();
+ expect(todayCell).toHaveAttribute("aria-current", "date");
+ expect(todayCell.className).toContain("ring-2");
+ expect(todayCell.className).toContain("ring-accent");
+
+ const todayBadge = screen.getByTestId("calendar-today-badge");
+ expect(todayBadge).toBeInTheDocument();
+ expect(todayBadge).toHaveTextContent("Today");
+ });
+
+ it("navigates to previous month and returns to current month via Today button", () => {
+ render();
+
+ const now = new Date();
+ const expectedLabel = now.toLocaleString("default", {
+ month: "long",
+ year: "numeric",
+ });
+
+ const prevMonthButton = screen.getByRole("button", {
+ name: "Previous month",
+ });
+ fireEvent.click(prevMonthButton);
+
+ const todayButton = screen.getByRole("button", { name: "Today" });
+ expect(todayButton).not.toBeDisabled();
+
+ fireEvent.click(todayButton);
+ expect(screen.getByTestId("calendar-current-month")).toHaveTextContent(
+ expectedLabel,
+ );
+ expect(todayButton).toBeDisabled();
+ });
+});
diff --git a/frontend/src/components/dashboard/CalendarView.tsx b/frontend/src/components/dashboard/CalendarView.tsx
index 46c96250..123e297d 100644
--- a/frontend/src/components/dashboard/CalendarView.tsx
+++ b/frontend/src/components/dashboard/CalendarView.tsx
@@ -145,6 +145,11 @@ export function CalendarView({
return days;
}, [currentDate, filteredStreams]);
+ const now = new Date();
+ const isViewingCurrentMonth =
+ currentDate.getFullYear() === now.getFullYear() &&
+ currentDate.getMonth() === now.getMonth();
+
const goToPrevMonth = () => {
setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
};
@@ -194,24 +199,34 @@ export function CalendarView({
{/* Navigation */}
-
+
{currentDate.toLocaleString("default", { month: "long", year: "numeric" })}