diff --git a/apps/studio/components/layouts/ProjectLayout/ProductMenuBar.tsx b/apps/studio/components/layouts/ProjectLayout/ProductMenuBar.tsx index ace91838fc7..8c0c63f9956 100644 --- a/apps/studio/components/layouts/ProjectLayout/ProductMenuBar.tsx +++ b/apps/studio/components/layouts/ProjectLayout/ProductMenuBar.tsx @@ -19,9 +19,7 @@ const ProductMenuBar = ({ title, children }: PropsWithChildren

{title}

-
- {children} -
+
{children}
) } diff --git a/apps/studio/components/layouts/SQLEditorLayout/QueryItem.tsx b/apps/studio/components/layouts/SQLEditorLayout/QueryItem.tsx index b14e366393a..c4bf3570abb 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/QueryItem.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/QueryItem.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx' import { useParams } from 'common' +import { noop } from 'lodash' import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useRef, useState } from 'react' @@ -8,6 +9,7 @@ import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, + Checkbox_Shadcn_, IconAlertCircle, IconAlertTriangle, IconEye, @@ -30,10 +32,19 @@ import { QueryItemActions } from './QueryItemActions' export interface QueryItemProps { tabInfo: SqlSnippet + isSelected?: boolean + hasQueriesSelected?: boolean + onSelectQuery?: () => void + onDeleteQuery?: (ids: string[]) => void } -const QueryItem = ({ tabInfo }: QueryItemProps) => { - const router = useRouter() +const QueryItem = ({ + tabInfo, + isSelected = false, + hasQueriesSelected = false, + onSelectQuery = noop, + onDeleteQuery = noop, +}: QueryItemProps) => { const [open, setOpen] = useState(false) const [renameModalOpen, setRenameModalOpen] = useState(false) const [shareModalOpen, setShareModalOpen] = useState(false) @@ -42,7 +53,7 @@ const QueryItem = ({ tabInfo }: QueryItemProps) => { const { ref, id: activeId } = useParams() const snap = useSqlEditorStateSnapshot() - const { id, name, description, content } = tabInfo || {} + const { id, name, description, content, visibility } = tabInfo || {} const isActive = id === activeId const activeItemRef = useRef(null) @@ -55,25 +66,10 @@ const QueryItem = ({ tabInfo }: QueryItemProps) => { isDownloadSnippetModalOpen const { mutate: deleteContent, isLoading: isDeleting } = useContentDeleteMutation({ - onSuccess(data) { - if (data.length > 0) snap.removeSnippet(data[0]) - - const existingSnippetIds = (snap.orders[ref!] ?? []).filter((x) => x !== id) - if (existingSnippetIds.length === 0) { - router.push(`/project/${ref}/sql/new`) - } else { - router.push(`/project/${ref}/sql/${existingSnippetIds[0]}`) - } - }, - onError(error, data) { + onSuccess: (data) => onDeleteQuery(data), + onError: (error, data) => { if (error.code === 404 && error.message.includes('Content not found')) { - if (data.ids) snap.removeSnippet(data.ids[0]) - const existingSnippetIds = (snap.orders[ref!] ?? []).filter((x) => x !== id) - if (existingSnippetIds.length === 0) { - router.push(`/project/${ref}/sql/new`) - } else { - router.push(`/project/${ref}/sql/${existingSnippetIds[0]}`) - } + onDeleteQuery(data.ids) } else { toast.error(`Failed to delete query: ${error.message}`) } @@ -114,26 +110,42 @@ const QueryItem = ({ tabInfo }: QueryItemProps) => {
) : null} > - -

onSelectQuery()} + /> + )} +

+ {name} -

