Skip to content

Commit

Permalink
feat(webapp): Show integration issues in their settings (#3044)
Browse files Browse the repository at this point in the history
<!-- Describe the problem and your solution --> 
Waiting on #3050

Shows missing field errors on the integrations page and highlights the
errors on the tab:

<img width="1382" alt="image"
src="https://github.com/user-attachments/assets/377167b2-2b49-4a55-8b28-76530eccb1a6">

And in the integrations list:

<img width="1381" alt="image"
src="https://github.com/user-attachments/assets/60d92ff1-00f8-4c11-8646-b69f3f5c21ac">


<!-- Issue ticket number and link (if applicable) -->


https://linear.app/nango/issue/NAN-2168/surface-integrationsconnections-errors-in-nango-ui

<!-- Testing instructions (skip if just adding/editing providers) -->

- 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
  • Loading branch information
nalanj authored Nov 27, 2024
1 parent b69fdb1 commit 1445793
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 9 deletions.
4 changes: 3 additions & 1 deletion packages/server/lib/controllers/config.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface Integration {
creationDate: Date | undefined;
connectionConfigParams?: string[];
credentialParams?: string[];
missing_fields_count: number;
}

export interface ListIntegration {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions packages/webapp/src/components/ErrorCircle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<span className="mx-1 cursor-auto flex h-4 w-4 rounded-full ring-red-base/[.35] ring-4">
<span className="flex items-center rounded-full bg-red-base h-4 w-4">
<span className={`flex items-center rounded-full ${variant === 'warning' ? 'bg-yellow-base' : 'bg-red-base'} h-4 w-4`}>
{icon === '!' && <IconExclamationMark className="ml-[2px] h-3 w-3 text-pure-black" />}
{icon === 'sync' && <IconRefresh className="ml-[2px] h-3 w-3 text-pure-black" />}
{icon === 'auth' && <IconLock className="ml-[2px] h-3 w-3 text-pure-black" />}
Expand Down
9 changes: 8 additions & 1 deletion packages/webapp/src/pages/Integrations/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -61,7 +63,7 @@ export default function IntegrationList() {
<div className="w-1/3">Connections</div>
<div className="w-24">Active Scripts</div>
</div>
{integrations?.map(({ uniqueKey, provider, connection_count, scripts }) => (
{integrations?.map(({ uniqueKey, provider, connection_count, scripts, missing_fields_count }) => (
<div
key={`tr-${uniqueKey}`}
className={`flex gap-4 ${
Expand All @@ -76,6 +78,11 @@ export default function IntegrationList() {
<IntegrationLogo provider={provider} height={7} width={7} />
</div>
<p className="truncate">{uniqueKey}</p>
{missing_fields_count > 0 && (
<SimpleTooltip tooltipContent="Missing configuration">
<ErrorCircle icon="!" variant="warning" />
</SimpleTooltip>
)}
</div>
<div className="flex items-center w-1/3">
<p className="">{connection_count}</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Record<string, string>> = {
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();

Expand Down Expand Up @@ -59,6 +97,12 @@ export const SettingsGeneral: React.FC<{ data: GetIntegration['Success']['data']

return (
<div className="flex flex-col gap-8">
{integration.missing_fields.length > 0 && (
<Info variant="warning">
This integration cannot create connections until the following fields are configured: {missingFieldsMessage(template, integration)}
</Info>
)}

<div className="grid grid-cols-2 gap-10">
<InfoBloc title="API Provider">{integration?.provider}</InfoBloc>
<InfoBloc title="Integration ID">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ export const ShowIntegration: React.FC = () => {
<Button variant={tab === 'home' ? 'active' : 'zombie'}>Endpoints</Button>
</Link>
<Link to="./settings">
<Button variant={tab === 'settings' ? 'active' : 'zombie'}>Settings</Button>
<Button variant={tab === 'settings' ? 'active' : 'zombie'}>
Settings
{data.integration.missing_fields.length > 0 && <span className="ml-2 bg-yellow-base h-1.5 w-1.5 rounded-full inline-block"></span>}
</Button>
</Link>
</nav>
<Routes>
Expand Down

0 comments on commit 1445793

Please sign in to comment.