mirror of https://github.com/buster-so/buster.git
relative app segmented
This commit is contained in:
parent
14e6164c26
commit
4eca05fb9c
|
@ -1,5 +1,5 @@
|
|||
import { type ReportElementsWithIds, getReport } from '@buster/database';
|
||||
import type { GetReportIndividualResponse, ReportElements } from '@buster/server-shared/reports';
|
||||
import type { GetReportResponse, ReportElements } from '@buster/server-shared/reports';
|
||||
import { markdownToPlatejs } from '@buster/server-utils/report';
|
||||
import { Hono } from 'hono';
|
||||
import { HTTPException } from 'hono/http-exception';
|
||||
|
@ -8,7 +8,7 @@ import { standardErrorHandler } from '../../../../utils/response';
|
|||
export async function getReportHandler(
|
||||
reportId: string,
|
||||
user: { id: string }
|
||||
): Promise<GetReportIndividualResponse> {
|
||||
): Promise<GetReportResponse> {
|
||||
const report = await getReport({ reportId, userId: user.id });
|
||||
|
||||
const platejsResult = await markdownToPlatejs(report.content);
|
||||
|
@ -19,7 +19,7 @@ export async function getReportHandler(
|
|||
|
||||
const content: ReportElementsWithIds = platejsResult.elements as ReportElementsWithIds;
|
||||
|
||||
const response: GetReportIndividualResponse = {
|
||||
const response: GetReportResponse = {
|
||||
...report,
|
||||
content,
|
||||
};
|
||||
|
@ -36,7 +36,7 @@ const app = new Hono()
|
|||
throw new HTTPException(404, { message: 'Report ID is required' });
|
||||
}
|
||||
|
||||
const response: GetReportIndividualResponse = await getReportHandler(reportId, user);
|
||||
const response: GetReportResponse = await getReportHandler(reportId, user);
|
||||
return c.json(response);
|
||||
})
|
||||
.onError(standardErrorHandler);
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import type {
|
||||
GetReportIndividualResponse,
|
||||
UpdateReportResponse,
|
||||
} from '@buster/server-shared/reports';
|
||||
import type { GetReportResponse, UpdateReportResponse } from '@buster/server-shared/reports';
|
||||
import {
|
||||
QueryClient,
|
||||
type UseQueryOptions,
|
||||
|
@ -95,12 +92,9 @@ export const prefetchGetReport = async (
|
|||
/**
|
||||
* Hook to get an individual report by ID
|
||||
*/
|
||||
export const useGetReport = <T = GetReportIndividualResponse>(
|
||||
export const useGetReport = <T = GetReportResponse>(
|
||||
{ reportId, versionNumber }: { reportId: string | undefined; versionNumber?: number },
|
||||
options?: Omit<
|
||||
UseQueryOptions<GetReportIndividualResponse, RustApiError, T>,
|
||||
'queryKey' | 'queryFn'
|
||||
>
|
||||
options?: Omit<UseQueryOptions<GetReportResponse, RustApiError, T>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
const queryFn = () => {
|
||||
return getReportById(reportId ?? '');
|
||||
|
@ -136,7 +130,7 @@ export const useUpdateReport = () => {
|
|||
UpdateReportResponse,
|
||||
RustApiError,
|
||||
Parameters<typeof updateReport>[0],
|
||||
{ previousReport?: GetReportIndividualResponse }
|
||||
{ previousReport?: GetReportResponse }
|
||||
>({
|
||||
mutationFn: updateReport,
|
||||
onMutate: async ({ reportId, ...data }) => {
|
||||
|
@ -146,7 +140,7 @@ export const useUpdateReport = () => {
|
|||
});
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousReport = queryClient.getQueryData<GetReportIndividualResponse>(
|
||||
const previousReport = queryClient.getQueryData<GetReportResponse>(
|
||||
reportsQueryKeys.reportsGetReport(reportId, 'LATEST').queryKey
|
||||
);
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import type { GetReportIndividualResponse } from '@buster/server-shared/reports';
|
||||
import type { GetReportResponse } from '@buster/server-shared/reports';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useSearch } from '@tanstack/react-router';
|
||||
import { useMemo } from 'react';
|
||||
import { reportsQueryKeys } from '@/api/query_keys/reports';
|
||||
|
||||
const stableVersionDataSelector = (data: GetReportIndividualResponse) => data.version_number;
|
||||
const stableVersionDataSelector = (data: GetReportResponse) => data.version_number;
|
||||
const stableVersionSearchSelector = (state: { report_version_number?: number | undefined }) =>
|
||||
state.report_version_number;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type {
|
||||
GetReportIndividualResponse,
|
||||
GetReportResponse,
|
||||
GetReportsListRequest,
|
||||
GetReportsListResponse,
|
||||
UpdateReportRequest,
|
||||
|
@ -21,7 +21,7 @@ export const getReportsList = async (params?: GetReportsListRequest) => {
|
|||
* Get an individual report by ID
|
||||
*/
|
||||
export const getReportById = async (reportId: string) => {
|
||||
return mainApiV2.get<GetReportIndividualResponse>(`/reports/${reportId}`).then((res) => res.data);
|
||||
return mainApiV2.get<GetReportResponse>(`/reports/${reportId}`).then((res) => res.data);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type {
|
||||
GetReportIndividualResponse,
|
||||
GetReportResponse,
|
||||
GetReportsListRequest,
|
||||
GetReportsListResponse,
|
||||
} from '@buster/server-shared/reports';
|
||||
|
@ -14,7 +14,7 @@ const reportsGetList = (filters?: GetReportsListRequest) =>
|
|||
});
|
||||
|
||||
const reportsGetReport = (reportId: string, versionNumber: number | 'LATEST') =>
|
||||
queryOptions<GetReportIndividualResponse>({
|
||||
queryOptions<GetReportResponse>({
|
||||
queryKey: ['reports', 'get', reportId, versionNumber || 'LATEST'] as const,
|
||||
staleTime: 60 * 1000, // 60 seconds
|
||||
});
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { Command, ReturnKey, TriangleWarning } from '@/components/ui/icons';
|
||||
import { PreventNavigation } from '@/components/ui/layouts/PreventNavigation';
|
||||
import { PopupContainer, PopupSplitter } from '@/components/ui/popup';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
|
|||
import { useState } from 'react';
|
||||
import { Checkbox } from '../checkbox';
|
||||
import { Grid, PaintRoller, Star, Storage } from '../icons';
|
||||
import { PreventNavigation } from '../layouts/PreventNavigation';
|
||||
import { AppSegmented } from './AppSegmented';
|
||||
|
||||
const meta: Meta<typeof AppSegmented> = {
|
||||
|
@ -130,68 +129,3 @@ export const WithOnlyIcons: Story = {
|
|||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithPreventDefault: Story = {
|
||||
args: {
|
||||
options: [
|
||||
{
|
||||
value: 'tab1',
|
||||
icon: <Star />,
|
||||
link: {
|
||||
to: '/app/datasets',
|
||||
},
|
||||
label: 'Tab 1',
|
||||
tooltip: 'Tooltip 1',
|
||||
},
|
||||
{
|
||||
value: 'tab2',
|
||||
icon: <Grid />,
|
||||
link: {
|
||||
to: '/app/datasets',
|
||||
},
|
||||
label: 'Tab 2',
|
||||
tooltip: 'Tooltip 2',
|
||||
},
|
||||
{
|
||||
value: 'tab3',
|
||||
icon: <Storage />,
|
||||
link: {
|
||||
to: '/app/datasets',
|
||||
},
|
||||
label: 'Tab 3',
|
||||
tooltip: 'Tooltip 3',
|
||||
},
|
||||
],
|
||||
},
|
||||
render: (args) => {
|
||||
const [isDirty, setIsDirty] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="flex w-full min-w-[500px] flex-col items-center justify-center gap-4">
|
||||
<AppSegmented {...args} />
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={isDirty}
|
||||
onCheckedChange={(checked) => setIsDirty(checked === 'indeterminate' ? true : checked)}
|
||||
/>
|
||||
<p>{isDirty ? 'Dirty' : 'Clean'}</p>
|
||||
</div>
|
||||
|
||||
<PreventNavigation
|
||||
isDirty={isDirty}
|
||||
title="Title"
|
||||
description="Description"
|
||||
onOk={() => {
|
||||
alert('ok');
|
||||
return Promise.resolve();
|
||||
}}
|
||||
onCancel={() => {
|
||||
alert('cancel');
|
||||
return Promise.resolve();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,34 +1,50 @@
|
|||
'use client';
|
||||
|
||||
import * as Tabs from '@radix-ui/react-tabs';
|
||||
import { Link, useNavigate } from '@tanstack/react-router';
|
||||
import {
|
||||
Link,
|
||||
type LinkProps,
|
||||
type RegisteredRouter,
|
||||
type ValidateFromPath,
|
||||
type ValidateLinkOptions,
|
||||
} from '@tanstack/react-router';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { motion } from 'framer-motion';
|
||||
import * as React from 'react';
|
||||
import { useEffect, useLayoutEffect, useState, useTransition } from 'react';
|
||||
import { useSize } from '@/hooks/useSize';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import type { OptionsTo } from '@/types/routes';
|
||||
import { Tooltip } from '../tooltip/Tooltip';
|
||||
|
||||
export interface SegmentedItem<T extends string | number = string> {
|
||||
export interface SegmentedItem<
|
||||
T extends string | number = string,
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
> {
|
||||
value: T;
|
||||
label?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
link?: OptionsTo;
|
||||
link?: ValidateLinkOptions<TRouter, TOptions, TFrom>;
|
||||
}
|
||||
|
||||
export interface AppSegmentedProps<T extends string | number = string> {
|
||||
options: SegmentedItem<T>[];
|
||||
export interface AppSegmentedProps<
|
||||
T extends string | number = string,
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
> {
|
||||
options: SegmentedItem<T, TRouter, TOptions, TFrom>[];
|
||||
value?: T;
|
||||
onChange?: (value: SegmentedItem<T>) => void;
|
||||
onChange?: (value: SegmentedItem<T, TRouter, TOptions, TFrom>) => void;
|
||||
className?: string;
|
||||
size?: 'default' | 'large';
|
||||
block?: boolean;
|
||||
type?: 'button' | 'track';
|
||||
disabled?: boolean;
|
||||
from?: ValidateFromPath<TRouter, TFrom>;
|
||||
}
|
||||
|
||||
const heightVariants = cva('h-6', {
|
||||
|
@ -85,7 +101,7 @@ const triggerVariants = cva(
|
|||
}
|
||||
);
|
||||
|
||||
const gliderVariants = cva('absolute border-border rounded border', {
|
||||
const gliderVariants = cva('glider absolute border-border rounded border', {
|
||||
variants: {
|
||||
type: {
|
||||
button: 'bg-item-select',
|
||||
|
@ -95,14 +111,26 @@ const gliderVariants = cva('absolute border-border rounded border', {
|
|||
});
|
||||
|
||||
// Create a type for the forwardRef component that includes displayName
|
||||
type AppSegmentedComponent = (<T extends string = string>(
|
||||
props: AppSegmentedProps<T> & { ref?: React.ForwardedRef<HTMLDivElement> }
|
||||
type AppSegmentedComponent = (<
|
||||
T extends string | number = string,
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
>(
|
||||
props: AppSegmentedProps<T, TRouter, TOptions, TFrom> & {
|
||||
ref?: React.ForwardedRef<HTMLDivElement>;
|
||||
}
|
||||
) => React.ReactElement) & {
|
||||
displayName?: string;
|
||||
};
|
||||
|
||||
// Update the component definition to properly handle generics
|
||||
export const AppSegmented: AppSegmentedComponent = (<T extends string | number = string>({
|
||||
export const AppSegmented: AppSegmentedComponent = (<
|
||||
T extends string | number = string,
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
>({
|
||||
options,
|
||||
type = 'track',
|
||||
value,
|
||||
|
@ -110,7 +138,8 @@ export const AppSegmented: AppSegmentedComponent = (<T extends string | number =
|
|||
className,
|
||||
size = 'default',
|
||||
block = false,
|
||||
}: AppSegmentedProps<T>) => {
|
||||
from,
|
||||
}: AppSegmentedProps<T, TRouter, TOptions, TFrom>) => {
|
||||
const rootRef = React.useRef<HTMLDivElement>(null);
|
||||
const elementSize = useSize(rootRef, 25);
|
||||
const tabRefs = React.useRef<Map<string, HTMLButtonElement>>(new Map());
|
||||
|
@ -120,7 +149,7 @@ export const AppSegmented: AppSegmentedComponent = (<T extends string | number =
|
|||
transform: 'translateX(0)',
|
||||
});
|
||||
const [isMeasured, setIsMeasured] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [_isPending, startTransition] = useTransition();
|
||||
|
||||
const handleTabClick = (value: string) => {
|
||||
const item = options.find((item) => item.value === value);
|
||||
|
@ -163,7 +192,6 @@ export const AppSegmented: AppSegmentedComponent = (<T extends string | number =
|
|||
<Tabs.Root
|
||||
ref={rootRef}
|
||||
value={selectedValue as string}
|
||||
// onValueChange={handleTabClick}
|
||||
className={cn(segmentedVariants({ block, type }), heightVariants({ size }), className)}
|
||||
>
|
||||
{isMeasured && (
|
||||
|
@ -197,12 +225,13 @@ export const AppSegmented: AppSegmentedComponent = (<T extends string | number =
|
|||
{options.map((item) => (
|
||||
<SegmentedTrigger
|
||||
key={item.value}
|
||||
item={item as SegmentedItem<string>}
|
||||
item={item as SegmentedItem<string, TRouter, TOptions, TFrom>}
|
||||
selectedValue={selectedValue as string}
|
||||
size={size}
|
||||
block={block}
|
||||
tabRefs={tabRefs}
|
||||
handleTabClick={handleTabClick}
|
||||
from={from}
|
||||
/>
|
||||
))}
|
||||
</Tabs.List>
|
||||
|
@ -212,34 +241,58 @@ export const AppSegmented: AppSegmentedComponent = (<T extends string | number =
|
|||
|
||||
AppSegmented.displayName = 'AppSegmented';
|
||||
|
||||
interface SegmentedTriggerProps<T extends string = string> {
|
||||
item: SegmentedItem<T>;
|
||||
interface SegmentedTriggerProps<
|
||||
T extends string = string,
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
> {
|
||||
item: SegmentedItem<T, TRouter, TOptions, TFrom>;
|
||||
selectedValue: string;
|
||||
size: AppSegmentedProps<T>['size'];
|
||||
block: AppSegmentedProps<T>['block'];
|
||||
size: AppSegmentedProps<T, TRouter, TOptions, TFrom>['size'];
|
||||
block: AppSegmentedProps<T, TRouter, TOptions, TFrom>['block'];
|
||||
tabRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
|
||||
handleTabClick: (value: string) => void;
|
||||
from?: ValidateFromPath<TRouter, TFrom>;
|
||||
}
|
||||
|
||||
function SegmentedTriggerComponent<T extends string = string>(props: SegmentedTriggerProps<T>) {
|
||||
const { item, selectedValue, size, block, tabRefs, handleTabClick } = props;
|
||||
function SegmentedTriggerComponent<
|
||||
T extends string = string,
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
>(props: SegmentedTriggerProps<T, TRouter, TOptions, TFrom>) {
|
||||
const { item, selectedValue, size, block, tabRefs, handleTabClick, from } = props;
|
||||
const { tooltip, label, icon, disabled, value, link } = item;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const LinkDiv = link ? Link : 'div';
|
||||
|
||||
const handleClick = async (e: React.MouseEvent) => {
|
||||
if (link) {
|
||||
e.preventDefault();
|
||||
handleTabClick(value);
|
||||
// Wait for a short duration to allow the animation to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
navigate(link);
|
||||
} else {
|
||||
handleTabClick(value);
|
||||
}
|
||||
const handleClick = (_e: React.MouseEvent) => {
|
||||
handleTabClick(value);
|
||||
};
|
||||
|
||||
const commonProps = {
|
||||
onClick: handleClick,
|
||||
'data-testid': `segmented-trigger-${value}`,
|
||||
};
|
||||
|
||||
const linkContent = (
|
||||
<>
|
||||
{icon && <span className={cn('flex items-center text-sm')}>{icon}</span>}
|
||||
{label && <span className={cn('text-sm')}>{label}</span>}
|
||||
</>
|
||||
);
|
||||
|
||||
const linkDiv = link ? (
|
||||
<Link
|
||||
{...(link as LinkProps)}
|
||||
from={(from === './' ? undefined : from) as '/'}
|
||||
{...commonProps}
|
||||
>
|
||||
{linkContent}
|
||||
</Link>
|
||||
) : (
|
||||
<div {...commonProps}>{linkContent}</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltip || ''} sideOffset={10} delayDuration={150}>
|
||||
<Tabs.Trigger
|
||||
|
@ -259,10 +312,7 @@ function SegmentedTriggerComponent<T extends string = string>(props: SegmentedTr
|
|||
})
|
||||
)}
|
||||
>
|
||||
<LinkDiv {...link} onClick={handleClick} data-testid={`segmented-trigger-${value}`}>
|
||||
{icon && <span className={cn('flex items-center text-sm')}>{icon}</span>}
|
||||
{label && <span className={cn('text-sm')}>{label}</span>}
|
||||
</LinkDiv>
|
||||
{linkDiv}
|
||||
</Tabs.Trigger>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import { type ParsedLocation, useLocation } from '@tanstack/react-router';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { AppSegmented } from '@/components/ui/segmented';
|
||||
import { useIsMetricReadOnly } from '@/context/Metrics/useIsMetricReadOnly';
|
||||
|
||||
interface MetricContainerHeaderSegmentProps {
|
||||
metricId: string;
|
||||
metric_version_number: number | undefined;
|
||||
}
|
||||
|
||||
export const MetricContainerHeaderSegment: React.FC<MetricContainerHeaderSegmentProps> = React.memo(
|
||||
(props) => {
|
||||
const { metricId, metric_version_number } = props;
|
||||
|
||||
const { isFetched, isError } = useIsMetricReadOnly({
|
||||
metricId,
|
||||
});
|
||||
|
||||
if (!isFetched || isError) return null;
|
||||
|
||||
return <MetricSegments {...props} />;
|
||||
}
|
||||
);
|
||||
|
||||
MetricContainerHeaderSegment.displayName = 'MetricContainerHeaderSegment';
|
||||
|
||||
type MetricView = 'chart' | 'results' | 'sql';
|
||||
|
||||
const MetricSegments: React.FC<MetricContainerHeaderSegmentProps> = React.memo(() => {
|
||||
const location = useLocation({
|
||||
select: useCallback((location: ParsedLocation) => {
|
||||
// Get the last value after a slash in the pathname
|
||||
const pathSegments = location.pathname.split('/').filter(Boolean);
|
||||
const lastSegment = pathSegments[pathSegments.length - 1] || '';
|
||||
return lastSegment;
|
||||
}, []),
|
||||
});
|
||||
|
||||
const selectedView: MetricView = useMemo(() => {
|
||||
if (location === 'chart') return 'chart';
|
||||
if (location === 'results') return 'results';
|
||||
if (location === 'sql') return 'sql';
|
||||
return 'chart';
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<AppSegmented
|
||||
type="button"
|
||||
from={'./' as unknown as '/app/metrics/$metricId'}
|
||||
options={[
|
||||
{
|
||||
label: 'Chart',
|
||||
value: 'chart' satisfies MetricView,
|
||||
link: {
|
||||
to: './chart',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Results',
|
||||
value: 'results' satisfies MetricView,
|
||||
link: {
|
||||
to: './results',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL',
|
||||
value: 'sql' satisfies MetricView,
|
||||
link: {
|
||||
to: './sql',
|
||||
},
|
||||
},
|
||||
]}
|
||||
value={selectedView}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
MetricSegments.displayName = 'MetricSegments';
|
|
@ -50,7 +50,7 @@ import { Route as AppAppAssetDashboardsDashboardIdIndexRouteImport } from './rou
|
|||
import { Route as AppAppAssetCollectionsCollectionIdIndexRouteImport } from './routes/app/_app/_asset/collections.$collectionId.index'
|
||||
import { Route as AppAppAssetChatsChatIdIndexRouteImport } from './routes/app/_app/_asset/chats.$chatId.index'
|
||||
import { Route as AppAppAssetMetricsMetricIdSqlRouteImport } from './routes/app/_app/_asset/metrics.$metricId.sql'
|
||||
import { Route as AppAppAssetMetricsMetricIdResultRouteImport } from './routes/app/_app/_asset/metrics.$metricId.result'
|
||||
import { Route as AppAppAssetMetricsMetricIdResultsRouteImport } from './routes/app/_app/_asset/metrics.$metricId.results'
|
||||
import { Route as AppAppAssetMetricsMetricIdChartRouteImport } from './routes/app/_app/_asset/metrics.$metricId.chart'
|
||||
import { Route as AppAppAssetReportsReportIdMetricsMetricIdRouteImport } from './routes/app/_app/_asset/reports.$reportId.metrics.$metricId'
|
||||
import { Route as AppAppAssetDashboardsDashboardIdMetricsMetricIdRouteImport } from './routes/app/_app/_asset/dashboards.$dashboardId.metrics.$metricId'
|
||||
|
@ -286,10 +286,10 @@ const AppAppAssetMetricsMetricIdSqlRoute =
|
|||
path: '/sql',
|
||||
getParentRoute: () => AppAppAssetMetricsMetricIdRoute,
|
||||
} as any)
|
||||
const AppAppAssetMetricsMetricIdResultRoute =
|
||||
AppAppAssetMetricsMetricIdResultRouteImport.update({
|
||||
id: '/result',
|
||||
path: '/result',
|
||||
const AppAppAssetMetricsMetricIdResultsRoute =
|
||||
AppAppAssetMetricsMetricIdResultsRouteImport.update({
|
||||
id: '/results',
|
||||
path: '/results',
|
||||
getParentRoute: () => AppAppAssetMetricsMetricIdRoute,
|
||||
} as any)
|
||||
const AppAppAssetMetricsMetricIdChartRoute =
|
||||
|
@ -464,7 +464,7 @@ export interface FileRoutesByFullPath {
|
|||
'/app/settings/datasources/add': typeof AppSettingsSettingsDatasourcesAddRoute
|
||||
'/app/settings/datasources/': typeof AppSettingsSettingsDatasourcesIndexRoute
|
||||
'/app/metrics/$metricId/chart': typeof AppAppAssetMetricsMetricIdChartRoute
|
||||
'/app/metrics/$metricId/result': typeof AppAppAssetMetricsMetricIdResultRoute
|
||||
'/app/metrics/$metricId/results': typeof AppAppAssetMetricsMetricIdResultsRoute
|
||||
'/app/metrics/$metricId/sql': typeof AppAppAssetMetricsMetricIdSqlRoute
|
||||
'/app/chats/$chatId': typeof AppAppAssetChatsChatIdIndexRoute
|
||||
'/app/collections/$collectionId': typeof AppAppAssetCollectionsCollectionIdIndexRoute
|
||||
|
@ -522,7 +522,7 @@ export interface FileRoutesByTo {
|
|||
'/app/settings/datasources/add': typeof AppSettingsSettingsDatasourcesAddRoute
|
||||
'/app/settings/datasources': typeof AppSettingsSettingsDatasourcesIndexRoute
|
||||
'/app/metrics/$metricId/chart': typeof AppAppAssetMetricsMetricIdChartRoute
|
||||
'/app/metrics/$metricId/result': typeof AppAppAssetMetricsMetricIdResultRoute
|
||||
'/app/metrics/$metricId/results': typeof AppAppAssetMetricsMetricIdResultsRoute
|
||||
'/app/metrics/$metricId/sql': typeof AppAppAssetMetricsMetricIdSqlRoute
|
||||
'/app/chats/$chatId': typeof AppAppAssetChatsChatIdIndexRoute
|
||||
'/app/collections/$collectionId': typeof AppAppAssetCollectionsCollectionIdIndexRoute
|
||||
|
@ -585,7 +585,7 @@ export interface FileRoutesById {
|
|||
'/app/_settings/settings/datasources/add': typeof AppSettingsSettingsDatasourcesAddRoute
|
||||
'/app/_settings/settings/datasources/': typeof AppSettingsSettingsDatasourcesIndexRoute
|
||||
'/app/_app/_asset/metrics/$metricId/chart': typeof AppAppAssetMetricsMetricIdChartRoute
|
||||
'/app/_app/_asset/metrics/$metricId/result': typeof AppAppAssetMetricsMetricIdResultRoute
|
||||
'/app/_app/_asset/metrics/$metricId/results': typeof AppAppAssetMetricsMetricIdResultsRoute
|
||||
'/app/_app/_asset/metrics/$metricId/sql': typeof AppAppAssetMetricsMetricIdSqlRoute
|
||||
'/app/_app/_asset/chats/$chatId/': typeof AppAppAssetChatsChatIdIndexRoute
|
||||
'/app/_app/_asset/collections/$collectionId/': typeof AppAppAssetCollectionsCollectionIdIndexRoute
|
||||
|
@ -646,7 +646,7 @@ export interface FileRouteTypes {
|
|||
| '/app/settings/datasources/add'
|
||||
| '/app/settings/datasources/'
|
||||
| '/app/metrics/$metricId/chart'
|
||||
| '/app/metrics/$metricId/result'
|
||||
| '/app/metrics/$metricId/results'
|
||||
| '/app/metrics/$metricId/sql'
|
||||
| '/app/chats/$chatId'
|
||||
| '/app/collections/$collectionId'
|
||||
|
@ -704,7 +704,7 @@ export interface FileRouteTypes {
|
|||
| '/app/settings/datasources/add'
|
||||
| '/app/settings/datasources'
|
||||
| '/app/metrics/$metricId/chart'
|
||||
| '/app/metrics/$metricId/result'
|
||||
| '/app/metrics/$metricId/results'
|
||||
| '/app/metrics/$metricId/sql'
|
||||
| '/app/chats/$chatId'
|
||||
| '/app/collections/$collectionId'
|
||||
|
@ -766,7 +766,7 @@ export interface FileRouteTypes {
|
|||
| '/app/_settings/settings/datasources/add'
|
||||
| '/app/_settings/settings/datasources/'
|
||||
| '/app/_app/_asset/metrics/$metricId/chart'
|
||||
| '/app/_app/_asset/metrics/$metricId/result'
|
||||
| '/app/_app/_asset/metrics/$metricId/results'
|
||||
| '/app/_app/_asset/metrics/$metricId/sql'
|
||||
| '/app/_app/_asset/chats/$chatId/'
|
||||
| '/app/_app/_asset/collections/$collectionId/'
|
||||
|
@ -1098,11 +1098,11 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof AppAppAssetMetricsMetricIdSqlRouteImport
|
||||
parentRoute: typeof AppAppAssetMetricsMetricIdRoute
|
||||
}
|
||||
'/app/_app/_asset/metrics/$metricId/result': {
|
||||
id: '/app/_app/_asset/metrics/$metricId/result'
|
||||
path: '/result'
|
||||
fullPath: '/app/metrics/$metricId/result'
|
||||
preLoaderRoute: typeof AppAppAssetMetricsMetricIdResultRouteImport
|
||||
'/app/_app/_asset/metrics/$metricId/results': {
|
||||
id: '/app/_app/_asset/metrics/$metricId/results'
|
||||
path: '/results'
|
||||
fullPath: '/app/metrics/$metricId/results'
|
||||
preLoaderRoute: typeof AppAppAssetMetricsMetricIdResultsRouteImport
|
||||
parentRoute: typeof AppAppAssetMetricsMetricIdRoute
|
||||
}
|
||||
'/app/_app/_asset/metrics/$metricId/chart': {
|
||||
|
@ -1261,15 +1261,15 @@ declare module '@tanstack/react-start/server' {
|
|||
|
||||
interface AppAppAssetMetricsMetricIdRouteChildren {
|
||||
AppAppAssetMetricsMetricIdChartRoute: typeof AppAppAssetMetricsMetricIdChartRoute
|
||||
AppAppAssetMetricsMetricIdResultRoute: typeof AppAppAssetMetricsMetricIdResultRoute
|
||||
AppAppAssetMetricsMetricIdResultsRoute: typeof AppAppAssetMetricsMetricIdResultsRoute
|
||||
AppAppAssetMetricsMetricIdSqlRoute: typeof AppAppAssetMetricsMetricIdSqlRoute
|
||||
}
|
||||
|
||||
const AppAppAssetMetricsMetricIdRouteChildren: AppAppAssetMetricsMetricIdRouteChildren =
|
||||
{
|
||||
AppAppAssetMetricsMetricIdChartRoute: AppAppAssetMetricsMetricIdChartRoute,
|
||||
AppAppAssetMetricsMetricIdResultRoute:
|
||||
AppAppAssetMetricsMetricIdResultRoute,
|
||||
AppAppAssetMetricsMetricIdResultsRoute:
|
||||
AppAppAssetMetricsMetricIdResultsRoute,
|
||||
AppAppAssetMetricsMetricIdSqlRoute: AppAppAssetMetricsMetricIdSqlRoute,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
||||
|
||||
export const Route = createFileRoute('/app/_app/_asset/metrics/$metricId/result')({
|
||||
...metricChartServerAssetContext,
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
||||
|
||||
export const Route = createFileRoute('/app/_app/_asset/metrics/$metricId/results')({
|
||||
...metricResultsServerAssetContext,
|
||||
});
|
|
@ -9,10 +9,7 @@ import { create } from 'mutative';
|
|||
import { useMemoizedFn } from '@/hooks';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
import type { RustApiError } from '../errors';
|
||||
import type {
|
||||
GetReportIndividualResponse,
|
||||
UpdateReportResponse
|
||||
} from '@buster/server-shared/reports';
|
||||
import type { GetReportResponse, UpdateReportResponse } from '@buster/server-shared/reports';
|
||||
import {
|
||||
getReportsList,
|
||||
getReportsList_server,
|
||||
|
@ -86,12 +83,9 @@ export const prefetchGetReportsListClient = async (
|
|||
/**
|
||||
* Hook to get an individual report by ID
|
||||
*/
|
||||
export const useGetReport = <T = GetReportIndividualResponse>(
|
||||
export const useGetReport = <T = GetReportResponse>(
|
||||
{ reportId, versionNumber }: { reportId: string | undefined; versionNumber?: number },
|
||||
options?: Omit<
|
||||
UseQueryOptions<GetReportIndividualResponse, RustApiError, T>,
|
||||
'queryKey' | 'queryFn'
|
||||
>
|
||||
options?: Omit<UseQueryOptions<GetReportResponse, RustApiError, T>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
const queryFn = useMemoizedFn(() => {
|
||||
return getReportById(reportId!);
|
||||
|
@ -127,7 +121,7 @@ export const useUpdateReport = () => {
|
|||
UpdateReportResponse,
|
||||
RustApiError,
|
||||
Parameters<typeof updateReport>[0],
|
||||
{ previousReport?: GetReportIndividualResponse }
|
||||
{ previousReport?: GetReportResponse }
|
||||
>({
|
||||
mutationFn: updateReport,
|
||||
onMutate: async ({ reportId, ...data }) => {
|
||||
|
@ -137,7 +131,7 @@ export const useUpdateReport = () => {
|
|||
});
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousReport = queryClient.getQueryData<GetReportIndividualResponse>(
|
||||
const previousReport = queryClient.getQueryData<GetReportResponse>(
|
||||
queryKeys.reportsGetReport(reportId).queryKey
|
||||
);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { BASE_URL_V2 } from '../config';
|
|||
import type {
|
||||
GetReportsListRequest,
|
||||
GetReportsListResponse,
|
||||
GetReportIndividualResponse,
|
||||
GetReportResponse,
|
||||
UpdateReportRequest,
|
||||
UpdateReportResponse
|
||||
} from '@buster/server-shared/reports';
|
||||
|
@ -34,14 +34,14 @@ export const getReportsList_server = async (params?: Parameters<typeof getReport
|
|||
* Get an individual report by ID
|
||||
*/
|
||||
export const getReportById = async (reportId: string) => {
|
||||
return mainApiV2.get<GetReportIndividualResponse>(`/reports/${reportId}`).then((res) => res.data);
|
||||
return mainApiV2.get<GetReportResponse>(`/reports/${reportId}`).then((res) => res.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Server-side version of getReportById
|
||||
*/
|
||||
export const getReportById_server = async (reportId: string) => {
|
||||
return await serverFetch<GetReportIndividualResponse>(`/reports/${reportId}`, {
|
||||
return await serverFetch<GetReportResponse>(`/reports/${reportId}`, {
|
||||
baseURL: BASE_URL_V2,
|
||||
method: 'GET'
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { queryOptions } from '@tanstack/react-query';
|
||||
import type {
|
||||
GetReportsListResponse,
|
||||
GetReportIndividualResponse,
|
||||
GetReportResponse,
|
||||
GetReportsListRequest
|
||||
} from '@buster/server-shared/reports';
|
||||
|
||||
|
@ -14,7 +14,7 @@ const reportsGetList = (filters?: GetReportsListRequest) =>
|
|||
});
|
||||
|
||||
const reportsGetReport = (reportId: string, versionNumber?: number | null) =>
|
||||
queryOptions<GetReportIndividualResponse>({
|
||||
queryOptions<GetReportResponse>({
|
||||
queryKey: ['reports', 'get', reportId, versionNumber || 'INITIAL'] as const,
|
||||
staleTime: 60 * 1000 // 60 seconds
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue