Swap useCheckPermissions with useAsyncCheckProjectPermissions part 3 (#37899)
* Swap useCheckPermissions with useAsyncCheckProjectPermissions part 3 * Fix loading state in edge function secrets
This commit is contained in:
@@ -25,7 +25,7 @@ import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import { AddonVariantId } from 'data/subscriptions/types'
|
||||
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
|
||||
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import {
|
||||
useIsAwsCloudProvider,
|
||||
@@ -66,8 +66,7 @@ import { NoticeBar } from './ui/NoticeBar'
|
||||
import { SpendCapDisabledSection } from './ui/SpendCapDisabledSection'
|
||||
|
||||
export function DiskManagementForm() {
|
||||
// isLoading is used to avoid a useCheckPermissions() race condition
|
||||
const { data: project, isLoading: isProjectLoading } = useSelectedProjectQuery()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { data: org } = useSelectedOrganizationQuery()
|
||||
const { ref: projectRef } = useParams()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -80,27 +79,18 @@ export function DiskManagementForm() {
|
||||
const isAws = useIsAwsCloudProvider()
|
||||
const isAwsK8s = useIsAwsK8sCloudProvider()
|
||||
|
||||
/**
|
||||
* Permissions
|
||||
*/
|
||||
const isPermissionsLoaded = usePermissionsLoaded()
|
||||
const canUpdateDiskConfiguration = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUpdateDiskConfiguration, isSuccess: isPermissionsLoaded } =
|
||||
useAsyncCheckProjectPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Component States
|
||||
*/
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false)
|
||||
const [refetchInterval, setRefetchInterval] = useState<number | false>(false)
|
||||
const [message, setMessageState] = useState<DiskManagementMessage | null>(null)
|
||||
const [advancedSettingsOpen, setAdvancedSettingsOpenState] = useState(false)
|
||||
|
||||
/**
|
||||
* Fetch form data
|
||||
*/
|
||||
const { data: databases, isSuccess: isReadReplicasSuccess } = useReadReplicasQuery({ projectRef })
|
||||
const { data, isSuccess: isDiskAttributesSuccess } = useDiskAttributesQuery(
|
||||
{ projectRef },
|
||||
@@ -146,9 +136,6 @@ export function DiskManagementForm() {
|
||||
const { data: diskAutoscaleConfig, isSuccess: isDiskAutoscaleConfigSuccess } =
|
||||
useDiskAutoscaleCustomConfigQuery({ projectRef }, { enabled: project != null && isAws })
|
||||
|
||||
/**
|
||||
* Handle default values
|
||||
*/
|
||||
const computeSize = project?.infra_compute_size
|
||||
? mapComputeSizeNameToAddonVariantId(project?.infra_compute_size)
|
||||
: undefined
|
||||
@@ -190,10 +177,6 @@ export function DiskManagementForm() {
|
||||
}
|
||||
}, [modifiedComputeSize, isDialogOpen, project])
|
||||
|
||||
/**
|
||||
* State handling
|
||||
*/
|
||||
|
||||
const isSuccess =
|
||||
isAddonsSuccess &&
|
||||
isDiskAttributesSuccess &&
|
||||
@@ -228,7 +211,7 @@ export function DiskManagementForm() {
|
||||
const isDirty = !!Object.keys(form.formState.dirtyFields).length
|
||||
const isProjectResizing = project?.status === PROJECT_STATUS.RESIZING
|
||||
const isProjectRequestingDiskChanges = isRequestingChanges && !isProjectResizing
|
||||
const noPermissions = isPermissionsLoaded && !canUpdateDiskConfiguration && !isProjectLoading
|
||||
const noPermissions = isPermissionsLoaded && !canUpdateDiskConfiguration
|
||||
|
||||
const { mutateAsync: updateDiskConfiguration, isLoading: isUpdatingDisk } =
|
||||
useUpdateDiskAttributesMutation({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { UseFormReturn } from 'react-hook-form'
|
||||
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { formatCurrency } from 'lib/helpers'
|
||||
@@ -153,11 +153,15 @@ export const DiskManagementReviewAndSubmitDialog = ({
|
||||
|
||||
const { formState, getValues } = form
|
||||
|
||||
const canUpdateDiskConfiguration = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUpdateDiskConfiguration } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Queries
|
||||
|
||||
@@ -5,7 +5,7 @@ import { toast } from 'sonner'
|
||||
|
||||
import AutoTextArea from 'components/to-be-cleaned/forms/AutoTextArea'
|
||||
import { executeSql } from 'data/sql/execute-sql-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { timeout } from 'lib/helpers'
|
||||
import { Loader } from 'lucide-react'
|
||||
@@ -42,7 +42,10 @@ const Description = ({ content, metadata, onChange = noop }: DescrptionProps) =>
|
||||
const hasChanged = value != contentText
|
||||
const animateCss = `transition duration-150`
|
||||
|
||||
const canUpdateDescription = useCheckPermissions(PermissionAction.TENANT_SQL_QUERY, '*')
|
||||
const { can: canUpdateDescription } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_QUERY,
|
||||
'*'
|
||||
)
|
||||
|
||||
const updateDescription = async () => {
|
||||
if (isUpdating || !canUpdateDescription) return false
|
||||
@@ -76,7 +79,9 @@ const Description = ({ content, metadata, onChange = noop }: DescrptionProps) =>
|
||||
|
||||
if (!canUpdateDescription) {
|
||||
return (
|
||||
<span className={`block ${value ? 'text-foreground' : ''}`}>{value || 'No description'}</span>
|
||||
<span className={`block text-sm ${value ? 'text-foreground' : ''}`}>
|
||||
{value || 'No description'}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
|
||||
import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query'
|
||||
import { useEdgeFunctionDeleteMutation } from 'data/edge-functions/edge-functions-delete-mutation'
|
||||
import { useEdgeFunctionUpdateMutation } from 'data/edge-functions/edge-functions-update-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import {
|
||||
Alert_Shadcn_,
|
||||
AlertDescription_Shadcn_,
|
||||
@@ -59,7 +59,10 @@ export const EdgeFunctionDetails = () => {
|
||||
const router = useRouter()
|
||||
const { ref: projectRef, functionSlug } = useParams()
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const canUpdateEdgeFunction = useCheckPermissions(PermissionAction.FUNCTIONS_WRITE, '*')
|
||||
const { can: canUpdateEdgeFunction } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.FUNCTIONS_WRITE,
|
||||
'*'
|
||||
)
|
||||
|
||||
const { data: apiKeys } = useAPIKeysQuery({ projectRef })
|
||||
const { data: settings } = useProjectSettingsV2Query({ projectRef })
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { Trash } from 'lucide-react'
|
||||
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import type { ProjectSecret } from 'data/secrets/secrets-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { TimestampInfo } from 'ui-patterns'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { TableCell, TableRow } from 'ui'
|
||||
import { TimestampInfo } from 'ui-patterns'
|
||||
|
||||
interface EdgeFunctionSecretProps {
|
||||
secret: ProjectSecret
|
||||
@@ -14,7 +13,10 @@ interface EdgeFunctionSecretProps {
|
||||
}
|
||||
|
||||
const EdgeFunctionSecret = ({ secret, onSelectDelete }: EdgeFunctionSecretProps) => {
|
||||
const canUpdateSecrets = useCheckPermissions(PermissionAction.SECRETS_WRITE, '*')
|
||||
const { can: canUpdateSecrets } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.SECRETS_WRITE,
|
||||
'*'
|
||||
)
|
||||
// [Joshen] Following API's validation:
|
||||
// https://github.com/supabase/infrastructure/blob/develop/api/src/routes/v1/projects/ref/secrets/secrets.controller.ts#L106
|
||||
const isReservedSecret = !!secret.name.match(/^(SUPABASE_).*/)
|
||||
|
||||
@@ -9,18 +9,8 @@ import NoPermission from 'components/ui/NoPermission'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useSecretsDeleteMutation } from 'data/secrets/secrets-delete-mutation'
|
||||
import { ProjectSecret, useSecretsQuery } from 'data/secrets/secrets-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import {
|
||||
Badge,
|
||||
Separator,
|
||||
Table,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
Card,
|
||||
} from 'ui'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { Badge, Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import AddNewSecretForm from './AddNewSecretForm'
|
||||
@@ -31,8 +21,14 @@ const EdgeFunctionSecrets = () => {
|
||||
const [searchString, setSearchString] = useState('')
|
||||
const [selectedSecret, setSelectedSecret] = useState<ProjectSecret>()
|
||||
|
||||
const canReadSecrets = useCheckPermissions(PermissionAction.SECRETS_READ, '*')
|
||||
const canUpdateSecrets = useCheckPermissions(PermissionAction.SECRETS_WRITE, '*')
|
||||
const { can: canReadSecrets, isLoading: isLoadingPermissions } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.SECRETS_READ,
|
||||
'*'
|
||||
)
|
||||
const { can: canUpdateSecrets } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.SECRETS_WRITE,
|
||||
'*'
|
||||
)
|
||||
|
||||
const { data, error, isLoading, isSuccess, isError } = useSecretsQuery({
|
||||
projectRef: projectRef,
|
||||
@@ -65,70 +61,76 @@ const EdgeFunctionSecrets = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && <GenericSkeletonLoader />}
|
||||
{isError && <AlertError error={error} subject="Failed to retrieve project secrets" />}
|
||||
{isSuccess && (
|
||||
{isLoading || isLoadingPermissions ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : (
|
||||
<>
|
||||
<div className="mb-6">
|
||||
{!canUpdateSecrets ? (
|
||||
<NoPermission resourceText="manage this project's edge function secrets" />
|
||||
) : (
|
||||
<AddNewSecretForm />
|
||||
)}
|
||||
</div>
|
||||
{canUpdateSecrets && !canReadSecrets ? (
|
||||
<NoPermission resourceText="view this project's edge function secrets" />
|
||||
) : canReadSecrets ? (
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-2">
|
||||
<Input
|
||||
size="small"
|
||||
className="w-full md:w-80"
|
||||
placeholder="Search for a secret"
|
||||
value={searchString}
|
||||
onChange={(e: any) => setSearchString(e.target.value)}
|
||||
icon={<Search size={14} />}
|
||||
/>
|
||||
</div>
|
||||
{isError && <AlertError error={error} subject="Failed to retrieve project secrets" />}
|
||||
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>{headers}</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{secrets.length > 0 ? (
|
||||
secrets.map((secret) => (
|
||||
<EdgeFunctionSecret
|
||||
key={secret.name}
|
||||
secret={secret}
|
||||
onSelectDelete={() => setSelectedSecret(secret)}
|
||||
/>
|
||||
))
|
||||
) : secrets.length === 0 && searchString.length > 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={headers.length}>
|
||||
<p className="text-sm text-foreground">No results found</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Your search for "{searchString}" did not return any results
|
||||
</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={headers.length}>
|
||||
<p className="text-sm text-foreground">No secrets created</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
There are no secrets associated with your project yet
|
||||
</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</div>
|
||||
) : null}
|
||||
{isSuccess && (
|
||||
<>
|
||||
<div className="mb-6">
|
||||
{!canUpdateSecrets ? (
|
||||
<NoPermission resourceText="manage this project's edge function secrets" />
|
||||
) : (
|
||||
<AddNewSecretForm />
|
||||
)}
|
||||
</div>
|
||||
{canUpdateSecrets && !canReadSecrets ? (
|
||||
<NoPermission resourceText="view this project's edge function secrets" />
|
||||
) : canReadSecrets ? (
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-2">
|
||||
<Input
|
||||
size="small"
|
||||
className="w-full md:w-80"
|
||||
placeholder="Search for a secret"
|
||||
value={searchString}
|
||||
onChange={(e: any) => setSearchString(e.target.value)}
|
||||
icon={<Search size={14} />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>{headers}</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{secrets.length > 0 ? (
|
||||
secrets.map((secret) => (
|
||||
<EdgeFunctionSecret
|
||||
key={secret.name}
|
||||
secret={secret}
|
||||
onSelectDelete={() => setSelectedSecret(secret)}
|
||||
/>
|
||||
))
|
||||
) : secrets.length === 0 && searchString.length > 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={headers.length}>
|
||||
<p className="text-sm text-foreground">No results found</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Your search for "{searchString}" did not return any results
|
||||
</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={headers.length}>
|
||||
<p className="text-sm text-foreground">No secrets created</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
There are no secrets associated with your project yet
|
||||
</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -35,12 +35,12 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { AlertTriangle, XIcon } from 'lucide-react'
|
||||
import { MouseEventHandler, useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
|
||||
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, cn } from 'ui'
|
||||
import { RoleImpersonationSelector } from '../RoleImpersonationSelector'
|
||||
import styles from './graphiql.module.css'
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
|
||||
export interface GraphiQLProps {
|
||||
fetcher: Fetcher
|
||||
@@ -76,7 +76,10 @@ const GraphiQLInterface = ({ theme }: GraphiQLInterfaceProps) => {
|
||||
const merge = useMergeQuery()
|
||||
const prettify = usePrettifyEditors()
|
||||
|
||||
const canReadJWTSecret = useCheckPermissions(PermissionAction.READ, 'field.jwt_secret')
|
||||
const { can: canReadJWTSecret } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.READ,
|
||||
'field.jwt_secret'
|
||||
)
|
||||
|
||||
const [rlsBypassedWarningDismissed, setRlsBypassedWarningDismissed] = useLocalStorage(
|
||||
LOCAL_STORAGE_KEYS.GRAPHIQL_RLS_BYPASS_WARNING,
|
||||
|
||||
@@ -9,7 +9,7 @@ import Panel from 'components/ui/Panel'
|
||||
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
|
||||
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
|
||||
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { Input, SimpleCodeBlock } from 'ui'
|
||||
|
||||
const generateInitSnippet = (endpoint: string) => ({
|
||||
@@ -65,7 +65,10 @@ const APIKeys = () => {
|
||||
|
||||
const jwtSecretUpdateStatus = data?.jwtSecretUpdateStatus
|
||||
|
||||
const canReadAPIKeys = useCheckPermissions(PermissionAction.READ, 'service_api_keys')
|
||||
const { can: canReadAPIKeys } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.READ,
|
||||
'service_api_keys'
|
||||
)
|
||||
|
||||
const isNotUpdatingJwtSecret =
|
||||
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useDatabaseCronJobCreateMutation } from 'data/database-cron-jobs/databa
|
||||
import { CronJob } from 'data/database-cron-jobs/database-cron-jobs-infinite-query'
|
||||
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
@@ -220,7 +220,7 @@ export const CreateCronJobSheet = ({
|
||||
const { mutate: upsertCronJob, isLoading: isUpserting } = useDatabaseCronJobCreateMutation()
|
||||
const isLoading = isLoadingGetCronJob || isUpserting
|
||||
|
||||
const canToggleExtensions = useCheckPermissions(
|
||||
const { can: canToggleExtensions } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
'extensions'
|
||||
)
|
||||
|
||||
@@ -74,7 +74,7 @@ const FORM_ID = 'create-queue-sidepanel'
|
||||
export const CreateQueueSheet = ({ isClosing, setIsClosing, onClose }: CreateQueueSheetProps) => {
|
||||
// This is for enabling pg_partman extension which will be used for partitioned queues (3rd kind of queue)
|
||||
// const [showEnableExtensionModal, setShowEnableExtensionModal] = useState(false)
|
||||
// const canToggleExtensions = useCheckPermissions(
|
||||
// const { can: canToggleExtensions } = useAsyncCheckPermissions(
|
||||
// PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
// 'extensions'
|
||||
// )
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from 'data/database-queues/database-queues-toggle-postgrest-mutation'
|
||||
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
|
||||
import { useTablesQuery } from 'data/tables/tables-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
Button,
|
||||
@@ -39,7 +39,7 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
export const QueuesSettings = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const canUpdatePostgrestConfig = useCheckPermissions(
|
||||
const { can: canUpdatePostgrestConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'custom_config_postgrest'
|
||||
)
|
||||
|
||||
@@ -6,16 +6,14 @@ import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
Input,
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from 'ui'
|
||||
|
||||
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
|
||||
import { useVaultSecretDecryptedValueQuery } from 'data/vault/vault-secret-decrypted-value-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Edit3, Eye, EyeOff, Key, Loader, MoreVertical, Trash } from 'lucide-react'
|
||||
import type { VaultSecret } from 'types'
|
||||
@@ -33,7 +31,10 @@ const SecretRow = ({ secret, onSelectRemove }: SecretRowProps) => {
|
||||
const [revealSecret, setRevealSecret] = useState(false)
|
||||
const name = secret?.name ?? 'No name provided'
|
||||
|
||||
const canManageSecrets = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
const { can: canManageSecrets } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
'tables'
|
||||
)
|
||||
|
||||
const { data: revealedValue, isFetching } = useVaultSecretDecryptedValueQuery(
|
||||
{
|
||||
@@ -102,41 +103,34 @@ const SecretRow = ({ secret, onSelectRemove }: SecretRowProps) => {
|
||||
<Button title="Manage Secret" type="text" className="px-1" icon={<MoreVertical />} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="bottom" align="end" className="w-32">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="space-x-2"
|
||||
disabled={!canManageSecrets}
|
||||
onClick={() => setModal(`edit`)}
|
||||
>
|
||||
<Edit3 size="14" />
|
||||
<p>Edit</p>
|
||||
</DropdownMenuItem>
|
||||
</TooltipTrigger>
|
||||
{!canManageSecrets && (
|
||||
<TooltipContent side="bottom">
|
||||
You need additional permissions to edit secrets
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
<DropdownMenuItemTooltip
|
||||
className="gap-x-2"
|
||||
disabled={!canManageSecrets}
|
||||
onClick={() => setModal(`edit`)}
|
||||
tooltip={{
|
||||
content: { side: 'left', text: 'You need additional permissions to edit secrets' },
|
||||
}}
|
||||
>
|
||||
<Edit3 size={12} />
|
||||
<p>Edit</p>
|
||||
</DropdownMenuItemTooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="space-x-2"
|
||||
disabled={!canManageSecrets}
|
||||
onClick={() => onSelectRemove(secret)}
|
||||
>
|
||||
<Trash stroke="red" size="14" />
|
||||
<p className="text-foreground-light">Delete</p>
|
||||
</DropdownMenuItem>
|
||||
</TooltipTrigger>
|
||||
{!canManageSecrets && (
|
||||
<TooltipContent side="bottom">
|
||||
You need additional permissions to delete secrets
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItemTooltip
|
||||
className="gap-x-2"
|
||||
disabled={!canManageSecrets}
|
||||
onClick={() => onSelectRemove(secret)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'left',
|
||||
text: 'You need additional permissions to delete secrets',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Trash size={12} />
|
||||
<p className="text-foreground-light">Delete</p>
|
||||
</DropdownMenuItemTooltip>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
|
||||
@@ -7,8 +7,9 @@ import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import type { VaultSecret } from 'types'
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
@@ -22,7 +23,6 @@ import {
|
||||
import AddNewSecretModal from './AddNewSecretModal'
|
||||
import DeleteSecretModal from './DeleteSecretModal'
|
||||
import SecretRow from './SecretRow'
|
||||
import type { VaultSecret } from 'types'
|
||||
|
||||
export const SecretsManagement = () => {
|
||||
const { search } = useParams()
|
||||
@@ -33,11 +33,10 @@ export const SecretsManagement = () => {
|
||||
const [selectedSecretToRemove, setSelectedSecretToRemove] = useState<VaultSecret>()
|
||||
const [selectedSort, setSelectedSort] = useState('updated_at')
|
||||
|
||||
const canManageSecrets = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
|
||||
useEffect(() => {
|
||||
if (search !== undefined) setSearchValue(search)
|
||||
}, [search])
|
||||
const { can: canManageSecrets } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
'tables'
|
||||
)
|
||||
|
||||
const { data, isLoading } = useVaultSecretsQuery({
|
||||
projectRef: project?.ref!,
|
||||
@@ -61,6 +60,10 @@ export const SecretsManagement = () => {
|
||||
}
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (search !== undefined) setSearchValue(search)
|
||||
}, [search])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-4 p-4 md:p-10">
|
||||
|
||||
@@ -7,7 +7,7 @@ import NoPermission from 'components/ui/NoPermission'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useHooksEnableMutation } from 'data/database/hooks-enable-mutation'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Admonition } from 'ui-patterns'
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
@@ -26,8 +26,10 @@ export const WebhooksOverviewTab = () => {
|
||||
})
|
||||
|
||||
const isHooksEnabled = schemas?.some((schema) => schema.name === 'supabase_functions')
|
||||
const canReadWebhooks = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_READ, 'triggers')
|
||||
const isPermissionsLoaded = usePermissionsLoaded()
|
||||
const { can: canReadWebhooks, isLoading: isLoadingPermissions } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_READ,
|
||||
'triggers'
|
||||
)
|
||||
|
||||
const { mutate: enableHooks, isLoading: isEnablingHooks } = useHooksEnableMutation({
|
||||
onSuccess: async () => {
|
||||
@@ -41,18 +43,18 @@ export const WebhooksOverviewTab = () => {
|
||||
enableHooks({ ref: projectRef })
|
||||
}
|
||||
|
||||
if (isPermissionsLoaded && !canReadWebhooks) {
|
||||
if (!isSchemasLoaded || isLoadingPermissions) {
|
||||
return (
|
||||
<div className="p-10">
|
||||
<NoPermission isFullPage resourceText="view database webhooks" />
|
||||
<GenericSkeletonLoader />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!isSchemasLoaded) {
|
||||
if (!canReadWebhooks) {
|
||||
return (
|
||||
<div className="p-10">
|
||||
<GenericSkeletonLoader />
|
||||
<NoPermission isFullPage resourceText="view database webhooks" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -73,14 +75,13 @@ export const WebhooksOverviewTab = () => {
|
||||
<ButtonTooltip
|
||||
className="w-fit"
|
||||
onClick={() => enableHooksForProject()}
|
||||
disabled={!isPermissionsLoaded || isEnablingHooks}
|
||||
disabled={isEnablingHooks}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text:
|
||||
isPermissionsLoaded && !canReadWebhooks
|
||||
? 'You need additional permissions to enable webhooks'
|
||||
: undefined,
|
||||
text: !canReadWebhooks
|
||||
? 'You need additional permissions to enable webhooks'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useState } from 'react'
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
Alert_Shadcn_,
|
||||
@@ -27,7 +27,11 @@ export const WrapperOverviewTab = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const [createWrapperShown, setCreateWrapperShown] = useState(false)
|
||||
const [isClosingCreateWrapper, setisClosingCreateWrapper] = useState(false)
|
||||
const canCreateWrapper = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'wrappers')
|
||||
|
||||
const { can: canCreateWrapper } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
'wrappers'
|
||||
)
|
||||
|
||||
const { data } = useDatabaseExtensionsQuery({
|
||||
projectRef: project?.ref,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useState } from 'react'
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import type { FDW } from 'data/fdw/fdws-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { Badge, Sheet, SheetContent, TableCell, TableRow } from 'ui'
|
||||
import { INTEGRATIONS } from '../Landing/Integrations.constants'
|
||||
import DeleteWrapperModal from './DeleteWrapperModal'
|
||||
@@ -20,7 +20,10 @@ interface WrapperRowProps {
|
||||
|
||||
const WrapperRow = ({ wrapper }: WrapperRowProps) => {
|
||||
const { ref, id } = useParams()
|
||||
const canManageWrappers = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'wrappers')
|
||||
const { can: canManageWrappers } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
'wrappers'
|
||||
)
|
||||
|
||||
const [editWrapperShown, setEditWrapperShown] = useState(false)
|
||||
const [isClosingEditWrapper, setIsClosingEditWrapper] = useState(false)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HTMLProps, ReactNode, useCallback, useState } from 'react'
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { FDW, useFDWsQuery } from 'data/fdw/fdws-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Sheet, SheetContent } from 'ui'
|
||||
import { CreateWrapperSheet } from './CreateWrapperSheet'
|
||||
@@ -19,7 +19,11 @@ export const WrappersTab = () => {
|
||||
const [selectedWrapperForDelete, setSelectedWrapperForDelete] = useState<FDW | null>(null)
|
||||
const [createWrapperShown, setCreateWrapperShown] = useState(false)
|
||||
const [isClosingCreateWrapper, setisClosingCreateWrapper] = useState(false)
|
||||
const canCreateWrapper = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'wrappers')
|
||||
|
||||
const { can: canCreateWrapper } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.TENANT_SQL_ADMIN_WRITE,
|
||||
'wrappers'
|
||||
)
|
||||
|
||||
const { data } = useFDWsQuery({
|
||||
projectRef: project?.ref,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useJwtSecretUpdateMutation } from 'data/config/jwt-secret-update-mutati
|
||||
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
|
||||
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
|
||||
import { useLegacyJWTSigningKeyQuery } from 'data/jwt-signing-keys/legacy-jwt-signing-key-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import {
|
||||
@@ -73,12 +73,18 @@ const JWTSettings = () => {
|
||||
const [isCreatingKey, setIsCreatingKey] = useState<boolean>(false)
|
||||
const [isRegeneratingKey, setIsGeneratingKey] = useState<boolean>(false)
|
||||
|
||||
const canReadJWTSecret = useCheckPermissions(PermissionAction.READ, 'field.jwt_secret')
|
||||
const canGenerateNewJWTSecret = useCheckPermissions(
|
||||
const { can: canReadJWTSecret } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.READ,
|
||||
'field.jwt_secret'
|
||||
)
|
||||
const { can: canGenerateNewJWTSecret } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.INFRA_EXECUTE,
|
||||
'queue_job.projects.update_jwt'
|
||||
)
|
||||
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
|
||||
const { can: canUpdateConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'custom_config_gotrue'
|
||||
)
|
||||
|
||||
const { data } = useJwtSecretUpdatingStatusQuery({ projectRef })
|
||||
const { data: config, isError } = useProjectPostgrestConfigQuery({ projectRef })
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import Link from 'next/link'
|
||||
import { useEffect } from 'react'
|
||||
import { SubmitHandler, useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import * as z from 'zod'
|
||||
@@ -18,7 +17,7 @@ import {
|
||||
REALTIME_DEFAULT_CONFIG,
|
||||
useRealtimeConfigurationQuery,
|
||||
} from 'data/realtime/realtime-config-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
@@ -42,17 +41,20 @@ export const RealtimeSettings = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { data: organization } = useSelectedOrganizationQuery()
|
||||
const canUpdateConfig = useCheckPermissions(PermissionAction.REALTIME_ADMIN_READ, '*')
|
||||
const { can: canUpdateConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.REALTIME_ADMIN_READ,
|
||||
'*'
|
||||
)
|
||||
|
||||
const { data: maxConn } = useMaxConnectionsQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const { data, error, isLoading, isSuccess, isError } = useRealtimeConfigurationQuery({
|
||||
const { data, error, isLoading, isError } = useRealtimeConfigurationQuery({
|
||||
projectRef,
|
||||
})
|
||||
|
||||
const { data: policies } = useDatabasePoliciesQuery({
|
||||
const { data: policies, isSuccess: isSuccessPolicies } = useDatabasePoliciesQuery({
|
||||
projectRef,
|
||||
connectionString: project?.connectionString,
|
||||
schema: 'realtime',
|
||||
@@ -96,6 +98,10 @@ export const RealtimeSettings = () => {
|
||||
...REALTIME_DEFAULT_CONFIG,
|
||||
allow_public: !REALTIME_DEFAULT_CONFIG.private_only,
|
||||
},
|
||||
values: {
|
||||
...(data ?? REALTIME_DEFAULT_CONFIG),
|
||||
allow_public: !(data?.private_only ?? REALTIME_DEFAULT_CONFIG.private_only),
|
||||
} as any,
|
||||
})
|
||||
|
||||
const { allow_public } = form.watch()
|
||||
@@ -111,14 +117,6 @@ export const RealtimeSettings = () => {
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// [Joshen] Temp typed with any - API typing marks all the properties as nullable,
|
||||
// but checked with Filipe that they're not supposed to
|
||||
if (isSuccess) {
|
||||
form.reset({ ...data, allow_public: !data.private_only } as any)
|
||||
}
|
||||
}, [isSuccess])
|
||||
|
||||
return (
|
||||
<ScaffoldSection isFullWidth>
|
||||
<Form_Shadcn_ {...form}>
|
||||
@@ -136,7 +134,7 @@ export const RealtimeSettings = () => {
|
||||
className="!p-0 !pt-2"
|
||||
header={<FormSectionLabel>Channel restrictions</FormSectionLabel>}
|
||||
>
|
||||
<FormSectionContent loading={isLoading} className="!gap-y-2">
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormItemLayout
|
||||
layout="flex"
|
||||
label="Allow public access"
|
||||
@@ -151,7 +149,7 @@ export const RealtimeSettings = () => {
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
|
||||
{!hasRealtimeMessagesPolicies && !allow_public && (
|
||||
{isSuccessPolicies && !hasRealtimeMessagesPolicies && !allow_public && (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type="warning"
|
||||
@@ -198,7 +196,7 @@ export const RealtimeSettings = () => {
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loading={isLoading} className="!gap-y-2">
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
@@ -241,7 +239,7 @@ export const RealtimeSettings = () => {
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loading={isLoading} className="!gap-y-2">
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
|
||||
@@ -12,7 +12,7 @@ const ReportPadding = ({ children }: PropsWithChildren<{}>) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col gap-4 px-5 py-6 mx-auto 1xl:px-28 lg:px-16 2xl:px-32 w-full',
|
||||
'flex flex-col flex-grow gap-4 px-5 py-6 mx-auto 1xl:px-28 lg:px-16 2xl:px-32 w-full',
|
||||
snap.open ? 'xl:px-6' : 'xl:px-22'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
useContentUpsertMutation,
|
||||
} from 'data/content/content-upsert-mutation'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Metric, TIME_PERIODS_REPORTS } from 'lib/constants/metrics'
|
||||
@@ -82,22 +82,30 @@ const Reports = () => {
|
||||
const currentReport = userContents?.content.find((report) => report.id === id)
|
||||
const currentReportContent = currentReport?.content as Dashboards.Content
|
||||
|
||||
const canReadReport = useCheckPermissions(PermissionAction.READ, 'user_content', {
|
||||
resource: {
|
||||
type: 'report',
|
||||
visibility: currentReport?.visibility,
|
||||
owner_id: currentReport?.owner_id,
|
||||
},
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
const canUpdateReport = useCheckPermissions(PermissionAction.UPDATE, 'user_content', {
|
||||
resource: {
|
||||
type: 'report',
|
||||
visibility: currentReport?.visibility,
|
||||
owner_id: currentReport?.owner_id,
|
||||
},
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
const { can: canReadReport, isLoading: isLoadingPermissions } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.READ,
|
||||
'user_content',
|
||||
{
|
||||
resource: {
|
||||
type: 'report',
|
||||
visibility: currentReport?.visibility,
|
||||
owner_id: currentReport?.owner_id,
|
||||
},
|
||||
subject: { id: profile?.id },
|
||||
}
|
||||
)
|
||||
const { can: canUpdateReport } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'user_content',
|
||||
{
|
||||
resource: {
|
||||
type: 'report',
|
||||
visibility: currentReport?.visibility,
|
||||
owner_id: currentReport?.owner_id,
|
||||
},
|
||||
subject: { id: profile?.id },
|
||||
}
|
||||
)
|
||||
|
||||
function handleDateRangePicker({ period_start, period_end }: any) {
|
||||
setStartDate(period_start.date)
|
||||
@@ -383,7 +391,7 @@ const Reports = () => {
|
||||
}
|
||||
}, [hasEdits, confirmNavigate, router])
|
||||
|
||||
if (isLoading) {
|
||||
if (isLoading || isLoadingPermissions) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useParams } from 'common'
|
||||
import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.queries'
|
||||
import { ActionCard } from 'components/layouts/Tabs/ActionCard'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
@@ -26,10 +26,14 @@ const SQLQuickstarts = () => {
|
||||
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
|
||||
const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
const { can: canCreateSQLSnippet } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'user_content',
|
||||
{
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
}
|
||||
)
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useParams } from 'common'
|
||||
import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.queries'
|
||||
import { ActionCard } from 'components/layouts/Tabs/ActionCard'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
@@ -26,10 +26,14 @@ const SQLTemplates = () => {
|
||||
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
|
||||
const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
const { can: canCreateSQLSnippet } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'user_content',
|
||||
{
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
}
|
||||
)
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import { useProfile } from 'lib/profile'
|
||||
@@ -24,10 +24,14 @@ export const useNewQuery = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
|
||||
const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
const { can: canCreateSQLSnippet } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'user_content',
|
||||
{
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
}
|
||||
)
|
||||
|
||||
const newQuery = async (sql: string, name: string, shouldRedirect: boolean = true) => {
|
||||
if (!ref) return console.error('Project ref is required')
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-co
|
||||
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
|
||||
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
AlertDescription_Shadcn_,
|
||||
@@ -101,7 +101,7 @@ export const PostgrestConfig = () => {
|
||||
|
||||
const formId = 'project-postgres-config'
|
||||
const hiddenSchema = ['auth', 'pgbouncer', 'hooks', 'extensions']
|
||||
const canUpdatePostgrestConfig = useCheckPermissions(
|
||||
const { can: canUpdatePostgrestConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'custom_config_postgrest'
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useProjectAddonRemoveMutation } from 'data/subscriptions/project-addon-
|
||||
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import type { AddonVariantId } from 'data/subscriptions/types'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { formatCurrency } from 'lib/helpers'
|
||||
@@ -32,7 +32,7 @@ const CustomDomainSidePanel = () => {
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState<string>('cd_none')
|
||||
|
||||
const canUpdateCustomDomain = useCheckPermissions(
|
||||
const { can: canUpdateCustomDomain } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.BILLING_WRITE,
|
||||
'stripe.subscriptions'
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useProjectAddonRemoveMutation } from 'data/subscriptions/project-addon-
|
||||
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import type { AddonVariantId } from 'data/subscriptions/types'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useIsAwsCloudProvider } from 'hooks/misc/useSelectedProject'
|
||||
import { formatCurrency } from 'lib/helpers'
|
||||
@@ -25,7 +25,10 @@ const IPv4SidePanel = () => {
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState<string>('ipv4_none')
|
||||
|
||||
const canUpdateIPv4 = useCheckPermissions(PermissionAction.BILLING_WRITE, 'stripe.subscriptions')
|
||||
const { can: canUpdateIPv4 } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.BILLING_WRITE,
|
||||
'stripe.subscriptions'
|
||||
)
|
||||
|
||||
const { panel, closePanel } = useAddonsPagePanel()
|
||||
const visible = panel === 'ipv4'
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useProjectAddonRemoveMutation } from 'data/subscriptions/project-addon-
|
||||
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import type { AddonVariantId } from 'data/subscriptions/types'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
@@ -61,7 +61,10 @@ const PITRSidePanel = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState<'on' | 'off'>('off')
|
||||
const [selectedOption, setSelectedOption] = useState<string>('pitr_0')
|
||||
|
||||
const canUpdatePitr = useCheckPermissions(PermissionAction.BILLING_WRITE, 'stripe.subscriptions')
|
||||
const { can: canUpdatePitr } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.BILLING_WRITE,
|
||||
'stripe.subscriptions'
|
||||
)
|
||||
const isBranchingEnabled =
|
||||
project?.is_branch_enabled === true || project?.parent_project_ref !== undefined
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FormPanel } from 'components/ui/Forms/FormPanel'
|
||||
import { useBannedIPsDeleteMutation } from 'data/banned-ips/banned-ips-delete-mutations'
|
||||
import { useBannedIPsQuery } from 'data/banned-ips/banned-ips-query'
|
||||
import { useUserIPAddressQuery } from 'data/misc/user-ip-address-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Badge, Skeleton } from 'ui'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
@@ -39,11 +39,15 @@ const BannedIPs = () => {
|
||||
const [showUnban, setShowUnban] = useState(false)
|
||||
const [confirmingIP, setConfirmingIP] = useState<string | null>(null) // Track the IP being confirmed for unban
|
||||
|
||||
const canUnbanNetworks = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUnbanNetworks } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { mutate: unbanIPs, isLoading: isUnbanning } = useBannedIPsDeleteMutation({
|
||||
onSuccess: () => {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { useMaxConnectionsQuery } from 'data/database/max-connections-query'
|
||||
import { usePgbouncerConfigQuery } from 'data/database/pgbouncer-config-query'
|
||||
import { usePgbouncerConfigurationUpdateMutation } from 'data/database/pgbouncer-config-update-mutation'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
@@ -51,7 +51,7 @@ export const ConnectionPooling = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { data: org } = useSelectedOrganizationQuery()
|
||||
|
||||
const canUpdateConnectionPoolingConfiguration = useCheckPermissions(
|
||||
const { can: canUpdateConnectionPoolingConfiguration } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{ resource: { project_id: project?.id } }
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import PasswordStrengthBar from 'components/ui/PasswordStrengthBar'
|
||||
import { useDatabasePasswordResetMutation } from 'data/database/database-password-reset-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { DEFAULT_MINIMUM_PASSWORD_STRENGTH } from 'lib/constants'
|
||||
import passwordStrength from 'lib/password-strength'
|
||||
@@ -20,11 +20,16 @@ const ResetDbPassword = ({ disabled = false }) => {
|
||||
const { ref } = useParams()
|
||||
const isProjectActive = useIsProjectActive()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const canResetDbPassword = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
|
||||
const { can: canResetDbPassword } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const [showResetDbPass, setShowResetDbPass] = useState<boolean>(false)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FormHeader } from 'components/ui/Forms/FormHeader'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { useProjectDiskResizeMutation } from 'data/config/project-disk-resize-mutation'
|
||||
import { useDatabaseSizeQuery } from 'data/database/database-size-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { useUrlState } from 'hooks/ui/useUrlState'
|
||||
@@ -35,11 +35,15 @@ const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfigurationProps)
|
||||
setUrlParams({ show_increase_disk_size_modal: show ? 'true' : undefined })
|
||||
}
|
||||
|
||||
const canUpdateDiskSizeConfig = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUpdateDiskSizeConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { isLoading: isUpdatingDiskSize } = useProjectDiskResizeMutation({
|
||||
onSuccess: (_, variables) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { FormPanel } from 'components/ui/Forms/FormPanel'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useNetworkRestrictionsQuery } from 'data/network-restrictions/network-restrictions-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
Badge,
|
||||
@@ -75,11 +75,15 @@ const NetworkRestrictions = () => {
|
||||
const [selectedRestrictionToRemove, setSelectedRestrictionToRemove] = useState<string>()
|
||||
|
||||
const { data, isLoading } = useNetworkRestrictionsQuery({ projectRef: ref })
|
||||
const canUpdateNetworkRestrictions = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUpdateNetworkRestrictions } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const hasAccessToRestrictions = data?.entitlement === 'allowed'
|
||||
const ipv4Restrictions = data?.config?.dbAllowedCidrs ?? []
|
||||
|
||||
@@ -13,7 +13,7 @@ import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui
|
||||
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
||||
import { useSSLEnforcementQuery } from 'data/ssl-enforcement/ssl-enforcement-query'
|
||||
import { useSSLEnforcementUpdateMutation } from 'data/ssl-enforcement/ssl-enforcement-update-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Alert, Button, Switch, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
|
||||
@@ -42,11 +42,15 @@ const SSLConfiguration = () => {
|
||||
}
|
||||
)
|
||||
|
||||
const canUpdateSSLEnforcement = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUpdateSSLEnforcement } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
const initialIsEnforced = isSuccess
|
||||
? sslEnforcementConfiguration.appliedSuccessfully &&
|
||||
sslEnforcementConfiguration.currentConfig.database
|
||||
@@ -143,7 +147,7 @@ const SSLConfiguration = () => {
|
||||
? 'You need additional permissions to update SSL enforcement for your project'
|
||||
: !hasAccessToSSLEnforcement
|
||||
? 'Your project does not have access to SSL enforcement'
|
||||
: ''}
|
||||
: undefined}
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui
|
||||
import { InlineLink } from 'components/ui/InlineLink'
|
||||
import { useComplianceConfigUpdateMutation } from 'data/config/project-compliance-config-mutation'
|
||||
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Switch, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
|
||||
@@ -21,9 +21,13 @@ const ComplianceConfig = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const [isSensitive, setIsSensitive] = useState(false)
|
||||
|
||||
const canUpdateComplianceConfig = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: { project_id: project?.id },
|
||||
})
|
||||
const { can: canUpdateComplianceConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: { project_id: project?.id },
|
||||
}
|
||||
)
|
||||
|
||||
const {
|
||||
data: settings,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui
|
||||
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
||||
import { useCheckCNAMERecordMutation } from 'data/custom-domains/check-cname-mutation'
|
||||
import { useCustomDomainCreateMutation } from 'data/custom-domains/custom-domains-create-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { Form, Input } from 'ui'
|
||||
|
||||
@@ -27,11 +27,15 @@ const CustomDomainsConfigureHostname = () => {
|
||||
|
||||
const FORM_ID = 'custom-domains-form'
|
||||
const endpoint = settings?.app_config?.endpoint
|
||||
const canConfigureCustomDomain = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canConfigureCustomDomain } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const onCreateCustomDomain = async (values: yup.InferType<typeof schema>) => {
|
||||
if (!ref) return console.error('Project ref is required')
|
||||
|
||||
@@ -2,7 +2,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { DeleteProjectModal } from './DeleteProjectModal'
|
||||
|
||||
@@ -14,9 +14,13 @@ const DeleteProjectButton = ({ type = 'danger' }: DeleteProjectButtonProps) => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
const canDeleteProject = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: { project_id: project?.id },
|
||||
})
|
||||
const { can: canDeleteProject } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: { project_id: project?.id },
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useProjectUpdateMutation } from 'data/projects/project-update-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useProjectByRefQuery, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import {
|
||||
@@ -33,11 +33,15 @@ const General = () => {
|
||||
|
||||
const formId = 'project-general-settings'
|
||||
const initialValues = { name: project?.name ?? '', ref: project?.ref ?? '' }
|
||||
const canUpdateProject = useCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
})
|
||||
const { can: canUpdateProject } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'projects',
|
||||
{
|
||||
resource: {
|
||||
project_id: project?.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { mutate: updateProject, isLoading: isUpdating } = useProjectUpdateMutation()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useIsProjectActive } from 'components/layouts/ProjectLayout/ProjectCont
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useProjectPauseMutation } from 'data/projects/project-pause-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useIsAwsK8sCloudProvider, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
@@ -25,7 +25,7 @@ const PauseProjectButton = () => {
|
||||
|
||||
const projectRef = project?.ref ?? ''
|
||||
const isPaused = project?.status === PROJECT_STATUS.INACTIVE
|
||||
const canPauseProject = useCheckPermissions(
|
||||
const { can: canPauseProject } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.INFRA_EXECUTE,
|
||||
'queue_jobs.projects.pause'
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useProjectRestartMutation } from 'data/projects/project-restart-mutation'
|
||||
import { useProjectRestartServicesMutation } from 'data/projects/project-restart-services-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useIsAwsK8sCloudProvider, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import {
|
||||
@@ -35,7 +35,10 @@ const RestartServerButton = () => {
|
||||
const projectRegion = project?.region ?? ''
|
||||
|
||||
const projectRestartDisabled = useFlag('disableProjectRestarts')
|
||||
const canRestartProject = useCheckPermissions(PermissionAction.INFRA_EXECUTE, 'reboot')
|
||||
const { can: canRestartProject } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.INFRA_EXECUTE,
|
||||
'reboot'
|
||||
)
|
||||
|
||||
const { mutate: restartProject, isLoading: isRestartingProject } = useProjectRestartMutation({
|
||||
onSuccess: () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useProjectTransferMutation } from 'data/projects/project-transfer-mutation'
|
||||
import { useProjectTransferPreviewQuery } from 'data/projects/project-transfer-preview-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { Button, InfoIcon, Listbox, Loading, Modal, WarningIcon } from 'ui'
|
||||
@@ -58,7 +58,10 @@ const TransferProjectButton = () => {
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const canTransferProject = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
||||
const { can: canTransferProject } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'organizations'
|
||||
)
|
||||
|
||||
const toggle = () => {
|
||||
setIsOpen(!isOpen)
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
ReplicaInitializationStatus,
|
||||
useReadReplicasStatusesQuery,
|
||||
} from 'data/read-replicas/replicas-status-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useIsOrioleDb, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { timeout } from 'lib/helpers'
|
||||
import Link from 'next/link'
|
||||
@@ -56,7 +56,10 @@ const InstanceConfigurationUI = () => {
|
||||
const [selectedReplicaToDrop, setSelectedReplicaToDrop] = useState<Database>()
|
||||
const [selectedReplicaToRestart, setSelectedReplicaToRestart] = useState<Database>()
|
||||
|
||||
const canManageReplicas = useCheckPermissions(PermissionAction.CREATE, 'projects')
|
||||
const { can: canManageReplicas } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'projects'
|
||||
)
|
||||
|
||||
const {
|
||||
data: loadBalancers,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { parseAsBoolean, useQueryState } from 'nuqs'
|
||||
import { Handle, NodeProps, Position } from 'reactflow'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
|
||||
import SparkBar from 'components/ui/SparkBar'
|
||||
import {
|
||||
DatabaseInitEstimations,
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
useReadReplicasStatusesQuery,
|
||||
} from 'data/read-replicas/replicas-status-query'
|
||||
import { formatDatabaseID } from 'data/read-replicas/replicas.utils'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import {
|
||||
@@ -181,7 +182,10 @@ export const ReplicaNode = ({ data }: NodeProps<ReplicaNodeData>) => {
|
||||
} = data
|
||||
const { ref } = useParams()
|
||||
const dbSelectorState = useDatabaseSelectorStateSnapshot()
|
||||
const canManageReplicas = useCheckPermissions(PermissionAction.CREATE, 'projects')
|
||||
const { can: canManageReplicas } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'projects'
|
||||
)
|
||||
const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false))
|
||||
|
||||
const { data: databaseStatuses } = useReadReplicasStatusesQuery({ projectRef: ref })
|
||||
@@ -370,24 +374,18 @@ export const ReplicaNode = ({ data }: NodeProps<ReplicaNodeData>) => {
|
||||
{/* <DropdownMenuItem className="gap-x-2" onClick={() => onSelectResizeReplica()}>
|
||||
Resize replica
|
||||
</DropdownMenuItem> */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="gap-x-2 !pointer-events-auto"
|
||||
disabled={!canManageReplicas}
|
||||
onClick={() => {
|
||||
if (canManageReplicas) onSelectDropReplica()
|
||||
}}
|
||||
>
|
||||
Drop replica
|
||||
</DropdownMenuItem>
|
||||
</TooltipTrigger>
|
||||
{!canManageReplicas && (
|
||||
<TooltipContent side="left">
|
||||
You need additional permissions to drop replicas
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
<DropdownMenuItemTooltip
|
||||
className="gap-x-2 !pointer-events-auto"
|
||||
disabled={!canManageReplicas}
|
||||
onClick={() => {
|
||||
if (canManageReplicas) onSelectDropReplica()
|
||||
}}
|
||||
tooltip={{
|
||||
content: { side: 'left', text: 'You need additional permissions to drop replicas' },
|
||||
}}
|
||||
>
|
||||
Drop replica
|
||||
</DropdownMenuItemTooltip>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
|
||||
import { Database, useReadReplicasQuery } from 'data/read-replicas/replicas-query'
|
||||
import { formatDatabaseID } from 'data/read-replicas/replicas.utils'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import type { AWS_REGIONS_KEYS } from 'shared-data'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
@@ -31,9 +32,6 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
ScrollArea,
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from 'ui'
|
||||
import { AVAILABLE_REPLICA_REGIONS, REPLICA_STATUS } from './InstanceConfiguration.constants'
|
||||
import GeographyData from './MapData.json'
|
||||
@@ -62,7 +60,10 @@ const MapView = ({
|
||||
y: number
|
||||
region: { key: string; country?: string; name?: string }
|
||||
}>()
|
||||
const canManageReplicas = useCheckPermissions(PermissionAction.CREATE, 'projects')
|
||||
const { can: canManageReplicas } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'projects'
|
||||
)
|
||||
const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false))
|
||||
|
||||
const { data } = useReadReplicasQuery({ projectRef: ref })
|
||||
@@ -325,22 +326,20 @@ const MapView = ({
|
||||
>
|
||||
Restart replica
|
||||
</DropdownMenuItem>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="gap-x-2 !pointer-events-auto"
|
||||
disabled={!canManageReplicas}
|
||||
onClick={() => onSelectDropReplica(database)}
|
||||
>
|
||||
Drop replica
|
||||
</DropdownMenuItem>
|
||||
</TooltipTrigger>
|
||||
{!canManageReplicas && (
|
||||
<TooltipContent side="left">
|
||||
You need additional permissions to drop replicas
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
<DropdownMenuItemTooltip
|
||||
className="gap-x-2 !pointer-events-auto"
|
||||
disabled={!canManageReplicas}
|
||||
onClick={() => onSelectDropReplica(database)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'left',
|
||||
text: 'You need additional permissions to drop replicas',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Drop replica
|
||||
</DropdownMenuItemTooltip>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useGitHubConnectionDeleteMutation } from 'data/integrations/github-conn
|
||||
import { useGitHubConnectionUpdateMutation } from 'data/integrations/github-connection-update-mutation'
|
||||
import { useGitHubRepositoriesQuery } from 'data/integrations/github-repositories-query'
|
||||
import type { GitHubConnection } from 'data/integrations/integrations.types'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { openInstallGitHubIntegrationWindow } from 'lib/github'
|
||||
@@ -72,17 +72,16 @@ const GitHubIntegrationConnectionForm = ({
|
||||
const [repoComboBoxOpen, setRepoComboboxOpen] = useState(false)
|
||||
const isParentProject = !Boolean(selectedProject?.parent_project_ref)
|
||||
|
||||
const canUpdateGitHubConnection = useCheckPermissions(
|
||||
const { can: canUpdateGitHubConnection } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'integrations.github_connections'
|
||||
)
|
||||
const canCreateGitHubConnection = useCheckPermissions(
|
||||
const { can: canCreateGitHubConnection } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'integrations.github_connections'
|
||||
)
|
||||
|
||||
const { data: gitHubAuthorization, isLoading: isLoadingGitHubAuthorization } =
|
||||
useGitHubAuthorizationQuery()
|
||||
const { data: gitHubAuthorization } = useGitHubAuthorizationQuery()
|
||||
|
||||
const { data: githubReposData, isLoading: isLoadingGitHubRepos } = useGitHubRepositoriesQuery<
|
||||
any[]
|
||||
|
||||
@@ -11,10 +11,11 @@ import {
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
import UpgradeToPro from 'components/ui/UpgradeToPro'
|
||||
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { BASE_PATH, IS_PLATFORM } from 'lib/constants'
|
||||
import { cn } from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import GitHubIntegrationConnectionForm from './GitHubIntegrationConnectionForm'
|
||||
|
||||
const IntegrationImageHandler = ({ title }: { title: 'vercel' | 'github' }) => {
|
||||
@@ -31,10 +32,8 @@ const GitHubSection = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const { data: organization } = useSelectedOrganizationQuery()
|
||||
|
||||
const canReadGitHubConnection = useCheckPermissions(
|
||||
PermissionAction.READ,
|
||||
'integrations.github_connections'
|
||||
)
|
||||
const { can: canReadGitHubConnection, isLoading: isLoadingPermissions } =
|
||||
useAsyncCheckProjectPermissions(PermissionAction.READ, 'integrations.github_connections')
|
||||
|
||||
const isProPlanAndUp = organization?.plan?.id !== 'free'
|
||||
const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
|
||||
@@ -51,22 +50,6 @@ const GitHubSection = () => {
|
||||
|
||||
const GitHubTitle = `GitHub Integration`
|
||||
|
||||
if (!canReadGitHubConnection) {
|
||||
return (
|
||||
<ScaffoldContainer>
|
||||
<ScaffoldSection>
|
||||
<ScaffoldSectionDetail title={GitHubTitle}>
|
||||
<p>Connect any of your GitHub repositories to a project.</p>
|
||||
<IntegrationImageHandler title="github" />
|
||||
</ScaffoldSectionDetail>
|
||||
<ScaffoldSectionContent>
|
||||
<NoPermission resourceText="view this organization's GitHub connections" />
|
||||
</ScaffoldSectionContent>
|
||||
</ScaffoldSection>
|
||||
</ScaffoldContainer>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ScaffoldContainer>
|
||||
<ScaffoldSection>
|
||||
@@ -75,28 +58,34 @@ const GitHubSection = () => {
|
||||
<IntegrationImageHandler title="github" />
|
||||
</ScaffoldSectionDetail>
|
||||
<ScaffoldSectionContent>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-2">How does the GitHub integration work?</h5>
|
||||
<p className="text-foreground-light text-sm mb-6">
|
||||
Connecting to GitHub allows you to sync preview branches with a chosen GitHub
|
||||
branch, keep your production branch in sync, and automatically create preview
|
||||
branches for every pull request.
|
||||
</p>
|
||||
{promptProPlanUpgrade && (
|
||||
<div className="mb-6">
|
||||
<UpgradeToPro
|
||||
primaryText="Upgrade to unlock GitHub integration"
|
||||
secondaryText="Connect your GitHub repository to automatically sync preview branches and deploy changes."
|
||||
source="github-integration"
|
||||
/>
|
||||
{isLoadingPermissions ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : !canReadGitHubConnection ? (
|
||||
<NoPermission resourceText="view this organization's GitHub connections" />
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-2">How does the GitHub integration work?</h5>
|
||||
<p className="text-foreground-light text-sm mb-6">
|
||||
Connecting to GitHub allows you to sync preview branches with a chosen GitHub
|
||||
branch, keep your production branch in sync, and automatically create preview
|
||||
branches for every pull request.
|
||||
</p>
|
||||
{promptProPlanUpgrade && (
|
||||
<div className="mb-6">
|
||||
<UpgradeToPro
|
||||
primaryText="Upgrade to unlock GitHub integration"
|
||||
secondaryText="Connect your GitHub repository to automatically sync preview branches and deploy changes."
|
||||
source="github-integration"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={cn(promptProPlanUpgrade && 'opacity-25 pointer-events-none')}>
|
||||
<GitHubIntegrationConnectionForm connection={existingConnection} />
|
||||
</div>
|
||||
)}
|
||||
<div className={cn(promptProPlanUpgrade && 'opacity-25 pointer-events-none')}>
|
||||
<GitHubIntegrationConnectionForm connection={existingConnection} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ScaffoldSectionContent>
|
||||
</ScaffoldSection>
|
||||
</ScaffoldContainer>
|
||||
|
||||
@@ -25,13 +25,14 @@ import type {
|
||||
IntegrationName,
|
||||
IntegrationProjectConnection,
|
||||
} from 'data/integrations/integrations.types'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { pluralize } from 'lib/helpers'
|
||||
import { getIntegrationConfigurationUrl } from 'lib/integration-utils'
|
||||
import { useSidePanelsStateSnapshot } from 'state/side-panels'
|
||||
import { Button, cn } from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import { IntegrationImageHandler } from '../IntegrationsSettings'
|
||||
import VercelIntegrationConnectionForm from './VercelIntegrationConnectionForm'
|
||||
|
||||
@@ -42,15 +43,13 @@ const VercelSection = ({ isProjectScoped }: { isProjectScoped: boolean }) => {
|
||||
const sidePanelsStateSnapshot = useSidePanelsStateSnapshot()
|
||||
const isBranch = project?.parent_project_ref !== undefined
|
||||
|
||||
const canReadVercelConnection = useCheckPermissions(
|
||||
PermissionAction.READ,
|
||||
'integrations.vercel_connections'
|
||||
)
|
||||
const canCreateVercelConnection = useCheckPermissions(
|
||||
const { can: canReadVercelConnection, isLoading: isLoadingPermissions } =
|
||||
useAsyncCheckProjectPermissions(PermissionAction.READ, 'integrations.vercel_connections')
|
||||
const { can: canCreateVercelConnection } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'integrations.vercel_connections'
|
||||
)
|
||||
const canUpdateVercelConnection = useCheckPermissions(
|
||||
const { can: canUpdateVercelConnection } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'integrations.vercel_connections'
|
||||
)
|
||||
@@ -162,7 +161,9 @@ You can change the scope of the access for Supabase by configuring
|
||||
<IntegrationImageHandler title="vercel" />
|
||||
</ScaffoldSectionDetail>
|
||||
<ScaffoldSectionContent>
|
||||
{!canReadVercelConnection ? (
|
||||
{isLoadingPermissions ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : !canReadVercelConnection ? (
|
||||
<NoPermission resourceText="view this organization's Vercel connections" />
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -10,8 +10,9 @@ import { IS_PLATFORM, useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { DownloadResultsButton } from 'components/ui/DownloadResultsButton'
|
||||
import { useSelectedLog } from 'hooks/analytics/useSelectedLog'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useProfile } from 'lib/profile'
|
||||
import { toast } from 'sonner'
|
||||
import { ResponseError } from 'types'
|
||||
import {
|
||||
Button,
|
||||
@@ -33,7 +34,6 @@ import { isDefaultLogPreviewFormat } from './Logs.utils'
|
||||
import { DefaultErrorRenderer } from './LogsErrorRenderers/DefaultErrorRenderer'
|
||||
import ResourcesExceededErrorRenderer from './LogsErrorRenderers/ResourcesExceededErrorRenderer'
|
||||
import { LogsTableEmptyState } from './LogsTableEmptyState'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
interface Props {
|
||||
data?: LogData[]
|
||||
@@ -94,10 +94,14 @@ const LogTable = ({
|
||||
const [selectionOpen, setSelectionOpen] = useState(false)
|
||||
const [selectedRow, setSelectedRow] = useState<LogData | null>(null)
|
||||
|
||||
const canCreateLogQuery = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'log_sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
const { can: canCreateLogQuery } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'user_content',
|
||||
{
|
||||
resource: { type: 'log_sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
}
|
||||
)
|
||||
|
||||
const firstRow = data[0]
|
||||
|
||||
|
||||
@@ -65,11 +65,13 @@ const Shimmer = () => (
|
||||
const FormSectionContent = ({
|
||||
children,
|
||||
loading = true,
|
||||
loaders,
|
||||
fullWidth,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode | string
|
||||
loading?: boolean
|
||||
loaders?: number
|
||||
fullWidth?: boolean
|
||||
className?: string
|
||||
}) => {
|
||||
@@ -81,7 +83,11 @@ const FormSectionContent = ({
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{loading ? Children.map(children, () => <Shimmer />) : children}
|
||||
{loading
|
||||
? !!loaders
|
||||
? new Array(loaders).fill(0).map((_, idx) => <Shimmer key={idx} />)
|
||||
: Children.map(children, (_, idx) => <Shimmer key={idx} />)
|
||||
: children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import ReportsLayout from 'components/layouts/ReportsLayout/ReportsLayout'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const PageLayout: NextPageWithLayout = () => (
|
||||
<div className="mx-auto flex flex-col gap-4 w-full">
|
||||
<div className="mx-auto flex flex-col gap-4 w-full flex-grow">
|
||||
<ReportPadding>
|
||||
<Reports />
|
||||
</ReportPadding>
|
||||
|
||||
Reference in New Issue
Block a user