report asset layout

This commit is contained in:
Nate Kelley 2025-08-02 23:47:27 -06:00
parent 21b3c22e5c
commit 08d7014725
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 85 additions and 13 deletions

View File

@ -65,18 +65,19 @@ export const prefetchGetReportsList = async (
/**
* Hook to get an individual report by ID
*/
export const useGetReportById = (
reportId: string,
export const useGetReport = (
reportId: string | undefined,
options?: Omit<UseQueryOptions<GetReportIndividualResponse, RustApiError>, 'queryKey' | 'queryFn'>
) => {
const queryFn = useMemoizedFn(() => {
return getReportById(reportId);
return getReportById(reportId!);
});
return useQuery({
...queryKeys.reportsGetById(reportId),
...queryKeys.reportsGetById(reportId!),
queryFn,
enabled: !!reportId,
select: options?.select,
...options
});
};

View File

@ -0,0 +1,46 @@
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
import type { Metadata } from 'next';
import { getTitle_server } from '@/api/buster_rest/title';
export default async function ReportLayout({
children,
params
}: {
children: React.ReactNode;
params: Promise<{ reportId: string }>;
}) {
const { reportId } = await params;
return (
<AppAssetCheckLayout assetId={reportId} type="report">
{children}
</AppAssetCheckLayout>
);
}
// Generate metadata dynamically based on the report - shared across all child pages
export async function generateMetadata({
params
}: {
params: Promise<{ reportId: string }>;
}): Promise<Metadata> {
const { reportId } = await params;
try {
// Fetch the report title using the server-side request
const response = await getTitle_server({
assetId: reportId,
assetType: 'report'
});
return {
title: response.title || 'New Report'
};
} catch (error) {
// Fallback title if the request fails
console.error('Failed to fetch report title:', error);
return {
title: 'New Report'
};
}
}

View File

@ -0,0 +1,5 @@
import { FileIndeterminateLoader } from '@/components/features/FileIndeterminateLoader';
export default function Loading() {
return <FileIndeterminateLoader />;
}

View File

@ -1,10 +1,7 @@
'use client';
import React, { useMemo, useState } from 'react';
import type {
GetDatasetGroupDatasetsResponse,
GetPermissionGroupUsersResponse
} from '@/api/asset_interfaces';
import type { GetDatasetGroupDatasetsResponse } from '@/api/asset_interfaces';
import { useUpdateDatasetGroupDatasets } from '@/api/buster_rest/dataset_groups';
import { PermissionAssignedCell } from '@/components/features/PermissionComponents';
import {
@ -42,7 +39,7 @@ export const DatasetGroupDatasetsListContainer: React.FC<{
title: 'Assigned',
dataIndex: 'assigned',
width: 130 + 85,
render: (assigned, permissionGroup: GetPermissionGroupUsersResponse) => {
render: (assigned, permissionGroup) => {
return (
<div className="flex justify-end">
<PermissionAssignedCell

View File

@ -3,7 +3,7 @@ import React from 'react';
export default function FileText() {
return (
<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<title>file-text</title>
<g fill="currentColor" strokeLinejoin="miter" strokeLinecap="butt">
<path

View File

@ -11,7 +11,7 @@ import { useDocumentTitle } from '@/hooks';
export type AppAssetCheckLayoutProps = {
assetId: string;
type: 'metric' | 'dashboard' | 'collection';
type: 'metric' | 'dashboard' | 'collection' | 'report';
versionNumber?: number;
};

View File

@ -6,8 +6,8 @@ import { useGetCollection } from '@/api/buster_rest/collections';
import { useGetDashboard } from '@/api/buster_rest/dashboards';
import type { RustApiError } from '@/api/buster_rest/errors';
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
type AssetType = 'metric' | 'dashboard' | 'collection';
import type { AssetType } from '@buster/server-shared/assets';
import { useGetReport } from '@/api/buster_rest/reports';
interface BaseGetAssetProps {
assetId: string;
@ -81,6 +81,7 @@ export const useGetAsset = (
const isMetric = props.type === 'metric';
const isDashboard = props.type === 'dashboard';
const isCollection = props.type === 'collection';
const isReport = props.type === 'report';
// Always call hooks at the top level with appropriate enabled flags
const {
@ -126,6 +127,15 @@ export const useGetAsset = (
select: (x) => x?.name
});
const {
isFetched: reportIsFetched,
error: reportError,
isError: reportIsError,
data: reportTitle
} = useGetReport(isReport ? props.assetId : undefined, {
select: (x) => x?.name
});
const currentQuery = useMemo((): AssetQueryResult => {
switch (props.type) {
case 'metric':
@ -149,6 +159,14 @@ export const useGetAsset = (
isError: collectionIsError,
showLoader: !collectionIsFetched && !collectionIsError
};
case 'report':
return {
isFetched: reportIsFetched,
error: reportError,
isError: reportIsError,
showLoader: !reportIsFetched && !reportIsError
};
default: {
const exhaustiveCheck: never = props.type;
return { isFetched: false, error: null, isError: false, showLoader: false };

View File

@ -21,6 +21,10 @@
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./assets": {
"types": "./dist/assets/index.d.ts",
"default": "./dist/assets/index.js"
},
"./chats": {
"types": "./dist/chats/index.d.ts",
"default": "./dist/chats/index.js"

View File

@ -0,0 +1 @@
export * from './asset-types.types';