From 2595e32f83c12f579ed9bcd9ea976c23f58d200b Mon Sep 17 00:00:00 2001 From: Johnny Date: Sat, 31 May 2025 11:14:47 +0800 Subject: [PATCH] chore: simplify date editor --- web/package.json | 1 - web/pnpm-lock.yaml | 41 --------------- web/src/components/DateTimeInput.tsx | 49 ++++++++++++++++++ web/src/components/MemoEditor/index.tsx | 50 +++++++++++-------- .../StatisticsView/MonthNavigator.tsx | 26 ++-------- 5 files changed, 83 insertions(+), 84 deletions(-) create mode 100644 web/src/components/DateTimeInput.tsx diff --git a/web/package.json b/web/package.json index e8de9c22f..6fb2bd3bd 100644 --- a/web/package.json +++ b/web/package.json @@ -32,7 +32,6 @@ "mobx": "^6.13.7", "mobx-react-lite": "^4.1.0", "react": "^18.3.1", - "react-datepicker": "^8.4.0", "react-dom": "^18.3.1", "react-force-graph-2d": "^1.27.1", "react-hot-toast": "^2.5.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 72a27888c..16b273c1c 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -80,9 +80,6 @@ importers: react: specifier: ^18.3.1 version: 18.3.1 - react-datepicker: - specifier: ^8.4.0 - version: 8.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) @@ -1021,12 +1018,6 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/react@0.27.9': - resolution: {integrity: sha512-Y0aCJBNtfVF6ikI1kVzA0WzSAhVBz79vFWOhvb5MLCRNODZ1ylGSLTuncchR7JsLyn9QzV6JD44DyZhhOtvpRw==} - peerDependencies: - react: '>=17.0.0' - react-dom: '>=17.0.0' - '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} @@ -2267,9 +2258,6 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -3306,12 +3294,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-datepicker@8.4.0: - resolution: {integrity: sha512-6nPDnj8vektWCIOy9ArS3avus9Ndsyz5XgFCJ7nBxXASSpBdSL6lG9jzNNmViPOAOPh6T5oJyGaXuMirBLECag==} - peerDependencies: - react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: @@ -3695,9 +3677,6 @@ packages: systemjs@6.15.1: resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - tailwind-merge@2.6.0: resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} @@ -4925,14 +4904,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@floating-ui/react@0.27.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@floating-ui/utils': 0.2.9 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - tabbable: 6.2.0 - '@floating-ui/utils@0.2.9': {} '@github/relative-time-element@4.4.8': {} @@ -6248,8 +6219,6 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - date-fns@4.1.0: {} - dayjs@1.11.13: {} debug@4.4.1: @@ -7407,14 +7376,6 @@ snapshots: queue-microtask@1.2.3: {} - react-datepicker@8.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@floating-ui/react': 0.27.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - clsx: 2.1.1 - date-fns: 4.1.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 @@ -7872,8 +7833,6 @@ snapshots: systemjs@6.15.1: {} - tabbable@6.2.0: {} - tailwind-merge@2.6.0: {} tailwindcss-animate@1.0.7(tailwindcss@3.4.17): diff --git a/web/src/components/DateTimeInput.tsx b/web/src/components/DateTimeInput.tsx new file mode 100644 index 000000000..33b45f2c0 --- /dev/null +++ b/web/src/components/DateTimeInput.tsx @@ -0,0 +1,49 @@ +import { isEqual } from "lodash-es"; +import toast from "react-hot-toast"; +import { cn } from "@/utils"; + +// Helper function to convert Date to local datetime string. +const toLocalDateTimeString = (date: Date | undefined): string => { + if (!date) return ""; + return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().slice(0, -1); +}; + +interface Props { + label: string; + value: Date | undefined; + originalValue: Date | undefined; + onChange: (date: Date) => void; +} + +const DateTimeInput: React.FC = ({ label, value, originalValue, onChange }) => { + return ( +
+ {label} + { + const inputValue = e.target.value; + if (inputValue) { + const date = new Date(inputValue); + if (!isNaN(date.getTime())) { + onChange(date); + } else { + toast.error("Invalid datetime format. Use format: 2023-12-31T23:59:59"); + e.target.value = toLocalDateTimeString(value); + } + } + }} + placeholder="YYYY-MM-DDTHH:mm:ss" + /> +
+ ); +}; + +export default DateTimeInput; diff --git a/web/src/components/MemoEditor/index.tsx b/web/src/components/MemoEditor/index.tsx index 98c6fa034..bafa7e410 100644 --- a/web/src/components/MemoEditor/index.tsx +++ b/web/src/components/MemoEditor/index.tsx @@ -3,7 +3,6 @@ import { isEqual } from "lodash-es"; import { LoaderIcon, SendIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import React, { useEffect, useMemo, useRef, useState } from "react"; -import DatePicker from "react-datepicker"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import useLocalStorage from "react-use/lib/useLocalStorage"; @@ -19,6 +18,7 @@ import { UserSetting } from "@/types/proto/api/v1/user_service"; import { cn } from "@/utils"; import { useTranslate } from "@/utils/i18n"; import { convertVisibilityFromString } from "@/utils/memo"; +import DateTimeInput from "../DateTimeInput"; import AddMemoRelationPopover from "./ActionButton/AddMemoRelationPopover"; import LocationSelector from "./ActionButton/LocationSelector"; import MarkdownMenu from "./ActionButton/MarkdownMenu"; @@ -30,7 +30,6 @@ import RelationListView from "./RelationListView"; import ResourceListView from "./ResourceListView"; import { handleEditorKeydownWithMarkdownShortcuts, hyperlinkHighlightedText } from "./handlers"; import { MemoEditorContext } from "./types"; -import "react-datepicker/dist/react-datepicker.css"; export interface Props { className?: string; @@ -71,7 +70,10 @@ const MemoEditor = observer((props: Props) => { isComposing: false, isDraggingFile: false, }); - const [displayTime, setDisplayTime] = useState(); + const [createTime, setCreateTime] = useState(); + const [updateTime, setUpdateTime] = useState(); + const [originalCreateTime, setOriginalCreateTime] = useState(); + const [originalUpdateTime, setOriginalUpdateTime] = useState(); const [hasContent, setHasContent] = useState(false); const [isVisibilitySelectorOpen, setIsVisibilitySelectorOpen] = useState(false); const editorRef = useRef(null); @@ -119,7 +121,10 @@ const MemoEditor = observer((props: Props) => { const memo = await memoStore.getOrFetchMemoByName(memoName); if (memo) { handleEditorFocus(); - setDisplayTime(memo.displayTime); + setCreateTime(memo.createTime); + setUpdateTime(memo.updateTime); + setOriginalCreateTime(memo.createTime); + setOriginalUpdateTime(memo.updateTime); setState((prevState) => ({ ...prevState, memoVisibility: memo.visibility, @@ -361,9 +366,13 @@ const MemoEditor = observer((props: Props) => { if (["content", "resources", "relations", "location"].some((key) => updateMask.has(key))) { updateMask.add("update_time"); } - if (!isEqual(displayTime, prevMemo.displayTime)) { - updateMask.add("display_time"); - memoPatch.displayTime = displayTime; + if (createTime && !isEqual(createTime, prevMemo.createTime)) { + updateMask.add("create_time"); + memoPatch.createTime = createTime; + } + if (updateTime && !isEqual(updateTime, prevMemo.updateTime)) { + updateMask.add("update_time"); + memoPatch.updateTime = updateTime; } if (updateMask.size === 0) { toast.error("No changes detected"); @@ -487,19 +496,6 @@ const MemoEditor = observer((props: Props) => { onCompositionStart={handleCompositionStart} onCompositionEnd={handleCompositionEnd} > - {memoName && displayTime && ( - date && setDisplayTime(date)} - showTimeSelect - showMonthDropdown - showYearDropdown - yearDropdownItemNumber={5} - dateFormatCalendar=" " - customInput={{displayTime.toLocaleString()}} - calendarClassName="ml-24 sm:ml-44" - /> - )} @@ -547,6 +543,20 @@ const MemoEditor = observer((props: Props) => { /> + + {/* Show memo metadata if memoName is provided */} + {memoName && ( +
+ {!isEqual(createTime, updateTime) && ( + + )} + +
+ ID: + {memoName} +
+
+ )} ); }); diff --git a/web/src/components/StatisticsView/MonthNavigator.tsx b/web/src/components/StatisticsView/MonthNavigator.tsx index d47d7561e..e8ba64260 100644 --- a/web/src/components/StatisticsView/MonthNavigator.tsx +++ b/web/src/components/StatisticsView/MonthNavigator.tsx @@ -1,9 +1,7 @@ import dayjs from "dayjs"; -import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; -import DatePicker from "react-datepicker"; +import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import i18n from "@/i18n"; import type { MonthNavigatorProps } from "@/types/statistics"; -import "react-datepicker/dist/react-datepicker.css"; export const MonthNavigator = ({ visibleMonth, onMonthChange }: MonthNavigatorProps) => { const currentMonth = dayjs(visibleMonth).toDate(); @@ -18,25 +16,9 @@ export const MonthNavigator = ({ visibleMonth, onMonthChange }: MonthNavigatorPr return (
-
- { - if (date) { - onMonthChange(dayjs(date).format("YYYY-MM")); - } - }} - dateFormat="MMMM yyyy" - showMonthYearPicker - showFullMonthYearPicker - customInput={ - - {currentMonth.toLocaleString(i18n.language, { year: "numeric", month: "long" })} - - } - popperPlacement="bottom-start" - calendarClassName="!bg-white !border-gray-200 !font-normal !shadow-lg" - /> +
+ + {currentMonth.toLocaleString(i18n.language, { year: "numeric", month: "long" })}