From 14457936e847bd88f9c1d0f459fdb75a29f716c1 Mon Sep 17 00:00:00 2001 From: nalanj <5594+nalanj@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:32:56 -0500 Subject: [PATCH] feat(webapp): Show integration issues in their settings (#3044) Waiting on https://github.com/NangoHQ/nango/pull/3050 Shows missing field errors on the integrations page and highlights the errors on the tab: image And in the integrations list: image https://linear.app/nango/issue/NAN-2168/surface-integrationsconnections-errors-in-nango-ui - Set up a new integration that needs configuration, but don't configure it - View the integration page. You should see a yellow dot in the settings tab and a banner saying what fields need to be configured --- .../lib/controllers/config.controller.ts | 4 +- .../webapp/src/components/ErrorCircle.tsx | 5 +- .../webapp/src/pages/Integrations/List.tsx | 9 +++- .../Settings/components/General.tsx | 52 +++++++++++++++++-- .../Integrations/providerConfigKey/Show.tsx | 5 +- 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/packages/server/lib/controllers/config.controller.ts b/packages/server/lib/controllers/config.controller.ts index 1440a04a00..03bd0d0b06 100644 --- a/packages/server/lib/controllers/config.controller.ts +++ b/packages/server/lib/controllers/config.controller.ts @@ -31,6 +31,7 @@ export interface Integration { creationDate: Date | undefined; connectionConfigParams?: string[]; credentialParams?: string[]; + missing_fields_count: number; } export interface ListIntegration { @@ -131,7 +132,8 @@ class ConfigController { provider: config.provider, scripts: activeFlows.length, connection_count: Number(config.connection_count), - creationDate: config.created_at + creationDate: config.created_at, + missing_fields_count: config.missing_fields.length }; // Used by legacy connection create diff --git a/packages/webapp/src/components/ErrorCircle.tsx b/packages/webapp/src/components/ErrorCircle.tsx index faa573a8ad..fbfb96e41e 100644 --- a/packages/webapp/src/components/ErrorCircle.tsx +++ b/packages/webapp/src/components/ErrorCircle.tsx @@ -2,10 +2,11 @@ import { IconExclamationMark, IconLock, IconRefresh } from '@tabler/icons-react' import type React from 'react'; export type ErrorCircleIcon = '!' | 'sync' | 'auth'; -export const ErrorCircle: React.FC<{ icon?: ErrorCircleIcon }> = ({ icon = '!' }) => { +export type ErrorCircleVariant = 'error' | 'warning'; +export const ErrorCircle: React.FC<{ icon?: ErrorCircleIcon; variant?: ErrorCircleVariant }> = ({ icon = '!', variant = 'error' }) => { return ( - + {icon === '!' && } {icon === 'sync' && } {icon === 'auth' && } diff --git a/packages/webapp/src/pages/Integrations/List.tsx b/packages/webapp/src/pages/Integrations/List.tsx index ed07f19226..641c0211ff 100644 --- a/packages/webapp/src/pages/Integrations/List.tsx +++ b/packages/webapp/src/pages/Integrations/List.tsx @@ -8,6 +8,8 @@ import IntegrationLogo from '../../components/ui/IntegrationLogo'; import { useStore } from '../../store'; import { useListIntegration } from '../../hooks/useIntegration'; +import { ErrorCircle } from '../../components/ErrorCircle'; +import { SimpleTooltip } from '../../components/SimpleTooltip'; import { Helmet } from 'react-helmet'; import { ErrorPageComponent } from '../../components/ErrorComponent'; @@ -61,7 +63,7 @@ export default function IntegrationList() {
Connections
Active Scripts
- {integrations?.map(({ uniqueKey, provider, connection_count, scripts }) => ( + {integrations?.map(({ uniqueKey, provider, connection_count, scripts, missing_fields_count }) => (

{uniqueKey}

+ {missing_fields_count > 0 && ( + + + + )}

{connection_count}

diff --git a/packages/webapp/src/pages/Integrations/providerConfigKey/Settings/components/General.tsx b/packages/webapp/src/pages/Integrations/providerConfigKey/Settings/components/General.tsx index e4abacc092..37bd5d0c2e 100644 --- a/packages/webapp/src/pages/Integrations/providerConfigKey/Settings/components/General.tsx +++ b/packages/webapp/src/pages/Integrations/providerConfigKey/Settings/components/General.tsx @@ -13,11 +13,49 @@ import { InfoBloc } from '../../../../../components/InfoBloc'; import { CopyButton } from '../../../../../components/ui/button/CopyButton'; import SecretInput from '../../../../../components/ui/input/SecretInput'; import type { EnvironmentAndAccount } from '@nangohq/server'; +import { Info } from '../../../../../components/Info'; -export const SettingsGeneral: React.FC<{ data: GetIntegration['Success']['data']; environment: EnvironmentAndAccount['environment'] }> = ({ - data: { integration, meta, template }, - environment -}) => { +const FIELD_DISPLAY_NAMES: Record> = { + OAUTH1: { + oauth_client_id: 'Client ID', + oauth_client_secret: 'Client Secret' + }, + OAUTH2: { + oauth_client_id: 'Client ID', + oauth_client_secret: 'Client Secret' + }, + TBA: { + oauth_client_id: 'Client ID', + oauth_client_secret: 'Client Secret' + }, + APP: { + oauth_client_id: 'App ID', + oauth_client_secret: 'App Private Key', + app_link: 'App Public Link' + }, + CUSTOM: { + oauth_client_id: 'Client ID', + oauth_client_secret: 'Client Secret', + app_link: 'App Public Link', + app_id: 'App ID', + private_key: 'App Private Key' + } +} as const; + +function missingFieldsMessage( + template: GetIntegration['Success']['data']['template'], + integration: GetIntegration['Success']['data']['integration'] +): string | null { + const mappings = FIELD_DISPLAY_NAMES[template.auth_mode]; + if (!mappings) return null; + + return integration.missing_fields.map((field) => mappings[field] || field).join(', '); +} + +export const SettingsGeneral: React.FC<{ + data: GetIntegration['Success']['data']; + environment: EnvironmentAndAccount['environment']; +}> = ({ data: { integration, meta, template }, environment }) => { const { toast } = useToast(); const navigate = useNavigate(); @@ -59,6 +97,12 @@ export const SettingsGeneral: React.FC<{ data: GetIntegration['Success']['data'] return (
+ {integration.missing_fields.length > 0 && ( + + This integration cannot create connections until the following fields are configured: {missingFieldsMessage(template, integration)} + + )} +
{integration?.provider} diff --git a/packages/webapp/src/pages/Integrations/providerConfigKey/Show.tsx b/packages/webapp/src/pages/Integrations/providerConfigKey/Show.tsx index 0cee9dee79..b6f64a88e2 100644 --- a/packages/webapp/src/pages/Integrations/providerConfigKey/Show.tsx +++ b/packages/webapp/src/pages/Integrations/providerConfigKey/Show.tsx @@ -194,7 +194,10 @@ export const ShowIntegration: React.FC = () => { - +