From 46f812d823fcf728986a7a956c07c0a21c6f9f7b Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Sat, 2 Aug 2025 23:58:14 -0600 Subject: [PATCH] add linking --- .../api/buster_rest/reports/queryRequests.ts | 21 ++++++++------- apps/web/src/api/query_keys/reports.ts | 4 +-- .../ui/list/BusterList/BusterList.stories.tsx | 19 ++++++++++--- .../context/Assets/BusterAssetsProvider.tsx | 5 ++++ .../AppAssetCheckLayout/useGetAsset.tsx | 20 ++++++++++++-- .../FileContainerHeader/CreateChatButtont.tsx | 6 +++++ apps/web/src/lib/assets/assetParamsToRoute.ts | 10 ++++++- apps/web/src/lib/assets/createReportRoute.ts | 27 +++++++++++++++++++ .../src/chats/chat-message.types.ts | 3 ++- 9 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 apps/web/src/lib/assets/createReportRoute.ts diff --git a/apps/web/src/api/buster_rest/reports/queryRequests.ts b/apps/web/src/api/buster_rest/reports/queryRequests.ts index 1a5580fdd..5f67f6b6e 100644 --- a/apps/web/src/api/buster_rest/reports/queryRequests.ts +++ b/apps/web/src/api/buster_rest/reports/queryRequests.ts @@ -65,16 +65,19 @@ export const prefetchGetReportsList = async ( /** * Hook to get an individual report by ID */ -export const useGetReport = ( +export const useGetReport = ( reportId: string | undefined, - options?: Omit, 'queryKey' | 'queryFn'> + options?: Omit< + UseQueryOptions, + 'queryKey' | 'queryFn' + > ) => { const queryFn = useMemoizedFn(() => { return getReportById(reportId!); }); return useQuery({ - ...queryKeys.reportsGetById(reportId!), + ...queryKeys.reportsGetReport(reportId!), queryFn, enabled: !!reportId, select: options?.select, @@ -89,7 +92,7 @@ export const prefetchGetReportById = async (reportId: string, queryClientProp?: const queryClient = queryClientProp || new QueryClient(); await queryClient.prefetchQuery({ - ...queryKeys.reportsGetById(reportId), + ...queryKeys.reportsGetReport(reportId), queryFn: () => getReportById_server(reportId) }); @@ -112,18 +115,18 @@ export const useUpdateReport = () => { onMutate: async ({ reportId, data }) => { // Cancel any outgoing refetches await queryClient.cancelQueries({ - queryKey: queryKeys.reportsGetById(reportId).queryKey + queryKey: queryKeys.reportsGetReport(reportId).queryKey }); // Snapshot the previous value const previousReport = queryClient.getQueryData( - queryKeys.reportsGetById(reportId).queryKey + queryKeys.reportsGetReport(reportId).queryKey ); // Optimistically update the individual report if (previousReport) { queryClient.setQueryData( - queryKeys.reportsGetById(reportId).queryKey, + queryKeys.reportsGetReport(reportId).queryKey, create(previousReport, (draft) => { if (data.name !== undefined) draft.name = data.name; if (data.description !== undefined) draft.description = data.description; @@ -141,14 +144,14 @@ export const useUpdateReport = () => { // If the mutation fails, use the context to roll back if (context?.previousReport) { queryClient.setQueryData( - queryKeys.reportsGetById(reportId).queryKey, + queryKeys.reportsGetReport(reportId).queryKey, context.previousReport ); } }, onSuccess: (data, { reportId, data: updateData }) => { // Update the individual report cache with server response - queryClient.setQueryData(queryKeys.reportsGetById(reportId).queryKey, data); + queryClient.setQueryData(queryKeys.reportsGetReport(reportId).queryKey, data); const nameChanged = updateData.name !== undefined && updateData.name !== data.name; diff --git a/apps/web/src/api/query_keys/reports.ts b/apps/web/src/api/query_keys/reports.ts index 3d0be8178..bc5423529 100644 --- a/apps/web/src/api/query_keys/reports.ts +++ b/apps/web/src/api/query_keys/reports.ts @@ -13,7 +13,7 @@ const reportsGetList = (filters?: GetReportsListRequest) => initialDataUpdatedAt: 0 }); -const reportsGetById = (reportId: string) => +const reportsGetReport = (reportId: string) => queryOptions({ queryKey: ['reports', 'get', reportId] as const, staleTime: 60 * 1000 // 60 seconds @@ -21,5 +21,5 @@ const reportsGetById = (reportId: string) => export const reportsQueryKeys = { reportsGetList, - reportsGetById + reportsGetReport }; diff --git a/apps/web/src/components/ui/list/BusterList/BusterList.stories.tsx b/apps/web/src/components/ui/list/BusterList/BusterList.stories.tsx index a5ac16555..85afbc74e 100644 --- a/apps/web/src/components/ui/list/BusterList/BusterList.stories.tsx +++ b/apps/web/src/components/ui/list/BusterList/BusterList.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import React, { useMemo } from 'react'; import type { ContextMenuProps } from '../../context-menu/ContextMenu'; import { BusterList } from './index'; -import type { BusterListRow } from './interfaces'; +import type { BusterListColumn, BusterListRow } from './interfaces'; const meta: Meta = { title: 'UI/List/BusterList', @@ -33,10 +33,19 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +// Define the data type for our rows +type SampleData = { + name: string; + age: number; + address?: string; + email?: string; + actions?: any; // For the actions column +}; + +type Story = StoryObj>; // Sample data for the stories -const sampleColumns = [ +const sampleColumns: BusterListColumn[] = [ { dataIndex: 'name', title: 'Name', @@ -51,7 +60,9 @@ const sampleColumns = [ dataIndex: 'actions', title: 'Actions', width: 100, - render: (_: any, record: any) => + render: (_: any, record: SampleData) => ( + + ) } ]; diff --git a/apps/web/src/context/Assets/BusterAssetsProvider.tsx b/apps/web/src/context/Assets/BusterAssetsProvider.tsx index d2ac0f436..42f620c7f 100644 --- a/apps/web/src/context/Assets/BusterAssetsProvider.tsx +++ b/apps/web/src/context/Assets/BusterAssetsProvider.tsx @@ -45,6 +45,11 @@ const useBusterAssets = () => { queryKey: queryKeys.chatsGetChat(assetId).queryKey, refetchType: 'all' }); + } else if (type === 'report') { + await queryClient.invalidateQueries({ + queryKey: queryKeys.reportsGetReport(assetId).queryKey, + refetchType: 'all' + }); } else { const exhaustiveCheck: never = type; } diff --git a/apps/web/src/layouts/AppAssetCheckLayout/useGetAsset.tsx b/apps/web/src/layouts/AppAssetCheckLayout/useGetAsset.tsx index f8ff59ce2..937547bce 100644 --- a/apps/web/src/layouts/AppAssetCheckLayout/useGetAsset.tsx +++ b/apps/web/src/layouts/AppAssetCheckLayout/useGetAsset.tsx @@ -167,6 +167,9 @@ export const useGetAsset = ( isError: reportIsError, showLoader: !reportIsFetched && !reportIsError }; + case 'chat': + // Chat type is not supported in this hook + return { isFetched: true, error: null, isError: false, showLoader: false }; default: { const exhaustiveCheck: never = props.type; return { isFetched: false, error: null, isError: false, showLoader: false }; @@ -182,15 +185,28 @@ export const useGetAsset = ( dashboardIsError, collectionIsFetched, collectionError, - collectionIsError + collectionIsError, + reportIsFetched, + reportError, + reportIsError ]); const title = useMemo(() => { if (isMetric) return metricTitle; if (isDashboard) return dashboardTitle; if (isCollection) return collectionTitle; + if (isReport) return reportTitle; return undefined; - }, [isMetric, isDashboard, isCollection, metricTitle, dashboardTitle, collectionTitle]); + }, [ + isMetric, + isDashboard, + isCollection, + isReport, + metricTitle, + dashboardTitle, + collectionTitle, + reportTitle + ]); const { hasAccess, passwordRequired, isPublic } = getAssetAccess(currentQuery.error); diff --git a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/CreateChatButtont.tsx b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/CreateChatButtont.tsx index 0606e92d5..7b0e14d45 100644 --- a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/CreateChatButtont.tsx +++ b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/CreateChatButtont.tsx @@ -35,6 +35,12 @@ export const CreateChatButton = React.memo( dashboardId: assetId, chatId: result.id }); + } else if (assetType === 'report') { + await onChangePage({ + route: BusterRoutes.APP_CHAT_ID_REPORT_ID, + reportId: assetId, + chatId: result.id + }); } else { const _exhaustiveCheck: never = assetType; } diff --git a/apps/web/src/lib/assets/assetParamsToRoute.ts b/apps/web/src/lib/assets/assetParamsToRoute.ts index 6fb7009e8..4d46d473e 100644 --- a/apps/web/src/lib/assets/assetParamsToRoute.ts +++ b/apps/web/src/lib/assets/assetParamsToRoute.ts @@ -9,6 +9,7 @@ import { createMetricRoute, type MetricRouteParams } from './createMetricRoute'; import { createDashboardRoute, type DashboardRouteParams } from './createDashboardRoute'; import { createReasoningRoute } from './createReasoningRoute'; import { createDatasetRoute } from './createDatasetRoute'; +import { createReportRoute } from './createReportRoute'; type UnionOfFileTypes = FileType | ReasoningFileType | ReasoingMessage_ThoughtFileType; @@ -22,7 +23,7 @@ type OtherRouteParams = { dashboardVersionNumber?: number; //if this is provided, it will be used instead of versionNumber page?: undefined; secondaryView?: undefined | null | string; - type: Exclude; + type: UnionOfFileTypes; }; type BaseParams = MetricRouteParams | DashboardRouteParams | OtherRouteParams; @@ -73,6 +74,13 @@ export const assetParamsToRoute = ({ }); } + if (type === 'report') { + return createReportRoute({ + assetId, + chatId + }); + } + if (type === 'reasoning') { return createReasoningRoute({ assetId, diff --git a/apps/web/src/lib/assets/createReportRoute.ts b/apps/web/src/lib/assets/createReportRoute.ts new file mode 100644 index 000000000..c6e53c146 --- /dev/null +++ b/apps/web/src/lib/assets/createReportRoute.ts @@ -0,0 +1,27 @@ +import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes'; + +export type ReportRouteParams = { + assetId: string; + chatId?: string; + type: 'report'; +}; + +export const createReportRoute = ({ + assetId: reportId, + chatId +}: Omit) => { + // Report routes within a chat context + if (chatId) { + return createBusterRoute({ + route: BusterRoutes.APP_CHAT_ID_REPORT_ID, + chatId, + reportId + }); + } + + // Standalone report route + return createBusterRoute({ + route: BusterRoutes.APP_REPORTS_ID, + reportId + }); +}; diff --git a/packages/server-shared/src/chats/chat-message.types.ts b/packages/server-shared/src/chats/chat-message.types.ts index 5a67669e0..c1f3379e6 100644 --- a/packages/server-shared/src/chats/chat-message.types.ts +++ b/packages/server-shared/src/chats/chat-message.types.ts @@ -29,7 +29,7 @@ const ResponseMessage_FileMetadataSchema = z.object({ timestamp: z.number().optional(), }); -const ResponseMessageFileTypeSchema = z.enum(['metric', 'dashboard', 'reasoning']); +const ResponseMessageFileTypeSchema = z.enum(['metric', 'dashboard', 'reasoning', 'report']); const ResponseMessage_FileSchema = z.object({ id: z.string(), @@ -59,6 +59,7 @@ const ReasoningMessage_TextSchema = z.object({ const ReasoningFileTypeSchema = z.enum([ 'metric', 'dashboard', + 'report', 'reasoning', 'agent-action', 'todo',