diff --git a/studio/components/grid/components/grid/Grid.tsx b/studio/components/grid/components/grid/Grid.tsx index 2f43c880290..ee5805736f5 100644 --- a/studio/components/grid/components/grid/Grid.tsx +++ b/studio/components/grid/components/grid/Grid.tsx @@ -8,7 +8,7 @@ import RowRenderer from './RowRenderer' import AwesomeDebouncePromise from 'awesome-debounce-promise' function rowKeyGetter(row: SupaRow) { - return row.idx + return row?.idx ?? -1 } interface IGrid extends GridProps { diff --git a/studio/components/grid/components/menu/RowContextMenu.tsx b/studio/components/grid/components/menu/RowContextMenu.tsx index a9b19acc00c..545f534c7ad 100644 --- a/studio/components/grid/components/menu/RowContextMenu.tsx +++ b/studio/components/grid/components/menu/RowContextMenu.tsx @@ -21,6 +21,8 @@ const RowContextMenu: React.FC = ({}) => { const { props } = p const { rowIdx } = props const row = state.rows[rowIdx] + if (!row) return + const { error } = state.rowService!.delete([row]) if (error) { if (state.onError) state.onError(error) diff --git a/studio/components/interfaces/Settings/Logs/LogSelection.tsx b/studio/components/interfaces/Settings/Logs/LogSelection.tsx index 8f02d8b09b9..01cc5473042 100644 --- a/studio/components/interfaces/Settings/Logs/LogSelection.tsx +++ b/studio/components/interfaces/Settings/Logs/LogSelection.tsx @@ -1,19 +1,15 @@ import { FC } from 'react' -import { IconX, Loading } from 'ui' +import { IconX } from 'ui' import { LogData, QueryType } from './Logs.types' -import DatabaseApiSelectionRender, { - DatabaseApiSelectionHeaderRender, -} from './LogSelectionRenderers/DatabaseApiSelectionRender' +import DatabaseApiSelectionRender from './LogSelectionRenderers/DatabaseApiSelectionRender' import DatabasePostgresSelectionRender from './LogSelectionRenderers/DatabasePostgresSelectionRender' -import FunctionInvocationSelectionRender, { - FunctionInvocationHeaderRender, -} from './LogSelectionRenderers/FunctionInvocationSelectionRender' +import FunctionInvocationSelectionRender from './LogSelectionRenderers/FunctionInvocationSelectionRender' import FunctionLogsSelectionRender from './LogSelectionRenderers/FunctionLogsSelectionRender' import DefaultExplorerSelectionRenderer from './LogSelectionRenderers/DefaultExplorerSelectionRenderer' import DefaultPreviewSelectionRenderer from './LogSelectionRenderers/DefaultPreviewSelectionRenderer' -import { isDefaultLogPreviewFormat, LogsEndpointParams, LogsTableName } from '.' +import { isDefaultLogPreviewFormat, LogsEndpointParams } from '.' import useSingleLog from 'hooks/analytics/useSingleLog' import Connecting from 'components/ui/Loading/Loading' diff --git a/studio/components/layouts/StorageLayout/StorageLayout.tsx b/studio/components/layouts/StorageLayout/StorageLayout.tsx index a04d241504d..0aa2181bc65 100644 --- a/studio/components/layouts/StorageLayout/StorageLayout.tsx +++ b/studio/components/layouts/StorageLayout/StorageLayout.tsx @@ -64,6 +64,11 @@ const StorageLayout: FC = ({ title, children }) => { storageExplorerStore.setLoaded(true) } + const onSelectCreateBucket = async (bucketName: string, isPublic: boolean) => { + const bucket = await createBucket(bucketName, isPublic) + if (bucket.name) router.push(`/project/${ref}/storage/buckets/${bucket.name}`) + } + const onSelectDeleteBucket = async (bucket: any) => { const res = await deleteBucket(bucket) // Ideally this should be within deleteBucket as its a necessary side effect @@ -101,7 +106,7 @@ const StorageLayout: FC = ({ title, children }) => { { function onCopyCell() { if (columns && cellPosition) { const { idx, rowIdx } = cellPosition - const colKey = columns[idx].key + const column = columns[idx] + if (!column) return + + const colKey = column.key const cellValue = results[rowIdx]?.[colKey] ?? '' const value = formatClipboardValue(cellValue) copyToClipboard(value) diff --git a/studio/components/to-be-cleaned/Storage/CreateBucketModal.tsx b/studio/components/to-be-cleaned/Storage/CreateBucketModal.tsx index 407f7abd82e..334df29c71f 100644 --- a/studio/components/to-be-cleaned/Storage/CreateBucketModal.tsx +++ b/studio/components/to-be-cleaned/Storage/CreateBucketModal.tsx @@ -4,13 +4,13 @@ import { Modal, Alert, Button, Input, Toggle } from 'ui' interface Props { visible: boolean onSelectCancel: () => {} - onSelectSave: () => {} + onSelectSave: (bucketName: string, isPublic: boolean) => {} } const CreateBucketModal: FC = ({ visible = false, onSelectCancel = () => {}, - onSelectSave = (bucketName: string, isPublic: boolean) => {}, + onSelectSave = () => {}, }) => { const [saving, setSaving] = useState(false) const [error, setError] = useState('') diff --git a/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.js b/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.js index 0c3cb6101ea..de10f46aefe 100644 --- a/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.js +++ b/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.js @@ -234,6 +234,8 @@ const StorageExplorer = observer(({ bucket }) => { const onDeleteSelectedFiles = async () => { if (selectedItemsToDelete.length === 1) { const [itemToDelete] = selectedItemsToDelete + if (!itemToDelete) return + switch (itemToDelete.type) { case STORAGE_ROW_TYPES.FOLDER: await deleteFolder(itemToDelete) diff --git a/studio/localStores/storageExplorer/StorageExplorerStore.js b/studio/localStores/storageExplorer/StorageExplorerStore.js index 071cdeb1c91..f3bcfd79a4b 100644 --- a/studio/localStores/storageExplorer/StorageExplorerStore.js +++ b/studio/localStores/storageExplorer/StorageExplorerStore.js @@ -404,31 +404,39 @@ class StorageExplorerStore { // Already generated signed URL copyToClipboard(filePreview.url, () => { this.ui.setNotification({ - message: `Copied URL for ${file.name} to clipboard.`, category: 'success', + message: `Copied URL for ${file.name} to clipboard.`, duration: 4000, }) }) } else { // Need to generate signed URL, and might as well save it to cache as well const signedUrl = await this.fetchFilePreview(file.name) - let formattedUrl = new URL(signedUrl) - formattedUrl.searchParams.set('t', new Date().toISOString()) - copyToClipboard(formattedUrl.toString(), () => { + try { + let formattedUrl = new URL(signedUrl) + formattedUrl.searchParams.set('t', new Date().toISOString()) + + copyToClipboard(formattedUrl.toString(), () => { + this.ui.setNotification({ + category: 'success', + message: `Copied URL for ${file.name} to clipboard.`, + duration: 4000, + }) + }) + const fileCache = { + id: file.id, + url: formattedUrl.toString(), + expiresIn: DEFAULT_EXPIRY, + fetchedAt: Date.now(), + } + this.addFileToPreviewCache(fileCache) + } catch (error) { this.ui.setNotification({ - message: `Copied URL for ${file.name} to clipboard.`, - category: 'success', - duration: 4000, + category: 'error', + message: `Failed to copy URL: ${error}`, }) - }) - const fileCache = { - id: file.id, - url: formattedUrl.toString(), - expiresIn: DEFAULT_EXPIRY, - fetchedAt: Date.now(), } - this.addFileToPreviewCache(fileCache) } } @@ -443,7 +451,7 @@ class StorageExplorerStore { }) } - const { error } = await this.supabaseClient.storage.createBucket(bucketName, { + const { data, error } = await this.supabaseClient.storage.createBucket(bucketName, { public: isPublic, }) if (error) { @@ -456,6 +464,7 @@ class StorageExplorerStore { await this.fetchBuckets() this.closeCreateBucketModal() + return data } openBucket = async (bucket) => { diff --git a/studio/pages/project/[ref]/logs-explorer/index.tsx b/studio/pages/project/[ref]/logs-explorer/index.tsx index 573c6840730..e4db358c99a 100644 --- a/studio/pages/project/[ref]/logs-explorer/index.tsx +++ b/studio/pages/project/[ref]/logs-explorer/index.tsx @@ -73,6 +73,9 @@ export const LogsExplorerPage: NextPageWithLayout = () => { pathname: router.pathname, query: { ...router.query, q: template.searchString }, }) + content.addRecentLogSqlSnippet({ + sql: template.searchString, + }) } const handleRun = (value?: string | React.MouseEvent) => { diff --git a/studio/pages/project/[ref]/logs-explorer/recent.tsx b/studio/pages/project/[ref]/logs-explorer/recent.tsx index 48eb03ca5c4..8edc8ffad66 100644 --- a/studio/pages/project/[ref]/logs-explorer/recent.tsx +++ b/studio/pages/project/[ref]/logs-explorer/recent.tsx @@ -1,8 +1,8 @@ -import React, { useEffect } from 'react' +import { useEffect } from 'react' import Link from 'next/link' import { observer } from 'mobx-react-lite' -import { Button, IconClock, IconSave, Loading } from 'ui' -import { useStore, withAuth } from 'hooks' +import { Button, IconClock } from 'ui' +import { useStore } from 'hooks' import RecentQueriesItem from 'components/interfaces/Settings/Logs/RecentQueriesItem' import LogsExplorerLayout from 'components/layouts/LogsExplorerLayout/LogsExplorerLayout'