import { PermissionAction } from '@supabase/shared-types/out/constants' import { useState } from 'react' import { useParams } from 'common' import { ScaffoldContainerLegacy } 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 NoPermission from 'components/ui/NoPermission' import ShimmeringLoader from 'components/ui/ShimmeringLoader' 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 { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { Alert, Button, Input } from 'ui' import AuthorizedAppRow from './AuthorizedAppRow' import DeleteAppModal from './DeleteAppModal' import OAuthAppRow from './OAuthAppRow' import PublishAppModal from './PublishAppSidePanel' import RevokeAppModal from './RevokeAppModal' import { X } from 'lucide-react' // [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 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 canReadOAuthApps = useCheckPermissions(PermissionAction.READ, 'approved_oauth_apps') const canCreateOAuthApps = useCheckPermissions(PermissionAction.CREATE, 'approved_oauth_apps') const { data: publishedApps, error: publishedAppsError, isLoading: 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, isLoading: 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)) }) if (!canReadOAuthApps) { return ( ) } 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 && (
)} {isErrorPublishedApps && ( )} {createdApp !== undefined && (

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

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

You do not have any published applications yet

) : ( , Name, Client ID, Client Secret, 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 && (
)} {isErrorAuthorizedApps && } {isSuccessAuthorizedApps && ( <> {(authorizedApps.length ?? 0) === 0 ? (

You do not have any authorized applications yet

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