Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: GareArc <garethcxy@dify.ai>
186 lines
6.6 KiB
TypeScript
186 lines
6.6 KiB
TypeScript
'use client'
|
|
import type { DeploymentEdition } from '@dify/contracts/api/console/system-features/types.gen'
|
|
import type { PeriodParams } from '@/app/components/app/overview/app-chart'
|
|
import type { I18nKeysByPrefix } from '@/types/i18n'
|
|
import { useSuspenseQuery } from '@tanstack/react-query'
|
|
import dayjs from 'dayjs'
|
|
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
|
|
import { useAtomValue } from 'jotai'
|
|
import * as React from 'react'
|
|
import { useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { TIME_PERIOD_MAPPING as LONG_TIME_PERIOD_MAPPING } from '@/app/components/app/log/filter'
|
|
import {
|
|
AvgResponseTime,
|
|
AvgSessionInteractions,
|
|
AvgUserInteractions,
|
|
ConversationsChart,
|
|
CostChart,
|
|
EndUsersChart,
|
|
MessagesChart,
|
|
TokenPerSecond,
|
|
UserSatisfactionRate,
|
|
WorkflowCostChart,
|
|
WorkflowDailyTerminalsChart,
|
|
WorkflowMessagesChart,
|
|
} from '@/app/components/app/overview/app-chart'
|
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
|
import { userProfileIdAtom } from '@/context/account-state'
|
|
import { useDocLink } from '@/context/i18n'
|
|
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
|
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
|
import { getAppACLCapabilities } from '@/utils/permission'
|
|
import LongTimeRangePicker from './long-time-range-picker'
|
|
import TimeRangePicker from './time-range-picker'
|
|
|
|
dayjs.extend(quarterOfYear)
|
|
|
|
const today = dayjs()
|
|
|
|
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
|
|
|
|
const TIME_PERIOD_MAPPING: { value: number; name: TimePeriodName }[] = [
|
|
{ value: 0, name: 'today' },
|
|
{ value: 7, name: 'last7days' },
|
|
{ value: 30, name: 'last30days' },
|
|
]
|
|
|
|
const queryDateFormat = 'YYYY-MM-DD HH:mm'
|
|
|
|
type IChartViewProps = {
|
|
appId: string
|
|
headerRight: React.ReactNode
|
|
}
|
|
|
|
export default function ChartView({ appId, headerRight }: IChartViewProps) {
|
|
const { data: deploymentEdition } = useSuspenseQuery({
|
|
...systemFeaturesQueryOptions(),
|
|
select: ({ deployment_edition }) => deployment_edition,
|
|
})
|
|
|
|
return (
|
|
<ChartViewContent
|
|
appId={appId}
|
|
headerRight={headerRight}
|
|
deploymentEdition={deploymentEdition}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function ChartViewContent({
|
|
appId,
|
|
headerRight,
|
|
deploymentEdition,
|
|
}: IChartViewProps & { deploymentEdition: DeploymentEdition }) {
|
|
const { t } = useTranslation()
|
|
const isCloudEdition = deploymentEdition === 'CLOUD'
|
|
const isNonCloudEdition = deploymentEdition === 'COMMUNITY' || deploymentEdition === 'ENTERPRISE'
|
|
const docLink = useDocLink()
|
|
const appDetail = useAppStore((state) => state.appDetail)
|
|
const currentUserId = useAtomValue(userProfileIdAtom)
|
|
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
|
|
const canMonitor = React.useMemo(
|
|
() =>
|
|
getAppACLCapabilities(appDetail?.permission_keys, {
|
|
currentUserId,
|
|
resourceMaintainer: appDetail?.maintainer,
|
|
workspacePermissionKeys,
|
|
}).canMonitor,
|
|
[appDetail?.maintainer, appDetail?.permission_keys, currentUserId, workspacePermissionKeys],
|
|
)
|
|
const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
|
|
const isWorkflow = appDetail?.mode === 'workflow'
|
|
const [period, setPeriod] = useState<PeriodParams>(() =>
|
|
isCloudEdition
|
|
? {
|
|
name: t(($) => $['filter.period.today'], { ns: 'appLog' }),
|
|
query: {
|
|
start: today.startOf('day').format(queryDateFormat),
|
|
end: today.endOf('day').format(queryDateFormat),
|
|
},
|
|
}
|
|
: {
|
|
name: t(($) => $['filter.period.last7days'], { ns: 'appLog' }),
|
|
query: {
|
|
start: today.subtract(7, 'day').startOf('day').format(queryDateFormat),
|
|
end: today.endOf('day').format(queryDateFormat),
|
|
},
|
|
},
|
|
)
|
|
|
|
if (!appDetail || !canMonitor) return null
|
|
|
|
return (
|
|
<div className="flex h-full min-h-0 flex-col">
|
|
<div className="h-[106px] shrink-0">
|
|
<div className="px-6 pt-3">
|
|
<div className="flex h-6 items-center">
|
|
<h1 className="title-2xl-semi-bold text-text-primary">
|
|
{t(($) => $['appMenus.overview'], { ns: 'common' })}
|
|
</h1>
|
|
</div>
|
|
<div className="mt-0.5 flex h-4 min-w-0 items-start gap-0.5 system-xs-regular text-text-tertiary">
|
|
<p className="min-w-0 truncate">
|
|
{t(($) => $['monitoring.description'], { ns: 'appLog' })}
|
|
</p>
|
|
<a
|
|
href={docLink('/use-dify/monitor/analysis')}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex shrink-0 items-center text-text-accent hover:underline"
|
|
>
|
|
<span>{t(($) => $['operation.learnMore'], { ns: 'common' })}</span>
|
|
<span className="i-ri-external-link-line size-3" aria-hidden="true" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div className="mt-1 flex h-10 items-center justify-between pr-10 pl-6">
|
|
{isCloudEdition && (
|
|
<TimeRangePicker
|
|
ranges={TIME_PERIOD_MAPPING}
|
|
onSelect={setPeriod}
|
|
queryDateFormat={queryDateFormat}
|
|
/>
|
|
)}
|
|
{isNonCloudEdition && (
|
|
<LongTimeRangePicker
|
|
periodMapping={LONG_TIME_PERIOD_MAPPING}
|
|
onSelect={setPeriod}
|
|
queryDateFormat={queryDateFormat}
|
|
/>
|
|
)}
|
|
|
|
{headerRight}
|
|
</div>
|
|
</div>
|
|
<div className="min-h-0 flex-1 overflow-y-auto px-6 pt-2 pb-6">
|
|
<div className="grid w-full grid-cols-1 gap-3 xl:grid-cols-2">
|
|
{!isWorkflow && (
|
|
<>
|
|
<ConversationsChart period={period} id={appId} />
|
|
<EndUsersChart period={period} id={appId} />
|
|
{isChatApp ? (
|
|
<AvgSessionInteractions period={period} id={appId} />
|
|
) : (
|
|
<AvgResponseTime period={period} id={appId} />
|
|
)}
|
|
<TokenPerSecond period={period} id={appId} />
|
|
<UserSatisfactionRate period={period} id={appId} />
|
|
<CostChart period={period} id={appId} />
|
|
{isChatApp && <MessagesChart period={period} id={appId} />}
|
|
</>
|
|
)}
|
|
{isWorkflow && (
|
|
<>
|
|
<WorkflowMessagesChart period={period} id={appId} />
|
|
<WorkflowDailyTerminalsChart period={period} id={appId} />
|
|
<WorkflowCostChart period={period} id={appId} />
|
|
<AvgUserInteractions period={period} id={appId} />
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|