mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
chore(studio): filter policies by bucket name (#40456)
* filter buckets * Simplify * General refactors * Smol * Add missing empty state for search * Clean up 😬 --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
8b5a087eb7
commit
eaf35c14c6
18 files changed
+139
-83
No files matched your search
@@ -27,9 +27,9 @@ import { getGeneralPolicyTemplates } from './PolicyEditorModal.constants'
|
||||
import PolicyEditorModalTitle from './PolicyEditorModalTitle'
|
||||
|
||||
interface PolicyEditorModalProps {
|
||||
visible: boolean
|
||||
schema: string
|
||||
table: string
|
||||
visible?: boolean
|
||||
schema?: string
|
||||
table?: string
|
||||
selectedPolicyToEdit: any
|
||||
showAssistantPreview?: boolean
|
||||
onSelectCancel: () => void
|
||||
|
||||
+2
-2
@@ -4,10 +4,10 @@ import { useState } from 'react'
|
||||
import { Badge, HoverCard, HoverCardContent, HoverCardTrigger, Input, cn } from 'ui'
|
||||
|
||||
import { Markdown } from 'components/interfaces/Markdown'
|
||||
import { SimpleCodeBlock } from 'ui'
|
||||
import CardButton from 'components/ui/CardButton'
|
||||
import CopyButton from 'components/ui/CopyButton'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { SimpleCodeBlock } from 'ui'
|
||||
import {
|
||||
getGeneralPolicyTemplates,
|
||||
getQueuePolicyTemplates,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'
|
||||
import { useParams } from 'common'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useState } from 'react'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useDatabaseHooksQuery } from 'data/database-triggers/database-triggers-query'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
|
||||
@@ -7,7 +7,7 @@ import { toast } from 'sonner'
|
||||
import { useParams } from 'common'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { useDatabasePublicationsQuery } from 'data/database-publications/database-publications-query'
|
||||
import { useDatabasePublicationUpdateMutation } from 'data/database-publications/database-publications-update-mutation'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
|
||||
@@ -4,9 +4,9 @@ import Link from 'next/link'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { useDatabasePublicationsQuery } from 'data/database-publications/database-publications-query'
|
||||
import { useTablesQuery } from 'data/tables/tables-query'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
@@ -94,7 +94,7 @@ export const PublicationsTables = () => {
|
||||
|
||||
{isSuccess &&
|
||||
(tables.length === 0 ? (
|
||||
<NoSearchResults />
|
||||
<NoSearchResults searchString={filterString} onResetFilter={() => setFilterString('')} />
|
||||
) : (
|
||||
<Card>
|
||||
<Table>
|
||||
|
||||
@@ -4,21 +4,21 @@ import { Plus, Search, X } from 'lucide-react'
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs'
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
import type { PostgresRole } from '@supabase/postgres-meta'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import SparkBar from 'components/ui/SparkBar'
|
||||
import { useDatabaseRolesQuery } from 'data/database-roles/database-roles-query'
|
||||
import { useMaxConnectionsQuery } from 'data/database/max-connections-query'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { handleErrorOnDelete, useQueryStateWithSelect } from 'hooks/misc/useQueryStateWithSelect'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Badge, Button, Input, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
import { CreateRolePanel } from './CreateRolePanel'
|
||||
import { DeleteRoleModal } from './DeleteRoleModal'
|
||||
import { RoleRow } from './RoleRow'
|
||||
import { RoleRowSkeleton } from './RoleRowSkeleton'
|
||||
import { SUPABASE_ROLES } from './Roles.constants'
|
||||
import type { PostgresRole } from '@supabase/postgres-meta'
|
||||
|
||||
type SUPABASE_ROLE = (typeof SUPABASE_ROLES)[number]
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import { useState } from 'react'
|
||||
|
||||
import { PostgresColumn } from '@supabase/postgres-meta'
|
||||
import { useParams } from 'common'
|
||||
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
|
||||
import { isTableLike } from 'data/table-editor/table-editor-types'
|
||||
@@ -120,7 +120,10 @@ export const ColumnList = ({
|
||||
{isSuccess && (
|
||||
<>
|
||||
{columns.length === 0 ? (
|
||||
<NoSearchResults />
|
||||
<NoSearchResults
|
||||
searchString={filterString}
|
||||
onResetFilter={() => setFilterString('')}
|
||||
/>
|
||||
) : (
|
||||
<div>
|
||||
<Table
|
||||
|
||||
@@ -3,7 +3,7 @@ import { UIEvent, useMemo } from 'react'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
|
||||
import { useOrgIntegrationsQuery } from 'data/integrations/integrations-query-org-only'
|
||||
import { usePermissionsQuery } from 'data/permissions/permissions-query'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Search } from 'lucide-react'
|
||||
import { parseAsString, useQueryState } from 'nuqs'
|
||||
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
|
||||
import { buttonVariants, cn, Tabs_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_ } from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { PostgresPolicy } from '@supabase/postgres-meta'
|
||||
import { difference, groupBy } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { Bucket } from 'data/storage/buckets-query'
|
||||
import { STORAGE_CLIENT_LIBRARY_MAPPINGS } from './Storage.constants'
|
||||
import type { StoragePolicyFormField } from './Storage.types'
|
||||
|
||||
@@ -20,8 +22,8 @@ const shortHash = (str: string) => {
|
||||
* Output: [{ bucket: <string>, policies: <Policy[]> }]
|
||||
* @param {Array} policies: All policies from a table in a schema
|
||||
*/
|
||||
export const formatPoliciesForStorage = (buckets: any[], policies: any[]) => {
|
||||
if (policies.length === 0) return policies
|
||||
export const formatPoliciesForStorage = (buckets: Bucket[], policies: PostgresPolicy[]) => {
|
||||
if (policies.length === 0) return []
|
||||
|
||||
/**
|
||||
* Format policies from storage objects to:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { PostgresPolicy } from '@supabase/postgres-meta'
|
||||
import { useParams } from 'common'
|
||||
import { filter, find, get, isEmpty } from 'lodash'
|
||||
import { useState } from 'react'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { Search, X } from 'lucide-react'
|
||||
import { parseAsString, useQueryState } from 'nuqs'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import PolicyEditorModal from 'components/interfaces/Auth/Policies/PolicyEditorModal'
|
||||
@@ -9,13 +12,16 @@ import {
|
||||
ScaffoldSectionDescription,
|
||||
ScaffoldSectionTitle,
|
||||
} from 'components/layouts/Scaffold'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
|
||||
import { useDatabasePolicyCreateMutation } from 'data/database-policies/database-policy-create-mutation'
|
||||
import { useDatabasePolicyDeleteMutation } from 'data/database-policies/database-policy-delete-mutation'
|
||||
import { useDatabasePolicyUpdateMutation } from 'data/database-policies/database-policy-update-mutation'
|
||||
import { useBucketsQuery } from 'data/storage/buckets-query'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Button } from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import { formatPoliciesForStorage } from '../Storage.utils'
|
||||
import { StoragePoliciesBucketRow } from './StoragePoliciesBucketRow'
|
||||
@@ -23,18 +29,24 @@ import StoragePoliciesEditPolicyModal from './StoragePoliciesEditPolicyModal'
|
||||
import StoragePoliciesPlaceholder from './StoragePoliciesPlaceholder'
|
||||
|
||||
export const StoragePolicies = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { ref: projectRef } = useParams()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
|
||||
const { data, isLoading: isLoadingBuckets } = useBucketsQuery({ projectRef })
|
||||
const buckets = data ?? []
|
||||
const [selectedPolicyToEdit, setSelectedPolicyToEdit] = useState<PostgresPolicy>()
|
||||
const [selectedPolicyToDelete, setSelectedPolicyToDelete] = useState<PostgresPolicy>()
|
||||
const [isEditingPolicyForBucket, setIsEditingPolicyForBucket] = useState<{
|
||||
bucket: string
|
||||
table: string
|
||||
}>()
|
||||
const [searchString, setSearchString] = useQueryState(
|
||||
'search',
|
||||
parseAsString.withDefault('').withOptions({ history: 'replace', clearOnDefault: true })
|
||||
)
|
||||
|
||||
const [selectedPolicyToEdit, setSelectedPolicyToEdit] = useState<any>({})
|
||||
const [selectedPolicyToDelete, setSelectedPolicyToDelete] = useState<any>({})
|
||||
const [isEditingPolicyForBucket, setIsEditingPolicyForBucket] = useState<any>({})
|
||||
const { data: buckets = [], isLoading: isLoadingBuckets } = useBucketsQuery({ projectRef })
|
||||
|
||||
const {
|
||||
data: policiesData,
|
||||
data: policies = [],
|
||||
refetch,
|
||||
isLoading: isLoadingPolicies,
|
||||
} = useDatabasePoliciesQuery({
|
||||
@@ -42,7 +54,6 @@ export const StoragePolicies = () => {
|
||||
connectionString: project?.connectionString,
|
||||
schema: 'storage',
|
||||
})
|
||||
const policies = policiesData ?? []
|
||||
|
||||
const isLoading = isLoadingBuckets || isLoadingPolicies
|
||||
|
||||
@@ -54,7 +65,7 @@ export const StoragePolicies = () => {
|
||||
onSuccess: async () => {
|
||||
await refetch()
|
||||
toast.success('Successfully deleted policy!')
|
||||
setSelectedPolicyToDelete({})
|
||||
setSelectedPolicyToDelete(undefined)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -62,24 +73,50 @@ export const StoragePolicies = () => {
|
||||
const showStoragePolicyEditor =
|
||||
isEmpty(selectedPolicyToEdit) &&
|
||||
!isEmpty(isEditingPolicyForBucket) &&
|
||||
get(isEditingPolicyForBucket, ['bucket'], '').length > 0
|
||||
(isEditingPolicyForBucket.bucket ?? '').length > 0
|
||||
|
||||
const showGeneralPolicyEditor = !isEmpty(isEditingPolicyForBucket) && !showStoragePolicyEditor
|
||||
|
||||
// Policies under storage.objects
|
||||
const storageObjectsPolicies = filter(policies, { table: 'objects' })
|
||||
const formattedStorageObjectPolicies = formatPoliciesForStorage(buckets, storageObjectsPolicies)
|
||||
const ungroupedPolicies = get(
|
||||
find(formattedStorageObjectPolicies, { name: 'Ungrouped' }),
|
||||
['policies'],
|
||||
[]
|
||||
const storageObjectsPolicies = policies.filter(
|
||||
(x) => x.schema === 'storage' && x.table === 'objects'
|
||||
)
|
||||
const formattedStorageObjectPolicies = formatPoliciesForStorage(buckets, storageObjectsPolicies)
|
||||
const ungroupedPolicies =
|
||||
formattedStorageObjectPolicies.find((x) => x.name === 'Ungrouped')?.policies ?? []
|
||||
|
||||
// Policies under storage.buckets
|
||||
const storageBucketPolicies = filter(policies, { table: 'buckets' })
|
||||
const storageBucketPolicies = policies.filter(
|
||||
(x) => x.schema === 'storage' && x.table === 'buckets'
|
||||
)
|
||||
|
||||
/**
|
||||
* Filter buckets based on search string
|
||||
* - Filter buckets by name matching the search string
|
||||
* - Show all policies for filtered buckets (policies are not filtered)
|
||||
*/
|
||||
const filteredBucketsWithPolicies = useMemo(() => {
|
||||
const searchFilter = searchString?.toLowerCase() || ''
|
||||
|
||||
// Filter buckets by name if search filter is present
|
||||
const filteredBucketsList = searchFilter
|
||||
? buckets.filter((bucket) => bucket.name.toLowerCase().includes(searchFilter))
|
||||
: buckets
|
||||
|
||||
// Get policies for filtered buckets (show all policies, don't filter them)
|
||||
// Show all filtered buckets, even if they don't have policies (similar to auth/policies.tsx)
|
||||
const filteredBucketsWithPoliciesList = filteredBucketsList.map((bucket) => {
|
||||
const policies =
|
||||
formattedStorageObjectPolicies.find((x) => x.name === bucket.name)?.policies ?? []
|
||||
return { bucket, policies }
|
||||
})
|
||||
|
||||
// Schema-level policies should always be shown, unaffected by search filter
|
||||
return filteredBucketsWithPoliciesList
|
||||
}, [buckets, searchString, formattedStorageObjectPolicies])
|
||||
|
||||
const onSelectPolicyAdd = (bucketName = '', table = '') => {
|
||||
setSelectedPolicyToEdit({})
|
||||
setSelectedPolicyToEdit(undefined)
|
||||
setIsEditingPolicyForBucket({ bucket: bucketName, table })
|
||||
}
|
||||
|
||||
@@ -89,11 +126,11 @@ export const StoragePolicies = () => {
|
||||
}
|
||||
|
||||
const onCancelPolicyEdit = () => {
|
||||
setIsEditingPolicyForBucket({})
|
||||
setIsEditingPolicyForBucket(undefined)
|
||||
}
|
||||
|
||||
const onSelectPolicyDelete = (policy: any) => setSelectedPolicyToDelete(policy)
|
||||
const onCancelPolicyDelete = () => setSelectedPolicyToDelete({})
|
||||
const onCancelPolicyDelete = () => setSelectedPolicyToDelete(undefined)
|
||||
|
||||
const onSavePolicySuccess = async () => {
|
||||
toast.success('Successfully saved policy!')
|
||||
@@ -155,6 +192,10 @@ export const StoragePolicies = () => {
|
||||
console.error('Project is required')
|
||||
return true
|
||||
}
|
||||
if (!selectedPolicyToEdit) {
|
||||
console.error('Unable to find policy')
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
await updateDatabasePolicy({
|
||||
@@ -172,6 +213,8 @@ export const StoragePolicies = () => {
|
||||
|
||||
const onDeletePolicy = async () => {
|
||||
if (!project) return console.error('Project is required')
|
||||
if (!selectedPolicyToDelete) return console.error('Unable to find policy')
|
||||
|
||||
deleteDatabasePolicy({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
@@ -180,7 +223,7 @@ export const StoragePolicies = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full w-full flex-col">
|
||||
<>
|
||||
{isLoading ? (
|
||||
<ScaffoldSection isFullWidth>
|
||||
<GenericSkeletonLoader />
|
||||
@@ -194,22 +237,51 @@ export const StoragePolicies = () => {
|
||||
</ScaffoldSectionDescription>
|
||||
{buckets.length === 0 && <StoragePoliciesPlaceholder />}
|
||||
|
||||
{buckets.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<Input
|
||||
size="tiny"
|
||||
placeholder="Filter buckets"
|
||||
className="block"
|
||||
containerClassName="w-full lg:w-52 [&>div>svg]:-mt-0.5"
|
||||
value={searchString || ''}
|
||||
onChange={(e) => {
|
||||
const str = e.target.value
|
||||
setSearchString(str)
|
||||
}}
|
||||
icon={<Search size={14} />}
|
||||
actions={
|
||||
searchString ? (
|
||||
<Button
|
||||
size="tiny"
|
||||
type="text"
|
||||
className="p-0 h-5 w-5"
|
||||
icon={<X />}
|
||||
onClick={() => setSearchString('')}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{searchString.length > 0 && filteredBucketsWithPolicies.length === 0 && (
|
||||
<NoSearchResults
|
||||
searchString={searchString}
|
||||
onResetFilter={() => setSearchString('')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Sections for policies grouped by buckets */}
|
||||
<div className="flex flex-col gap-y-4">
|
||||
{buckets.map((bucket) => {
|
||||
const bucketPolicies = get(
|
||||
find(formattedStorageObjectPolicies, { name: bucket.name }),
|
||||
['policies'],
|
||||
[]
|
||||
).sort((a: any, b: any) => a.name.localeCompare(b.name))
|
||||
|
||||
{filteredBucketsWithPolicies.map(({ bucket, policies }) => {
|
||||
return (
|
||||
<StoragePoliciesBucketRow
|
||||
key={bucket.name}
|
||||
table="objects"
|
||||
label={bucket.name}
|
||||
bucket={bucket}
|
||||
policies={bucketPolicies}
|
||||
policies={policies}
|
||||
onSelectPolicyAdd={onSelectPolicyAdd}
|
||||
onSelectPolicyEdit={onSelectPolicyEdit}
|
||||
onSelectPolicyDelete={onSelectPolicyDelete}
|
||||
@@ -253,7 +325,7 @@ export const StoragePolicies = () => {
|
||||
{/* Only used for adding policies to buckets */}
|
||||
<StoragePoliciesEditPolicyModal
|
||||
visible={showStoragePolicyEditor}
|
||||
bucketName={isEditingPolicyForBucket.bucket}
|
||||
bucketName={isEditingPolicyForBucket?.bucket}
|
||||
onSelectCancel={onCancelPolicyEdit}
|
||||
onCreatePolicies={onCreatePolicies}
|
||||
onSaveSuccess={onSavePolicySuccess}
|
||||
@@ -263,7 +335,7 @@ export const StoragePolicies = () => {
|
||||
<PolicyEditorModal
|
||||
schema="storage"
|
||||
visible={showGeneralPolicyEditor}
|
||||
table={isEditingPolicyForBucket.table}
|
||||
table={isEditingPolicyForBucket?.table ?? ''}
|
||||
selectedPolicyToEdit={selectedPolicyToEdit}
|
||||
onSelectCancel={onCancelPolicyEdit}
|
||||
onCreatePolicy={onCreatePolicy}
|
||||
@@ -275,12 +347,12 @@ export const StoragePolicies = () => {
|
||||
danger
|
||||
visible={!isEmpty(selectedPolicyToDelete)}
|
||||
title="Confirm to delete policy"
|
||||
description={`This is permanent! Are you sure you want to delete the policy "${selectedPolicyToDelete.name}"`}
|
||||
description={`This is permanent! Are you sure you want to delete the policy "${selectedPolicyToDelete?.name}"`}
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={onCancelPolicyDelete}
|
||||
onSelectConfirm={onDeletePolicy}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import SVG from 'react-inlinesvg'
|
||||
|
||||
/**
|
||||
* To be deprecated in favor of NoSearchResults in components/ui
|
||||
*/
|
||||
export const NoSearchResults = () => {
|
||||
return (
|
||||
<div className="flex h-64 flex-col items-center justify-center">
|
||||
<SVG
|
||||
src={`${BASE_PATH}/img/no-search-results.svg`}
|
||||
preProcessor={(code) =>
|
||||
code.replace(/svg/, 'svg className="mb-2 w-16 h-16 text-color-inherit"')
|
||||
}
|
||||
/>
|
||||
<p className="w-64 text-center text-sm opacity-50">
|
||||
Hmm, we couldn't find any results that match your query.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoSearchResults
|
||||
@@ -32,5 +32,3 @@ export const NoSearchResults = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoSearchResults
|
||||
@@ -7,7 +7,7 @@ import { PROJECT_STATUS } from 'lib/constants'
|
||||
import type { ResponseError, UseCustomQueryOptions } from 'types'
|
||||
import { databasePoliciesKeys } from './keys'
|
||||
|
||||
export type DatabasePoliciesVariables = {
|
||||
type DatabasePoliciesVariables = {
|
||||
projectRef?: string
|
||||
connectionString?: string | null
|
||||
schema?: string
|
||||
|
||||
@@ -10,7 +10,7 @@ import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
|
||||
import { withAuth } from 'hooks/misc/withAuth'
|
||||
|
||||
@@ -13,7 +13,7 @@ import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import NoSearchResults from 'components/ui/NoSearchResults'
|
||||
import { NoSearchResults } from 'components/ui/NoSearchResults'
|
||||
import { DOCS_URL } from 'lib/constants'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
|
||||
@@ -100,7 +100,11 @@ const BucketPage: NextPageWithLayout = () => {
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Link href={`/project/${ref}/storage/files/policies`}>Policies</Link>
|
||||
<Link
|
||||
href={`/project/${ref}/storage/files/policies?search=${encodeURIComponent(bucket?.name ?? '')}`}
|
||||
>
|
||||
Policies
|
||||
</Link>
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
||||
Reference in new issue
Block a user