Skip to content

Commit

Permalink
Move Vercel integration page to client (#1236)
Browse files Browse the repository at this point in the history
* Move Vercel integration page to client

* Cleanup debugging, type

* Rename file

* Fix loading flash

* Cleanup unused deps

* Refactor to use FetchResult

* Regen GQL
  • Loading branch information
djfarrelly committed Mar 19, 2024
1 parent 678d85e commit 753c530
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
import { graphql } from '@/gql';
import graphqlAPI from '@/queries/graphqlAPI';
import { getProductionEnvironment } from '@/queries/server-only/getEnvironment';
import { type VercelProject, type VercelProjectViaAPI } from './VercelIntegration';

const GetSavedVercelProjectsDocument = graphql(`
query GetSavedVercelProjects($environmentID: ID!) {
environment: workspace(id: $environmentID) {
savedVercelProjects: vercelApps {
projectID
path
}
}
}
`);
import mergeVercelProjectData from './mergeVercelProjectData';
import { GetSavedVercelProjectsDocument } from './queries';

/**
* Enriches the Vercel projects with the serve paths and whether the project is enabled.
Expand All @@ -29,30 +19,8 @@ export default async function enrichVercelProjects(
});
const savedVercelProjects = getSavedVercelProjectsResponse.environment.savedVercelProjects;

const projects: VercelProject[] = vercelProjects.map((project) => {
const savedProject = savedVercelProjects.find(
(savedProject) => savedProject.projectID === project.id
);
const isProjectEnabled = savedProject !== undefined;
return {
id: project.id,
name: project.name,
servePath: savedProject?.path ?? undefined,
isEnabled: isProjectEnabled,
ssoProtection: project.ssoProtection,
};
});

// We need to sort the projects alphabetically so that the order is consistent
const alphabeticallySortedProjects = projects.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
return mergeVercelProjectData({
vercelProjects,
savedProjects: savedVercelProjects,
});

return alphabeticallySortedProjects;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { type VercelApp } from '@/gql/graphql';
import { type VercelProject, type VercelProjectViaAPI } from './VercelIntegration';

export default function mergeVercelProjectData({
vercelProjects = [],
savedProjects = [],
}: {
vercelProjects: VercelProjectViaAPI[];
savedProjects: VercelApp[];
}): VercelProject[] {
const projects: VercelProject[] = vercelProjects.map((project) => {
const savedProject = savedProjects.find(
(savedProject) => savedProject.projectID === project.id
);
const isProjectEnabled = savedProject !== undefined;
return {
id: project.id,
name: project.name,
servePath: savedProject?.path ?? undefined,
isEnabled: isProjectEnabled,
ssoProtection: project.ssoProtection,
};
});

// We need to sort the projects alphabetically so that the order is consistent
const alphabeticallySortedProjects = projects.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});

return alphabeticallySortedProjects;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import VercelIntegrationForm from './VercelIntegrationForm';
import getVercelIntegration from './getVercelIntegration';
'use client';

export default async function VercelIntegrationPage() {
const vercelIntegration = await getVercelIntegration();
import LoadingIcon from '@/icons/LoadingIcon';
import VercelIntegrationForm from './VercelIntegrationForm';
import { useVercelIntegration } from './useVercelIntegration';

return <VercelIntegrationForm vercelIntegration={vercelIntegration} />;
export default function VercelIntegrationPage() {
const { data, fetching, error } = useVercelIntegration();
if (fetching) {
return (
<div className="flex h-full w-full items-center justify-center">
<LoadingIcon />
</div>
);
}
if (error) {
throw error;
}
return <VercelIntegrationForm vercelIntegration={data} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { graphql } from '@/gql';

export const GetSavedVercelProjectsDocument = graphql(`
query GetSavedVercelProjects($environmentID: ID!) {
environment: workspace(id: $environmentID) {
savedVercelProjects: vercelApps {
id
projectID
path
workspaceID
}
}
}
`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use client';

import { useMemo } from 'react';
import { useQuery } from 'urql';

import { useEnvironments } from '@/queries';
import { getProductionEnvironment } from '@/utils/environments';
import { useRestAPIRequest } from '@/utils/useRestAPIRequest';
import { type VercelIntegration, type VercelProjectAPIResponse } from './VercelIntegration';
import mergeVercelProjectData from './mergeVercelProjectData';
import { GetSavedVercelProjectsDocument } from './queries';

const notEnabledVercelIntegration: VercelIntegration = {
id: 'not-enabled',
name: 'Vercel',
slug: 'vercel',
projects: [],
enabled: false,
};

export function useVercelIntegration(): {
data: VercelIntegration;
fetching: boolean;
error: Error | undefined;
} {
const [{ data: environments, fetching, error: environmentError }] = useEnvironments();

const productionEnvironmentId = useMemo(() => {
if (!environments) return null;
const env = getProductionEnvironment(environments);
return env?.id;
}, [environments]);

// Use memo as the URL object will change on every render
const url = useMemo(() => {
if (!productionEnvironmentId) {
return null;
}
const url = new URL('/v1/integrations/vercel/projects', process.env.NEXT_PUBLIC_API_URL);
url.searchParams.set('workspaceID', productionEnvironmentId);
return url;
}, [productionEnvironmentId]);

// Fetch data from REST and GQL and merge
const {
data,
isLoading: isLoadingSavedProjects,
error,
} = useRestAPIRequest<VercelProjectAPIResponse>({ url, method: 'GET' });
const [{ data: savedVercelProjects }] = useQuery({
query: GetSavedVercelProjectsDocument,
variables: {
environmentID: productionEnvironmentId || '',
},
pause: !productionEnvironmentId,
});

const projects = mergeVercelProjectData({
vercelProjects: data?.projects || [],
savedProjects: savedVercelProjects?.environment.savedVercelProjects || [],
});

const vercelIntegration = data
? {
id: 'enabled-integration-id',
name: 'Vercel',
slug: 'vercel',
projects,
enabled: true,
}
: notEnabledVercelIntegration;

return {
data: vercelIntegration,
fetching: fetching || isLoadingSavedProjects,
error: environmentError || error,
};
}
4 changes: 2 additions & 2 deletions ui/apps/dashboard/src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const documents = {
"\n query GetPaymentIntents {\n account {\n paymentIntents {\n status\n createdAt\n amountLabel\n description\n invoiceURL\n }\n }\n }\n": types.GetPaymentIntentsDocument,
"\n mutation UpdatePaymentMethod($token: String!) {\n updatePaymentMethod(token: $token) {\n brand\n last4\n expMonth\n expYear\n createdAt\n default\n }\n }\n": types.UpdatePaymentMethodDocument,
"\n query GetBillingInfo {\n account {\n billingEmail\n name\n plan {\n id\n name\n amount\n billingPeriod\n features\n }\n subscription {\n nextInvoiceDate\n }\n\n paymentMethods {\n brand\n last4\n expMonth\n expYear\n createdAt\n default\n }\n }\n\n plans {\n id\n name\n amount\n billingPeriod\n features\n }\n }\n": types.GetBillingInfoDocument,
"\n query GetSavedVercelProjects($environmentID: ID!) {\n environment: workspace(id: $environmentID) {\n savedVercelProjects: vercelApps {\n projectID\n path\n }\n }\n }\n": types.GetSavedVercelProjectsDocument,
"\n query GetSavedVercelProjects($environmentID: ID!) {\n environment: workspace(id: $environmentID) {\n savedVercelProjects: vercelApps {\n id\n projectID\n path\n workspaceID\n }\n }\n }\n": types.GetSavedVercelProjectsDocument,
"\n mutation CreateVercelApp($input: CreateVercelAppInput!) {\n createVercelApp(input: $input) {\n success\n }\n }\n": types.CreateVercelAppDocument,
"\n mutation UpdateVercelApp($input: UpdateVercelAppInput!) {\n updateVercelApp(input: $input) {\n success\n }\n }\n": types.UpdateVercelAppDocument,
"\n mutation RemoveVercelApp($input: RemoveVercelAppInput!) {\n removeVercelApp(input: $input) {\n success\n }\n }\n": types.RemoveVercelAppDocument,
Expand Down Expand Up @@ -365,7 +365,7 @@ export function graphql(source: "\n query GetBillingInfo {\n account {\n
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query GetSavedVercelProjects($environmentID: ID!) {\n environment: workspace(id: $environmentID) {\n savedVercelProjects: vercelApps {\n projectID\n path\n }\n }\n }\n"): (typeof documents)["\n query GetSavedVercelProjects($environmentID: ID!) {\n environment: workspace(id: $environmentID) {\n savedVercelProjects: vercelApps {\n projectID\n path\n }\n }\n }\n"];
export function graphql(source: "\n query GetSavedVercelProjects($environmentID: ID!) {\n environment: workspace(id: $environmentID) {\n savedVercelProjects: vercelApps {\n id\n projectID\n path\n workspaceID\n }\n }\n }\n"): (typeof documents)["\n query GetSavedVercelProjects($environmentID: ID!) {\n environment: workspace(id: $environmentID) {\n savedVercelProjects: vercelApps {\n id\n projectID\n path\n workspaceID\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading

0 comments on commit 753c530

Please sign in to comment.