diff --git a/web/src/components/MemoContent/TaskListItem.tsx b/web/src/components/MemoContent/TaskListItem.tsx index eeebf1444..d90df7a6c 100644 --- a/web/src/components/MemoContent/TaskListItem.tsx +++ b/web/src/components/MemoContent/TaskListItem.tsx @@ -1,4 +1,5 @@ -import { useContext } from "react"; +import { useContext, useRef } from "react"; +import { Checkbox } from "@/components/ui/checkbox"; import { memoStore } from "@/store"; import { toggleTaskAtIndex } from "@/utils/markdown-manipulation"; import { MemoContentContext } from "./MemoContentContext"; @@ -20,19 +21,16 @@ interface TaskListItemProps extends React.InputHTMLAttributes export const TaskListItem: React.FC = ({ checked, ...props }) => { const context = useContext(MemoContentContext); + const checkboxRef = useRef(null); - const handleChange = async (e: React.ChangeEvent) => { - e.stopPropagation(); - + const handleChange = async (newChecked: boolean) => { // Don't update if readonly or no memo context if (context.readonly || !context.memoName) { return; } - const newChecked = e.target.checked; - // Find the task index by walking up the DOM - const listItem = e.target.closest("li.task-list-item"); + const listItem = checkboxRef.current?.closest("li.task-list-item"); if (!listItem) { return; } @@ -78,5 +76,7 @@ export const TaskListItem: React.FC = ({ checked, ...props }) // Override the disabled prop from remark-gfm (which defaults to true) // We want interactive checkboxes, only disabled when readonly - return ; + return ( + + ); }; diff --git a/web/src/components/StatisticsView/StatCard.tsx b/web/src/components/StatisticsView/StatCard.tsx deleted file mode 100644 index 48673a579..000000000 --- a/web/src/components/StatisticsView/StatCard.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { cloneElement, isValidElement } from "react"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import { cn } from "@/lib/utils"; -import type { StatCardProps } from "@/types/statistics"; - -export const StatCard = ({ icon, label, count, onClick, tooltip, className }: StatCardProps) => { - const iconNode = isValidElement(icon) - ? cloneElement(icon, { - className: cn("h-3.5 w-3.5", icon.props.className), - }) - : icon; - - const countNode = (() => { - if (typeof count === "number" || typeof count === "string") { - return {count}; - } - if (isValidElement(count)) { - return cloneElement(count, { - className: cn("text-foreground/80", count.props.className), - }); - } - return {count}; - })(); - - const button = ( - - ); - - if (!tooltip) { - return button; - } - - return ( - - - {button} - -

{tooltip}

-
-
-
- ); -}; diff --git a/web/src/components/StatisticsView/StatisticsView.tsx b/web/src/components/StatisticsView/StatisticsView.tsx index 6e3aa4b4c..2b167529b 100644 --- a/web/src/components/StatisticsView/StatisticsView.tsx +++ b/web/src/components/StatisticsView/StatisticsView.tsx @@ -1,17 +1,10 @@ import dayjs from "dayjs"; -import { CheckCircleIcon, Code2Icon, LinkIcon, ListTodoIcon, BookmarkIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { useState, useCallback } from "react"; -import { matchPath, useLocation } from "react-router-dom"; -import useCurrentUser from "@/hooks/useCurrentUser"; -import { Routes } from "@/router"; -import { userStore } from "@/store"; -import memoFilterStore, { FilterFactor } from "@/store/memoFilter"; +import memoFilterStore from "@/store/memoFilter"; import type { StatisticsData } from "@/types/statistics"; -import { useTranslate } from "@/utils/i18n"; import ActivityCalendar from "../ActivityCalendar"; import { MonthNavigator } from "./MonthNavigator"; -import { StatCard } from "./StatCard"; export type StatisticsViewContext = "home" | "explore" | "archived" | "profile"; @@ -31,13 +24,8 @@ interface Props { } const StatisticsView = observer((props: Props) => { - const { context = "home", statisticsData } = props; - const t = useTranslate(); - const location = useLocation(); - const currentUser = useCurrentUser(); - - const { memoTypeStats, activityStats } = statisticsData; - + const { statisticsData } = props; + const { activityStats } = statisticsData; const [selectedDate] = useState(new Date()); const [visibleMonthString, setVisibleMonthString] = useState(dayjs().format("YYYY-MM")); @@ -46,18 +34,6 @@ const StatisticsView = observer((props: Props) => { memoFilterStore.addFilter({ factor: "displayTime", value: date }); }, []); - const handleFilterClick = useCallback((factor: FilterFactor, value: string = "") => { - memoFilterStore.addFilter({ factor, value }); - }, []); - - const isRootPath = matchPath(Routes.ROOT, location.pathname); - const hasPinnedMemos = currentUser && (userStore.state.currentUserStats?.pinnedMemos || []).length > 0; - - // Determine if we should show the pinned stat card - // Only show on home page (root path) for the current user with pinned memos - // Don't show on explore page since it's global - const shouldShowPinned = context === "home" && isRootPath && hasPinnedMemos; - return (
@@ -70,49 +46,6 @@ const StatisticsView = observer((props: Props) => { onClick={handleCalendarClick} />
- -
- {shouldShowPinned && ( - } - label={t("common.pinned")} - count={userStore.state.currentUserStats!.pinnedMemos.length} - onClick={() => handleFilterClick("pinned")} - /> - )} - - } - label={t("memo.links")} - count={memoTypeStats.linkCount} - onClick={() => handleFilterClick("property.hasLink")} - /> - - 0 ? : } - label={t("memo.to-do")} - count={ - memoTypeStats.undoCount > 0 ? ( -
- {memoTypeStats.todoCount - memoTypeStats.undoCount} - / - {memoTypeStats.todoCount} -
- ) : ( - memoTypeStats.todoCount - ) - } - onClick={() => handleFilterClick("property.hasTaskList")} - tooltip={memoTypeStats.undoCount > 0 ? "Done / Total" : undefined} - /> - - } - label={t("memo.code")} - count={memoTypeStats.codeCount} - onClick={() => handleFilterClick("property.hasCode")} - /> -
); }); diff --git a/web/src/hooks/useFilteredMemoStats.ts b/web/src/hooks/useFilteredMemoStats.ts index 0576ac972..403274e49 100644 --- a/web/src/hooks/useFilteredMemoStats.ts +++ b/web/src/hooks/useFilteredMemoStats.ts @@ -3,7 +3,6 @@ import { countBy } from "lodash-es"; import { useEffect, useState } from "react"; import { memoServiceClient } from "@/grpcweb"; import { State } from "@/types/proto/api/v1/common"; -import { UserStats_MemoTypeStats } from "@/types/proto/api/v1/user_service"; import type { StatisticsData } from "@/types/statistics"; export interface FilteredMemoStats { @@ -47,7 +46,6 @@ export interface FilteredMemoStats { export const useFilteredMemoStats = (filter?: string, state: State = State.NORMAL, orderBy?: string): FilteredMemoStats => { const [data, setData] = useState({ statistics: { - memoTypeStats: UserStats_MemoTypeStats.fromPartial({}), activityStats: {}, }, tags: {}, @@ -69,7 +67,6 @@ export const useFilteredMemoStats = (filter?: string, state: State = State.NORMA }); // Compute statistics and tags from fetched memos - const memoTypeStats = UserStats_MemoTypeStats.fromPartial({}); const displayTimeList: Date[] = []; const tagCount: Record = {}; @@ -86,24 +83,6 @@ export const useFilteredMemoStats = (filter?: string, state: State = State.NORMA tagCount[tag] = (tagCount[tag] || 0) + 1; } } - - // Count memo properties - if (memo.property) { - if (memo.property.hasLink) { - memoTypeStats.linkCount += 1; - } - if (memo.property.hasTaskList) { - memoTypeStats.todoCount += 1; - // Check if there are undone tasks - const undoneMatches = memo.content.match(/- \[ \]/g); - if (undoneMatches && undoneMatches.length > 0) { - memoTypeStats.undoCount += 1; - } - } - if (memo.property.hasCode) { - memoTypeStats.codeCount += 1; - } - } } } @@ -111,7 +90,7 @@ export const useFilteredMemoStats = (filter?: string, state: State = State.NORMA const activityStats = countBy(displayTimeList.map((date) => dayjs(date).format("YYYY-MM-DD"))); setData({ - statistics: { memoTypeStats, activityStats }, + statistics: { activityStats }, tags: tagCount, loading: false, }); @@ -119,7 +98,6 @@ export const useFilteredMemoStats = (filter?: string, state: State = State.NORMA console.error("Failed to fetch memos for statistics:", error); setData({ statistics: { - memoTypeStats: UserStats_MemoTypeStats.fromPartial({}), activityStats: {}, }, tags: {}, diff --git a/web/src/types/statistics.ts b/web/src/types/statistics.ts index 87b08feec..4e2ae2634 100644 --- a/web/src/types/statistics.ts +++ b/web/src/types/statistics.ts @@ -1,5 +1,3 @@ -import { UserStats_MemoTypeStats } from "@/types/proto/api/v1/user_service"; - export interface ActivityData { date: string; count: number; @@ -11,19 +9,6 @@ export interface CalendarDay { date?: string; } -export interface StatCardData { - id: string; - icon: React.ComponentType<{ className?: string }>; - label: string; - count: number; - filter: { - factor: string; - value?: string; - }; - tooltip?: string; - visible?: boolean; -} - export interface StatisticsViewProps { className?: string; } @@ -40,16 +25,6 @@ export interface ActivityCalendarProps { onClick?: (date: string) => void; } -export interface StatCardProps { - icon: React.ReactNode; - label: string; - count: number | React.ReactNode; - onClick: () => void; - tooltip?: string; - className?: string; -} - export interface StatisticsData { - memoTypeStats: UserStats_MemoTypeStats; activityStats: Record; }