Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Commit

Permalink
Connect workflow (#64)
Browse files Browse the repository at this point in the history
* Add Tabs component for storybook usage

* Update Connections card component and story

* Update add connections tab content to use new card

* UI improvements based on code review

* Split Portal tabs content into separate components

* Fix search input align with content

* Fix casing for category header

* fixing orgId assignment

---------

Co-authored-by: Rodrigo Arze Leon <[email protected]>
Co-authored-by: Amadeo Pellicce <[email protected]>
  • Loading branch information
3 people authored Oct 24, 2024
1 parent 2ac7c1b commit bcb49cb
Show file tree
Hide file tree
Showing 11 changed files with 347 additions and 206 deletions.
2 changes: 1 addition & 1 deletion apps/web/app/connect/portal/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default async function PortalPage({
}

const isAgMode =
viewer.orgId = 'org_2nJZrA4Dk8i3wszhm6PsP3M2Vwy' ||
viewer.orgId === 'org_2nJZrA4Dk8i3wszhm6PsP3M2Vwy' ||
viewer.orgId === 'org_2lcCCimyICKI8cpPNQt195h5zrP' ||
viewer.orgId === 'org_2ms9FdeczlbrDIHJLcwGdpv3dTx'

Expand Down
29 changes: 29 additions & 0 deletions packages/engine-frontend/components/AddConnectionTabContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {ConnectorConfigFilters} from '../hocs/WithConnectConfig'
import {ConnectIntegrations} from './ConnectIntegrations'

interface AddConnectionTabContentProps {
connectorConfigFilters: ConnectorConfigFilters
refetch: () => void
}

export function AddConnectionTabContent({
connectorConfigFilters,
refetch,
}: AddConnectionTabContentProps) {
return (
<div className="flex flex-col gap-2 p-4">
<div>
<p className="text-base font-semibold">Setup a new Connection</p>
<p className="text-base">Choose a connector config to start</p>
</div>
<ConnectIntegrations
connectorConfigFilters={connectorConfigFilters}
onEvent={(event) => {
if (event.type === 'close') {
refetch()
}
}}
/>
</div>
)
}
34 changes: 34 additions & 0 deletions packages/engine-frontend/components/ConnectIntegrations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
WithConnectConfig,
type ConnectorConfigFilters,
} from '../hocs/WithConnectConfig'
import {IntegrationSearch} from './IntegrationSearch'

export function ConnectIntegrations({
connectorConfigFilters,
connectorNames = [],
onEvent,
}: {
connectorConfigFilters: ConnectorConfigFilters
connectorNames?: string[]
onEvent: (event: any) => void
}) {
return (
<WithConnectConfig {...connectorConfigFilters}>
{({ccfgs}) => {
const filteredCcfgs = ccfgs.filter(
(c) => !connectorNames.includes(c.connectorName),
)

return (
<IntegrationSearch
connectorConfigs={filteredCcfgs}
onEvent={(e) => {
if (onEvent) onEvent(e)
}}
/>
)
}}
</WithConnectConfig>
)
}
177 changes: 32 additions & 145 deletions packages/engine-frontend/components/ConnectionPortal.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
'use client'

import {Loader, Settings} from 'lucide-react'
import type {Id} from '@openint/cdk'
import type {UIPropsNoChildren} from '@openint/ui'
import {
Badge,
ConnectorLogo,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Separator,
useToast,
} from '@openint/ui'
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@openint/ui/shadcn/Tabs'
import {useToast} from '@openint/ui'
import {Tabs} from '@openint/ui/components/Tabs'
import {cn} from '@openint/ui/utils'
import {R} from '@openint/util'
import {WithConnectConfig} from '../hocs/WithConnectConfig'
import {_trpcReact} from '../providers/TRPCProvider'
import {ConnectDialog} from './ConnectDialog'
import {AddConnectionTabContent} from './AddConnectionTabContent'
import {ConnectionsTabContent} from './ConnectionsTabContent'

type ConnectEventType = 'open' | 'close' | 'error'

Expand Down Expand Up @@ -77,139 +68,35 @@ export function ConnectionPortal({className}: ConnectionPortalProps) {
const connectionCount = connections.length
console.log({connectionCount})

const tabConfig = [
{
key: 'connections',
title: `My Connections (${connectionCount})`,
content: (
<ConnectionsTabContent
connectionCount={connectionCount}
refetch={listConnectionsRes.refetch}
isLoading={listConnectionsRes.isLoading}
deleteResource={deleteResource.mutate}
categoriesWithConnections={categoriesWithConnections}
/>
),
},
{
key: 'add-connection',
title: 'Add a Connection',
content: (
<AddConnectionTabContent
connectorConfigFilters={{}}
refetch={listConnectionsRes.refetch}
/>
),
},
]

return (
<div className={cn('gap-4 p-8', className)}>
{/* Listing by categories */}
<Tabs defaultValue="connections" className="w-[400px]">
<TabsList>
<TabsTrigger value="connections">
My Connections ({connectionCount})
</TabsTrigger>
<TabsTrigger value="add-connection">
Add a Connection
</TabsTrigger>
</TabsList>
<TabsContent value="connections">
{connectionCount === 0 ? (
<div className="flex flex-col gap-2 p-4">
<div>
<p className="text-base font-semibold">
No connections yet
</p>
<p className="text-base">
Add a connection to get started
</p>
</div>
<ConnectDialog
className="self-end bg-[#8A5DF6] hover:bg-[#A082E9]"
connectorConfigFilters={{}}
onEvent={(event) => {
if (event.type === 'close') {
listConnectionsRes.refetch(); // Trigger refetch
}
}}
></ConnectDialog>
</div>
) : (
<div className="p-4">
{listConnectionsRes.isLoading ? (
<div className="flex h-full items-center justify-center">
<Loader className="size-5 animate-spin text-[#8A5DF6]" />
</div>
) : (
categoriesWithConnections.map((category) => (
<div
key={category.name}
className="flex flex-col space-y-4">
{category.connections.map((conn) => (
<>
<div
key={conn.id}
className="flex flex-row justify-between gap-4">
<div className="flex flex-row gap-4">
<ConnectorLogo
connector={conn.connectorConfig.connector}
className="size-[64px] rounded-lg"
/>
<div className="flex h-full flex-col justify-center">
<div className="flex flex-row items-center gap-2">
<h4 className="font-bold">
{conn.connectorName
.charAt(0)
.toUpperCase() +
conn.connectorName.slice(1)}
</h4>
<Badge variant="secondary">
{category.name}
</Badge>
</div>
{conn.pipelineIds.length > 0 && (
<div className="mt-2">
{conn.syncInProgress ? (
<div className="flex flex-row items-center justify-start gap-2">
<Loader className="size-5 animate-spin text-[#8A5DF6]" />
<p className="font-semibold">
Syncing...
</p>
</div>
) : (
<p>Successfully synced</p>
)}
</div>
)}
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger>
<Settings className="size-5 text-[#808080]" />
</DropdownMenuTrigger>
<DropdownMenuContent className="flex w-[80px] items-center justify-center">
<DropdownMenuItem
className="flex items-center justify-center"
onSelect={() =>
deleteResource.mutate({id: conn.id})
}>
<span className="text-center font-medium text-red-500">
Delete
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* TODO: Improve the condition to hide the separator for the last item, right now it iterates
over all categories and all connections, would be good to have a single array of connections with the category
information included already */}
<Separator className="w-full" />
</>
))}
</div>
))
)}
</div>
)}
</TabsContent>
<TabsContent value="add-connection">
<div className="flex flex-col gap-2 p-4">
<div>
<p className="text-base font-semibold">
Setup a new Connection
</p>
<p className="text-base">
Choose a connector config to start
</p>
</div>
<ConnectDialog
className="self-end bg-[#8A5DF6] hover:bg-[#A082E9]"
connectorConfigFilters={{}}
onEvent={(event) => {
if (event.type === 'close') {
listConnectionsRes.refetch(); // Trigger refetch
}
}}
></ConnectDialog>
</div>
</TabsContent>
</Tabs>
<div className={cn('gap-4 p-4 lg:p-8', className)}>
<Tabs tabConfig={tabConfig} defaultValue="connections" />
</div>
)
}}
Expand Down
120 changes: 120 additions & 0 deletions packages/engine-frontend/components/ConnectionsTabContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {Loader, Settings} from 'lucide-react'
import {
Badge,
ConnectorLogo,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Separator,
} from '@openint/ui'
import type {ConnectorConfig} from '../hocs/WithConnectConfig'
import {ConnectDialog} from './ConnectDialog'

