From 6aa38c22e72ee36776bf1cbb288922cdfb78bf84 Mon Sep 17 00:00:00 2001 From: Yuhan Zhang Date: Thu, 29 Jan 2026 20:48:15 +0900 Subject: [PATCH 1/5] fix: prevent calendar cell redirect and add date deselection --- .../StatisticsView/StatisticsView.tsx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index 62e135186..c8e0fdfba 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -4,6 +4,7 @@ import { MonthCalendar } from "@/components/ActivityCalendar"; import { useDateFilterNavigation } from "@/hooks"; import type { StatisticsData } from "@/types/statistics"; import { MonthNavigator } from "./MonthNavigator"; +import { type MemoFilter, useMemoFilterContext } from "@/contexts/MemoFilterContext"; interface Props { statisticsData: StatisticsData; @@ -12,20 +13,36 @@ interface Props { const StatisticsView = (props: Props) => { const { statisticsData } = props; const { activityStats } = statisticsData; - const navigateToDateFilter = useDateFilterNavigation(); const [visibleMonthString, setVisibleMonthString] = useState(dayjs().format("YYYY-MM")); + const { getFiltersByFactor, addFilter, removeFilter } = useMemoFilterContext(); const maxCount = useMemo(() => { const counts = Object.values(activityStats); return Math.max(...counts, 1); }, [activityStats]); + const handleClick = (date: string) =>{ + const displayTimeFilters = getFiltersByFactor("displayTime"); + const isActive = displayTimeFilters.some((f: MemoFilter) => f.value === date); + + if (isActive) { + removeFilter((f: MemoFilter) => f.factor === "displayTime" && f.value === date); + } else { + // Remove all existing tag filters first, then add the new one + removeFilter((f: MemoFilter) => f.factor === "displayTime"); + addFilter({ + factor: "displayTime", + value: date, + }); + } + } + return (
- +
); From 710eff6f5725ca497cb585fc4878aa97baaa2326 Mon Sep 17 00:00:00 2001 From: Yuhan Zhang Date: Thu, 29 Jan 2026 21:01:46 +0900 Subject: [PATCH 2/5] chore: remove unused import --- web/src/components/StatisticsView/StatisticsView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index c8e0fdfba..2bf099405 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -1,7 +1,6 @@ import dayjs from "dayjs"; import { useMemo, useState } from "react"; import { MonthCalendar } from "@/components/ActivityCalendar"; -import { useDateFilterNavigation } from "@/hooks"; import type { StatisticsData } from "@/types/statistics"; import { MonthNavigator } from "./MonthNavigator"; import { type MemoFilter, useMemoFilterContext } from "@/contexts/MemoFilterContext"; From 468425547fc9a1651ab0ab19cef945a209d942ab Mon Sep 17 00:00:00 2001 From: Yuhan Zhang Date: Thu, 29 Jan 2026 22:40:40 +0900 Subject: [PATCH 3/5] feat(calendar): add visual indicator for selected date --- web/src/components/ActivityCalendar/MonthCalendar.tsx | 9 +++++++-- web/src/components/ActivityCalendar/types.ts | 1 + web/src/components/ActivityCalendar/useCalendar.ts | 4 ++-- web/src/components/StatisticsView/StatisticsView.tsx | 7 +++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/web/src/components/ActivityCalendar/MonthCalendar.tsx b/web/src/components/ActivityCalendar/MonthCalendar.tsx index 891a285be..44c86bb4b 100644 --- a/web/src/components/ActivityCalendar/MonthCalendar.tsx +++ b/web/src/components/ActivityCalendar/MonthCalendar.tsx @@ -7,6 +7,7 @@ import { useTodayDate, useWeekdayLabels } from "./hooks"; import type { CalendarSize, MonthCalendarProps } from "./types"; import { useCalendarMatrix } from "./useCalendar"; import { getTooltipText } from "./utils"; +import dayjs from "dayjs"; const GRID_STYLES: Record = { small: { gap: "gap-1.5", headerText: "text-[10px]" }, @@ -35,11 +36,15 @@ const WeekdayHeader = memo(({ weekDays, size }: WeekdayHeaderProps) => ( WeekdayHeader.displayName = "WeekdayHeader"; export const MonthCalendar = memo((props: MonthCalendarProps) => { - const { month, data, maxCount, size = "default", onClick, className, disableTooltips = false } = props; + const { month, selectedDate, data, maxCount, size = "default", onClick, className, disableTooltips = false } = props; const t = useTranslate(); const { generalSetting } = useInstance(); const today = useTodayDate(); const weekDays = useWeekdayLabels(); + const selectedDateFormatted = useMemo(() => { + if (!selectedDate) return null; + return dayjs(selectedDate).format("YYYY-MM-DD"); +}, [selectedDate]); const { weeks, weekDays: rotatedWeekDays } = useCalendarMatrix({ month, @@ -47,7 +52,7 @@ export const MonthCalendar = memo((props: MonthCalendarProps) => { weekDays, weekStartDayOffset: generalSetting.weekStartDayOffset, today, - selectedDate: "", + selectedDate: selectedDateFormatted, }); const flatDays = useMemo(() => weeks.flatMap((week) => week.days), [weeks]); diff --git a/web/src/components/ActivityCalendar/types.ts b/web/src/components/ActivityCalendar/types.ts index 306b21b9b..98dc7c2aa 100644 --- a/web/src/components/ActivityCalendar/types.ts +++ b/web/src/components/ActivityCalendar/types.ts @@ -22,6 +22,7 @@ export interface CalendarMatrixResult { export interface MonthCalendarProps { month: string; + selectedDate: string | null; data: Record; maxCount: number; size?: CalendarSize; diff --git a/web/src/components/ActivityCalendar/useCalendar.ts b/web/src/components/ActivityCalendar/useCalendar.ts index 783767e44..91ba23fd4 100644 --- a/web/src/components/ActivityCalendar/useCalendar.ts +++ b/web/src/components/ActivityCalendar/useCalendar.ts @@ -9,7 +9,7 @@ export interface UseCalendarMatrixParams { weekDays: string[]; weekStartDayOffset: number; today: string; - selectedDate: string; + selectedDate: string | null; } const createCalendarDayCell = ( @@ -17,7 +17,7 @@ const createCalendarDayCell = ( monthKey: string, data: Record, today: string, - selectedDate: string, + selectedDate: string | null, ): CalendarDayCell => { const isoDate = current.format("YYYY-MM-DD"); const isCurrentMonth = current.format("YYYY-MM") === monthKey; diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index 2bf099405..ed74942d0 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -12,6 +12,7 @@ interface Props { const StatisticsView = (props: Props) => { const { statisticsData } = props; const { activityStats } = statisticsData; + const [selectedDate, setSelectedDate] = useState(null); const [visibleMonthString, setVisibleMonthString] = useState(dayjs().format("YYYY-MM")); const { getFiltersByFactor, addFilter, removeFilter } = useMemoFilterContext(); @@ -26,6 +27,7 @@ const StatisticsView = (props: Props) => { if (isActive) { removeFilter((f: MemoFilter) => f.factor === "displayTime" && f.value === date); + setSelectedDate(null); } else { // Remove all existing tag filters first, then add the new one removeFilter((f: MemoFilter) => f.factor === "displayTime"); @@ -33,15 +35,16 @@ const StatisticsView = (props: Props) => { factor: "displayTime", value: date, }); + setSelectedDate(new Date(date)); } - } + }; return (
- +
); From f6b95056a33683d0e9991077e5ac87005b4d042d Mon Sep 17 00:00:00 2001 From: Yuhan Zhang Date: Sun, 1 Feb 2026 11:37:54 +0900 Subject: [PATCH 4/5] fix(types): fix TypeScript type errors --- .../ActivityCalendar/MonthCalendar.tsx | 1 + .../ActivityCalendar/YearCalendar.tsx | 15 +++++++++++++-- .../StatisticsView/StatisticsView.tsx | 19 +++++++++---------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/web/src/components/ActivityCalendar/MonthCalendar.tsx b/web/src/components/ActivityCalendar/MonthCalendar.tsx index 44c86bb4b..ecbc453f5 100644 --- a/web/src/components/ActivityCalendar/MonthCalendar.tsx +++ b/web/src/components/ActivityCalendar/MonthCalendar.tsx @@ -46,6 +46,7 @@ export const MonthCalendar = memo((props: MonthCalendarProps) => { return dayjs(selectedDate).format("YYYY-MM-DD"); }, [selectedDate]); + const { weeks, weekDays: rotatedWeekDays } = useCalendarMatrix({ month, data, diff --git a/web/src/components/ActivityCalendar/YearCalendar.tsx b/web/src/components/ActivityCalendar/YearCalendar.tsx index e8b8f91f9..7228982ab 100644 --- a/web/src/components/ActivityCalendar/YearCalendar.tsx +++ b/web/src/components/ActivityCalendar/YearCalendar.tsx @@ -73,12 +73,13 @@ interface MonthCardProps { data: Record; maxCount: number; onDateClick: (date: string) => void; + selectedDate: string | null; } -const MonthCard = memo(({ month, data, maxCount, onDateClick }: MonthCardProps) => ( +const MonthCard = memo(({ month, data, maxCount, onDateClick, selectedDate }: MonthCardProps) => (
{getMonthLabel(month)}
- +
)); MonthCard.displayName = "MonthCard"; @@ -104,11 +105,21 @@ export const YearCalendar = memo(({ selectedYear, data, onYearChange, onDateClic canGoNext={canGoNext} /> +<<<<<<< HEAD
{months.map((month) => ( ))}
+======= + +
+ {months.map((month) => ( + + ))} +
+
+>>>>>>> 48116cde (fix(types): fix TypeScript type errors) ); }); diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index ed74942d0..a5fa0593d 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -12,22 +12,22 @@ interface Props { const StatisticsView = (props: Props) => { const { statisticsData } = props; const { activityStats } = statisticsData; - const [selectedDate, setSelectedDate] = useState(null); const [visibleMonthString, setVisibleMonthString] = useState(dayjs().format("YYYY-MM")); const { getFiltersByFactor, addFilter, removeFilter } = useMemoFilterContext(); + const displayTimeFilter = getFiltersByFactor("displayTime").length > 0 ? getFiltersByFactor("displayTime") : []; + const selectedDate = useMemo(() => { + if (!displayTimeFilter.length) return null; + return new Date(displayTimeFilter[0].value); + }, [displayTimeFilter]); const maxCount = useMemo(() => { const counts = Object.values(activityStats); return Math.max(...counts, 1); }, [activityStats]); - const handleClick = (date: string) =>{ - const displayTimeFilters = getFiltersByFactor("displayTime"); - const isActive = displayTimeFilters.some((f: MemoFilter) => f.value === date); - - if (isActive) { + const handleDateCellClick = (date: string) => { + if (displayTimeFilter.length > 0 && displayTimeFilter[0].value === date) { removeFilter((f: MemoFilter) => f.factor === "displayTime" && f.value === date); - setSelectedDate(null); } else { // Remove all existing tag filters first, then add the new one removeFilter((f: MemoFilter) => f.factor === "displayTime"); @@ -35,16 +35,15 @@ const StatisticsView = (props: Props) => { factor: "displayTime", value: date, }); - setSelectedDate(new Date(date)); } - }; + }; return (
- +
); From 12505a0a930ad552b9d80c6c7d206eed6d2905d1 Mon Sep 17 00:00:00 2001 From: Yuhan Zhang Date: Mon, 2 Feb 2026 22:21:23 +0900 Subject: [PATCH 5/5] chore(lint): fix formatting and import order --- .../ActivityCalendar/MonthCalendar.tsx | 9 ++++---- .../ActivityCalendar/YearCalendar.tsx | 22 +++++++++---------- .../StatisticsView/StatisticsView.tsx | 10 +++++++-- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/web/src/components/ActivityCalendar/MonthCalendar.tsx b/web/src/components/ActivityCalendar/MonthCalendar.tsx index ecbc453f5..f16354fcf 100644 --- a/web/src/components/ActivityCalendar/MonthCalendar.tsx +++ b/web/src/components/ActivityCalendar/MonthCalendar.tsx @@ -1,3 +1,4 @@ +import dayjs from "dayjs"; import { memo, useMemo } from "react"; import { useInstance } from "@/contexts/InstanceContext"; import { cn } from "@/lib/utils"; @@ -7,7 +8,6 @@ import { useTodayDate, useWeekdayLabels } from "./hooks"; import type { CalendarSize, MonthCalendarProps } from "./types"; import { useCalendarMatrix } from "./useCalendar"; import { getTooltipText } from "./utils"; -import dayjs from "dayjs"; const GRID_STYLES: Record = { small: { gap: "gap-1.5", headerText: "text-[10px]" }, @@ -42,10 +42,9 @@ export const MonthCalendar = memo((props: MonthCalendarProps) => { const today = useTodayDate(); const weekDays = useWeekdayLabels(); const selectedDateFormatted = useMemo(() => { - if (!selectedDate) return null; - return dayjs(selectedDate).format("YYYY-MM-DD"); -}, [selectedDate]); - + if (!selectedDate) return null; + return dayjs(selectedDate).format("YYYY-MM-DD"); + }, [selectedDate]); const { weeks, weekDays: rotatedWeekDays } = useCalendarMatrix({ month, diff --git a/web/src/components/ActivityCalendar/YearCalendar.tsx b/web/src/components/ActivityCalendar/YearCalendar.tsx index 7228982ab..d03f10840 100644 --- a/web/src/components/ActivityCalendar/YearCalendar.tsx +++ b/web/src/components/ActivityCalendar/YearCalendar.tsx @@ -79,7 +79,15 @@ interface MonthCardProps { const MonthCard = memo(({ month, data, maxCount, onDateClick, selectedDate }: MonthCardProps) => (
{getMonthLabel(month)}
- +
)); MonthCard.displayName = "MonthCard"; @@ -105,21 +113,11 @@ export const YearCalendar = memo(({ selectedYear, data, onYearChange, onDateClic canGoNext={canGoNext} /> -<<<<<<< HEAD
{months.map((month) => ( - + ))}
-======= - -
- {months.map((month) => ( - - ))} -
-
->>>>>>> 48116cde (fix(types): fix TypeScript type errors) ); }); diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index a5fa0593d..f88d18174 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -1,9 +1,9 @@ import dayjs from "dayjs"; import { useMemo, useState } from "react"; import { MonthCalendar } from "@/components/ActivityCalendar"; +import { type MemoFilter, useMemoFilterContext } from "@/contexts/MemoFilterContext"; import type { StatisticsData } from "@/types/statistics"; import { MonthNavigator } from "./MonthNavigator"; -import { type MemoFilter, useMemoFilterContext } from "@/contexts/MemoFilterContext"; interface Props { statisticsData: StatisticsData; @@ -43,7 +43,13 @@ const StatisticsView = (props: Props) => {
- +
);