11import { create } from "@bufbuild/protobuf" ;
2- import { timestampDate } from "@bufbuild/protobuf/wkt" ;
2+ import { timestampFromDate } from "@bufbuild/protobuf/wkt" ;
33import { isEqual } from "lodash-es" ;
44import { CheckCircleIcon , Code2Icon , HashIcon , LinkIcon } from "lucide-react" ;
5+ import toast from "react-hot-toast" ;
6+ import EditableTimestamp from "@/components/EditableTimestamp" ;
7+ import { useUpdateMemo } from "@/hooks/useMemoQueries" ;
58import { cn } from "@/lib/utils" ;
69import { Memo , Memo_PropertySchema , MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb" ;
710import { useTranslate } from "@/utils/i18n" ;
@@ -18,84 +21,92 @@ const MemoDetailSidebar = ({ memo, className, parentPage }: Props) => {
1821 const property = create ( Memo_PropertySchema , memo . property || { } ) ;
1922 const hasSpecialProperty = property . hasLink || property . hasTaskList || property . hasCode || property . hasIncompleteTasks ;
2023 const shouldShowRelationGraph = memo . relations . filter ( ( r ) => r . type === MemoRelation_Type . REFERENCE ) . length > 0 ;
24+ const { mutate : updateMemo } = useUpdateMemo ( ) ;
25+
26+ const handleUpdateTimestamp = ( field : "createTime" | "updateTime" , date : Date ) => {
27+ const timestamp = timestampFromDate ( date ) ;
28+ updateMemo (
29+ {
30+ update : {
31+ name : memo . name ,
32+ [ field ] : timestamp ,
33+ } ,
34+ updateMask : [ field === "createTime" ? "create_time" : "update_time" ] ,
35+ } ,
36+ {
37+ onSuccess : ( ) => {
38+ toast . success ( "Updated successfully" ) ;
39+ } ,
40+ onError : ( error ) => {
41+ toast . error ( error . message ) ;
42+ } ,
43+ } ,
44+ ) ;
45+ } ;
2146
2247 return (
2348 < aside
2449 className = { cn ( "relative w-full h-auto max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start" , className ) }
2550 >
26- < div className = "flex flex-col justify-start items-start w-full px-1 gap-2 h-auto shrink-0 flex-nowrap hide-scrollbar" >
51+ < div className = "flex flex-col justify-start items-start w-full gap-4 h-auto shrink-0 flex-nowrap hide-scrollbar" >
2752 { shouldShowRelationGraph && (
28- < div className = "relative w-full h-36 border border-border rounded-lg bg-muted" >
53+ < div className = "relative w-full h-36 border border-border rounded-lg bg-muted overflow-hidden " >
2954 < MemoRelationForceGraph className = "w-full h-full" memo = { memo } parentPage = { parentPage } />
30- < div className = "absolute top-1 left-2 text-xs opacity- 60 font-mono gap-1 flex flex-row items-center" >
55+ < div className = "absolute top-2 left-2 text-xs text-muted-foreground/ 60 font-medium gap-1 flex flex-row items-center" >
3156 < span > { t ( "common.relations" ) } </ span >
3257 < span className = "text-xs opacity-60" > (Beta)</ span >
3358 </ div >
3459 </ div >
3560 ) }
36- < div className = "w-full flex flex-col" >
37- < p className = "flex flex-row justify-start items-center w-full gap-1 mb-1 text-sm leading-6 text-muted-foreground select-none" >
38- < span > { t ( "common.created-at" ) } </ span >
39- </ p >
40- < p className = "text-sm text-muted-foreground" > { memo . createTime && timestampDate ( memo . createTime ) . toLocaleString ( ) } </ p >
61+ < div className = "w-full space-y-1" >
62+ < p className = "text-xs font-medium text-muted-foreground/60 uppercase tracking-wide px-1" > { t ( "common.created-at" ) } </ p >
63+ < EditableTimestamp timestamp = { memo . createTime } onChange = { ( date ) => handleUpdateTimestamp ( "createTime" , date ) } />
4164 </ div >
4265 { ! isEqual ( memo . createTime , memo . updateTime ) && (
43- < div className = "w-full flex flex-col" >
44- < p className = "flex flex-row justify-start items-center w-full gap-1 mb-1 text-sm leading-6 text-muted-foreground select-none" >
45- < span > { t ( "common.last-updated-at" ) } </ span >
46- </ p >
47- < p className = "text-sm text-muted-foreground" > { memo . updateTime && timestampDate ( memo . updateTime ) . toLocaleString ( ) } </ p >
66+ < div className = "w-full space-y-1" >
67+ < p className = "text-xs font-medium text-muted-foreground/60 uppercase tracking-wide px-1" > { t ( "common.last-updated-at" ) } </ p >
68+ < EditableTimestamp timestamp = { memo . updateTime } onChange = { ( date ) => handleUpdateTimestamp ( "updateTime" , date ) } />
4869 </ div >
4970 ) }
5071 { hasSpecialProperty && (
51- < div className = "w-full flex flex-col" >
52- < p className = "flex flex-row justify-start items-center w-full gap-1 mb-1 text-sm leading-6 text-muted-foreground select-none" >
53- < span > { t ( "common.properties" ) } </ span >
54- </ p >
55- < div className = "w-full flex flex-row justify-start items-center gap-x-2 gap-y-1 flex-wrap text-muted-foreground" >
72+ < div className = "w-full space-y-2" >
73+ < p className = "text-xs font-medium text-muted-foreground/60 uppercase tracking-wide px-1" > { t ( "common.properties" ) } </ p >
74+ < div className = "w-full flex flex-row justify-start items-center gap-2 flex-wrap px-1" >
5675 { property . hasLink && (
57- < div className = "w-auto border border-border pl-1 pr-1.5 rounded-md flex justify-between items-center" >
58- < div className = "w-auto flex justify-start items-center mr-1" >
59- < LinkIcon className = "w-4 h-auto mr-1" />
60- < span className = "block text-sm" > { t ( "memo.links" ) } </ span >
61- </ div >
76+ < div className = "inline-flex items-center gap-1.5 px-2 py-1 bg-muted/50 border border-border/50 rounded-md text-xs text-muted-foreground" >
77+ < LinkIcon className = "w-3.5 h-3.5" />
78+ < span > { t ( "memo.links" ) } </ span >
6279 </ div >
6380 ) }
6481 { property . hasTaskList && (
65- < div className = "w-auto border border-border pl-1 pr-1.5 rounded-md flex justify-between items-center" >
66- < div className = "w-auto flex justify-start items-center mr-1" >
67- < CheckCircleIcon className = "w-4 h-auto mr-1" />
68- < span className = "block text-sm" > { t ( "memo.to-do" ) } </ span >
69- </ div >
82+ < div className = "inline-flex items-center gap-1.5 px-2 py-1 bg-muted/50 border border-border/50 rounded-md text-xs text-muted-foreground" >
83+ < CheckCircleIcon className = "w-3.5 h-3.5" />
84+ < span > { t ( "memo.to-do" ) } </ span >
7085 </ div >
7186 ) }
7287 { property . hasCode && (
73- < div className = "w-auto border border-border pl-1 pr-1.5 rounded-md flex justify-between items-center" >
74- < div className = "w-auto flex justify-start items-center mr-1" >
75- < Code2Icon className = "w-4 h-auto mr-1" />
76- < span className = "block text-sm" > { t ( "memo.code" ) } </ span >
77- </ div >
88+ < div className = "inline-flex items-center gap-1.5 px-2 py-1 bg-muted/50 border border-border/50 rounded-md text-xs text-muted-foreground" >
89+ < Code2Icon className = "w-3.5 h-3.5" />
90+ < span > { t ( "memo.code" ) } </ span >
7891 </ div >
7992 ) }
8093 </ div >
8194 </ div >
8295 ) }
8396 { memo . tags . length > 0 && (
84- < div className = "w-full" >
85- < div className = "flex flex-row justify-start items-center w-full gap-1 mb-1 text-sm leading-6 text-muted-foreground select-none " >
86- < span > { t ( "common.tags" ) } </ span >
87- < span className = "shrink-0 " > ({ memo . tags . length } )</ span >
97+ < div className = "w-full space-y-2 " >
98+ < div className = "flex flex-row justify-start items-center gap-1.5 px-1 " >
99+ < p className = "text-xs font-medium text-muted-foreground/60 uppercase tracking-wide" > { t ( "common.tags" ) } </ p >
100+ < span className = "text-xs text-muted-foreground/40 " > ({ memo . tags . length } )</ span >
88101 </ div >
89- < div className = "w-full flex flex-row justify-start items-center relative flex-wrap gap-x-2 gap-y -1" >
102+ < div className = "w-full flex flex-row justify-start items-center flex-wrap gap-1.5 px -1" >
90103 { memo . tags . map ( ( tag ) => (
91104 < div
92105 key = { tag }
93- className = "shrink-0 w-auto max-w-full text-sm rounded-md leading-6 flex flex-row justify-start items-center select-none hover:opacity-80 text-muted-foreground "
106+ className = "inline-flex items-center gap-1 px-2 py-0.5 bg-muted/50 border border-border/50 rounded-md text-xs text-muted-foreground hover:bg-muted transition-colors cursor-pointer group "
94107 >
95- < HashIcon className = "group-hover:hidden w-4 h-auto shrink-0 opacity-40" />
96- < div className = { cn ( "inline-flex flex-nowrap ml-0.5 gap-0.5 cursor-pointer max-w-[calc(100%-16px)]" ) } >
97- < span className = "truncate opacity-80" > { tag } </ span >
98- </ div >
108+ < HashIcon className = "w-3 h-3 opacity-40 group-hover:opacity-60 transition-opacity" />
109+ < span className = "opacity-80 group-hover:opacity-100 transition-opacity" > { tag } </ span >
99110 </ div >
100111 ) ) }
101112 </ div >
0 commit comments