| import type { FC } from 'react' | import type { FC } from 'react' | ||||
| import React, { useState } from 'react' | import React, { useState } from 'react' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import dayjs from 'dayjs' | |||||
| import EditItem, { EditItemType } from './edit-item' | import EditItem, { EditItemType } from './edit-item' | ||||
| import Drawer from '@/app/components/base/drawer-plus' | import Drawer from '@/app/components/base/drawer-plus' | ||||
| import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication' | import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication' | ||||
| import Toast from '@/app/components/base/toast' | import Toast from '@/app/components/base/toast' | ||||
| import { useProviderContext } from '@/context/provider-context' | import { useProviderContext } from '@/context/provider-context' | ||||
| import AnnotationFull from '@/app/components/billing/annotation-full' | import AnnotationFull from '@/app/components/billing/annotation-full' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type Props = { | type Props = { | ||||
| isShow: boolean | isShow: boolean | ||||
| onHide: () => void | onHide: () => void | ||||
| onlyEditResponse, | onlyEditResponse, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const { plan, enableBilling } = useProviderContext() | const { plan, enableBilling } = useProviderContext() | ||||
| const isAdd = !annotationId | const isAdd = !annotationId | ||||
| const isAnnotationFull = (enableBilling && plan.usage.annotatedResponse >= plan.total.annotatedResponse) | const isAnnotationFull = (enableBilling && plan.usage.annotatedResponse >= plan.total.annotatedResponse) | ||||
| <MessageCheckRemove /> | <MessageCheckRemove /> | ||||
| <div>{t('appAnnotation.editModal.removeThisCache')}</div> | <div>{t('appAnnotation.editModal.removeThisCache')}</div> | ||||
| </div> | </div> | ||||
| {createdAt && <div>{t('appAnnotation.editModal.createdAt')} {dayjs(createdAt * 1000).format('YYYY-MM-DD HH:mm')}</div>} | |||||
| {createdAt && <div>{t('appAnnotation.editModal.createdAt')} {formatTime(createdAt, t('appLog.dateTimeFormat') as string)}</div>} | |||||
| </div> | </div> | ||||
| ) | ) | ||||
| : undefined | : undefined | ||||
| } | } | ||||
| </div> | </div> | ||||
| } | } | ||||
| > | |||||
| </Drawer> | |||||
| /> | |||||
| <DeleteConfirmModal | <DeleteConfirmModal | ||||
| isShow={showModal} | isShow={showModal} | ||||
| onHide={() => setShowModal(false)} | onHide={() => setShowModal(false)} |
| import React from 'react' | import React from 'react' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import dayjs from 'dayjs' | |||||
| import { Edit02, Trash03 } from '../../base/icons/src/vender/line/general' | import { Edit02, Trash03 } from '../../base/icons/src/vender/line/general' | ||||
| import s from './style.module.css' | import s from './style.module.css' | ||||
| import type { AnnotationItem } from './type' | import type { AnnotationItem } from './type' | ||||
| import RemoveAnnotationConfirmModal from './remove-annotation-confirm-modal' | import RemoveAnnotationConfirmModal from './remove-annotation-confirm-modal' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type Props = { | type Props = { | ||||
| list: AnnotationItem[] | list: AnnotationItem[] | ||||
| onRemove, | onRemove, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const [currId, setCurrId] = React.useState<string | null>(null) | const [currId, setCurrId] = React.useState<string | null>(null) | ||||
| const [showConfirmDelete, setShowConfirmDelete] = React.useState(false) | const [showConfirmDelete, setShowConfirmDelete] = React.useState(false) | ||||
| return ( | return ( | ||||
| className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]' | className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]' | ||||
| title={item.answer} | title={item.answer} | ||||
| >{item.answer}</td> | >{item.answer}</td> | ||||
| <td>{dayjs(item.created_at * 1000).format('YYYY-MM-DD HH:mm')}</td> | |||||
| <td>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td> | |||||
| <td>{item.hit_count}</td> | <td>{item.hit_count}</td> | ||||
| <td className='w-[96px]' onClick={e => e.stopPropagation()}> | <td className='w-[96px]' onClick={e => e.stopPropagation()}> | ||||
| {/* Actions */} | {/* Actions */} |
| import React, { useEffect, useState } from 'react' | import React, { useEffect, useState } from 'react' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import dayjs from 'dayjs' | |||||
| import { Pagination } from 'react-headless-pagination' | import { Pagination } from 'react-headless-pagination' | ||||
| import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline' | import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline' | ||||
| import EditItem, { EditItemType } from '../edit-annotation-modal/edit-item' | import EditItem, { EditItemType } from '../edit-annotation-modal/edit-item' | ||||
| import TabSlider from '@/app/components/base/tab-slider-plain' | import TabSlider from '@/app/components/base/tab-slider-plain' | ||||
| import { fetchHitHistoryList } from '@/service/annotation' | import { fetchHitHistoryList } from '@/service/annotation' | ||||
| import { APP_PAGE_LIMIT } from '@/config' | import { APP_PAGE_LIMIT } from '@/config' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type Props = { | type Props = { | ||||
| appId: string | appId: string | ||||
| const [newQuestion, setNewQuery] = useState(question) | const [newQuestion, setNewQuery] = useState(question) | ||||
| const [newAnswer, setNewAnswer] = useState(answer) | const [newAnswer, setNewAnswer] = useState(answer) | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const [currPage, setCurrPage] = React.useState<number>(0) | const [currPage, setCurrPage] = React.useState<number>(0) | ||||
| const [total, setTotal] = useState(0) | const [total, setTotal] = useState(0) | ||||
| const [hitHistoryList, setHitHistoryList] = useState<HitHistoryItem[]>([]) | const [hitHistoryList, setHitHistoryList] = useState<HitHistoryItem[]>([]) | ||||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td> | <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td> | ||||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td> | <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td> | ||||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</td> | <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</td> | ||||
| <td className='whitespace-nowrap w-[140px]'>{t('appAnnotation.hitHistoryTable.time')}</td> | |||||
| <td className='whitespace-nowrap w-[160px]'>{t('appAnnotation.hitHistoryTable.time')}</td> | |||||
| </tr> | </tr> | ||||
| </thead> | </thead> | ||||
| <tbody className="text-gray-500"> | <tbody className="text-gray-500"> | ||||
| >{item.response}</td> | >{item.response}</td> | ||||
| <td>{item.source}</td> | <td>{item.source}</td> | ||||
| <td>{item.score ? item.score.toFixed(2) : '-'}</td> | <td>{item.score ? item.score.toFixed(2) : '-'}</td> | ||||
| <td>{dayjs(item.created_at * 1000).format('YYYY-MM-DD HH:mm')}</td> | |||||
| <td>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td> | |||||
| </tr> | </tr> | ||||
| ))} | ))} | ||||
| </tbody> | </tbody> | ||||
| <MessageCheckRemove /> | <MessageCheckRemove /> | ||||
| <div>{t('appAnnotation.editModal.removeThisCache')}</div> | <div>{t('appAnnotation.editModal.removeThisCache')}</div> | ||||
| </div> | </div> | ||||
| <div>{t('appAnnotation.editModal.createdAt')} {dayjs(createdAt * 1000).format('YYYY-MM-DD HH:mm')}</div> | |||||
| <div>{t('appAnnotation.editModal.createdAt')} {formatTime(createdAt, t('appLog.dateTimeFormat') as string)}</div> | |||||
| </div> | </div> | ||||
| ) | ) | ||||
| : undefined} | : undefined} | ||||
| > | |||||
| </Drawer> | |||||
| /> | |||||
| <DeleteConfirmModal | <DeleteConfirmModal | ||||
| isShow={showModal} | isShow={showModal} | ||||
| onHide={() => setShowModal(false)} | onHide={() => setShowModal(false)} |
| import { get } from 'lodash-es' | import { get } from 'lodash-es' | ||||
| import InfiniteScroll from 'react-infinite-scroll-component' | import InfiniteScroll from 'react-infinite-scroll-component' | ||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import utc from 'dayjs/plugin/utc' | |||||
| import timezone from 'dayjs/plugin/timezone' | |||||
| import { createContext, useContext } from 'use-context-selector' | import { createContext, useContext } from 'use-context-selector' | ||||
| import { useShallow } from 'zustand/react/shallow' | import { useShallow } from 'zustand/react/shallow' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import PromptLogModal from '@/app/components/base/prompt-log-modal' | import PromptLogModal from '@/app/components/base/prompt-log-modal' | ||||
| import MessageLogModal from '@/app/components/base/message-log-modal' | import MessageLogModal from '@/app/components/base/message-log-modal' | ||||
| import { useStore as useAppStore } from '@/app/components/app/store' | import { useStore as useAppStore } from '@/app/components/app/store' | ||||
| import { useAppContext } from '@/context/app-context' | |||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| dayjs.extend(utc) | |||||
| dayjs.extend(timezone) | |||||
| type IConversationList = { | type IConversationList = { | ||||
| logs?: ChatConversationsResponse | CompletionConversationsResponse | logs?: ChatConversationsResponse | CompletionConversationsResponse | ||||
| } | } | ||||
| // Format interface data for easy display | // Format interface data for easy display | ||||
| const getFormattedChatList = (messages: ChatMessage[], conversationId: string) => { | |||||
| const getFormattedChatList = (messages: ChatMessage[], conversationId: string, timezone: string, format: string) => { | |||||
| const newChatList: IChatItem[] = [] | const newChatList: IChatItem[] = [] | ||||
| messages.forEach((item: ChatMessage) => { | messages.forEach((item: ChatMessage) => { | ||||
| newChatList.push({ | newChatList.push({ | ||||
| query: item.query, | query: item.query, | ||||
| }, | }, | ||||
| more: { | more: { | ||||
| time: dayjs.unix(item.created_at).format('hh:mm A'), | |||||
| time: dayjs.unix(item.created_at).tz(timezone).format(format), | |||||
| tokens: item.answer_tokens + item.message_tokens, | tokens: item.answer_tokens + item.message_tokens, | ||||
| latency: item.provider_response_latency.toFixed(2), | latency: item.provider_response_latency.toFixed(2), | ||||
| }, | }, | ||||
| } | } | ||||
| function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionConversationFullDetailResponse>({ detail, onFeedback }: IDetailPanel<T>) { | function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionConversationFullDetailResponse>({ detail, onFeedback }: IDetailPanel<T>) { | ||||
| const { userProfile: { timezone } } = useAppContext() | |||||
| const { formatTime } = useTimestamp() | |||||
| const { onClose, appDetail } = useContext(DrawerContext) | const { onClose, appDetail } = useContext(DrawerContext) | ||||
| const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal, showMessageLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({ | const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal, showMessageLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({ | ||||
| currentLogItem: state.currentLogItem, | currentLogItem: state.currentLogItem, | ||||
| const varValues = messageRes.data[0].inputs | const varValues = messageRes.data[0].inputs | ||||
| setVarValues(varValues) | setVarValues(varValues) | ||||
| } | } | ||||
| const newItems = [...getFormattedChatList(messageRes.data, detail.id), ...items] | |||||
| const newItems = [...getFormattedChatList(messageRes.data, detail.id, timezone!, t('appLog.dateTimeFormat') as string), ...items] | |||||
| if (messageRes.has_more === false && detail?.model_config?.configs?.introduction) { | if (messageRes.has_more === false && detail?.model_config?.configs?.introduction) { | ||||
| newItems.unshift({ | newItems.unshift({ | ||||
| id: 'introduction', | id: 'introduction', | ||||
| <div className='border-b border-gray-100 py-4 px-6 flex items-center justify-between'> | <div className='border-b border-gray-100 py-4 px-6 flex items-center justify-between'> | ||||
| <div> | <div> | ||||
| <div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div> | <div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div> | ||||
| <div className='text-gray-700 text-[13px] leading-[18px]'>{isChatMode ? detail.id?.split('-').slice(-1)[0] : dayjs.unix(detail.created_at).format(t('appLog.dateTimeFormat') as string)}</div> | |||||
| <div className='text-gray-700 text-[13px] leading-[18px]'>{isChatMode ? detail.id?.split('-').slice(-1)[0] : formatTime(detail.created_at, t('appLog.dateTimeFormat') as string)}</div> | |||||
| </div> | </div> | ||||
| <div className='flex items-center flex-wrap gap-y-1 justify-end'> | <div className='flex items-center flex-wrap gap-y-1 justify-end'> | ||||
| {!isAdvanced && ( | {!isAdvanced && ( | ||||
| */ | */ | ||||
| const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => { | const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const media = useBreakpoints() | const media = useBreakpoints() | ||||
| const isMobile = media === MediaType.mobile | const isMobile = media === MediaType.mobile | ||||
| <Tooltip | <Tooltip | ||||
| htmlContent={ | htmlContent={ | ||||
| <span className='text-xs text-gray-500 inline-flex items-center'> | <span className='text-xs text-gray-500 inline-flex items-center'> | ||||
| <EditIconSolid className='mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${dayjs.unix(annotation?.created_at || dayjs().unix()).format('MM-DD hh:mm A')}`} | |||||
| <EditIconSolid className='mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`} | |||||
| </span> | </span> | ||||
| } | } | ||||
| className={(isHighlight && !isChatMode) ? '' : '!hidden'} | className={(isHighlight && !isChatMode) ? '' : '!hidden'} | ||||
| setCurrentConversation(log) | setCurrentConversation(log) | ||||
| }}> | }}> | ||||
| <td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td> | <td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td> | ||||
| <td className='w-[160px]'>{dayjs.unix(log.created_at).format(t('appLog.dateTimeFormat') as string)}</td> | |||||
| <td className='w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td> | |||||
| <td>{renderTdValue(endUser || defaultValue, !endUser)}</td> | <td>{renderTdValue(endUser || defaultValue, !endUser)}</td> | ||||
| <td style={{ maxWidth: isChatMode ? 300 : 200 }}> | <td style={{ maxWidth: isChatMode ? 300 : 200 }}> | ||||
| {renderTdValue(leftValue || t('appLog.table.empty.noChat'), !leftValue, isChatMode && log.annotated)} | {renderTdValue(leftValue || t('appLog.table.empty.noChat'), !leftValue, isChatMode && log.annotated)} |
| 'use client' | 'use client' | ||||
| import type { FC } from 'react' | import type { FC } from 'react' | ||||
| import React, { useState } from 'react' | import React, { useState } from 'react' | ||||
| import dayjs from 'dayjs' | |||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import s from './style.module.css' | import s from './style.module.css' | ||||
| import Drawer from '@/app/components/base/drawer' | import Drawer from '@/app/components/base/drawer' | ||||
| import Indicator from '@/app/components/header/indicator' | import Indicator from '@/app/components/header/indicator' | ||||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type ILogs = { | type ILogs = { | ||||
| logs?: WorkflowLogsResponse | logs?: WorkflowLogsResponse | ||||
| const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => { | const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const media = useBreakpoints() | const media = useBreakpoints() | ||||
| const isMobile = media === MediaType.mobile | const isMobile = media === MediaType.mobile | ||||
| setShowDrawer(true) | setShowDrawer(true) | ||||
| }}> | }}> | ||||
| <td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td> | <td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td> | ||||
| <td className='w-[160px]'>{dayjs.unix(log.created_at).format(t('appLog.dateTimeFormat') as string)}</td> | |||||
| <td className='w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td> | |||||
| <td>{statusTdRender(log.workflow_run.status)}</td> | <td>{statusTdRender(log.workflow_run.status)}</td> | ||||
| <td> | <td> | ||||
| <div className={cn( | <div className={cn( |
| 'use client' | 'use client' | ||||
| import type { FC } from 'react' | import type { FC } from 'react' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import dayjs from 'dayjs' | |||||
| import StatusPanel from '@/app/components/workflow/run/status' | import StatusPanel from '@/app/components/workflow/run/status' | ||||
| import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' | import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' | ||||
| import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' | import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type ResultPanelProps = { | type ResultPanelProps = { | ||||
| status: string | status: string | ||||
| inputs?: any | inputs?: any | ||||
| outputs?: any | outputs?: any | ||||
| created_by?: string | created_by?: string | ||||
| created_at?: string | |||||
| created_at: string | |||||
| agentMode?: string | agentMode?: string | ||||
| tools?: string[] | tools?: string[] | ||||
| iterations?: number | iterations?: number | ||||
| inputs, | inputs, | ||||
| outputs, | outputs, | ||||
| created_by, | created_by, | ||||
| created_at = 0, | |||||
| created_at, | |||||
| agentMode, | agentMode, | ||||
| tools, | tools, | ||||
| iterations, | iterations, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| return ( | return ( | ||||
| <div className='bg-white py-2'> | <div className='bg-white py-2'> | ||||
| <div className='flex'> | <div className='flex'> | ||||
| <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.startTime')}</div> | <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.startTime')}</div> | ||||
| <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'> | <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'> | ||||
| <span>{dayjs(created_at).format('YYYY-MM-DD hh:mm:ss')}</span> | |||||
| <span>{formatTime(Date.parse(created_at) / 1000, t('appLog.dateTimeFormat') as string)}</span> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className='flex'> | <div className='flex'> |
| } from 'react' | } from 'react' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import { produce, setAutoFreeze } from 'immer' | import { produce, setAutoFreeze } from 'immer' | ||||
| import dayjs from 'dayjs' | |||||
| import type { | import type { | ||||
| ChatConfig, | ChatConfig, | ||||
| ChatItem, | ChatItem, | ||||
| import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel' | import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel' | ||||
| import type { Annotation } from '@/models/log' | import type { Annotation } from '@/models/log' | ||||
| import { WorkflowRunningStatus } from '@/app/components/workflow/types' | import { WorkflowRunningStatus } from '@/app/components/workflow/types' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type GetAbortController = (abortController: AbortController) => void | type GetAbortController = (abortController: AbortController) => void | ||||
| type SendCallback = { | type SendCallback = { | ||||
| stopChat?: (taskId: string) => void, | stopChat?: (taskId: string) => void, | ||||
| ) => { | ) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const { notify } = useToastContext() | const { notify } = useToastContext() | ||||
| const connversationId = useRef('') | const connversationId = useRef('') | ||||
| const hasStopResponded = useRef(false) | const hasStopResponded = useRef(false) | ||||
| : []), | : []), | ||||
| ], | ], | ||||
| more: { | more: { | ||||
| time: dayjs.unix(newResponseItem.created_at).format('hh:mm A'), | |||||
| time: formatTime(newResponseItem.created_at, 'hh:mm A'), | |||||
| tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens, | tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens, | ||||
| latency: newResponseItem.provider_response_latency.toFixed(2), | latency: newResponseItem.provider_response_latency.toFixed(2), | ||||
| }, | }, | ||||
| promptVariablesConfig, | promptVariablesConfig, | ||||
| handleUpdateChatList, | handleUpdateChatList, | ||||
| handleResponding, | handleResponding, | ||||
| formatTime, | |||||
| ]) | ]) | ||||
| const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => { | const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => { |
| import { useDebounceFn } from 'ahooks' | import { useDebounceFn } from 'ahooks' | ||||
| import { ArrowDownIcon, TrashIcon } from '@heroicons/react/24/outline' | import { ArrowDownIcon, TrashIcon } from '@heroicons/react/24/outline' | ||||
| import { ExclamationCircleIcon } from '@heroicons/react/24/solid' | import { ExclamationCircleIcon } from '@heroicons/react/24/solid' | ||||
| import dayjs from 'dayjs' | |||||
| import { pick } from 'lodash-es' | import { pick } from 'lodash-es' | ||||
| import { useContext } from 'use-context-selector' | import { useContext } from 'use-context-selector' | ||||
| import { useRouter } from 'next/navigation' | import { useRouter } from 'next/navigation' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import dayjs from 'dayjs' | |||||
| import s from './style.module.css' | import s from './style.module.css' | ||||
| import Switch from '@/app/components/base/switch' | import Switch from '@/app/components/base/switch' | ||||
| import Divider from '@/app/components/base/divider' | import Divider from '@/app/components/base/divider' | ||||
| import { DataSourceType, type DocumentDisplayStatus, type SimpleDocumentDetail } from '@/models/datasets' | import { DataSourceType, type DocumentDisplayStatus, type SimpleDocumentDetail } from '@/models/datasets' | ||||
| import type { CommonResponse } from '@/models/common' | import type { CommonResponse } from '@/models/common' | ||||
| import { DotsHorizontal, HelpCircle } from '@/app/components/base/icons/src/vender/line/general' | import { DotsHorizontal, HelpCircle } from '@/app/components/base/icons/src/vender/line/general' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| export const SettingsIcon = ({ className }: SVGProps<SVGElement>) => { | export const SettingsIcon = ({ className }: SVGProps<SVGElement>) => { | ||||
| return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}> | return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}> | ||||
| */ | */ | ||||
| const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents = [], datasetId, onUpdate }) => { | const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents = [], datasetId, onUpdate }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const router = useRouter() | const router = useRouter() | ||||
| const [localDocs, setLocalDocs] = useState<LocalDoc[]>(documents) | const [localDocs, setLocalDocs] = useState<LocalDoc[]>(documents) | ||||
| const [enableSort, setEnableSort] = useState(false) | const [enableSort, setEnableSort] = useState(false) | ||||
| <td>{renderCount(doc.word_count)}</td> | <td>{renderCount(doc.word_count)}</td> | ||||
| <td>{renderCount(doc.hit_count)}</td> | <td>{renderCount(doc.hit_count)}</td> | ||||
| <td className='text-gray-500 text-[13px]'> | <td className='text-gray-500 text-[13px]'> | ||||
| {dayjs.unix(doc.created_at).format(t('datasetHitTesting.dateTimeFormat') as string)} | |||||
| {formatTime(doc.created_at, t('datasetHitTesting.dateTimeFormat') as string)} | |||||
| </td> | </td> | ||||
| <td> | <td> | ||||
| { | { |
| import useSWR from 'swr' | import useSWR from 'swr' | ||||
| import { omit } from 'lodash-es' | import { omit } from 'lodash-es' | ||||
| import cn from 'classnames' | import cn from 'classnames' | ||||
| import dayjs from 'dayjs' | |||||
| import { useBoolean } from 'ahooks' | import { useBoolean } from 'ahooks' | ||||
| import { useContext } from 'use-context-selector' | import { useContext } from 'use-context-selector' | ||||
| import SegmentCard from '../documents/detail/completed/SegmentCard' | import SegmentCard from '../documents/detail/completed/SegmentCard' | ||||
| import DatasetDetailContext from '@/context/dataset-detail' | import DatasetDetailContext from '@/context/dataset-detail' | ||||
| import type { RetrievalConfig } from '@/types/app' | import type { RetrievalConfig } from '@/types/app' | ||||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| const limit = 10 | const limit = 10 | ||||
| const HitTesting: FC<Props> = ({ datasetId }: Props) => { | const HitTesting: FC<Props> = ({ datasetId }: Props) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const media = useBreakpoints() | const media = useBreakpoints() | ||||
| const isMobile = media === MediaType.mobile | const isMobile = media === MediaType.mobile | ||||
| </td> | </td> | ||||
| <td className='max-w-xs group-hover:text-primary-600'>{record.content}</td> | <td className='max-w-xs group-hover:text-primary-600'>{record.content}</td> | ||||
| <td className='w-36'> | <td className='w-36'> | ||||
| {dayjs.unix(record.created_at).format(t('datasetHitTesting.dateTimeFormat') as string)} | |||||
| {formatTime(record.created_at, t('datasetHitTesting.dateTimeFormat') as string)} | |||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| })} | })} |
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid' | import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid' | ||||
| import useSWR, { useSWRConfig } from 'swr' | import useSWR, { useSWRConfig } from 'swr' | ||||
| import { useContext } from 'use-context-selector' | |||||
| import copy from 'copy-to-clipboard' | import copy from 'copy-to-clipboard' | ||||
| import SecretKeyGenerateModal from './secret-key-generate' | import SecretKeyGenerateModal from './secret-key-generate' | ||||
| import s from './style.module.css' | import s from './style.module.css' | ||||
| import Tooltip from '@/app/components/base/tooltip' | import Tooltip from '@/app/components/base/tooltip' | ||||
| import Loading from '@/app/components/base/loading' | import Loading from '@/app/components/base/loading' | ||||
| import Confirm from '@/app/components/base/confirm' | import Confirm from '@/app/components/base/confirm' | ||||
| import I18n from '@/context/i18n' | |||||
| import { LanguagesSupported } from '@/i18n/language' | |||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| import { useAppContext } from '@/context/app-context' | import { useAppContext } from '@/context/app-context' | ||||
| type ISecretKeyModalProps = { | type ISecretKeyModalProps = { | ||||
| onClose, | onClose, | ||||
| }: ISecretKeyModalProps) => { | }: ISecretKeyModalProps) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const { currentWorkspace, isCurrentWorkspaceManager } = useAppContext() | const { currentWorkspace, isCurrentWorkspaceManager } = useAppContext() | ||||
| const [showConfirmDelete, setShowConfirmDelete] = useState(false) | const [showConfirmDelete, setShowConfirmDelete] = useState(false) | ||||
| const [isVisible, setVisible] = useState(false) | const [isVisible, setVisible] = useState(false) | ||||
| const [delKeyID, setDelKeyId] = useState('') | const [delKeyID, setDelKeyId] = useState('') | ||||
| const { locale } = useContext(I18n) | |||||
| // const [isCopied, setIsCopied] = useState(false) | |||||
| const [copyValue, setCopyValue] = useState('') | const [copyValue, setCopyValue] = useState('') | ||||
| useEffect(() => { | useEffect(() => { | ||||
| return `${token.slice(0, 3)}...${token.slice(-20)}` | return `${token.slice(0, 3)}...${token.slice(-20)}` | ||||
| } | } | ||||
| const formatDate = (timestamp: string) => { | |||||
| if (locale === LanguagesSupported[0]) | |||||
| return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000) | |||||
| else | |||||
| return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000) | |||||
| } | |||||
| return ( | return ( | ||||
| <Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`${s.customModal} px-8 flex flex-col`}> | <Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`${s.customModal} px-8 flex flex-col`}> | ||||
| <XMarkIcon className={`w-6 h-6 absolute cursor-pointer text-gray-500 ${s.close}`} onClick={onClose} /> | <XMarkIcon className={`w-6 h-6 absolute cursor-pointer text-gray-500 ${s.close}`} onClick={onClose} /> | ||||
| <div className='flex flex-col flex-grow mt-4 overflow-hidden'> | <div className='flex flex-col flex-grow mt-4 overflow-hidden'> | ||||
| <div className='flex items-center flex-shrink-0 text-xs font-semibold text-gray-500 border-b border-solid h-9'> | <div className='flex items-center flex-shrink-0 text-xs font-semibold text-gray-500 border-b border-solid h-9'> | ||||
| <div className='flex-shrink-0 w-64 px-3'>{t('appApi.apiKeyModal.secretKey')}</div> | <div className='flex-shrink-0 w-64 px-3'>{t('appApi.apiKeyModal.secretKey')}</div> | ||||
| <div className='flex-shrink-0 px-3 w-28'>{t('appApi.apiKeyModal.created')}</div> | |||||
| <div className='flex-shrink-0 px-3 w-28'>{t('appApi.apiKeyModal.lastUsed')}</div> | |||||
| <div className='flex-shrink-0 px-3 w-[200px]'>{t('appApi.apiKeyModal.created')}</div> | |||||
| <div className='flex-shrink-0 px-3 w-[200px]'>{t('appApi.apiKeyModal.lastUsed')}</div> | |||||
| <div className='flex-grow px-3'></div> | <div className='flex-grow px-3'></div> | ||||
| </div> | </div> | ||||
| <div className='flex-grow overflow-auto'> | <div className='flex-grow overflow-auto'> | ||||
| {apiKeysList.data.map(api => ( | {apiKeysList.data.map(api => ( | ||||
| <div className='flex items-center text-sm font-normal text-gray-700 border-b border-solid h-9' key={api.id}> | <div className='flex items-center text-sm font-normal text-gray-700 border-b border-solid h-9' key={api.id}> | ||||
| <div className='flex-shrink-0 w-64 px-3 font-mono truncate'>{generateToken(api.token)}</div> | <div className='flex-shrink-0 w-64 px-3 font-mono truncate'>{generateToken(api.token)}</div> | ||||
| <div className='flex-shrink-0 px-3 truncate w-28'>{formatDate(api.created_at)}</div> | |||||
| {/* <div className='flex-shrink-0 px-3 truncate w-28'>{dayjs((+api.created_at) * 1000).format('MMM D, YYYY')}</div> */} | |||||
| {/* <div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? dayjs((+api.last_used_at) * 1000).format('MMM D, YYYY') : 'Never'}</div> */} | |||||
| <div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? formatDate(api.last_used_at) : t('appApi.never')}</div> | |||||
| <div className='flex-shrink-0 px-3 truncate w-[200px]'>{formatTime(Number(api.created_at), t('appLog.dateTimeFormat') as string)}</div> | |||||
| <div className='flex-shrink-0 px-3 truncate w-[200px]'>{api.last_used_at ? formatTime(Number(api.created_at), t('appLog.dateTimeFormat') as string) : t('appApi.never')}</div> | |||||
| <div className='flex flex-grow px-3'> | <div className='flex flex-grow px-3'> | ||||
| <Tooltip | <Tooltip | ||||
| selector={`key-${api.token}`} | selector={`key-${api.token}`} |
| .customModal { | .customModal { | ||||
| max-width: 40rem !important; | |||||
| max-width: 800px !important; | |||||
| max-height: calc(100vh - 80px); | max-height: calc(100vh - 80px); | ||||
| } | } | ||||
| import { memo } from 'react' | import { memo } from 'react' | ||||
| import dayjs from 'dayjs' | |||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import { useWorkflow } from '../hooks' | import { useWorkflow } from '../hooks' | ||||
| import { useStore } from '@/app/components/workflow/store' | import { useStore } from '@/app/components/workflow/store' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| const EditingTitle = () => { | const EditingTitle = () => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| const { formatTimeFromNow } = useWorkflow() | const { formatTimeFromNow } = useWorkflow() | ||||
| const draftUpdatedAt = useStore(state => state.draftUpdatedAt) | const draftUpdatedAt = useStore(state => state.draftUpdatedAt) | ||||
| const publishedAt = useStore(state => state.publishedAt) | const publishedAt = useStore(state => state.publishedAt) | ||||
| { | { | ||||
| !!draftUpdatedAt && ( | !!draftUpdatedAt && ( | ||||
| <> | <> | ||||
| {t('workflow.common.autoSaved')} {dayjs(draftUpdatedAt).format('HH:mm:ss')} | |||||
| {t('workflow.common.autoSaved')} {formatTime(draftUpdatedAt / 1000, 'HH:mm:ss')} | |||||
| </> | </> | ||||
| ) | ) | ||||
| } | } |
| 'use client' | 'use client' | ||||
| import type { FC } from 'react' | import type { FC } from 'react' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| // import cn from 'classnames' | |||||
| import dayjs from 'dayjs' | |||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| type Props = { | type Props = { | ||||
| status: string | status: string | ||||
| showSteps = true, | showSteps = true, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime } = useTimestamp() | |||||
| return ( | return ( | ||||
| <div className='relative'> | <div className='relative'> | ||||
| <div className='my-[5px] w-[72px] h-2 rounded-sm bg-[rgba(0,0,0,0.05)]'/> | <div className='my-[5px] w-[72px] h-2 rounded-sm bg-[rgba(0,0,0,0.05)]'/> | ||||
| )} | )} | ||||
| {status !== 'running' && ( | {status !== 'running' && ( | ||||
| <span>{dayjs(startTime * 1000).format('YYYY-MM-DD hh:mm:ss')}</span> | |||||
| <span>{formatTime(startTime, t('appLog.dateTimeFormat') as string)}</span> | |||||
| )} | )} | ||||
| </div> | </div> | ||||
| </div> | </div> |
| 'use client' | 'use client' | ||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import dayjs from 'dayjs' | |||||
| import { formatFileSize, formatNumber, formatTime } from '@/utils/format' | import { formatFileSize, formatNumber, formatTime } from '@/utils/format' | ||||
| import type { DocType } from '@/models/datasets' | import type { DocType } from '@/models/datasets' | ||||
| import useTimestamp from '@/hooks/use-timestamp' | |||||
| export type inputType = 'input' | 'select' | 'textarea' | export type inputType = 'input' | 'select' | 'textarea' | ||||
| export type metadataType = DocType | 'originInfo' | 'technicalParameters' | export type metadataType = DocType | 'originInfo' | 'technicalParameters' | ||||
| export const useMetadataMap = (): MetadataMap => { | export const useMetadataMap = (): MetadataMap => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const { formatTime: formatTimestamp } = useTimestamp() | |||||
| return { | return { | ||||
| book: { | book: { | ||||
| text: t('datasetDocuments.metadata.type.book'), | text: t('datasetDocuments.metadata.type.book'), | ||||
| }, | }, | ||||
| 'created_at': { | 'created_at': { | ||||
| label: t(`${fieldPrefix}.originInfo.uploadDate`), | label: t(`${fieldPrefix}.originInfo.uploadDate`), | ||||
| render: value => dayjs.unix(value).format(t('datasetDocuments.metadata.dateTimeFormat') as string), | |||||
| render: value => formatTimestamp(value, t('datasetDocuments.metadata.dateTimeFormat') as string), | |||||
| }, | }, | ||||
| 'completed_at': { | 'completed_at': { | ||||
| label: t(`${fieldPrefix}.originInfo.lastUpdateDate`), | label: t(`${fieldPrefix}.originInfo.lastUpdateDate`), | ||||
| render: value => dayjs.unix(value).format(t('datasetDocuments.metadata.dateTimeFormat') as string), | |||||
| render: value => formatTimestamp(value, t('datasetDocuments.metadata.dateTimeFormat') as string), | |||||
| }, | }, | ||||
| 'data_source_type': { | 'data_source_type': { | ||||
| label: t(`${fieldPrefix}.originInfo.source`), | label: t(`${fieldPrefix}.originInfo.source`), |
| 'use client' | |||||
| import { useCallback } from 'react' | |||||
| import dayjs from 'dayjs' | |||||
| import utc from 'dayjs/plugin/utc' | |||||
| import timezone from 'dayjs/plugin/timezone' | |||||
| import { useAppContext } from '@/context/app-context' | |||||
| dayjs.extend(utc) | |||||
| dayjs.extend(timezone) | |||||
| const useTimestamp = () => { | |||||
| const { userProfile: { timezone } } = useAppContext() | |||||
| const formatTime = useCallback((value: number, format: string) => { | |||||
| return dayjs.unix(value).tz(timezone).format(format) | |||||
| }, [timezone]) | |||||
| return { formatTime } | |||||
| } | |||||
| export default useTimestamp |