Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions web/app/account/(commonLayout)/account-page/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client'
import { useState } from 'react'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import {
RiGraduationCapFill,
Expand All @@ -23,8 +22,9 @@ import PremiumBadge from '@/app/components/base/premium-badge'
import { useGlobalPublicStore } from '@/context/global-public-context'
import EmailChangeModal from './email-change-modal'
import { validPassword } from '@/config'
import { fetchAppList } from '@/service/apps'

import type { App } from '@/types/app'
import { useAppList } from '@/service/use-apps'

const titleClassName = `
system-sm-semibold text-text-secondary
Expand All @@ -36,7 +36,7 @@ const descriptionClassName = `
export default function AccountPage() {
const { t } = useTranslation()
const { systemFeatures } = useGlobalPublicStore()
const { data: appList } = useSWR({ url: '/apps', params: { page: 1, limit: 100, name: '' } }, fetchAppList)
const { data: appList } = useAppList({ page: 1, limit: 100, name: '' })
const apps = appList?.data || []
const { mutateUserProfile, userProfile } = useAppContext()
const { isEducationAccount } = useProviderContext()
Expand Down
64 changes: 38 additions & 26 deletions web/app/components/app/overview/app-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { FC } from 'react'
import React from 'react'
import ReactECharts from 'echarts-for-react'
import type { EChartsOption } from 'echarts'
import useSWR from 'swr'
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import { get } from 'lodash-es'
Expand All @@ -13,7 +12,20 @@ import { formatNumber } from '@/utils/format'
import Basic from '@/app/components/app-sidebar/basic'
import Loading from '@/app/components/base/loading'
import type { AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppTokenCostsResponse } from '@/models/app'
import { getAppDailyConversations, getAppDailyEndUsers, getAppDailyMessages, getAppStatistics, getAppTokenCosts, getWorkflowDailyConversations } from '@/service/apps'
import {
useAppAverageResponseTime,
useAppAverageSessionInteractions,
useAppDailyConversations,
useAppDailyEndUsers,
useAppDailyMessages,
useAppSatisfactionRate,
useAppTokenCosts,
useAppTokensPerSecond,
useWorkflowAverageInteractions,
useWorkflowDailyConversations,
useWorkflowDailyTerminals,
useWorkflowTokenCosts,
} from '@/service/use-apps'
const valueFormatter = (v: string | number) => v

const COLOR_TYPE_MAP = {
Expand Down Expand Up @@ -272,8 +284,8 @@ const getDefaultChartData = ({ start, end, key = 'count' }: { start: string; end

export const MessagesChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/statistics/daily-messages`, params: period.query }, getAppDailyMessages)
if (!response)
const { data: response, isLoading } = useAppDailyMessages(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -286,8 +298,8 @@ export const MessagesChart: FC<IBizChartProps> = ({ id, period }) => {

export const ConversationsChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/statistics/daily-conversations`, params: period.query }, getAppDailyConversations)
if (!response)
const { data: response, isLoading } = useAppDailyConversations(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -301,8 +313,8 @@ export const ConversationsChart: FC<IBizChartProps> = ({ id, period }) => {
export const EndUsersChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()

const { data: response } = useSWR({ url: `/apps/${id}/statistics/daily-end-users`, id, params: period.query }, getAppDailyEndUsers)
if (!response)
const { data: response, isLoading } = useAppDailyEndUsers(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -315,8 +327,8 @@ export const EndUsersChart: FC<IBizChartProps> = ({ id, period }) => {

export const AvgSessionInteractions: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/statistics/average-session-interactions`, params: period.query }, getAppStatistics)
if (!response)
const { data: response, isLoading } = useAppAverageSessionInteractions(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -331,8 +343,8 @@ export const AvgSessionInteractions: FC<IBizChartProps> = ({ id, period }) => {

export const AvgResponseTime: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/statistics/average-response-time`, params: period.query }, getAppStatistics)
if (!response)
const { data: response, isLoading } = useAppAverageResponseTime(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -348,8 +360,8 @@ export const AvgResponseTime: FC<IBizChartProps> = ({ id, period }) => {

export const TokenPerSecond: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/statistics/tokens-per-second`, params: period.query }, getAppStatistics)
if (!response)
const { data: response, isLoading } = useAppTokensPerSecond(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -366,8 +378,8 @@ export const TokenPerSecond: FC<IBizChartProps> = ({ id, period }) => {

export const UserSatisfactionRate: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/statistics/user-satisfaction-rate`, params: period.query }, getAppStatistics)
if (!response)
const { data: response, isLoading } = useAppSatisfactionRate(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -384,8 +396,8 @@ export const UserSatisfactionRate: FC<IBizChartProps> = ({ id, period }) => {
export const CostChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()

const { data: response } = useSWR({ url: `/apps/${id}/statistics/token-costs`, params: period.query }, getAppTokenCosts)
if (!response)
const { data: response, isLoading } = useAppTokenCosts(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -398,8 +410,8 @@ export const CostChart: FC<IBizChartProps> = ({ id, period }) => {

export const WorkflowMessagesChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/daily-conversations`, params: period.query }, getWorkflowDailyConversations)
if (!response)
const { data: response, isLoading } = useWorkflowDailyConversations(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -414,8 +426,8 @@ export const WorkflowMessagesChart: FC<IBizChartProps> = ({ id, period }) => {
export const WorkflowDailyTerminalsChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()

const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/daily-terminals`, id, params: period.query }, getAppDailyEndUsers)
if (!response)
const { data: response, isLoading } = useWorkflowDailyTerminals(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -429,8 +441,8 @@ export const WorkflowDailyTerminalsChart: FC<IBizChartProps> = ({ id, period })
export const WorkflowCostChart: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()

const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/token-costs`, params: period.query }, getAppTokenCosts)
if (!response)
const { data: response, isLoading } = useWorkflowTokenCosts(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand All @@ -443,8 +455,8 @@ export const WorkflowCostChart: FC<IBizChartProps> = ({ id, period }) => {

export const AvgUserInteractions: FC<IBizChartProps> = ({ id, period }) => {
const { t } = useTranslation()
const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/average-app-interactions`, params: period.query }, getAppStatistics)
if (!response)
const { data: response, isLoading } = useWorkflowAverageInteractions(id, period.query)
if (isLoading || !response)
return <Loading />
const noDataFlag = !response.data || response.data.length === 0
return <Chart
Expand Down
2 changes: 1 addition & 1 deletion web/app/components/apps/empty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Empty = () => {
return (
<>
<DefaultCards />
<div className='absolute inset-0 z-20 flex items-center justify-center bg-gradient-to-t from-background-body to-transparent pointer-events-none'>
<div className='pointer-events-none absolute inset-0 z-20 flex items-center justify-center bg-gradient-to-t from-background-body to-transparent'>
<span className='system-md-medium text-text-tertiary'>
{t('app.newApp.noAppsFound')}
</span>
Expand Down
85 changes: 36 additions & 49 deletions web/app/components/apps/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import {
useRouter,
} from 'next/navigation'
import useSWRInfinite from 'swr/infinite'
import { useTranslation } from 'react-i18next'
import { useDebounceFn } from 'ahooks'
import {
Expand All @@ -19,8 +18,6 @@ import AppCard from './app-card'
import NewAppCard from './new-app-card'
import useAppsQueryState from './hooks/use-apps-query-state'
import { useDSLDragDrop } from './hooks/use-dsl-drag-drop'
import type { AppListResponse } from '@/models/app'
import { fetchAppList } from '@/service/apps'
import { useAppContext } from '@/context/app-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { CheckModal } from '@/hooks/use-pay'
Expand All @@ -35,6 +32,7 @@ import Empty from './empty'
import Footer from './footer'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AppModeEnum } from '@/types/app'
import { useInfiniteAppList } from '@/service/use-apps'

const TagManagementModal = dynamic(() => import('@/app/components/base/tag-management'), {
ssr: false,
Expand All @@ -43,30 +41,6 @@ const CreateFromDSLModal = dynamic(() => import('@/app/components/app/create-fro
ssr: false,
})

const getKey = (
pageIndex: number,
previousPageData: AppListResponse,
activeTab: string,
isCreatedByMe: boolean,
tags: string[],
keywords: string,
) => {
if (!pageIndex || previousPageData.has_more) {
const params: any = { url: 'apps', params: { page: pageIndex + 1, limit: 30, name: keywords, is_created_by_me: isCreatedByMe } }

if (activeTab !== 'all')
params.params.mode = activeTab
else
delete params.params.mode

if (tags.length)
params.params.tag_ids = tags

return params
}
return null
}

const List = () => {
const { t } = useTranslation()
const { systemFeatures } = useGlobalPublicStore()
Expand Down Expand Up @@ -102,16 +76,24 @@ const List = () => {
enabled: isCurrentWorkspaceEditor,
})

const { data, isLoading, error, setSize, mutate } = useSWRInfinite(
(pageIndex: number, previousPageData: AppListResponse) => getKey(pageIndex, previousPageData, activeTab, isCreatedByMe, tagIDs, searchKeywords),
fetchAppList,
{
revalidateFirstPage: true,
shouldRetryOnError: false,
dedupingInterval: 500,
errorRetryCount: 3,
},
)
const appListQueryParams = {
page: 1,
limit: 30,
name: searchKeywords,
tag_ids: tagIDs,
is_created_by_me: isCreatedByMe,
...(activeTab !== 'all' ? { mode: activeTab as AppModeEnum } : {}),
}

const {
data,
isLoading,
isFetchingNextPage,
fetchNextPage,
hasNextPage,
error,
refetch,
} = useInfiniteAppList(appListQueryParams, { enabled: !isCurrentWorkspaceDatasetOperator })

const anchorRef = useRef<HTMLDivElement>(null)
const options = [
Expand All @@ -126,17 +108,19 @@ const List = () => {
useEffect(() => {
if (localStorage.getItem(NEED_REFRESH_APP_LIST_KEY) === '1') {
localStorage.removeItem(NEED_REFRESH_APP_LIST_KEY)
mutate()
refetch()
}
}, [mutate, t])
}, [refetch])

useEffect(() => {
if (isCurrentWorkspaceDatasetOperator)
return router.replace('/datasets')
}, [router, isCurrentWorkspaceDatasetOperator])

useEffect(() => {
const hasMore = data?.at(-1)?.has_more ?? true
if (isCurrentWorkspaceDatasetOperator)
return
const hasMore = hasNextPage ?? true
let observer: IntersectionObserver | undefined

if (error) {
Expand All @@ -151,8 +135,8 @@ const List = () => {
const dynamicMargin = Math.max(100, Math.min(containerHeight * 0.2, 200)) // Clamps to 100-200px range, using 20% of container height as the base value

observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !isLoading && !error && hasMore)
setSize((size: number) => size + 1)
if (entries[0].isIntersecting && !isLoading && !isFetchingNextPage && !error && hasMore)
fetchNextPage()
}, {
root: containerRef.current,
rootMargin: `${dynamicMargin}px`,
Expand All @@ -161,7 +145,7 @@ const List = () => {
observer.observe(anchorRef.current)
}
return () => observer?.disconnect()
}, [isLoading, setSize, data, error])
}, [isLoading, isFetchingNextPage, fetchNextPage, error, hasNextPage, isCurrentWorkspaceDatasetOperator])

const { run: handleSearch } = useDebounceFn(() => {
setSearchKeywords(keywords)
Expand All @@ -185,6 +169,9 @@ const List = () => {
setQuery(prev => ({ ...prev, isCreatedByMe: newValue }))
}, [isCreatedByMe, setQuery])

const pages = data?.pages ?? []
const hasAnyApp = (pages[0]?.total ?? 0) > 0

return (
<>
<div ref={containerRef} className='relative flex h-0 shrink-0 grow flex-col overflow-y-auto bg-background-body'>
Expand Down Expand Up @@ -217,17 +204,17 @@ const List = () => {
/>
</div>
</div>
{(data && data[0].total > 0)
{hasAnyApp
? <div className='relative grid grow grid-cols-1 content-start gap-4 px-12 pt-2 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6'>
{isCurrentWorkspaceEditor
&& <NewAppCard ref={newAppCardRef} onSuccess={mutate} selectedAppType={activeTab} />}
{data.map(({ data: apps }) => apps.map(app => (
<AppCard key={app.id} app={app} onRefresh={mutate} />
&& <NewAppCard ref={newAppCardRef} onSuccess={refetch} selectedAppType={activeTab} />}
{pages.map(({ data: apps }) => apps.map(app => (
<AppCard key={app.id} app={app} onRefresh={refetch} />
)))}
</div>
: <div className='relative grid grow grid-cols-1 content-start gap-4 overflow-hidden px-12 pt-2 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6'>
{isCurrentWorkspaceEditor
&& <NewAppCard ref={newAppCardRef} className='z-10' onSuccess={mutate} selectedAppType={activeTab} />}
&& <NewAppCard ref={newAppCardRef} className='z-10' onSuccess={refetch} selectedAppType={activeTab} />}
<Empty />
</div>}

Expand Down Expand Up @@ -261,7 +248,7 @@ const List = () => {
onSuccess={() => {
setShowCreateFromDSLModal(false)
setDroppedDSLFile(undefined)
mutate()
refetch()
}}
droppedFile={droppedDSLFile}
/>
Expand Down
Loading