interface ConnectionsTabContentProps {
connectionCount: number
categoriesWithConnections: Array<{
name: string
connections: Array<{
id: string
connectorConfig: ConnectorConfig
connectorName: string
pipelineIds: string[]
syncInProgress: boolean
}>
}>
refetch: () => void
isLoading: boolean
deleteResource: ({id}: {id: string}) => void
}

export function ConnectionsTabContent({
connectionCount,
refetch,
isLoading,
deleteResource,
categoriesWithConnections,
}: ConnectionsTabContentProps) {
return connectionCount === 0 ? (
<div className="flex flex-col gap-2 p-4">
<div>
<p className="text-base font-semibold">No connections yet</p>
<p className="text-base">Add a connection to get started</p>
</div>
<ConnectDialog
className="self-end bg-[#8A5DF6] hover:bg-[#A082E9]"
connectorConfigFilters={{}}
onEvent={(event) => {
if (event.type === 'close') {
refetch() // Trigger refetch
}
}}></ConnectDialog>
</div>
) : (
<div className="p-4">
{isLoading ? (
<div className="flex h-full items-center justify-center">
<Loader className="size-5 animate-spin text-[#8A5DF6]" />
</div>
) : (
categoriesWithConnections.map((category) => (
<div key={category.name} className="flex flex-col space-y-4">
{category.connections.map((conn) => (
<>
<div
key={conn.id}
className="flex flex-row justify-between gap-4">
<div className="flex flex-row gap-4">
<ConnectorLogo
connector={conn.connectorConfig.connector}
className="size-[64px] rounded-lg"
/>
<div className="flex h-full flex-col justify-center">
<div className="flex flex-row items-center gap-2">
<h4 className="font-bold">
{conn.connectorName.charAt(0).toUpperCase() +
conn.connectorName.slice(1)}
</h4>
<Badge variant="secondary">{category.name}</Badge>
</div>
{conn.pipelineIds.length > 0 && (
<div className="mt-2">
{conn.syncInProgress ? (
<div className="flex flex-row items-center justify-start gap-2">
<Loader className="size-5 animate-spin text-[#8A5DF6]" />
<p className="font-semibold">Syncing...</p>
</div>
) : (
<p>Successfully synced</p>
)}
</div>
)}
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger>
<Settings className="size-5 text-[#808080]" />
</DropdownMenuTrigger>
<DropdownMenuContent className="flex w-[80px] items-center justify-center">
<DropdownMenuItem
className="flex items-center justify-center"
onSelect={() => deleteResource({id: conn.id})}>
<span className="text-center font-medium text-red-500">
Delete
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* TODO: Improve the condition to hide the separator for the last item, right now it iterates
over all categories and all connections, would be good to have a single array of connections with the category
information included already */}
<Separator className="w-full" />
</>
))}
</div>
))
)}
</div>
)
}
Loading

1 comment on commit bcb49cb

@vercel
Copy link

@vercel vercel bot commented on bcb49cb Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.