From b826e90276a244ad952a9e7f08eb007d7863e0eb Mon Sep 17 00:00:00 2001 From: Johnny Date: Mon, 29 Dec 2025 23:40:35 +0800 Subject: [PATCH] refactor: polish ActivityCalendar components with modern design --- .../ActivityCalendar/ActivityCalendar.tsx | 63 ------------- .../ActivityCalendar/CalendarCell.tsx | 16 +--- .../ActivityCalendar/CalendarHeader.tsx | 73 --------------- .../ActivityCalendar/CalendarPopover.tsx | 32 ------- ...actMonthCalendar.tsx => MonthCalendar.tsx} | 19 ++-- .../components/ActivityCalendar/MonthCard.tsx | 17 ---- .../ActivityCalendar/YearCalendar.tsx | 93 +++++++++++++++++++ .../components/ActivityCalendar/constants.ts | 20 ++-- .../ActivityCalendar/{shared.ts => hooks.ts} | 14 --- web/src/components/ActivityCalendar/index.ts | 30 +----- web/src/components/ActivityCalendar/types.ts | 11 +-- .../{useCalendarMatrix.ts => useCalendar.ts} | 8 ++ web/src/components/ActivityCalendar/utils.ts | 29 ++++-- .../components/MemoExplorer/MemoExplorer.tsx | 7 +- .../StatisticsView/MonthNavigator.tsx | 60 ++++++------ .../StatisticsView/StatisticsView.tsx | 11 +-- web/src/types/statistics.ts | 19 +--- 17 files changed, 193 insertions(+), 329 deletions(-) delete mode 100644 web/src/components/ActivityCalendar/ActivityCalendar.tsx delete mode 100644 web/src/components/ActivityCalendar/CalendarHeader.tsx delete mode 100644 web/src/components/ActivityCalendar/CalendarPopover.tsx rename web/src/components/ActivityCalendar/{CompactMonthCalendar.tsx => MonthCalendar.tsx} (70%) delete mode 100644 web/src/components/ActivityCalendar/MonthCard.tsx create mode 100644 web/src/components/ActivityCalendar/YearCalendar.tsx rename web/src/components/ActivityCalendar/{shared.ts => hooks.ts} (52%) rename web/src/components/ActivityCalendar/{useCalendarMatrix.ts => useCalendar.ts} (85%) diff --git a/web/src/components/ActivityCalendar/ActivityCalendar.tsx b/web/src/components/ActivityCalendar/ActivityCalendar.tsx deleted file mode 100644 index f1a642736..000000000 --- a/web/src/components/ActivityCalendar/ActivityCalendar.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import dayjs from "dayjs"; -import { memo, useMemo } from "react"; -import { TooltipProvider } from "@/components/ui/tooltip"; -import { useInstance } from "@/contexts/InstanceContext"; -import type { ActivityCalendarProps } from "@/types/statistics"; -import { useTranslate } from "@/utils/i18n"; -import { CalendarCell } from "./CalendarCell"; -import { getTooltipText, useTodayDate, useWeekdayLabels } from "./shared"; -import { useCalendarMatrix } from "./useCalendarMatrix"; - -export const ActivityCalendar = memo((props: ActivityCalendarProps) => { - const t = useTranslate(); - const { month, selectedDate, data, onClick } = props; - const { generalSetting } = useInstance(); - const weekStartDayOffset = generalSetting.weekStartDayOffset; - - const today = useTodayDate(); - const weekDaysRaw = useWeekdayLabels(); - const selectedDateFormatted = useMemo(() => dayjs(selectedDate).format("YYYY-MM-DD"), [selectedDate]); - - const { weeks, weekDays, maxCount } = useCalendarMatrix({ - month, - data, - weekDays: weekDaysRaw, - weekStartDayOffset, - today, - selectedDate: selectedDateFormatted, - }); - - return ( - -
-
- {weekDays.map((label, index) => ( -
- {label} -
- ))} -
- -
- {weeks.map((week, weekIndex) => - week.days.map((day, dayIndex) => { - const tooltipText = getTooltipText(day.count, day.date, t); - - return ( - - ); - }), - )} -
-
-
- ); -}); - -ActivityCalendar.displayName = "ActivityCalendar"; diff --git a/web/src/components/ActivityCalendar/CalendarCell.tsx b/web/src/components/ActivityCalendar/CalendarCell.tsx index 94084783a..48026e460 100644 --- a/web/src/components/ActivityCalendar/CalendarCell.tsx +++ b/web/src/components/ActivityCalendar/CalendarCell.tsx @@ -26,7 +26,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => { const smallExtraClasses = size === "small" ? `${SMALL_CELL_SIZE.dimensions} min-h-0` : ""; const baseClasses = cn( - "aspect-square w-full border flex items-center justify-center text-center transition-transform duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-1 focus-visible:ring-offset-background select-none", + "aspect-square w-full flex items-center justify-center text-center transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-2 select-none", sizeConfig.font, sizeConfig.borderRadius, smallExtraClasses, @@ -35,23 +35,17 @@ export const CalendarCell = memo((props: CalendarCellProps) => { const ariaLabel = day.isSelected ? `${tooltipText} (selected)` : tooltipText; if (!day.isCurrentMonth) { - return ( -
- {day.label} -
- ); + return
{day.label}
; } const intensityClass = getCellIntensityClass(day, maxCount); const buttonClasses = cn( baseClasses, - "border-transparent text-muted-foreground", - (day.isToday || day.isSelected) && "border-border", - day.isSelected && "font-medium", - day.isWeekend && "text-muted-foreground/80", intensityClass, - isInteractive ? "cursor-pointer hover:scale-105" : "cursor-default", + day.isToday && "ring-2 ring-primary/30 ring-offset-1 font-semibold z-10", + day.isSelected && "ring-2 ring-primary ring-offset-1 font-bold z-10", + isInteractive ? "cursor-pointer hover:scale-110 hover:shadow-md hover:z-20" : "cursor-default", ); const button = ( diff --git a/web/src/components/ActivityCalendar/CalendarHeader.tsx b/web/src/components/ActivityCalendar/CalendarHeader.tsx deleted file mode 100644 index 154adbbb8..000000000 --- a/web/src/components/ActivityCalendar/CalendarHeader.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; -import { useMemo } from "react"; -import { Button } from "@/components/ui/button"; -import { useTranslate } from "@/utils/i18n"; - -interface CalendarHeaderProps { - selectedYear: number; - onYearChange: (year: number) => void; - canGoPrev: boolean; - canGoNext: boolean; -} - -export const CalendarHeader = ({ selectedYear, onYearChange, canGoPrev, canGoNext }: CalendarHeaderProps) => { - const t = useTranslate(); - const currentYear = useMemo(() => new Date().getFullYear(), []); - const isCurrentYear = selectedYear === currentYear; - - const handlePrevYear = () => { - if (canGoPrev) { - onYearChange(selectedYear - 1); - } - }; - - const handleNextYear = () => { - if (canGoNext) { - onYearChange(selectedYear + 1); - } - }; - - const handleToday = () => { - onYearChange(currentYear); - }; - - return ( -
-

{selectedYear}

- -
- - - - - -
-
- ); -}; diff --git a/web/src/components/ActivityCalendar/CalendarPopover.tsx b/web/src/components/ActivityCalendar/CalendarPopover.tsx deleted file mode 100644 index 9abdb9bd3..000000000 --- a/web/src/components/ActivityCalendar/CalendarPopover.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useMemo } from "react"; -import { calculateYearMaxCount, filterDataByYear, generateMonthsForYear } from "@/components/ActivityCalendar"; -import { TooltipProvider } from "@/components/ui/tooltip"; -import { cn } from "@/lib/utils"; -import { CalendarHeader } from "./CalendarHeader"; -import { getMaxYear, MIN_YEAR } from "./constants"; -import { MonthCard } from "./MonthCard"; -import type { CalendarPopoverProps } from "./types"; - -export const CalendarPopover = ({ selectedYear, data, onYearChange, onDateClick, className }: CalendarPopoverProps) => { - const yearData = useMemo(() => filterDataByYear(data, selectedYear), [data, selectedYear]); - const months = useMemo(() => generateMonthsForYear(selectedYear), [selectedYear]); - const yearMaxCount = useMemo(() => calculateYearMaxCount(yearData), [yearData]); - const canGoPrev = selectedYear > MIN_YEAR; - const canGoNext = selectedYear < getMaxYear(); - - return ( -
- - - -
-
- {months.map((month) => ( - - ))} -
-
-
-
- ); -}; diff --git a/web/src/components/ActivityCalendar/CompactMonthCalendar.tsx b/web/src/components/ActivityCalendar/MonthCalendar.tsx similarity index 70% rename from web/src/components/ActivityCalendar/CompactMonthCalendar.tsx rename to web/src/components/ActivityCalendar/MonthCalendar.tsx index 9af08da4b..b1a9a973d 100644 --- a/web/src/components/ActivityCalendar/CompactMonthCalendar.tsx +++ b/web/src/components/ActivityCalendar/MonthCalendar.tsx @@ -4,12 +4,13 @@ import { cn } from "@/lib/utils"; import { useTranslate } from "@/utils/i18n"; import { CalendarCell } from "./CalendarCell"; import { DEFAULT_CELL_SIZE, SMALL_CELL_SIZE } from "./constants"; -import { getTooltipText, useTodayDate, useWeekdayLabels } from "./shared"; -import type { CompactMonthCalendarProps } from "./types"; -import { useCalendarMatrix } from "./useCalendarMatrix"; +import { useTodayDate, useWeekdayLabels } from "./hooks"; +import type { MonthCalendarProps } from "./types"; +import { useCalendarMatrix } from "./useCalendar"; +import { getTooltipText } from "./utils"; -export const CompactMonthCalendar = memo((props: CompactMonthCalendarProps) => { - const { month, data, maxCount, size = "default", onClick } = props; +export const MonthCalendar = memo((props: MonthCalendarProps) => { + const { month, data, maxCount, size = "default", onClick, className } = props; const t = useTranslate(); const { generalSetting } = useInstance(); @@ -30,10 +31,10 @@ export const CompactMonthCalendar = memo((props: CompactMonthCalendarProps) => { const sizeConfig = size === "small" ? SMALL_CELL_SIZE : DEFAULT_CELL_SIZE; return ( -
-
+
+
{rotatedWeekDays.map((label, index) => ( -
+
{label}
))} @@ -61,4 +62,4 @@ export const CompactMonthCalendar = memo((props: CompactMonthCalendarProps) => { ); }); -CompactMonthCalendar.displayName = "CompactMonthCalendar"; +MonthCalendar.displayName = "MonthCalendar"; diff --git a/web/src/components/ActivityCalendar/MonthCard.tsx b/web/src/components/ActivityCalendar/MonthCard.tsx deleted file mode 100644 index aaea282e1..000000000 --- a/web/src/components/ActivityCalendar/MonthCard.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { CompactMonthCalendar, getMonthLabel } from "@/components/ActivityCalendar"; -import { cn } from "@/lib/utils"; -import type { MonthCardProps } from "./types"; - -export const MonthCard = ({ month, data, maxCount, onClick, className }: MonthCardProps) => { - return ( -
-
{getMonthLabel(month)}
- -
- ); -}; diff --git a/web/src/components/ActivityCalendar/YearCalendar.tsx b/web/src/components/ActivityCalendar/YearCalendar.tsx new file mode 100644 index 000000000..aa875aa82 --- /dev/null +++ b/web/src/components/ActivityCalendar/YearCalendar.tsx @@ -0,0 +1,93 @@ +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; +import { useMemo } from "react"; +import { + calculateYearMaxCount, + filterDataByYear, + generateMonthsForYear, + getMonthLabel, + MonthCalendar, +} from "@/components/ActivityCalendar"; +import { Button } from "@/components/ui/button"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import { useTranslate } from "@/utils/i18n"; +import { getMaxYear, MIN_YEAR } from "./constants"; +import type { YearCalendarProps } from "./types"; + +export const YearCalendar = ({ selectedYear, data, onYearChange, onDateClick, className }: YearCalendarProps) => { + const t = useTranslate(); + const currentYear = useMemo(() => new Date().getFullYear(), []); + const yearData = useMemo(() => filterDataByYear(data, selectedYear), [data, selectedYear]); + const months = useMemo(() => generateMonthsForYear(selectedYear), [selectedYear]); + const yearMaxCount = useMemo(() => calculateYearMaxCount(yearData), [yearData]); + + const canGoPrev = selectedYear > MIN_YEAR; + const canGoNext = selectedYear < getMaxYear(); + const isCurrentYear = selectedYear === currentYear; + + const handlePrevYear = () => canGoPrev && onYearChange(selectedYear - 1); + const handleNextYear = () => canGoNext && onYearChange(selectedYear + 1); + const handleToday = () => onYearChange(currentYear); + + return ( +
+
+

{selectedYear}

+ +
+ + + + + +
+
+ + +
+
+ {months.map((month) => ( +
+
{getMonthLabel(month)}
+ +
+ ))} +
+
+
+
+ ); +}; diff --git a/web/src/components/ActivityCalendar/constants.ts b/web/src/components/ActivityCalendar/constants.ts index 1908dc73f..e5cab3f08 100644 --- a/web/src/components/ActivityCalendar/constants.ts +++ b/web/src/components/ActivityCalendar/constants.ts @@ -13,15 +13,23 @@ export const INTENSITY_THRESHOLDS = { MINIMAL: 0, } as const; +export const CELL_STYLES = { + HIGH: "bg-primary text-primary-foreground shadow-sm", + MEDIUM: "bg-primary/80 text-primary-foreground shadow-sm", + LOW: "bg-primary/60 text-primary-foreground shadow-sm", + MINIMAL: "bg-primary/40 text-foreground", + EMPTY: "bg-secondary/30 text-muted-foreground hover:bg-secondary/50", +} as const; + export const SMALL_CELL_SIZE = { - font: "text-[10px]", - dimensions: "max-w-6 max-h-6", - borderRadius: "rounded-sm", - gap: "gap-px", + font: "text-xs", + dimensions: "w-8 h-8 mx-auto", + borderRadius: "rounded-md", + gap: "gap-1", } as const; export const DEFAULT_CELL_SIZE = { font: "text-xs", - borderRadius: "rounded", - gap: "gap-0.5", + borderRadius: "rounded-md", + gap: "gap-1.5", } as const; diff --git a/web/src/components/ActivityCalendar/shared.ts b/web/src/components/ActivityCalendar/hooks.ts similarity index 52% rename from web/src/components/ActivityCalendar/shared.ts rename to web/src/components/ActivityCalendar/hooks.ts index 5b9aa5ef7..e3d2cfb79 100644 --- a/web/src/components/ActivityCalendar/shared.ts +++ b/web/src/components/ActivityCalendar/hooks.ts @@ -2,8 +2,6 @@ import dayjs from "dayjs"; import { useMemo } from "react"; import { useTranslate } from "@/utils/i18n"; -export type TranslateFunction = ReturnType; - export const useWeekdayLabels = () => { const t = useTranslate(); return useMemo(() => [t("days.sun"), t("days.mon"), t("days.tue"), t("days.wed"), t("days.thu"), t("days.fri"), t("days.sat")], [t]); @@ -12,15 +10,3 @@ export const useWeekdayLabels = () => { export const useTodayDate = () => { return dayjs().format("YYYY-MM-DD"); }; - -export const getTooltipText = (count: number, date: string, t: TranslateFunction): string => { - if (count === 0) { - return date; - } - - return t("memo.count-memos-in-date", { - count, - memos: count === 1 ? t("common.memo") : t("common.memos"), - date, - }).toLowerCase(); -}; diff --git a/web/src/components/ActivityCalendar/index.ts b/web/src/components/ActivityCalendar/index.ts index 121b21684..2e9454d46 100644 --- a/web/src/components/ActivityCalendar/index.ts +++ b/web/src/components/ActivityCalendar/index.ts @@ -1,26 +1,4 @@ -export { ActivityCalendar as default } from "./ActivityCalendar"; -export { CalendarCell, type CalendarCellProps } from "./CalendarCell"; -export { CalendarHeader } from "./CalendarHeader"; -export { CalendarPopover } from "./CalendarPopover"; -export { CompactMonthCalendar } from "./CompactMonthCalendar"; -export * from "./constants"; -export { MonthCard } from "./MonthCard"; -export { getTooltipText, type TranslateFunction, useTodayDate, useWeekdayLabels } from "./shared"; -export type { - CalendarDayCell, - CalendarDayRow, - CalendarMatrixResult, - CalendarPopoverProps, - CalendarSize, - CompactMonthCalendarProps, - MonthCardProps, -} from "./types"; -export { type UseCalendarMatrixParams, useCalendarMatrix } from "./useCalendarMatrix"; -export { - calculateYearMaxCount, - filterDataByYear, - generateMonthsForYear, - getCellIntensityClass, - getMonthLabel, - hasActivityData, -} from "./utils"; +export * from "./MonthCalendar"; +export * from "./types"; +export * from "./utils"; +export * from "./YearCalendar"; diff --git a/web/src/components/ActivityCalendar/types.ts b/web/src/components/ActivityCalendar/types.ts index aaf9dba16..00880fbfb 100644 --- a/web/src/components/ActivityCalendar/types.ts +++ b/web/src/components/ActivityCalendar/types.ts @@ -20,23 +20,16 @@ export interface CalendarMatrixResult { maxCount: number; } -export interface CompactMonthCalendarProps { +export interface MonthCalendarProps { month: string; data: Record; maxCount: number; size?: CalendarSize; onClick?: (date: string) => void; -} - -export interface MonthCardProps { - month: string; - data: Record; - maxCount: number; - onClick?: (date: string) => void; className?: string; } -export interface CalendarPopoverProps { +export interface YearCalendarProps { selectedYear: number; data: Record; onYearChange: (year: number) => void; diff --git a/web/src/components/ActivityCalendar/useCalendarMatrix.ts b/web/src/components/ActivityCalendar/useCalendar.ts similarity index 85% rename from web/src/components/ActivityCalendar/useCalendarMatrix.ts rename to web/src/components/ActivityCalendar/useCalendar.ts index 0c8dab832..783767e44 100644 --- a/web/src/components/ActivityCalendar/useCalendarMatrix.ts +++ b/web/src/components/ActivityCalendar/useCalendar.ts @@ -45,6 +45,9 @@ const calculateCalendarBoundaries = (monthStart: dayjs.Dayjs, weekStartDayOffset return { calendarStart, dayCount }; }; +/** + * Generates a matrix of calendar days for a given month, handling week alignment and data mapping. + */ export const useCalendarMatrix = ({ month, data, @@ -54,16 +57,20 @@ export const useCalendarMatrix = ({ selectedDate, }: UseCalendarMatrixParams): CalendarMatrixResult => { return useMemo(() => { + // Determine the start of the month and its formatted key (YYYY-MM) const monthStart = dayjs(month).startOf("month"); const monthKey = monthStart.format("YYYY-MM"); + // Rotate week labels based on the user's preferred start of the week const rotatedWeekDays = weekDays.slice(weekStartDayOffset).concat(weekDays.slice(0, weekStartDayOffset)); + // Calculate the start and end dates for the calendar grid to ensure full weeks const { calendarStart, dayCount } = calculateCalendarBoundaries(monthStart, weekStartDayOffset); const weeks: CalendarMatrixResult["weeks"] = []; let maxCount = 0; + // Iterate through each day in the calendar grid for (let index = 0; index < dayCount; index += 1) { const current = calendarStart.add(index, "day"); const weekIndex = Math.floor(index / DAYS_IN_WEEK); @@ -72,6 +79,7 @@ export const useCalendarMatrix = ({ weeks[weekIndex] = { days: [] }; } + // Create the day cell object with data and status flags const dayCell = createCalendarDayCell(current, monthKey, data, today, selectedDate); weeks[weekIndex].days.push(dayCell); maxCount = Math.max(maxCount, dayCell.count); diff --git a/web/src/components/ActivityCalendar/utils.ts b/web/src/components/ActivityCalendar/utils.ts index a5a20e089..0ce1029db 100644 --- a/web/src/components/ActivityCalendar/utils.ts +++ b/web/src/components/ActivityCalendar/utils.ts @@ -1,22 +1,25 @@ import dayjs from "dayjs"; import isSameOrAfter from "dayjs/plugin/isSameOrAfter"; import isSameOrBefore from "dayjs/plugin/isSameOrBefore"; -import { INTENSITY_THRESHOLDS, MIN_COUNT, MONTHS_IN_YEAR } from "./constants"; +import { useTranslate } from "@/utils/i18n"; +import { CELL_STYLES, INTENSITY_THRESHOLDS, MIN_COUNT, MONTHS_IN_YEAR } from "./constants"; import type { CalendarDayCell } from "./types"; dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); +export type TranslateFunction = ReturnType; + export const getCellIntensityClass = (day: CalendarDayCell, maxCount: number): string => { if (!day.isCurrentMonth || day.count === 0) { - return "bg-transparent"; + return CELL_STYLES.EMPTY; } const ratio = day.count / maxCount; - if (ratio > INTENSITY_THRESHOLDS.HIGH) return "bg-primary text-primary-foreground border-primary"; - if (ratio > INTENSITY_THRESHOLDS.MEDIUM) return "bg-primary/80 text-primary-foreground border-primary/90"; - if (ratio > INTENSITY_THRESHOLDS.LOW) return "bg-primary/60 text-primary-foreground border-primary/70"; - return "bg-primary/40 text-primary"; + if (ratio > INTENSITY_THRESHOLDS.HIGH) return CELL_STYLES.HIGH; + if (ratio > INTENSITY_THRESHOLDS.MEDIUM) return CELL_STYLES.MEDIUM; + if (ratio > INTENSITY_THRESHOLDS.LOW) return CELL_STYLES.LOW; + return CELL_STYLES.MINIMAL; }; export const generateMonthsForYear = (year: number): string[] => { @@ -32,7 +35,7 @@ export const calculateYearMaxCount = (data: Record): number => { }; export const getMonthLabel = (month: string): string => { - return dayjs(month).format("MMM YYYY"); + return dayjs(month).format("MMM"); }; export const filterDataByYear = (data: Record, year: number): Record => { @@ -55,3 +58,15 @@ export const filterDataByYear = (data: Record, year: number): Re export const hasActivityData = (data: Record): boolean => { return Object.values(data).some((count) => count > 0); }; + +export const getTooltipText = (count: number, date: string, t: TranslateFunction): string => { + if (count === 0) { + return date; + } + + return t("memo.count-memos-in-date", { + count, + memos: count === 1 ? t("common.memo") : t("common.memos"), + date, + }).toLowerCase(); +}; diff --git a/web/src/components/MemoExplorer/MemoExplorer.tsx b/web/src/components/MemoExplorer/MemoExplorer.tsx index 967d9cf23..056352349 100644 --- a/web/src/components/MemoExplorer/MemoExplorer.tsx +++ b/web/src/components/MemoExplorer/MemoExplorer.tsx @@ -13,7 +13,6 @@ export interface MemoExplorerFeatures { statistics?: boolean; shortcuts?: boolean; tags?: boolean; - statisticsContext?: MemoExplorerContext; } interface Props { @@ -32,7 +31,6 @@ const getDefaultFeatures = (context: MemoExplorerContext): MemoExplorerFeatures statistics: true, shortcuts: false, // Global explore doesn't use shortcuts tags: true, - statisticsContext: "explore", }; case "archived": return { @@ -40,7 +38,6 @@ const getDefaultFeatures = (context: MemoExplorerContext): MemoExplorerFeatures statistics: true, shortcuts: false, // Archived doesn't typically use shortcuts tags: true, - statisticsContext: "archived", }; case "profile": return { @@ -48,7 +45,6 @@ const getDefaultFeatures = (context: MemoExplorerContext): MemoExplorerFeatures statistics: true, shortcuts: false, // Profile view doesn't use shortcuts tags: true, - statisticsContext: "profile", }; case "home": default: @@ -57,7 +53,6 @@ const getDefaultFeatures = (context: MemoExplorerContext): MemoExplorerFeatures statistics: true, shortcuts: true, tags: true, - statisticsContext: "home", }; } }; @@ -81,7 +76,7 @@ const MemoExplorer = (props: Props) => { > {features.search && }
- {features.statistics && } + {features.statistics && } {features.shortcuts && currentUser && } {features.tags && }
diff --git a/web/src/components/StatisticsView/MonthNavigator.tsx b/web/src/components/StatisticsView/MonthNavigator.tsx index c1dc292b9..fe4eadb46 100644 --- a/web/src/components/StatisticsView/MonthNavigator.tsx +++ b/web/src/components/StatisticsView/MonthNavigator.tsx @@ -1,24 +1,17 @@ -import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; +import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import { useState } from "react"; -import { CalendarPopover } from "@/components/ActivityCalendar"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import useCurrentUser from "@/hooks/useCurrentUser"; -import { useFilteredMemoStats } from "@/hooks/useFilteredMemoStats"; +import { YearCalendar } from "@/components/ActivityCalendar"; +import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import i18n from "@/i18n"; import { addMonths, formatMonth, getMonthFromDate, getYearFromDate, setYearAndMonth } from "@/lib/calendar-utils"; import type { MonthNavigatorProps } from "@/types/statistics"; -export const MonthNavigator = ({ visibleMonth, onMonthChange }: MonthNavigatorProps) => { - const currentUser = useCurrentUser(); +export const MonthNavigator = ({ visibleMonth, onMonthChange, activityStats }: MonthNavigatorProps) => { const [isOpen, setIsOpen] = useState(false); const currentMonth = new Date(visibleMonth); const currentYear = getYearFromDate(visibleMonth); const currentMonthNum = getMonthFromDate(visibleMonth); - const { statistics } = useFilteredMemoStats({ - userName: currentUser?.name, - }); - const handlePrevMonth = () => { onMonthChange(addMonths(visibleMonth, -1)); }; @@ -37,28 +30,33 @@ export const MonthNavigator = ({ visibleMonth, onMonthChange }: MonthNavigatorPr }; return ( -
- - - +
+ + + + + + Select Month + + + +
+ -
diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index ca5558559..91db83014 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -1,14 +1,11 @@ import dayjs from "dayjs"; import { useMemo, useState } from "react"; -import { CompactMonthCalendar } from "@/components/ActivityCalendar"; +import { MonthCalendar } from "@/components/ActivityCalendar"; import { useDateFilterNavigation } from "@/hooks"; import type { StatisticsData } from "@/types/statistics"; import { MonthNavigator } from "./MonthNavigator"; -export type StatisticsViewContext = "home" | "explore" | "archived" | "profile"; - interface Props { - context?: StatisticsViewContext; statisticsData: StatisticsData; } @@ -24,11 +21,11 @@ const StatisticsView = (props: Props) => { }, [activityStats]); return ( -
- +
+
- +
); diff --git a/web/src/types/statistics.ts b/web/src/types/statistics.ts index 4e2ae2634..acb2330e4 100644 --- a/web/src/types/statistics.ts +++ b/web/src/types/statistics.ts @@ -1,14 +1,3 @@ -export interface ActivityData { - date: string; - count: number; -} - -export interface CalendarDay { - day: number; - isCurrentMonth: boolean; - date?: string; -} - export interface StatisticsViewProps { className?: string; } @@ -16,13 +5,7 @@ export interface StatisticsViewProps { export interface MonthNavigatorProps { visibleMonth: string; onMonthChange: (month: string) => void; -} - -export interface ActivityCalendarProps { - month: string; - selectedDate: string; - data: Record; - onClick?: (date: string) => void; + activityStats: Record; } export interface StatisticsData {