Merge pull request #454 from SuanmoSuanyangTechnology/fix/memory_web_zy
fix(web): memory use modal replace
This commit is contained in:
@@ -2276,6 +2276,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
|||||||
suggestions: 'Personalized Suggestions',
|
suggestions: 'Personalized Suggestions',
|
||||||
suggestionLoading: 'Your personalized suggestions are being generated',
|
suggestionLoading: 'Your personalized suggestions are being generated',
|
||||||
item: 'item',
|
item: 'item',
|
||||||
|
noData: 'Emotion suggestion data does not exist, please click the refresh button to initialize',
|
||||||
},
|
},
|
||||||
reflectionEngine: {
|
reflectionEngine: {
|
||||||
reflectionEngineConfig: 'Reflection Engine Configuration',
|
reflectionEngineConfig: 'Reflection Engine Configuration',
|
||||||
@@ -2523,7 +2524,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
|||||||
supporting_evidence: 'Preference Source',
|
supporting_evidence: 'Preference Source',
|
||||||
specific_examples: 'Source',
|
specific_examples: 'Source',
|
||||||
wordEmpty: 'Click on a node in the left chart to view preference details',
|
wordEmpty: 'Click on a node in the left chart to view preference details',
|
||||||
noData: 'Portrait data does not exist, please click the refresh button in the top right corner to initialize',
|
noData: 'Portrait data does not exist, please click the refresh button to initialize',
|
||||||
},
|
},
|
||||||
shortTermDetail: {
|
shortTermDetail: {
|
||||||
title: 'Short-term memory is the "workbench" of the AI system, connecting instant conversations with long-term knowledge bases. Through real-time capture, deep retrieval, intelligent extraction and filtering transformation, temporary unstructured information is converted into valuable long-term knowledge.',
|
title: 'Short-term memory is the "workbench" of the AI system, connecting instant conversations with long-term knowledge bases. Through real-time capture, deep retrieval, intelligent extraction and filtering transformation, temporary unstructured information is converted into valuable long-term knowledge.',
|
||||||
|
|||||||
@@ -2272,6 +2272,7 @@ export const zh = {
|
|||||||
suggestions: '个性化建议',
|
suggestions: '个性化建议',
|
||||||
suggestionLoading: '您的个性化建议正在生成中',
|
suggestionLoading: '您的个性化建议正在生成中',
|
||||||
item: '个',
|
item: '个',
|
||||||
|
noData: '情绪建议数据不存在,请点击刷新按钮进行初始化',
|
||||||
},
|
},
|
||||||
reflectionEngine: {
|
reflectionEngine: {
|
||||||
reflectionEngineConfig: '反思引擎配置',
|
reflectionEngineConfig: '反思引擎配置',
|
||||||
@@ -2519,7 +2520,7 @@ export const zh = {
|
|||||||
supporting_evidence: '偏好来源',
|
supporting_evidence: '偏好来源',
|
||||||
specific_examples: '来源',
|
specific_examples: '来源',
|
||||||
wordEmpty: '点击左侧图表中的节点查看偏好详情',
|
wordEmpty: '点击左侧图表中的节点查看偏好详情',
|
||||||
noData: '画像数据不存在,请点击右上角刷新进行初始化',
|
noData: '画像数据不存在,请点击刷新按钮进行初始化',
|
||||||
},
|
},
|
||||||
shortTermDetail: {
|
shortTermDetail: {
|
||||||
title: '短期记忆是AI系统的"工作台",连接即时对话与长期知识库。通过实时捕获、深度检索、智能提取和筛选转化,将临时的非结构化信息转化为有价值的长期知识。',
|
title: '短期记忆是AI系统的"工作台",连接即时对话与长期知识库。通过实时捕获、深度检索、智能提取和筛选转化,将临时的非结构化信息转化为有价值的长期知识。',
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 18:31:50
|
* @Date: 2026-02-03 18:31:50
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-02-03 18:31:50
|
* @Last Modified time: 2026-03-04 16:22:03
|
||||||
*/
|
*/
|
||||||
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react'
|
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { App } from 'antd'
|
||||||
|
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
import RbCard from '@/components/RbCard/Card'
|
import RbCard from '@/components/RbCard/Card'
|
||||||
@@ -20,6 +21,7 @@ import RbAlert from '@/components/RbAlert'
|
|||||||
* @property {Array} suggestions - List of suggestions with actionable steps
|
* @property {Array} suggestions - List of suggestions with actionable steps
|
||||||
*/
|
*/
|
||||||
interface Suggestions {
|
interface Suggestions {
|
||||||
|
exists?: boolean;
|
||||||
health_summary: string;
|
health_summary: string;
|
||||||
suggestions: Array<{
|
suggestions: Array<{
|
||||||
type: string;
|
type: string;
|
||||||
@@ -35,9 +37,10 @@ interface Suggestions {
|
|||||||
* Displays emotional health suggestions with actionable steps
|
* Displays emotional health suggestions with actionable steps
|
||||||
* Shows health summary and prioritized recommendations
|
* Shows health summary and prioritized recommendations
|
||||||
*/
|
*/
|
||||||
const Suggestions = forwardRef<{ handleRefresh: () => void; }>((_props, ref) => {
|
const Suggestions = forwardRef<{ handleRefresh: () => void; }, { refresh: () => void; }>(({ refresh }, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
|
const { modal } = App.useApp()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [suggestions, setSuggestions] = useState<Suggestions | null>(null)
|
const [suggestions, setSuggestions] = useState<Suggestions | null>(null)
|
||||||
|
|
||||||
@@ -52,7 +55,19 @@ const Suggestions = forwardRef<{ handleRefresh: () => void; }>((_props, ref) =>
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
getEmotionSuggestions(id)
|
getEmotionSuggestions(id)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setSuggestions(res as Suggestions)
|
const response = res as Suggestions
|
||||||
|
if (!response.exists && (!response.suggestions || !response.suggestions?.length)) {
|
||||||
|
modal.confirm({
|
||||||
|
title: t('statementDetail.noData'),
|
||||||
|
okText: t('common.refresh'),
|
||||||
|
cancelText: t('common.cancel'),
|
||||||
|
onOk: () => {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setSuggestions(res as Suggestions)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ZhaoYing
|
||||||
|
* @Date: 2026-01-08 19:46:02
|
||||||
|
* @Last Modified by: ZhaoYing
|
||||||
|
* @Last Modified time: 2026-03-04 16:26:55
|
||||||
|
*/
|
||||||
import { forwardRef, useImperativeHandle, useRef, useEffect } from 'react'
|
import { forwardRef, useImperativeHandle, useRef, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Row, Col, App } from 'antd'
|
import { Row, Col, App } from 'antd'
|
||||||
@@ -12,25 +18,40 @@ import {
|
|||||||
implicitCheckData,
|
implicitCheckData,
|
||||||
} from '@/api/memory'
|
} from '@/api/memory'
|
||||||
|
|
||||||
const ImplicitDetail = forwardRef<{ handleRefresh: () => void; }>((_props, ref) => {
|
/**
|
||||||
|
* ImplicitDetail Component - Displays user's implicit memory profile
|
||||||
|
* Shows unconscious preferences, personality traits, interests and habits
|
||||||
|
*/
|
||||||
|
const ImplicitDetail = forwardRef<{ handleRefresh: () => void; }, { refresh: () => void; }>(({
|
||||||
|
refresh
|
||||||
|
}, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { message } = App.useApp()
|
const { modal } = App.useApp()
|
||||||
const preferencesRef = useRef<{ handleRefresh: () => void; }>(null)
|
const preferencesRef = useRef<{ handleRefresh: () => void; }>(null)
|
||||||
const portraitRef = useRef<{ handleRefresh: () => void; }>(null)
|
const portraitRef = useRef<{ handleRefresh: () => void; }>(null)
|
||||||
const interestAreasRef = useRef<{ handleRefresh: () => void; }>(null)
|
const interestAreasRef = useRef<{ handleRefresh: () => void; }>(null)
|
||||||
const habitsRef = useRef<{ handleRefresh: () => void; }>(null)
|
const habitsRef = useRef<{ handleRefresh: () => void; }>(null)
|
||||||
|
|
||||||
|
// Check if implicit data exists, prompt user to initialize if not
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
implicitCheckData(id)
|
implicitCheckData(id)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!(res as { exists: boolean }).exists) {
|
if (!(res as { exists: boolean }).exists) {
|
||||||
message.warning(t('implicitDetail.noData'))
|
modal.confirm({
|
||||||
|
title: t('implicitDetail.noData'),
|
||||||
|
okText: t('common.refresh'),
|
||||||
|
cancelText: t('common.cancel'),
|
||||||
|
onOk: () => {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [id])
|
}, [id])
|
||||||
|
|
||||||
|
// Refresh all implicit memory components by regenerating profile
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ZhaoYing
|
||||||
|
* @Date: 2025-12-19 16:54:52
|
||||||
|
* @Last Modified by: ZhaoYing
|
||||||
|
* @Last Modified time: 2026-03-04 16:28:00
|
||||||
|
*/
|
||||||
import { forwardRef, useImperativeHandle, useRef } from 'react'
|
import { forwardRef, useImperativeHandle, useRef } from 'react'
|
||||||
import { Row, Col, Space } from 'antd';
|
import { Row, Col, Space } from 'antd';
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
@@ -9,9 +15,17 @@ import Suggestions from '../components/Suggestions'
|
|||||||
import { generateSuggestions } from '@/api/memory'
|
import { generateSuggestions } from '@/api/memory'
|
||||||
|
|
||||||
|
|
||||||
const StatementDetail = forwardRef((_props, ref) => {
|
/**
|
||||||
|
* StatementDetail - Displays emotional memory analysis for a user
|
||||||
|
* Shows word cloud, emotion tags, health index, and personalized suggestions
|
||||||
|
*/
|
||||||
|
const StatementDetail = forwardRef<{ handleRefresh: () => void },{ refresh: () => void; }>(({
|
||||||
|
refresh
|
||||||
|
}, ref) => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const suggestionsRef = useRef<{ handleRefresh: () => void; }>(null)
|
const suggestionsRef = useRef<{ handleRefresh: () => void; }>(null)
|
||||||
|
|
||||||
|
// Regenerate suggestions and refresh the Suggestions child component
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@@ -41,7 +55,7 @@ const StatementDetail = forwardRef((_props, ref) => {
|
|||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Suggestions ref={suggestionsRef} />
|
<Suggestions ref={suggestionsRef} refresh={refresh} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ZhaoYing
|
||||||
|
* @Date: 2026-01-07 20:37:34
|
||||||
|
* @Last Modified by: ZhaoYing
|
||||||
|
* @Last Modified time: 2026-03-04 16:27:14
|
||||||
|
*/
|
||||||
import { type FC, useEffect, useState, useMemo, useRef } from 'react'
|
import { type FC, useEffect, useState, useMemo, useRef } from 'react'
|
||||||
import { useParams, useNavigate } from 'react-router-dom'
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Dropdown, Button } from 'antd'
|
import { Dropdown, Button } from 'antd'
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
import PageHeader from '../components/PageHeader'
|
import PageHeader from '../components/PageHeader'
|
||||||
import StatementDetail from './StatementDetail'
|
import StatementDetail from './StatementDetail'
|
||||||
@@ -19,11 +24,16 @@ import {
|
|||||||
import refreshIcon from '@/assets/images/refresh_hover.svg'
|
import refreshIcon from '@/assets/images/refresh_hover.svg'
|
||||||
import GraphDetail from './GraphDetail'
|
import GraphDetail from './GraphDetail'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detail page for user memory - renders different memory type views
|
||||||
|
* based on the `type` route param
|
||||||
|
*/
|
||||||
const Detail: FC = () => {
|
const Detail: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { id, type } = useParams()
|
const { id, type } = useParams()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [name, setName] = useState<string>('')
|
const [name, setName] = useState<string>('')
|
||||||
|
// Refs for child components that support imperative refresh
|
||||||
const forgetDetailRef = useRef<{ handleRefresh: () => void }>(null)
|
const forgetDetailRef = useRef<{ handleRefresh: () => void }>(null)
|
||||||
const statementDetailRef = useRef<{ handleRefresh: () => void }>(null)
|
const statementDetailRef = useRef<{ handleRefresh: () => void }>(null)
|
||||||
const implicitDetailRef = useRef<{ handleRefresh: () => void }>(null)
|
const implicitDetailRef = useRef<{ handleRefresh: () => void }>(null)
|
||||||
@@ -33,6 +43,7 @@ const Detail: FC = () => {
|
|||||||
getData()
|
getData()
|
||||||
}, [id])
|
}, [id])
|
||||||
|
|
||||||
|
// Fetch end user profile to display the user's name in the header
|
||||||
const getData = () => {
|
const getData = () => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
getEndUserProfile(id).then((res) => {
|
getEndUserProfile(id).then((res) => {
|
||||||
@@ -40,15 +51,21 @@ const Detail: FC = () => {
|
|||||||
setName(response.other_name || response.id)
|
setName(response.other_name || response.id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build dropdown menu items for switching between memory types
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
return ['PERCEPTUAL_MEMORY', 'WORKING_MEMORY', 'EMOTIONAL_MEMORY', 'SHORT_TERM_MEMORY', 'IMPLICIT_MEMORY', 'EPISODIC_MEMORY', 'EXPLICIT_MEMORY', 'FORGET_MEMORY']
|
return ['PERCEPTUAL_MEMORY', 'WORKING_MEMORY', 'EMOTIONAL_MEMORY', 'SHORT_TERM_MEMORY', 'IMPLICIT_MEMORY', 'EPISODIC_MEMORY', 'EXPLICIT_MEMORY', 'FORGET_MEMORY']
|
||||||
.map(key => ({ key, label: t(`userMemory.${key}`) }))
|
.map(key => ({ key, label: t(`userMemory.${key}`) }))
|
||||||
}, [t])
|
}, [t])
|
||||||
|
|
||||||
|
// Navigate to the selected memory type detail page
|
||||||
const onClick = ({ key }: { key: string }) => {
|
const onClick = ({ key }: { key: string }) => {
|
||||||
navigate(`/user-memory/detail/${id}/${key}`, { replace: true })
|
navigate(`/user-memory/detail/${id}/${key}`, { replace: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
// Trigger refresh on the active memory type's child component
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
let response: any = null
|
let response: any = null
|
||||||
@@ -64,6 +81,7 @@ const Detail: FC = () => {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the child returns a Promise, wait for it before clearing loading state
|
||||||
if (response instanceof Promise) {
|
if (response instanceof Promise) {
|
||||||
response.finally(() => {
|
response.finally(() => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -99,9 +117,9 @@ const Detail: FC = () => {
|
|||||||
</Button>}
|
</Button>}
|
||||||
/>
|
/>
|
||||||
<div className="rb:h-[calc(100vh-64px)] rb:overflow-y-auto rb:py-3 rb:px-4">
|
<div className="rb:h-[calc(100vh-64px)] rb:overflow-y-auto rb:py-3 rb:px-4">
|
||||||
{type === 'EMOTIONAL_MEMORY' && <StatementDetail ref={statementDetailRef} />}
|
{type === 'EMOTIONAL_MEMORY' && <StatementDetail ref={statementDetailRef} refresh={handleRefresh} />}
|
||||||
{type === 'FORGET_MEMORY' && <ForgetDetail ref={forgetDetailRef} />}
|
{type === 'FORGET_MEMORY' && <ForgetDetail ref={forgetDetailRef} />}
|
||||||
{type === 'IMPLICIT_MEMORY' && <ImplicitDetail ref={implicitDetailRef} />}
|
{type === 'IMPLICIT_MEMORY' && <ImplicitDetail ref={implicitDetailRef} refresh={handleRefresh} />}
|
||||||
{type === 'SHORT_TERM_MEMORY' && <ShortTermDetail />}
|
{type === 'SHORT_TERM_MEMORY' && <ShortTermDetail />}
|
||||||
{type === 'PERCEPTUAL_MEMORY' && <PerceptualDetail />}
|
{type === 'PERCEPTUAL_MEMORY' && <PerceptualDetail />}
|
||||||
{type === 'EPISODIC_MEMORY' && <EpisodicDetail />}
|
{type === 'EPISODIC_MEMORY' && <EpisodicDetail />}
|
||||||
|
|||||||
Reference in New Issue
Block a user