feat(web): user memory detail
This commit is contained in:
250
web/src/views/UserMemoryDetail/pages/EpisodicDetail.tsx
Normal file
250
web/src/views/UserMemoryDetail/pages/EpisodicDetail.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Row, Col, Select, Form, Space, Skeleton, Input } from 'antd'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import {
|
||||
getEpisodicOverview,
|
||||
getEpisodicDetail,
|
||||
} from '@/api/memory'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import Tag from '@/components/Tag'
|
||||
import RbAlert from '@/components/RbAlert'
|
||||
import Empty from '@/components/Empty'
|
||||
|
||||
interface EpisodicMemory {
|
||||
id: string;
|
||||
title: string;
|
||||
type: string;
|
||||
created_at: number;
|
||||
}
|
||||
interface EpisodicOverviewData {
|
||||
total: number;
|
||||
total_all: number;
|
||||
episodic_memories: EpisodicMemory[]
|
||||
}
|
||||
interface EpisodicMemoryDetail {
|
||||
id: string;
|
||||
created_at: number;
|
||||
involved_objects: string[];
|
||||
episodic_type: string;
|
||||
content_records: string[];
|
||||
emotion: string;
|
||||
}
|
||||
|
||||
const TAG_COLORS: Record<string, "processing" | "success" | "warning" | "error" | "default"> = {
|
||||
conversation: "processing",
|
||||
project_work: "success",
|
||||
learning: "warning",
|
||||
decision: "warning",
|
||||
important_event: "error",
|
||||
}
|
||||
const BG_COLORS: Record<string, string> = {
|
||||
conversation: "rb:bg-[#155EEF]",
|
||||
project_work: "rb:bg-[#369F21]",
|
||||
learning: "rb:bg-[#FF5D34]",
|
||||
decision: "rb:bg-[#FF5D34]",
|
||||
important_event: "rb:bg-[#5B6167]",
|
||||
}
|
||||
|
||||
// Map display types to internal keys
|
||||
const getTypeKey = (type: string): string => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'Learning': 'learning',
|
||||
'Project/Work': 'project_work',
|
||||
'Conversation': 'conversation',
|
||||
'Decision': 'decision',
|
||||
'Important Event': 'important_event',
|
||||
}
|
||||
return typeMap[type] || type.toLowerCase().replace(/[^a-z0-9]/g, '_')
|
||||
}
|
||||
const EpisodicDetail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams()
|
||||
const [form] = Form.useForm()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [data, setData] = useState<EpisodicOverviewData>({} as EpisodicOverviewData)
|
||||
const values = Form.useWatch([], form)
|
||||
const [detailLoading, setDetailLoading] = useState<boolean>(false)
|
||||
const [detail, setDetail] = useState<EpisodicMemoryDetail | null>(null)
|
||||
const [selected, setSelected] = useState<EpisodicMemory | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return
|
||||
// getData()
|
||||
}, [id])
|
||||
|
||||
// 记忆洞察
|
||||
const getData = () => {
|
||||
if (!id) return
|
||||
setLoading(true)
|
||||
setSelected(null)
|
||||
setDetail(null)
|
||||
getEpisodicOverview({
|
||||
end_user_id: id,
|
||||
...values
|
||||
}).then((res) => {
|
||||
const response = res as EpisodicOverviewData
|
||||
setData(response)
|
||||
if (response.episodic_memories.length > 0) {
|
||||
setSelected(response.episodic_memories[0])
|
||||
}
|
||||
setLoading(false)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getData()
|
||||
}, [values])
|
||||
|
||||
useEffect(() => {
|
||||
getDetail()
|
||||
}, [selected])
|
||||
|
||||
const getDetail = () => {
|
||||
if (!selected || !selected.id) return
|
||||
|
||||
setDetailLoading(true)
|
||||
getEpisodicDetail({
|
||||
end_user_id: id as string,
|
||||
summary_id: selected.id
|
||||
})
|
||||
.then(res => {
|
||||
setDetail(res as EpisodicMemoryDetail)
|
||||
})
|
||||
.finally(() => {
|
||||
setDetailLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||
<div className="rb:flex rb:justify-between rb:items-center rb:text-[#FFFFFF] rb:leading-5 rb:h-30 rb:p-5 rb:bg-[url('@/assets/images/userMemory/shortTerm.png')] rb:bg-cover rb:mb-6">
|
||||
<div className="rb:max-w-135">{t('episodicDetail.title')}</div>
|
||||
|
||||
<div className="rb:grid rb:grid-cols-1 rb:gap-4">
|
||||
<div className="rb:bg-[rgba(255,255,255,0.2)] rb:rounded-lg rb:p-3.5 rb:text-[12px] rb:text-center">
|
||||
<div className="rb:text-[24px] rb:leading-8 rb:mb-1">{data.total_all ?? 0}</div>
|
||||
{t(`episodicDetail.total_all`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Form form={form} initialValues={{ time_range: 'all', episodic_type: 'all' }}>
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Form.Item name="time_range">
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[
|
||||
{ value: 'all', label: t('episodicDetail.all') },
|
||||
{ value: 'today', label: t('episodicDetail.today') },
|
||||
{ value: 'this_week', label: t('episodicDetail.this_week') },
|
||||
{ value: 'this_month', label: t('episodicDetail.this_month') },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="episodic_type">
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[
|
||||
{ value: 'all', label: t('episodicDetail.all') },
|
||||
{ value: 'conversation', label: t('episodicDetail.conversation') },
|
||||
{ value: 'project_work', label: t('episodicDetail.project_work') },
|
||||
{ value: 'learning', label: t('episodicDetail.learning') },
|
||||
{ value: 'decision', label: t('episodicDetail.decision') },
|
||||
{ value: 'important_event', label: t('episodicDetail.important_event') },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="title_keyword">
|
||||
<Input placeholder={t('episodicDetail.titleKeywordPlaceholder')} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<RbCard
|
||||
title={<>{t('episodicDetail.curResult')}<span className="rb:text-[#5B6167] rb:font-regular!"> ({data.total || 0}{t('episodicDetail.unix')})</span></>}
|
||||
headerType="borderless"
|
||||
>
|
||||
{loading
|
||||
? <Skeleton active />
|
||||
: !data.episodic_memories || data.episodic_memories.length === 0
|
||||
? <Empty />
|
||||
: (
|
||||
<Space size={8} direction="vertical" className="rb:w-full">
|
||||
{data.episodic_memories.map((vo, index) => (
|
||||
<div
|
||||
key={vo.id}
|
||||
className={clsx("rb:cursor-pointer rb:flex rb:items-center rb:bg-[#FFFFFF] rb:border rb:rounded-lg rb:px-3 rb:py-2 rb:leading-5", {
|
||||
'rb:border-[#DFE4ED] rb:shadow-[0px_2px_4px_0px_rgba(33,35,50,0.16)]': selected?.id !== vo.id,
|
||||
'rb:border-[#155EEF]': selected?.id === vo.id,
|
||||
})}
|
||||
onClick={() => setSelected(vo)}
|
||||
>
|
||||
<div className={clsx("rb:bg-[#369F21] rb:rounded-lg rb:text-[#FFFFFF] rb:size-6 rb:text-[12px] rb:leading-6 rb:text-center rb:mr-3", BG_COLORS[getTypeKey(vo.type)])}>{index + 1}</div>
|
||||
<div className="rb:flex-1">
|
||||
<div className="rb:flex rb:items-center rb:justify-between">{vo.title} <Tag color={TAG_COLORS[getTypeKey(vo.type)]}>{t(`episodicDetail.${getTypeKey(vo.type)}`)}</Tag></div>
|
||||
<div className="rb:text-[#5B6167] rb:text-[12px]">{formatDateTime(vo.created_at)}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
</RbCard>
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<RbCard
|
||||
title={selected?.title}
|
||||
headerType="borderless"
|
||||
>
|
||||
{detailLoading
|
||||
? <Skeleton active />
|
||||
: !selected || !detail
|
||||
? <Empty className="rb:mt-14" />
|
||||
: (
|
||||
<Space size={12} direction="vertical" className="rb:w-full">
|
||||
<div className="rb:bg-[#FFFFFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg rb:px-3 rb:py-2 rb:leading-5">
|
||||
<Row gutter={[12, 16]}>
|
||||
<Col span={12}>
|
||||
<div className="rb:text-[#5B6167]">{t('episodicDetail.created')}<br />{formatDateTime(detail.created_at)}</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className="rb:text-[#5B6167]">{t('episodicDetail.episodic_type')}<br />{detail.episodic_type}</div>
|
||||
</Col>
|
||||
{detail.involved_objects.length > 0 && <Col span={24}>
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-1">{t('episodicDetail.involved_objects')}</div>
|
||||
<Space size={8}>{detail.involved_objects.map((vo, index) => <Tag key={index}>{vo}</Tag>)}</Space>
|
||||
</Col>}
|
||||
</Row>
|
||||
</div>
|
||||
<div className="rb:bg-[#FFFFFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg rb:px-3 rb:py-2 rb:leading-5">
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-1">{t('episodicDetail.content_records')}</div>
|
||||
{detail.content_records.map((vo, index) => <div key={index} className="rb:text-[#5B6167] rb:leading-5">- {vo}</div>)}
|
||||
</div>
|
||||
<RbAlert>
|
||||
{t('episodicDetail.emotion')}: {t(`statementDetail.${detail.emotion}`)}
|
||||
</RbAlert>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
</RbCard>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default EpisodicDetail
|
||||
@@ -20,7 +20,7 @@ const statusTagColors: Record<string, 'success' | 'purple' | 'default' | 'warnin
|
||||
chunk: 'warning',
|
||||
}
|
||||
|
||||
const ForgetOverview: FC = () => {
|
||||
const ForgetDetail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
@@ -156,4 +156,4 @@ const ForgetOverview: FC = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ForgetOverview
|
||||
export default ForgetDetail
|
||||
14
web/src/views/UserMemoryDetail/pages/GraphDetail.tsx
Normal file
14
web/src/views/UserMemoryDetail/pages/GraphDetail.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Row, Col } from 'antd'
|
||||
|
||||
const GraphDetail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||
GraphDetail
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default GraphDetail
|
||||
34
web/src/views/UserMemoryDetail/pages/ImplicitDetail.tsx
Normal file
34
web/src/views/UserMemoryDetail/pages/ImplicitDetail.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Row, Col } from 'antd'
|
||||
|
||||
import Preferences from '../components/Preferences'
|
||||
import Portrait from '../components/Portrait'
|
||||
import InterestAreas from '../components/InterestAreas'
|
||||
import Habits from '../components/Habits'
|
||||
|
||||
const ImplicitDetail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||
<div className="rb:text-[#5B6167] rb:leading-5 rb:mt-3">{t('implicitDetail.title')}</div>
|
||||
|
||||
<Preferences />
|
||||
|
||||
<div className="rb:bg-[rgba(21,94,239,0.12)] rb:px-3 rb:py-2.5 rb:font-medium rb:leading-5 rb:mb-4 rb:mt-6 rb:rounded-md">{t('implicitDetail.portraitTitle')}</div>
|
||||
<div className="rb:my-3 rb:text-[#5B6167] rb:leading-5">{t('implicitDetail.portraitSubTitle')}</div>
|
||||
<Row gutter={[16, 16]} className="rb:mt-4">
|
||||
<Col span={12}>
|
||||
<Portrait />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<InterestAreas />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Habits />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ImplicitDetail
|
||||
32
web/src/views/UserMemoryDetail/pages/PerceptualDetail.tsx
Normal file
32
web/src/views/UserMemoryDetail/pages/PerceptualDetail.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Row, Col } from 'antd'
|
||||
|
||||
import PerceptualLastInfo from '../components/PerceptualLastInfo'
|
||||
import Timeline from '../components/Timeline'
|
||||
|
||||
const PerceptualDetail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||
<div className="rb:bg-[rgba(21,94,239,0.12)] rb:px-3 rb:py-2.5 rb:font-medium rb:leading-5 rb:mt-6 rb:rounded-md rb:mb-4">{t('perceptualDetail.lastInfo')}</div>
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={8}>
|
||||
<PerceptualLastInfo type="last_visual" />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<PerceptualLastInfo type="last_listen" />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<PerceptualLastInfo type="last_text" />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div className="rb:bg-[rgba(21,94,239,0.12)] rb:px-3 rb:py-2.5 rb:font-medium rb:leading-5 rb:mt-6 rb:rounded-md rb:mb-4">{t('perceptualDetail.timeLine')}</div>
|
||||
<Timeline />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default PerceptualDetail
|
||||
114
web/src/views/UserMemoryDetail/pages/ShortTermDetail.tsx
Normal file
114
web/src/views/UserMemoryDetail/pages/ShortTermDetail.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Space, Skeleton } from 'antd'
|
||||
import {
|
||||
getShortTerm,
|
||||
} from '@/api/memory'
|
||||
import Empty from '@/components/Empty'
|
||||
|
||||
interface ShortTermItem {
|
||||
retrieval: Array<{ query: string; retrieval: string[]; }>;
|
||||
message: string;
|
||||
answer: string;
|
||||
}
|
||||
interface LongTermItem {
|
||||
query: string;
|
||||
retrieval: string;
|
||||
}
|
||||
interface ShortData {
|
||||
short_term: ShortTermItem[];
|
||||
long_term: LongTermItem[];
|
||||
entity: number;
|
||||
retrieval_number: number;
|
||||
long_term_number: number;
|
||||
}
|
||||
const ShortTermDetail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [data, setData] = useState<ShortData>({} as ShortData)
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return
|
||||
getData()
|
||||
}, [id])
|
||||
|
||||
const getData = () => {
|
||||
if (!id) return
|
||||
setLoading(true)
|
||||
getShortTerm(id).then((res) => {
|
||||
const response = res as ShortData
|
||||
setData(response)
|
||||
setLoading(false)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||
<div className="rb:flex rb:justify-between rb:items-center rb:text-[#FFFFFF] rb:leading-5 rb:h-30 rb:p-5 rb:bg-[url('@/assets/images/userMemory/shortTerm.png')] rb:bg-cover">
|
||||
<div className="rb:max-w-135">{t('shortTermDetail.title')}</div>
|
||||
|
||||
<div className="rb:grid rb:grid-cols-3 rb:gap-4">
|
||||
{(['retrieval_number', 'entity', 'long_term_number'] as const).map(key => (
|
||||
<div key={key} className="rb:bg-[rgba(255,255,255,0.2)] rb:rounded-lg rb:p-3.5 rb:text-[12px] rb:text-center">
|
||||
<div className="rb:text-[24px] rb:leading-8 rb:mb-1">{(data as any)[key] ?? 0}</div>
|
||||
{t(`shortTermDetail.${key}`)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="rb:bg-[rgba(21,94,239,0.12)] rb:px-3 rb:py-2.5 rb:font-medium rb:leading-5 rb:mb-4 rb:mt-6 rb:rounded-md">{t('shortTermDetail.shortTermTitle')}</div>
|
||||
<div className="rb:my-3 rb:text-[#5B6167] rb:leading-5">{t('shortTermDetail.shortTermSubTitle')}</div>
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
{loading
|
||||
? <Skeleton active />
|
||||
: !data.short_term || data.short_term.length === 0
|
||||
? <Empty />
|
||||
:data.short_term?.map((vo, voIdx) => (
|
||||
<div key={voIdx} className="rb:leading-5 rb:shadow-[inset_3px_0px_0px_0px_#155EEF] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg rb:px-6 rb:py-3">
|
||||
<div className="rb:font-medium rb:text-[16px] rb:leading-5.5 rb:mb-3">{vo.message}</div>
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
{vo.retrieval.map((item, index) => (
|
||||
<div key={index} className="rb:bg-[#FFFFFF] rb:border rb:border-[#DFE4ED] rb:rounded-md rb:px-3 rb:py-2.5 rb:leading-5">
|
||||
<div className="rb:font-medium rb:mb-3">{t('shortTermDetail.query')}: {item.query}</div>
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-1">{t('shortTermDetail.answer')}:</div>
|
||||
{item.retrieval.length > 0 ? item.retrieval.map((retrieval, retrievalIdx) => (
|
||||
<div key={retrievalIdx} className="rb:text-[#5B6167] rb:text-[12px]">- {retrieval}</div>
|
||||
)) : <div className="rb:text-[#5B6167] rb:text-[12px]">{t('shortTermDetail.noAnswer')}</div>}
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-1">{t('shortTermDetail.answer')}</div>
|
||||
<div className="rb:bg-[#FFFFFF] rb:border rb:border-[#DFE4ED] rb:rounded-md rb:px-3 rb:py-2.5 rb:leading-5">{vo.answer}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</Space>
|
||||
|
||||
<div className="rb:bg-[rgba(21,94,239,0.12)] rb:px-3 rb:py-2.5 rb:font-medium rb:leading-5 rb:mb-4 rb:mt-6 rb:rounded-md">{t('shortTermDetail.longTermTitle')}</div>
|
||||
<div className="rb:my-3 rb:text-[#5B6167] rb:leading-5">{t('shortTermDetail.shortTermSubTitle')}</div>
|
||||
<Space size={16} direction="vertical" className="rb:w-full">
|
||||
{loading
|
||||
? <Skeleton active />
|
||||
: !data.long_term || data.long_term.length === 0
|
||||
? <Empty />
|
||||
: data.long_term?.map((vo, voIdx) => (
|
||||
<div key={voIdx} className="rb:leading-5 rb:shadow-[inset_3px_0px_0px_0px_#155EEF] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg rb:px-6 rb:py-3">
|
||||
<div className="rb:mb-1 rb:font-medium rb:leading-5.5">{vo.query}</div>
|
||||
<div className="rb:mt-1 rb:leading-5 rb:text-[#5B6167] rb:text-[12px]">{vo.retrieval}</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ShortTermDetail
|
||||
@@ -1,15 +1,23 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { type FC, useEffect, useState, useMemo } from 'react'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Dropdown } from 'antd'
|
||||
|
||||
import PageHeader from '../components/PageHeader'
|
||||
import StatementDetail from './StatementDetail'
|
||||
import ForgetDetail from './ForgetDetail'
|
||||
import ImplicitDetail from './ImplicitDetail'
|
||||
import ShortTermDetail from './ShortTermDetail'
|
||||
import PerceptualDetail from './PerceptualDetail'
|
||||
import EpisodicDetail from './EpisodicDetail'
|
||||
import {
|
||||
getEndUserProfile,
|
||||
} from '@/api/memory'
|
||||
|
||||
const Detail: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { id, type } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const [name, setName] = useState<string>('')
|
||||
useEffect(() => {
|
||||
if (!id) return
|
||||
@@ -23,17 +31,37 @@ const Detail: FC = () => {
|
||||
setName(response.other_name || response.id)
|
||||
})
|
||||
}
|
||||
const items = useMemo(() => {
|
||||
return ['PERCEPTUAL_MEMORY', 'WORKING_MEMORY', 'EMOTIONAL_MEMORY', 'SHORT_TERM_MEMORY', 'IMPLICIT_MEMORY', 'EPISODIC_MEMORY', 'EXPLICIT_MEMORY', 'FORGETTING_MANAGEMENT']
|
||||
.map(key => ({ key, label: t(`userMemory.${key}`) }))
|
||||
}, [t])
|
||||
const onClick = ({ key }: { key: string }) => {
|
||||
navigate(`/user-memory/detail/${id}/${key}`, { replace: true })
|
||||
}
|
||||
|
||||
console.log('Detail', name)
|
||||
return (
|
||||
<div className="rb:h-full rb:w-full">
|
||||
<PageHeader
|
||||
name={name}
|
||||
source="node"
|
||||
operation={
|
||||
<Dropdown menu={{ items, onClick, selectedKeys: type ? [type] : [] }}>
|
||||
<div className="rb:cursor-pointer rb:group rb:flex rb:items-center rb:gap-1">
|
||||
- {type ? t(`userMemory.${type}`) : ''}
|
||||
<div
|
||||
className="rb:w-5 rb:h-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/userMemory/up_border.svg')] rb:transform-[rotate(180deg)] rb:group-hover:transform-[rotate(0deg)]"
|
||||
></div>
|
||||
</div>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
<div className="rb:h-[calc(100vh-64px)] rb:overflow-y-auto rb:py-3 rb:px-4">
|
||||
{type === 'EMOTIONAL_MEMORY' && <StatementDetail />}
|
||||
{type === 'FORGETTING_MANAGEMENT' && <ForgetDetail />}
|
||||
{type === 'IMPLICIT_MEMORY' && <ImplicitDetail />}
|
||||
{type === 'SHORT_TERM_MEMORY' && <ShortTermDetail />}
|
||||
{type === 'PERCEPTUAL_MEMORY' && <PerceptualDetail />} {/** TODO */}
|
||||
{type === 'EPISODIC_MEMORY' && <EpisodicDetail />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user