diff --git a/apps/web/src/api/buster_rest/reports/queryRequests.ts b/apps/web/src/api/buster_rest/reports/queryRequests.ts index 9746b78e8..aa114f3e0 100644 --- a/apps/web/src/api/buster_rest/reports/queryRequests.ts +++ b/apps/web/src/api/buster_rest/reports/queryRequests.ts @@ -11,7 +11,7 @@ import { queryKeys } from '@/api/query_keys'; import type { RustApiError } from '../errors'; import type { GetReportIndividualResponse, - UpdateReportRequest, + GetReportsListResponse, UpdateReportResponse } from '@buster/server-shared/reports'; import { @@ -21,6 +21,7 @@ import { getReportById_server, updateReport } from './requests'; +import { useDebounceFn } from '@/hooks/useDebounce'; /** * Hook to get a list of reports @@ -61,6 +62,20 @@ export const prefetchGetReportsList = async ( return queryClient; }; +export const prefetchGetReportsListClient = async ( + params?: Parameters[0], + queryClientProp?: QueryClient +) => { + const queryClient = queryClientProp || new QueryClient(); + + await queryClient.prefetchQuery({ + ...queryKeys.reportsGetList(params), + queryFn: () => getReportsList(params) + }); + + return queryClient; +}; + /** * Hook to get an individual report by ID */ @@ -107,11 +122,11 @@ export const useUpdateReport = () => { return useMutation< UpdateReportResponse, RustApiError, - { reportId: string; data: UpdateReportRequest }, + Parameters[0], { previousReport?: GetReportIndividualResponse } >({ - mutationFn: ({ reportId, data }) => updateReport(reportId, data), - onMutate: async ({ reportId, data }) => { + mutationFn: updateReport, + onMutate: async ({ reportId, ...data }) => { // Cancel any outgoing refetches await queryClient.cancelQueries({ queryKey: queryKeys.reportsGetReport(reportId).queryKey @@ -145,18 +160,32 @@ export const useUpdateReport = () => { ); } }, - onSuccess: (data, { reportId, data: updateData }) => { + onSuccess: (data, { reportId, ...updateData }, ctx) => { // Update the individual report cache with server response queryClient.setQueryData(queryKeys.reportsGetReport(reportId).queryKey, data); - const nameChanged = updateData.name !== undefined && updateData.name !== data.name; + const nameChanged = + updateData.name !== undefined && + ctx?.previousReport?.name !== undefined && + updateData.name !== ctx?.previousReport?.name; // Invalidate the list cache to ensure it's fresh if (nameChanged) { - queryClient.invalidateQueries({ - queryKey: queryKeys.reportsGetList().queryKey, - refetchType: 'all' + const listQueryKey = queryKeys.reportsGetList().queryKey; + const hasActiveQuery = queryClient.getQueryCache().find({ + queryKey: listQueryKey, + exact: true, + type: 'active' }); + + if (hasActiveQuery) { + queryClient.invalidateQueries({ + queryKey: listQueryKey, + refetchType: 'all' + }); + } else { + prefetchGetReportsListClient(); + } } } }); diff --git a/apps/web/src/api/buster_rest/reports/requests.ts b/apps/web/src/api/buster_rest/reports/requests.ts index e3c0e5831..8b8f44cc3 100644 --- a/apps/web/src/api/buster_rest/reports/requests.ts +++ b/apps/web/src/api/buster_rest/reports/requests.ts @@ -50,6 +50,9 @@ export const getReportById_server = async (reportId: string) => { /** * Update a report */ -export const updateReport = async (reportId: string, data: UpdateReportRequest) => { +export const updateReport = async ({ + reportId, + ...data +}: UpdateReportRequest & { reportId: string }) => { return mainApiV2.put(`/reports/${reportId}`, data).then((res) => res.data); }; diff --git a/apps/web/src/app/app/(primary_layout)/(chat_experience)/reports/[reportId]/page.tsx b/apps/web/src/app/app/(primary_layout)/(chat_experience)/reports/[reportId]/page.tsx index a2cee6718..6247e7056 100644 --- a/apps/web/src/app/app/(primary_layout)/(chat_experience)/reports/[reportId]/page.tsx +++ b/apps/web/src/app/app/(primary_layout)/(chat_experience)/reports/[reportId]/page.tsx @@ -1,9 +1,9 @@ -//import { ReportsListController } from '@/controllers/ReportsListController'; +import { ReportPageController } from '@/controllers/ReportPageControllers/ReportPageController'; export default async function Page(props: { params: Promise<{ reportId: string }> }) { const params = await props.params; const { reportId } = params; - return
Report with an id of {reportId}
; + return ; } diff --git a/apps/web/src/controllers/ReportPageControllers/ReportPageController/ReportPageController.tsx b/apps/web/src/controllers/ReportPageControllers/ReportPageController/ReportPageController.tsx new file mode 100644 index 000000000..7032140df --- /dev/null +++ b/apps/web/src/controllers/ReportPageControllers/ReportPageController/ReportPageController.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { + prefetchGetReportsListClient, + useGetReport, + useGetReportsList, + useUpdateReport +} from '@/api/buster_rest/reports'; +import { cn } from '@/lib/utils'; +import React from 'react'; +import { ReportPageHeader } from './ReportPageHeader'; +import { useMemoizedFn } from '@/hooks/useMemoizedFn'; +import { useDebounceFn } from '@/hooks/useDebounce'; + +export const ReportPageController: React.FC<{ + reportId: string; + readOnly?: boolean; + className?: string; +}> = ({ reportId, readOnly = false, className = '' }) => { + const { data: report } = useGetReport({ reportId, versionNumber: undefined }); + + const { mutate: updateReport } = useUpdateReport(); + + const onChangeName = useMemoizedFn((name: string) => { + updateReport({ reportId, name }); + }); + + const { run: debouncedUpdateReport } = useDebounceFn(updateReport, { wait: 300 }); + + return ( +
+ +
+ ); +}; diff --git a/apps/web/src/controllers/ReportPageControllers/ReportPageController/ReportPageHeader.tsx b/apps/web/src/controllers/ReportPageControllers/ReportPageController/ReportPageHeader.tsx new file mode 100644 index 000000000..e66cf7909 --- /dev/null +++ b/apps/web/src/controllers/ReportPageControllers/ReportPageController/ReportPageHeader.tsx @@ -0,0 +1,35 @@ +import React, { useMemo } from 'react'; +import { formatDate } from '@/lib/date'; +import { EditableTitle } from '@/components/ui/typography/EditableTitle'; +import { Paragraph } from '@/components/ui/typography/Paragraph'; +import { cn } from '@/lib/utils'; + +const DEFAULT_CREATED_BY = 'Created by Buster'; + +export const ReportPageHeader = React.forwardRef< + HTMLInputElement, + { + className?: string; + name?: string; + updatedAt?: string; + onChangeName: (name: string) => void; + } +>(({ name = '', updatedAt = '', className = '', onChangeName }, ref) => { + const updatedAtFormatted = useMemo(() => { + if (!updatedAt) return ''; + return formatDate({ date: updatedAt, format: 'll' }); + }, [updatedAt]); + + return ( +
+ + {name} + + + {updatedAtFormatted} • {DEFAULT_CREATED_BY} + +
+ ); +}); + +ReportPageHeader.displayName = 'ReportPageHeader'; diff --git a/apps/web/src/controllers/ReportPageControllers/ReportPageController/index.ts b/apps/web/src/controllers/ReportPageControllers/ReportPageController/index.ts new file mode 100644 index 000000000..c14a541e3 --- /dev/null +++ b/apps/web/src/controllers/ReportPageControllers/ReportPageController/index.ts @@ -0,0 +1 @@ +export * from './ReportPageController';