import { PermissionAction } from '@supabase/shared-types/out/constants' import { Check, X } from 'lucide-react' import { useState } from 'react' import { useParams } from 'common' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import CopyButton from 'components/ui/CopyButton' import NoPermission from 'components/ui/NoPermission' import { AuthorizedApp, useAuthorizedAppsQuery } from 'data/oauth/authorized-apps-query' import { OAuthAppCreateResponse } from 'data/oauth/oauth-app-create-mutation' import { OAuthApp, useOAuthAppsQuery } from 'data/oauth/oauth-apps-query' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { Button, cn } from 'ui' import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' import { AuthorizedAppRow } from './AuthorizedAppRow' import { DeleteAppModal } from './DeleteAppModal' import { OAuthAppRow } from './OAuthAppRow' import { PublishAppSidePanel } from './PublishAppSidePanel' import { RevokeAppModal } from './RevokeAppModal' // [Joshen] Note on nav UX // Kang Ming mentioned that it might be better to split Published Apps and Authorized Apps into 2 separate tabs // to prevent any confusion (case study: GitHub). Authorized apps could be in the "integrations" tab, but let's // check in again after we wrap up Vercel integration export const OAuthApps = () => { const { slug } = useParams() const [showPublishModal, setShowPublishModal] = useState(false) const [createdApp, setCreatedApp] = useState() const [selectedAppToUpdate, setSelectedAppToUpdate] = useState() const [selectedAppToDelete, setSelectedAppToDelete] = useState() const [selectedAppToRevoke, setSelectedAppToRevoke] = useState() const { can: canReadOAuthApps, isLoading: isLoadingPermissions } = useAsyncCheckPermissions( PermissionAction.READ, 'approved_oauth_apps' ) const { can: canCreateOAuthApps } = useAsyncCheckPermissions( PermissionAction.CREATE, 'approved_oauth_apps' ) const { data: publishedApps, error: publishedAppsError, isPending: isLoadingPublishedApps, isSuccess: isSuccessPublishedApps, isError: isErrorPublishedApps, } = useOAuthAppsQuery({ slug }, { enabled: canReadOAuthApps }) const sortedPublishedApps = publishedApps?.sort((a, b) => { return Number(new Date(a.created_at ?? '')) - Number(new Date(b.created_at ?? '')) }) const { data: authorizedApps, isPending: isLoadingAuthorizedApps, isSuccess: isSuccessAuthorizedApps, isError: isErrorAuthorizedApps, } = useAuthorizedAppsQuery({ slug }) const sortedAuthorizedApps = authorizedApps?.sort((a, b) => { return Number(new Date(a.authorized_at)) - Number(new Date(b.authorized_at)) }) return ( <>

Published Apps

Build integrations that extend Supabase's functionality

setShowPublishModal(true)} tooltip={{ content: { side: 'bottom', text: !canCreateOAuthApps ? 'You need additional permissions to create apps' : undefined, }, }} > Add application
{isLoadingPublishedApps || isLoadingPermissions ? (
) : !canReadOAuthApps ? ( ) : null} {isErrorPublishedApps && ( )} {createdApp !== undefined && (

You've created your new OAuth application.

Ensure that you store the client secret securely - you will not be able to see it again.

Client ID

{createdApp.client_id}

Client Secret

{createdApp.client_secret}

)} {isSuccessPublishedApps && ( <> {(publishedApps?.length ?? 0) === 0 ? (

You do not have any published applications yet

) : ( , Name, Client ID, Created at, , ]} body={ sortedPublishedApps?.map((app) => ( { setShowPublishModal(true) setSelectedAppToUpdate(app) }} onSelectDelete={() => setSelectedAppToDelete(app)} /> )) ?? [] } /> )} )}

Authorized Apps

Applications that have access to your organization's settings and projects

{isLoadingAuthorizedApps || isLoadingPermissions ? (
) : !canReadOAuthApps ? ( ) : null} {isErrorAuthorizedApps && } {isSuccessAuthorizedApps && ( <> {(authorizedApps.length ?? 0) === 0 ? (

You do not have any authorized applications yet

) : (
, Name, Created by, App ID, Authorized at, , ]} body={ sortedAuthorizedApps?.map((app) => ( setSelectedAppToRevoke(app)} /> )) ?? [] } /> )} )} { setSelectedAppToUpdate(undefined) setShowPublishModal(false) }} onCreateSuccess={setCreatedApp} /> setSelectedAppToDelete(undefined)} /> setSelectedAppToRevoke(undefined)} /> ) }