feat(web): user memory & detail ui upgrade
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:57:26
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:57:26
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-09 14:28:34
|
||||
*/
|
||||
/**
|
||||
* Neo4j User Memory Detail View
|
||||
@@ -10,12 +10,12 @@
|
||||
* Shows profile, interests, node statistics, relationships, and insights
|
||||
*/
|
||||
|
||||
import { type FC, useRef, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Row, Col, Space, Button } from 'antd'
|
||||
import { type FC, useRef, useState, type MouseEvent } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import { Flex, Popover } from 'antd'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import PageHeader from './components/PageHeader'
|
||||
import EndUserProfile from './components/EndUserProfile'
|
||||
import AboutMe from './components/AboutMe'
|
||||
import InterestDistribution from './components/InterestDistribution'
|
||||
@@ -28,21 +28,29 @@ import {
|
||||
} from '@/api/memory'
|
||||
|
||||
const Neo4j: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { id } = useParams()
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [name, setName] = useState('')
|
||||
const ref = useRef<EndUserProfileRef>(null)
|
||||
const memoryInsightRef = useRef<MemoryInsightRef>(null)
|
||||
const aboutMeRef = useRef<AboutMeRef>(null)
|
||||
const [selectedKey, setSelectedKey] = useState<string | null>(null)
|
||||
|
||||
/** Update displayed name */
|
||||
const handleNameUpdate = (data: { other_name?: string; id: string }) => {
|
||||
setName(data.other_name && data.other_name !== '' ? data.other_name : data.id)
|
||||
}
|
||||
|
||||
/** Navigate back */
|
||||
const goBack = () => {
|
||||
navigate('/user-memory', { replace: true })
|
||||
}
|
||||
|
||||
/** Refresh analytics data */
|
||||
const handleRefresh = () => {
|
||||
if (loading) return;
|
||||
setLoading(true)
|
||||
analyticsRefresh(id as string)
|
||||
.then(res => {
|
||||
@@ -59,43 +67,104 @@ const Neo4j: FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const onOpenChange = (e: MouseEvent, type: string) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setSelectedKey(type)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:h-full rb:w-full">
|
||||
<PageHeader
|
||||
name={name}
|
||||
operation={(
|
||||
<Button
|
||||
loading={loading}
|
||||
className="rb:group rb:h-7! rb:bg-transparent! rb:border-[#5B6167] rb:text-[#5B6167] rb:ml-3"
|
||||
onClick={handleRefresh}
|
||||
>
|
||||
{!loading && <div
|
||||
className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/refresh.svg')] rb:group-hover:bg-[url('@/assets/images/refresh_hover.svg')]"
|
||||
></div>}
|
||||
{t('common.refresh')}
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
<div className="rb:h-[calc(100vh-64px)] rb:overflow-y-auto rb:py-3 rb:px-4">
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
<EndUserProfile ref={ref} onDataLoaded={handleNameUpdate} />
|
||||
<AboutMe ref={aboutMeRef} />
|
||||
<InterestDistribution />
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
<NodeStatistics />
|
||||
<RelationshipNetwork />
|
||||
<MemoryInsight ref={memoryInsightRef} />
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div className="rb:h-screen rb:w-screen rb:p-3 rb:relative" onClick={() => setSelectedKey(null)}>
|
||||
<Flex className="rb:h-[calc(100vh-24px)]" gap={12}>
|
||||
<Flex gap={15} vertical justify="space-between" align="center" className="rb:h-full! rb:px-4! rb:pt-6! rb:pb-5! rb:bg-white rb:w-20 rb:rounded-xl">
|
||||
<Flex gap={15} vertical>
|
||||
<Popover
|
||||
content={t('userMemory.memoryWindow', { name: name })}
|
||||
placement="right"
|
||||
arrow={false}
|
||||
trigger="hover"
|
||||
>
|
||||
<div className="rb:mb-4.25! rb:size-12 rb:rounded-xl rb:bg-cover rb:bg-[url('@/assets/images/userMemory/logo.png')]"></div>
|
||||
</Popover>
|
||||
|
||||
<Flex
|
||||
align="center"
|
||||
justify="center"
|
||||
className={clsx("rb:cursor-pointer rb:size-12 rb:rounded-xl rb:group", {
|
||||
'rb:bg-[#155EEF]': selectedKey === 'userProfile',
|
||||
'rb:hover:bg-[#F0F3F8]': selectedKey !== 'userProfile',
|
||||
})}
|
||||
onClick={(e) => onOpenChange(e, 'userProfile')}
|
||||
>
|
||||
<div className={clsx("rb:size-6 rb:bg-cover", {
|
||||
"rb:bg-[url('@/assets/images/userMemory/userProfile.svg')]": selectedKey !== 'userProfile',
|
||||
"rb:bg-[url('@/assets/images/userMemory/userProfile_active.svg')]": selectedKey === 'userProfile'
|
||||
})}></div>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
align="center"
|
||||
justify="center"
|
||||
className={clsx("rb:cursor-pointer rb:size-12 rb:rounded-xl rb:group", {
|
||||
'rb:bg-[#155EEF]': selectedKey === 'aboutMe',
|
||||
'rb:hover:bg-[#F0F3F8]': selectedKey !== 'aboutMe',
|
||||
})}
|
||||
onClick={(e) => onOpenChange(e, 'aboutMe')}
|
||||
>
|
||||
<div className={clsx("rb:size-6 rb:bg-cover", {
|
||||
"rb:bg-[url('@/assets/images/userMemory/aboutMe.svg')]": selectedKey !== 'aboutMe',
|
||||
"rb:bg-[url('@/assets/images/userMemory/aboutMe_active.svg')]": selectedKey === 'aboutMe'
|
||||
})}></div>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
align="center"
|
||||
justify="center"
|
||||
className={clsx("rb:cursor-pointer rb:size-12 rb:rounded-xl rb:group", {
|
||||
'rb:bg-[#155EEF]': selectedKey === 'interestDistribution',
|
||||
'rb:hover:bg-[#F0F3F8]': selectedKey !== 'interestDistribution',
|
||||
})}
|
||||
onClick={(e) => onOpenChange(e, 'interestDistribution')}
|
||||
>
|
||||
<div className={clsx("rb:size-6 rb:bg-cover", {
|
||||
"rb:bg-[url('@/assets/images/userMemory/interestDistribution.svg')]": selectedKey !== 'interestDistribution',
|
||||
"rb:bg-[url('@/assets/images/userMemory/interestDistribution_active.svg')]": selectedKey === 'interestDistribution'
|
||||
})}></div>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
align="center"
|
||||
justify="center"
|
||||
className={clsx("rb:cursor-pointer rb:size-12 rb:rounded-xl rb:group", {
|
||||
'rb:bg-[#155EEF]': selectedKey === 'memoryInsight',
|
||||
'rb:hover:bg-[#F0F3F8]': selectedKey !== 'memoryInsight',
|
||||
})}
|
||||
onClick={(e) => onOpenChange(e, 'memoryInsight')}
|
||||
>
|
||||
<div className={clsx("rb:size-6 rb:bg-cover", {
|
||||
"rb:bg-[url('@/assets/images/userMemory/memoryInsight.svg')]": selectedKey !== 'memoryInsight',
|
||||
"rb:bg-[url('@/assets/images/userMemory/memoryInsight_active.svg')]": selectedKey === 'memoryInsight'
|
||||
})}></div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex vertical gap={24}>
|
||||
<div className={clsx("rb:cursor-pointer rb:size-6 rb:bg-cover rb:bg-[url('@/assets/images/userMemory/refresh.svg')]", {
|
||||
"rb:animate-spin": loading
|
||||
})} onClick={handleRefresh}></div>
|
||||
<div className="rb:cursor-pointer rb:size-6 rb:bg-cover rb:bg-[url('@/assets/images/userMemory/logout.svg')]" onClick={goBack}></div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex vertical className="rb:flex-1">
|
||||
<NodeStatistics />
|
||||
<RelationshipNetwork />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<EndUserProfile ref={ref} onDataLoaded={handleNameUpdate} className={selectedKey === 'userProfile' ? 'rb:block!' : 'rb:hidden!'} />
|
||||
<AboutMe ref={aboutMeRef} className={selectedKey === 'aboutMe' ? 'rb:block!' : 'rb:hidden!'} />
|
||||
<InterestDistribution className={selectedKey === 'interestDistribution' ? 'rb:block!' : 'rb:hidden!'} />
|
||||
<MemoryInsight ref={memoryInsightRef} className={selectedKey === 'memoryInsight' ? 'rb:block!' : 'rb:hidden!'} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user