From 63664f857cab2b2a30c2892dbef5659908468027 Mon Sep 17 00:00:00 2001 From: Amadeo Pellicce Date: Sat, 19 Oct 2024 01:34:04 +0200 Subject: [PATCH] Ag touches (#56) * fixing first name * reusing same ConnectDialog for AG and non AG portals and styling AG portal * bubling up handlers to trigger refetch on adding component * improving error state and triggering refetch --- connectors/connector-postgres/server.ts | 20 +-- .../components/AGConnectionPortal.tsx | 157 ++++-------------- .../{ConnectButton.tsx => ConnectDialog.tsx} | 63 ++++--- .../components/ConnectionPortal.tsx | 26 ++- .../hocs/WithConnectorConnect.tsx | 7 +- 5 files changed, 102 insertions(+), 171 deletions(-) rename packages/engine-frontend/components/{ConnectButton.tsx => ConnectDialog.tsx} (61%) diff --git a/connectors/connector-postgres/server.ts b/connectors/connector-postgres/server.ts index 8a6f5271..2b6a7fc6 100644 --- a/connectors/connector-postgres/server.ts +++ b/connectors/connector-postgres/server.ts @@ -16,8 +16,7 @@ const agTableMappings = [ {from: 'integration_ats_candidate', to: 'IntegrationATSCandidate'}, {from: 'integration_ats_job_opening', to: 'IntegrationATSJobOpening'}, {from: 'integration_ats_offer', to: 'IntegrationATSOffer'}, - {from: 'integration_connection', to: 'IntegrationConnection'}, - {from: 'integration_ats_opening', to: 'IntegrationATSOpening'}, + {from: 'integration_connection', to: 'IntegrationConnection'} ] async function setupTable({ @@ -193,7 +192,7 @@ export const postgresServer = { ), ) }, - destinationSync: ({endUser, source, settings: {databaseUrl, migrateTables}}) => { + destinationSync: ({endUser, source, settings: {databaseUrl}}) => { console.log('[destinationSync] Will makePostgresClient', { // databaseUrl, // migrationsPath: __dirname + '/migrations', @@ -210,10 +209,6 @@ export const postgresServer = { const migrationRan: Record = {} async function runMigration(pool: DatabasePool, tableName: string) { - if (!migrateTables) { - console.log('[destinationSync] Will not run migration for', tableName, 'as migrateTables is false'); - return; - } console.log('will run migration for', tableName) if (migrationRan[tableName]) { return @@ -250,7 +245,7 @@ export const postgresServer = { isOpenInt: true, } - const isAgInsert = + const isAgInsert = endUser?.orgId === 'org_2lcCCimyICKI8cpPNQt195h5zrP' || endUser?.orgId === 'org_2ms9FdeczlbrDIHJLcwGdpv3dTx' @@ -261,10 +256,13 @@ export const postgresServer = { rowToInsert['external_job_id'] = data.entity?.raw?.id || ''; } else if (tableName === 'IntegrationAtsCandidate') { rowToInsert['opening_external_id'] = data.entity?.raw?.id || ''; - rowToInsert['candidate_name'] = data.entity?.raw?.name + ' ' + data.entity?.raw?.last_name || ''; + rowToInsert['candidate_name'] = data.entity?.raw?.first_name + ' ' + data.entity?.raw?.last_name || ''; } else if (tableName === 'IntegrationAtsJobOpening') { - rowToInsert['opening_external_id'] = data.entity?.raw?.opening_id || ''; - rowToInsert['job_id'] = data.entity?.raw?.job_id || ''; + rowToInsert['opening_external_id'] = data.entity?.raw?.id || ''; + // NOTE Job openings are nested within Jobs and that o bject does not contain an id of the parent (job id) + // Depends on the implementation we may have to change this, leaving empty for now + // https://developers.greenhouse.io/harvest.html#the-job-object + rowToInsert['job_id'] = ''; } else if (tableName === 'IntegrationAtsOffer') { // Note: These fields seemed duplicated from the nested objects rowToInsert['opening_external_id'] = data.entity?.raw?.opening?.id || ''; diff --git a/packages/engine-frontend/components/AGConnectionPortal.tsx b/packages/engine-frontend/components/AGConnectionPortal.tsx index 737aaf62..e2bd8919 100644 --- a/packages/engine-frontend/components/AGConnectionPortal.tsx +++ b/packages/engine-frontend/components/AGConnectionPortal.tsx @@ -2,26 +2,14 @@ import {AlertTriangle} from 'lucide-react' import React from 'react' -import type {Id, Vertical} from '@openint/cdk' -import {VERTICAL_BY_KEY} from '@openint/cdk' +import type {Id} from '@openint/cdk' import type {UIPropsNoChildren} from '@openint/ui' -import { - Card, - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - ResourceCard, -} from '@openint/ui' +import { Card, ResourceCard } from '@openint/ui' import {cn} from '@openint/ui/utils' import {R} from '@openint/util' import {WithConnectConfig} from '../hocs/WithConnectConfig' -import type {ConnectorConfigFilters} from '../hocs/WithConnectConfig' import {_trpcReact} from '../providers/TRPCProvider' -import {ConnectButton} from './ConnectButton' -import {IntegrationSearch} from './IntegrationSearch' +import {ConnectDialog} from './ConnectDialog' import {AgResourceRowActions} from './AgResourceRowActions' type ConnectEventType = 'open' | 'close' | 'error' @@ -57,10 +45,14 @@ const AGConnectionPortalComponent: React.FC = ({ // const iframe = document.getElementById('openint-connect-iframeId'); // iframe?.contentWindow.postMessage({type: 'triggerConnectDialog', value: true },'*'); - const handleMessage = React.useCallback((event: MessageEvent) => { + const handleMessage = React.useCallback(async (event: MessageEvent) => { if (event.data.type === 'triggerConnectDialog') { console.log('triggerConnectDialog', event.data.value) - setOpenDialog(event.data.value || true) + if(event.data.value) { + await listConnectionsRes.refetch().then(() => { + setOpenDialog(event.data.value) + }) + } } }, []) @@ -100,6 +92,12 @@ const AGConnectionPortalComponent: React.FC = ({ ), })) + if (categoriesWithConnections.length > 1) { + console.warn( + 'AG Connection Portal only currently supports 1 category being rendered via postMessage', + ) + } + return (
{/* Listing by categories */} @@ -126,18 +124,23 @@ const AGConnectionPortalComponent: React.FC = ({ /> ))} - 0} - connectorNames={category.connections.map( - (c) => c.connectorName, - )} - /> + {category.connections.length === 0 && ( + +
+ +

+ {`No data source connected`} +

+
+
+ )} {openDialog && ( - listConnectionsRes.refetch()} /> )}
@@ -154,107 +157,3 @@ export const AGConnectionPortal = React.memo( AGConnectionPortalComponent, areEqual, ) - -const NewConnectionCard = ({ - category, - hasExisting, - connectorNames, -}: { - category: Vertical - hasExisting: boolean - connectorNames: string[] -}) => ( - -
- -

- {hasExisting - ? `Connect another ${category.name} integration` - : `No ${category.name} integration connected`} -

-
- -

- Connect an integration here ASAP. This integration is needed to keep your{' '} - {category.name} data accurate. -

- -
-) - -function AgConnectDialog({ - connectorConfigFilters, - open, - setOpen, -}: { - connectorConfigFilters: ConnectorConfigFilters - open: boolean - setOpen: React.Dispatch> -}) { - console.log('AgConnectDialog', open) - const {verticalKey: categoryKey} = connectorConfigFilters - - return ( - - {({ccfgs}) => { - const [first, ...rest] = ccfgs - if (!first) { - return ( -
- No connectors configured for {categoryKey}. Please check your - settings -
- ) - } - const category = categoryKey ? VERTICAL_BY_KEY[categoryKey] : undefined - const content = ( - { - if (e.type === 'close' || e.type === 'error') { - setOpen(false) - } - }} - /> - ) - - return ( - { - console.log('onOpenChange', v) - setOpen(v) - }} - modal={false}> - {/* - */} - - - New connection - - Choose a connector config to start - - - {category && ( - <> -

Select your first {category.name} integration

-

{category.description}

- - )} - {content} - - {/* Cancel here */} - -
-
- ) - }} -
- ) -} diff --git a/packages/engine-frontend/components/ConnectButton.tsx b/packages/engine-frontend/components/ConnectDialog.tsx similarity index 61% rename from packages/engine-frontend/components/ConnectButton.tsx rename to packages/engine-frontend/components/ConnectDialog.tsx index 44a3fe42..ed977be4 100644 --- a/packages/engine-frontend/components/ConnectButton.tsx +++ b/packages/engine-frontend/components/ConnectDialog.tsx @@ -20,21 +20,27 @@ import type { import {WithConnectConfig} from '../hocs/WithConnectConfig' import {IntegrationSearch} from './IntegrationSearch' -interface ConnectButtonCommonProps { +interface ConnectDialogCommonProps { className?: string children?: React.ReactNode } // TODO: Refactor WithOpenConnect out of ConnectButton // such that users can render their own trigger fully -export function ConnectButton({ +export function ConnectDialog({ connectorNames = [], connectorConfigFilters, + open: controlledOpen, + setOpen: controlledSetOpen, + onEvent, ...commonProps }: { connectorConfigFilters: ConnectorConfigFilters connectorNames?: string[] -} & ConnectButtonCommonProps) { + open?: boolean + setOpen?: (open: boolean) => void + onEvent?: (event: any) => void +} & ConnectDialogCommonProps) { const {verticalKey: categoryKey} = connectorConfigFilters return ( @@ -45,20 +51,30 @@ export function ConnectButton({ const [first, ...rest] = filteredCcfgs if (!first) { return ( -
- No connectors configured for {categoryKey}. Please check your - settings -
+ {}}> + + + No Integrations Available + + You have no further integrations available. If you believe this is an error, please contact support. + + + + + + + ) } - // Render dialog for MultiConnector scenarios - // This would be the case for greenhouse + lever const category = categoryKey ? VERTICAL_BY_KEY[categoryKey] : undefined return ( ) }} @@ -70,22 +86,30 @@ function MultipleConnectButton({ children, className, connectorConfigs, + open: controlledOpen, + setOpen: controlledSetOpen, + onEvent, }: { connectorConfigs: ConnectorConfig[] - /** Should correspond to connectorConfigs, but we can't guarantee that statically here... */ category?: Vertical -} & ConnectButtonCommonProps) { - const [open, setOpen] = React.useState(false) + open?: boolean + setOpen?: (open: boolean) => void + onEvent?: (event: any) => void +} & ConnectDialogCommonProps) { + const [internalOpen, setInternalOpen] = React.useState(false) + + // Determine if the component is controlled or uncontrolled + const isControlled = controlledOpen !== undefined && controlledSetOpen !== undefined + const open = isControlled ? controlledOpen : internalOpen + const setOpen = isControlled ? controlledSetOpen : setInternalOpen // Unconditional render to avoid delay when dialog is opened const content = ( { + if (onEvent) onEvent(e); if (e.type === 'close' || e.type === 'error') { - // Cannot close during open event otherwise whole thing becomes unmounted - // and we end up closing the connect dialog itself... - // Once we have a global UserInputDialog setOpen(false) } }} @@ -98,9 +122,11 @@ function MultipleConnectButton({ // as well as other modals introduced by things like Plaid - + {!isControlled && ( + + )} @@ -110,7 +136,6 @@ function MultipleConnectButton({ {content} - {/* Cancel here */} diff --git a/packages/engine-frontend/components/ConnectionPortal.tsx b/packages/engine-frontend/components/ConnectionPortal.tsx index 0b0b8e43..8e2e8821 100644 --- a/packages/engine-frontend/components/ConnectionPortal.tsx +++ b/packages/engine-frontend/components/ConnectionPortal.tsx @@ -18,7 +18,7 @@ import {cn} from '@openint/ui/utils' import {R} from '@openint/util' import {WithConnectConfig} from '../hocs/WithConnectConfig' import {_trpcReact} from '../providers/TRPCProvider' -import {ConnectButton} from './ConnectButton' +import {ConnectDialog} from './ConnectDialog' type ConnectEventType = 'open' | 'close' | 'error' @@ -100,11 +100,15 @@ export function ConnectionPortal({className}: ConnectionPortalProps) { Add a connection to get started