- -
- { + + {!hasQueriesSelected && ( { onSelectShareQuery={() => setShareModalOpen(true)} onSelectDownloadQuery={() => setIsDownloadSnippetModalOpen(true)} /> - } + )}
@@ -200,7 +212,7 @@ const QueryItem = ({ tabInfo }: QueryItemProps) => {
- {tabInfo.visibility === 'project' && ( + {visibility === 'project' && ( This SQL snippet will be lost forever diff --git a/apps/studio/components/layouts/SQLEditorLayout/QueryItemActions.tsx b/apps/studio/components/layouts/SQLEditorLayout/QueryItemActions.tsx index 5e3f1a92703..a01b5a7c930 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/QueryItemActions.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/QueryItemActions.tsx @@ -54,7 +54,6 @@ export const QueryItemActions = ({ const project = useSelectedProject() const { id, name, visibility, content } = tabInfo || {} - const isActive = id === activeId const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', { resource: { type: 'sql', owner_id: profile?.id }, @@ -111,20 +110,13 @@ export const QueryItemActions = ({ {IS_PLATFORM ? ( - setOpen(true)} - className={clsx( - 'rounded p-1 cursor-pointer', - isActive - ? 'text-foreground-light hover:bg-border-stronger' - : 'text-background hover:bg-overlay-hover group-hover:text-foreground-light' - )} - > - - + - - } - placeholder="Search queries" - onChange={(e) => setSearchText(e.target.value.trim())} - value={searchText} - size="tiny" - actions={ - searchText && ( - - ) - } - /> -
-
- {searchText.length === 0 && ( -
- - -
- )} + const onConfirmDelete = async () => { + if (!ref) return console.error('Project ref is required') + deleteContent({ projectRef: ref, ids: selectedQueries }) + } - {searchText.length > 0 && - filteredFavoriteSnippets.length === 0 && - filteredProjectSnippets.length === 0 && - personalSnippets.length === 0 && ( -
-
-

No queries found

-

- Your search for "{searchText}" did not return any results -

-
- + return ( + <> +
+ + {isLoading ? ( +
+ + + +
+ ) : isSuccess ? ( +
+
+ + + } + placeholder="Search queries" + onChange={(e) => setSearchText(e.target.value.trim())} + value={searchText} + size="tiny" + actions={ + searchText && ( + + ) + } + /> +
+
+ {searchText.length === 0 && ( +
+ +
)} - {filteredProjectSnippets.length > 0 ? ( -
- + {searchText.length > 0 && + filteredFavoriteSnippets.length === 0 && + filteredProjectSnippets.length === 0 && + personalSnippets.length === 0 && ( +
+
+

No queries found

+

+ Your search for "{searchText}" did not return any results +

+
+ +
+ )} -
- {filteredProjectSnippets.map((tabInfo) => { - const { id } = tabInfo || {} - return - })} -
-
- ) : null} + {filteredProjectSnippets.length > 0 ? ( +
+ - {filteredFavoriteSnippets.length > 0 ? ( -
- -
- {filteredFavoriteSnippets.map((tabInfo) => { - const { id } = tabInfo || {} - return - })} +
+ {filteredProjectSnippets.map((tabInfo) => { + const { id } = tabInfo || {} + return ( + 0} + /> + ) + })} +
-
- ) : null} + ) : null} - {personalSnippets.length > 0 ? ( -
- -
- {personalSnippets.map((tabInfo) => { - const { id } = tabInfo || {} - return - })} + {selectedQueries.length > 0 && ( + <> + +
+ + + +
+ + )} + + {filteredFavoriteSnippets.length > 0 ? ( +
+ +
+ {filteredFavoriteSnippets.map((tabInfo) => { + const { id } = tabInfo || {} + return ( + 0} + onSelectQuery={() => onSelectQuery(id)} + onDeleteQuery={postDeleteCleanup} + /> + ) + })} +
-
- ) : null} + ) : null} + + {personalSnippets.length > 0 ? ( +
+ +
+ {personalSnippets.map((tabInfo) => { + const { id } = tabInfo || {} + return ( + 0} + onSelectQuery={() => onSelectQuery(id)} + onDeleteQuery={postDeleteCleanup} + /> + ) + })} +
+
+ ) : null} +
+
+ ) : ( +
+ )} +
+
+ setShowDeleteModal(false)} + > + +
+
+
+ + + This action cannot be undone + + The selected SQL snippets cannot be recovered once deleted + + +

+ Are you sure you want to delete the selected {selectedQueries.length} quer + {selectedQueries.length > 1 ? 'ies' : 'y'}? +

+
- ) : ( -
- )} - -
+ + + ) }) diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx b/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx index ceb23b01f9e..06d4de34363 100644 --- a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx +++ b/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx @@ -13,13 +13,13 @@ const FileExplorerHeaderSelection = () => { } = storageExplorerStore return ( -
+
-
+