Refactor sharing request types and enhance asset icon mapping

- Updated sharing request types to use SharePermissionsUpdateRequest for clarity across collections, dashboards, and metrics.
- Enhanced asset icon mapping to include additional asset types for better UI representation.
- Improved error handling in the MetricDataTruncatedWarning component to provide user feedback during download failures.
This commit is contained in:
dal 2025-08-14 08:56:30 -06:00
parent a133653ccc
commit 895e28f8d4
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
6 changed files with 52 additions and 23 deletions

View File

@ -234,7 +234,7 @@ export const useUpdateCollectionShare = () => {
return create(previousData, (draft) => {
draft.individual_permissions = (
draft.individual_permissions?.map((t) => {
const found = params.users?.find((v) => v.email === t.email);
const found = params.users?.find((v: { email: string; role: string }) => v.email === t.email);
if (found) return { ...t, ...found };
return t;
}) || []

View File

@ -2,7 +2,7 @@ import type { ShareAssetType } from '@buster/server-shared/share';
import type { BusterCollection, BusterCollectionListItem } from '@/api/asset_interfaces/collection';
import mainApi from '@/api/buster_rest/instances';
import type { SharePostRequest } from '@buster/server-shared/share';
import type { ShareDeleteRequest, ShareUpdateRequest } from '@buster/server-shared/share';
import type { ShareDeleteRequest, SharePermissionsUpdateRequest } from '@buster/server-shared/share';
export const collectionsGetList = async (params: {
/** Current page number (1-based indexing) */
@ -91,7 +91,7 @@ export const updateCollectionShare = async ({
id
}: {
id: string;
params: ShareUpdateRequest;
params: SharePermissionsUpdateRequest;
}) => {
return mainApi
.put<BusterCollection>(`/collections/${id}/sharing`, params)

View File

@ -6,7 +6,7 @@ import type {
import type {
SharePostRequest,
ShareDeleteRequest,
ShareUpdateRequest
SharePermissionsUpdateRequest
} from '@buster/server-shared/share';
import mainApi from '@/api/buster_rest/instances';
import { serverFetch } from '@/api/createServerInstance';
@ -103,7 +103,7 @@ export const updateDashboardShare = async ({
id
}: {
id: string;
params: ShareUpdateRequest;
params: SharePermissionsUpdateRequest;
}) => {
return mainApi
.put<BusterDashboardResponse>(`/dashboards/${id}/sharing`, params)

View File

@ -17,7 +17,7 @@ import type {
ShareUpdateResponse,
MetricDownloadResponse
} from '@buster/server-shared/metrics';
import type { ShareDeleteRequest, ShareUpdateRequest } from '@buster/server-shared/share';
import type { ShareDeleteRequest, SharePermissionsUpdateRequest } from '@buster/server-shared/share';
import { serverFetch } from '@/api/createServerInstance';
import { mainApi, mainApiV2 } from '../instances';
import { SharePostRequest } from '@buster/server-shared/share';
@ -99,7 +99,7 @@ export const updateMetricShare = async ({
id
}: {
id: string;
params: ShareUpdateRequest;
params: SharePermissionsUpdateRequest;
}) => {
return mainApi
.put<ShareUpdateResponse>(`/metric_files/${id}/sharing`, params)

View File

@ -25,15 +25,26 @@ export const ASSET_ICONS = {
export const assetTypeToIcon = (assetType: ShareAssetType) => {
switch (assetType) {
case 'metric':
case 'metric_file':
return ASSET_ICONS.metrics;
case 'dashboard':
case 'dashboard_file':
return ASSET_ICONS.dashboards;
case 'collection':
case 'collection_file':
return ASSET_ICONS.collections;
case 'chat':
case 'thread':
return ASSET_ICONS.chats;
case 'report':
return ASSET_ICONS.reports;
case 'data_source':
case 'filter':
case 'dataset':
case 'tool':
case 'source':
case 'dataset_permission':
return ASSET_ICONS.table;
default: {
const _result: never = assetType;
return ASSET_ICONS.metrics;

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Text } from '@/components/ui/typography';
import { Button } from '@/components/ui/buttons';
import { Download4 } from '@/components/ui/icons';
import { Download4, CircleWarning } from '@/components/ui/icons';
import { cn } from '@/lib/classMerge';
import { downloadMetricFile } from '@/api/buster_rest/metrics/requests';
@ -15,21 +15,30 @@ export const MetricDataTruncatedWarning: React.FC<MetricDataTruncatedWarningProp
metricId
}) => {
const [isDownloading, setIsDownloading] = useState(false);
const [hasError, setHasError] = useState(false);
const handleDownload = async () => {
try {
setIsDownloading(true);
// Call the API to get the download URL
const response = await downloadMetricFile(metricId);
setHasError(false);
// Create a timeout promise that rejects after 3 minutes
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Download timeout')), 3 * 60 * 1000); // 3 minutes
});
// Race between the API call and the timeout
const response = await Promise.race([
downloadMetricFile(metricId),
timeoutPromise
]) as Awaited<ReturnType<typeof downloadMetricFile>>;
// Simply navigate to the download URL
// The response-content-disposition header will force a download
window.location.href = response.downloadUrl;
} catch (error) {
console.error('Failed to download metric file:', error);
// You might want to show an error toast here
setHasError(true);
} finally {
// Add a small delay before removing loading state since download happens async
setTimeout(() => {
@ -40,22 +49,31 @@ export const MetricDataTruncatedWarning: React.FC<MetricDataTruncatedWarningProp
return (
<div
className={cn('bg-background flex items-center justify-between rounded border p-4 shadow', className)}>
className={cn(
'bg-background flex items-center justify-between rounded border p-4 shadow',
hasError && 'border-red-500',
className
)}>
<div className="flex flex-col space-y-1">
<Text className="font-medium">This request returned more than 5,000 records</Text>
<Text size="xs" variant="secondary">
To see all records, you&apos;ll need to download the results.
<Text className="font-medium">
{hasError
? 'Download failed'
: 'This request returned more than 5,000 records'}
</Text>
<Text size="xs" variant={hasError ? 'danger' : 'secondary'}>
{hasError
? 'The download took too long or encountered an error. Please try again.'
: 'To see all records, you\'ll need to download the results.'}
</Text>
</div>
<Button
onClick={handleDownload}
loading={isDownloading}
variant="default"
variant={hasError ? 'danger' : 'default'}
className="ml-4"
prefix={<Download4 />}
>
Download
prefix={hasError ? <CircleWarning /> : <Download4 />}>
{hasError ? 'Try Again' : 'Download'}
</Button>
</div>
);
};
};