- + onEvent={(event) => { + if (event.type === 'close') { + listConnectionsRes.refetch(); // Trigger refetch + } + }} + > ) : (
@@ -194,11 +198,15 @@ export function ConnectionPortal({className}: ConnectionPortalProps) { Choose a connector config to start

- + onEvent={(event) => { + if (event.type === 'close') { + listConnectionsRes.refetch(); // Trigger refetch + } + }} + > diff --git a/packages/engine-frontend/hocs/WithConnectorConnect.tsx b/packages/engine-frontend/hocs/WithConnectorConnect.tsx index 638e7d1b..3f980d7f 100644 --- a/packages/engine-frontend/hocs/WithConnectorConnect.tsx +++ b/packages/engine-frontend/hocs/WithConnectorConnect.tsx @@ -138,8 +138,8 @@ export const WithConnectorConnect = ({ onSuccess(msg) { if (msg) { toast({ - title: `Success (${ccfg.connector.displayName})`, - description: `${msg}`, + title: `Successfully connected to ${ccfg.connector.displayName}`, + // description: `${msg}`, variant: 'success', }) } @@ -150,9 +150,10 @@ export const WithConnectorConnect = ({ if (err === CANCELLATION_TOKEN) { return } + console.log(ccfg.connector.displayName + ' connection error:',err) toast({ title: `Failed to connect to ${ccfg.connector.displayName}`, - description: `${err}`, + // description: `${err}`, variant: 'destructive', }) onEvent?.({type: 'error'})