mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 01:40:13 -04:00
2a018833fd
## Context Part of marketplace integrations, this moves the last integration over to the new integrations UI Just a heads up though that the code changes imo are quite messy - am trying to figure out how to shift a lot of the integration logic (e.g installing, installation progress tracking, etc) into a code configuration within `Integrations.constants.ts` so pardon the mid-transition state. I reckon we'll be able to clean up things once requirements are bit more clearer. (Refer to "Moving forward" section below for details) ## Changes involved - Much of the details on the Stripe Sync Engine page will now live in the Installation panel <img width="1143" height="512" alt="image" src="https://github.com/user-attachments/assets/cb23e49d-cc4e-4ad6-8a47-0bc3fe81ede7" /> <img width="656" height="955" alt="image" src="https://github.com/user-attachments/assets/ff0e33c5-52ab-480f-b941-ebf3fd0708c5" /> - Code wise, `useStripeSyncStatus` will retrieve `ref` and `connectionString` itself, don't need to pass in as parameters ## To test - [ ] Verify that you can install + uninstall Stripe Sync Engine with the flag off - [ ] Verify that you can install + uninstall Stripe Sync Engine with the flag on ## Moving forward Couple of notes + open questions at the top of my head - We'll need to do away with integration-specific overview UI (e.g Queues, Data API, Webhooks, Wrappers, Stripe Sync Engine) - Everything needs to be defined in code, so `IntegrationDefinition` within `Integrations.constants.ts` is also a bit fluid at the moment as we figure out what properties we need / don't need - We'll need to figure out a way to do the following from a code config POV, keeping in mind that integrations will be fetched remotely from a DB - How to trigger the installation of the integration (e.g from a set of commands? SQL?) - How to track the progress of the installation (e.g We can do long polling but on what data?) - How to uninstall the integration (if applicable, e.g Stripe Sync Engine supports this) - How all this this can work for self-hosted/CLI <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * New modular integration install experience: interactive install sheet with form inputs, installation overview (Extensions & SQL), advanced per-extension schema settings, and dedicated installation settings. * New Stripe Sync Engine overview and action controls for install/upgrade/uninstall flows. * **Bug Fixes & Improvements** * Improved installation status handling, long-polling/status checks, progress/error reporting and telemetry; more robust install/uninstall flows and error recovery. * **Tests** * Updated install-sheet tests to better simulate form submission. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
import { useParams } from 'common'
|
|
import { PropsWithChildren, ReactNode } from 'react'
|
|
import { Badge, Card, CardContent, cn, Separator } from 'ui'
|
|
|
|
import { INTEGRATIONS } from '../Landing/Integrations.constants'
|
|
import { BuiltBySection } from './BuildBySection'
|
|
import { MarkdownContent } from './MarkdownContent'
|
|
import { MissingExtensionAlert } from './MissingExtensionAlert'
|
|
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
|
|
export interface IntegrationOverviewTabProps {
|
|
actions?: ReactNode
|
|
status?: string | ReactNode
|
|
alert?: ReactNode
|
|
hideRequiredExtensionsSection?: boolean
|
|
}
|
|
|
|
/** [Joshen] This will eventually get replaced by IntegrationOverviewTabV2 */
|
|
export const IntegrationOverviewTab = ({
|
|
actions,
|
|
alert,
|
|
status,
|
|
children,
|
|
hideRequiredExtensionsSection = false,
|
|
}: PropsWithChildren<IntegrationOverviewTabProps>) => {
|
|
const { id } = useParams()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const integration = INTEGRATIONS.find((i) => i.id === id)
|
|
|
|
const { data: extensions } = useDatabaseExtensionsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
if (!integration) {
|
|
return <div>Unsupported integration type</div>
|
|
}
|
|
|
|
const dependsOnExtension = (integration.requiredExtensions ?? []).length > 0
|
|
|
|
const installableExtensions = (extensions ?? []).filter((ext) =>
|
|
(integration.requiredExtensions ?? []).includes(ext.name)
|
|
)
|
|
const hasToInstallExtensions = installableExtensions.some((x) => !x.installed_version)
|
|
|
|
// The integration requires extensions that are not available to install on the current database image
|
|
const hasMissingExtensions =
|
|
installableExtensions.length !== integration.requiredExtensions.length
|
|
|
|
return (
|
|
<div className="flex flex-col gap-8 py-10">
|
|
<BuiltBySection integration={integration} status={status} />
|
|
|
|
{!!alert && <div className="px-10 max-w-4xl">{alert}</div>}
|
|
|
|
<MarkdownContent key={integration.id} integrationId={integration.id} />
|
|
|
|
<Separator />
|
|
|
|
{dependsOnExtension && !hideRequiredExtensionsSection && (
|
|
<div className="px-4 md:px-10 max-w-4xl flex flex-col gap-y-4">
|
|
<h4>Required extensions</h4>
|
|
<Card>
|
|
<CardContent className="p-0">
|
|
<ul className="text-foreground-light text-sm">
|
|
{(integration.requiredExtensions ?? []).map((requiredExtension, idx) => {
|
|
const extension = (extensions ?? []).find((ext) => ext.name === requiredExtension)
|
|
const isInstalled = !!extension?.installed_version
|
|
const isLastRow = idx === (integration.requiredExtensions?.length ?? 0) - 1
|
|
|
|
return (
|
|
<li
|
|
key={requiredExtension}
|
|
className={cn(
|
|
'flex items-center justify-between gap-3 py-2 px-3',
|
|
!isLastRow ? 'border-b' : ''
|
|
)}
|
|
>
|
|
<code className="text-xs">{requiredExtension}</code>
|
|
|
|
<div className="shrink-0">
|
|
{extension ? (
|
|
isInstalled ? (
|
|
<Badge>Installed</Badge>
|
|
) : (
|
|
<MissingExtensionAlert extension={extension} />
|
|
)
|
|
) : (
|
|
<span className="text-foreground-muted">Unavailable</span>
|
|
)}
|
|
</div>
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
|
|
{hasMissingExtensions && (
|
|
<div className="py-3 px-4 border-t">{integration.missingExtensionsAlert}</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)}
|
|
|
|
{!!actions && (
|
|
<div
|
|
aria-disabled={hasToInstallExtensions && !hideRequiredExtensionsSection}
|
|
className={cn(
|
|
'px-10 max-w-4xl',
|
|
hasToInstallExtensions &&
|
|
!hideRequiredExtensionsSection &&
|
|
'opacity-25 [&_button]:pointer-events-none'
|
|
)}
|
|
>
|
|
{actions}
|
|
</div>
|
|
)}
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|