diff --git a/apps/web/src/api/buster_rest/collections/queryRequests.ts b/apps/web/src/api/buster_rest/collections/queryRequests.ts index 822fb9573..2b972a7ca 100644 --- a/apps/web/src/api/buster_rest/collections/queryRequests.ts +++ b/apps/web/src/api/buster_rest/collections/queryRequests.ts @@ -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; }) || [] diff --git a/apps/web/src/api/buster_rest/collections/requests.ts b/apps/web/src/api/buster_rest/collections/requests.ts index e4d2bfad8..c1aaef093 100644 --- a/apps/web/src/api/buster_rest/collections/requests.ts +++ b/apps/web/src/api/buster_rest/collections/requests.ts @@ -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(`/collections/${id}/sharing`, params) diff --git a/apps/web/src/api/buster_rest/dashboards/requests.ts b/apps/web/src/api/buster_rest/dashboards/requests.ts index c19a6e60b..54a12e2a6 100644 --- a/apps/web/src/api/buster_rest/dashboards/requests.ts +++ b/apps/web/src/api/buster_rest/dashboards/requests.ts @@ -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(`/dashboards/${id}/sharing`, params) diff --git a/apps/web/src/api/buster_rest/metrics/requests.ts b/apps/web/src/api/buster_rest/metrics/requests.ts index 171041fd1..bbd8e8931 100644 --- a/apps/web/src/api/buster_rest/metrics/requests.ts +++ b/apps/web/src/api/buster_rest/metrics/requests.ts @@ -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(`/metric_files/${id}/sharing`, params) diff --git a/apps/web/src/components/features/config/assetIcons.tsx b/apps/web/src/components/features/config/assetIcons.tsx index 00f683c44..5c0f91117 100644 --- a/apps/web/src/components/features/config/assetIcons.tsx +++ b/apps/web/src/components/features/config/assetIcons.tsx @@ -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; diff --git a/apps/web/src/controllers/MetricController/MetricViewChart/MetricDataTruncatedWarning.tsx b/apps/web/src/controllers/MetricController/MetricViewChart/MetricDataTruncatedWarning.tsx index 017d7dca3..d94831d0c 100644 --- a/apps/web/src/controllers/MetricController/MetricViewChart/MetricDataTruncatedWarning.tsx +++ b/apps/web/src/controllers/MetricController/MetricViewChart/MetricDataTruncatedWarning.tsx @@ -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 { 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>; + // 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 + className={cn( + 'bg-background flex items-center justify-between rounded border p-4 shadow', + hasError && 'border-red-500', + className + )}>
- This request returned more than 5,000 records - - To see all records, you'll need to download the results. + + {hasError + ? 'Download failed' + : 'This request returned more than 5,000 records'} + + + {hasError + ? 'The download took too long or encountered an error. Please try again.' + : 'To see all records, you\'ll need to download the results.'}
); -}; \ No newline at end of file +};