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) => { return create(previousData, (draft) => {
draft.individual_permissions = ( draft.individual_permissions = (
draft.individual_permissions?.map((t) => { 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 }; if (found) return { ...t, ...found };
return t; 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 type { BusterCollection, BusterCollectionListItem } from '@/api/asset_interfaces/collection';
import mainApi from '@/api/buster_rest/instances'; import mainApi from '@/api/buster_rest/instances';
import type { SharePostRequest } from '@buster/server-shared/share'; 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: { export const collectionsGetList = async (params: {
/** Current page number (1-based indexing) */ /** Current page number (1-based indexing) */
@ -91,7 +91,7 @@ export const updateCollectionShare = async ({
id id
}: { }: {
id: string; id: string;
params: ShareUpdateRequest; params: SharePermissionsUpdateRequest;
}) => { }) => {
return mainApi return mainApi
.put<BusterCollection>(`/collections/${id}/sharing`, params) .put<BusterCollection>(`/collections/${id}/sharing`, params)

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Text } from '@/components/ui/typography'; import { Text } from '@/components/ui/typography';
import { Button } from '@/components/ui/buttons'; 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 { cn } from '@/lib/classMerge';
import { downloadMetricFile } from '@/api/buster_rest/metrics/requests'; import { downloadMetricFile } from '@/api/buster_rest/metrics/requests';
@ -15,21 +15,30 @@ export const MetricDataTruncatedWarning: React.FC<MetricDataTruncatedWarningProp
metricId metricId
}) => { }) => {
const [isDownloading, setIsDownloading] = useState(false); const [isDownloading, setIsDownloading] = useState(false);
const [hasError, setHasError] = useState(false);
const handleDownload = async () => { const handleDownload = async () => {
try { try {
setIsDownloading(true); setIsDownloading(true);
setHasError(false);
// Call the API to get the download URL
const response = await downloadMetricFile(metricId); // 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 // Simply navigate to the download URL
// The response-content-disposition header will force a download // The response-content-disposition header will force a download
window.location.href = response.downloadUrl; window.location.href = response.downloadUrl;
} catch (error) { } catch (error) {
console.error('Failed to download metric file:', error); console.error('Failed to download metric file:', error);
// You might want to show an error toast here setHasError(true);
} finally { } finally {
// Add a small delay before removing loading state since download happens async // Add a small delay before removing loading state since download happens async
setTimeout(() => { setTimeout(() => {
@ -40,22 +49,31 @@ export const MetricDataTruncatedWarning: React.FC<MetricDataTruncatedWarningProp
return ( return (
<div <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"> <div className="flex flex-col space-y-1">
<Text className="font-medium">This request returned more than 5,000 records</Text> <Text className="font-medium">
<Text size="xs" variant="secondary"> {hasError
To see all records, you&apos;ll need to download the results. ? '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> </Text>
</div> </div>
<Button <Button
onClick={handleDownload} onClick={handleDownload}
loading={isDownloading} loading={isDownloading}
variant="default" variant={hasError ? 'danger' : 'default'}
className="ml-4" className="ml-4"
prefix={<Download4 />} prefix={hasError ? <CircleWarning /> : <Download4 />}>
> {hasError ? 'Try Again' : 'Download'}
Download
</Button> </Button>
</div> </div>
); );
}; };