Skip to content

Commit

Permalink
feat: frontend-snippet
Browse files Browse the repository at this point in the history
  • Loading branch information
naelob committed Nov 13, 2023
1 parent 3455ce9 commit 0618745
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 16 deletions.
64 changes: 55 additions & 9 deletions packages/frontend-snippet/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,71 @@
import { useState } from 'react';
import './App.css'
import useOAuth from './hooks/useOAuth';

const CLIENT_ID = import.meta.env.VITE_CLIENT_ID; // Replace with your OAuth client ID
const SCOPES = 'crm.lists.read%20crm.objects.contacts.read%20crm.objects.contacts.write%20crm.objects.custom.read%20crm.objects.custom.write%20crm.objects.companies.write%20crm.schemas.contacts.read%20crm.objects.feedback_submissions.read%20crm.lists.write%20crm.objects.companies.read%20crm.objects.deals.read%20crm.objects.deals.write%20crm.schemas.contacts.write'; // Replace with your requested scopes
const REDIRECT_URI = 'http://localhost:3000/oauth/callback'; // Replace with your redirect URI
import ProviderModal from './lib/ProviderModal';
import { CRM_PROVIDERS } from './helpers/utils';

function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [providerId, setProviderId] = useState("");
const [returnUrl, setReturnUrl] = useState("");
const [projectId, setPojectId] = useState("1");
const [userId, setUserId] = useState("10");

const { open, isReady } = useOAuth({
linkToken: 'ADD_GENERATED_LINK_TOKEN', // Replace with your link token
clientId: CLIENT_ID!,
scopes: SCOPES,
redirectUri: REDIRECT_URI,
//clientId: CLIENT_ID!, not needed as we manage it for now
providerName: providerId,
returnUrl: returnUrl, //CLIENT WOULD PROVIDER IT
projectId: projectId,
linkedUserId: userId, //CLIENT WOULD PROVIDER IT
onSuccess: () => console.log('OAuth successful')
});
const handleProviderSelection = (providerName: string) => {
console.log(`Selected provider: ${providerName}`);
switch (providerName) {
case CRM_PROVIDERS.HUBSPOT:
setProviderId("hubspot");
//TODO: handle scopes
break;

case CRM_PROVIDERS.PIPEDRIVE:
setProviderId("pipedrive");
//TODO: handle scopes
break;

case CRM_PROVIDERS.ZENDESK:
setProviderId("zendesk");
//TODO: handle scopes
break;

case CRM_PROVIDERS.ZOHO:
setProviderId("zoho");
//TODO: handle scopes
break;

case CRM_PROVIDERS.FRESHSALES:
setProviderId("freshsales");
//TODO: handle scopes
break;

default:
break;

}
open();
setIsModalOpen(false);
};
return (
<>
<h1>Integrations Flow</h1>
<div className="card">
<button disabled={!isReady} onClick={open}>
<button disabled={!isReady} onClick={() => setIsModalOpen(true)}>
Start OAuth Flow
</button>
<ProviderModal
isOpen={isModalOpen}
onSelectProvider={handleProviderSelection}
onClose={() => setIsModalOpen(false)}
/>
</div>
</>
)
Expand Down
42 changes: 42 additions & 0 deletions packages/frontend-snippet/src/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export enum CRM_PROVIDERS {
ZOHO = 'zoho',
ZENDESK = 'zendesk',
HUBSPOT = 'hubspot',
PIPEDRIVE = 'pipedrive',
FRESHSALES = 'freshsales',
}

export const providersConfig = {
'CRM': {
'hubspot': {
clientId: 'ba591170-a7c7-4fca-8086-1bd178c6b14d',
scopes: 'crm.objects.contacts.read crm.objects.contacts.write'
},
// TODO
'zoho': {
clientId: 'Zoho_Client_Id',
scopes: 'Zoho_Scope'
},
'pipedrive': {
clientId: 'Pipedrive_Client_Id',
scopes: 'Pipedrive_Scope'
},
'freshsales': {
clientId: 'Pipedrive_Client_Id',
scopes: 'Pipedrive_Scope'
},
'zendesk': {
clientId: 'Pipedrive_Client_Id',
scopes: 'Pipedrive_Scope'
},

}
};

export const providerAuthBaseUrls = {
'hubspot': `https://app-eu1.hubspot.com/oauth/authorize`, // TODO: HANDLE EU/US DOMAIN
'zoho': `https://accounts.zoho.com/oauth/v2/auth`,
'pipedrive': `https://oauth.pipedrive.com/oauth/authorize`,
'freshsales': `https://some-freshsales-auth-url`, // Replace with actual URL
'zendesk': `https://some-zendesk-auth-url`, // Replace with actual URL
};
37 changes: 30 additions & 7 deletions packages/frontend-snippet/src/hooks/useOAuth.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,47 @@
import { useState, useEffect } from 'react';
import { providerAuthBaseUrls, providersConfig } from '../helpers/utils';

type UseOAuthProps = {
linkToken: string;
clientId: string; // Your OAuth client ID
scopes: string; // The scopes you are requesting
redirectUri: string; // The redirect URI registered with the OAuth provider
clientId?: string;
providerName: string; // Name of the OAuth provider
returnUrl: string; // Return URL after OAuth flow
projectId: string; // Project ID
linkedUserId: string; // Linked User ID
onSuccess: () => void;
};

const useOAuth = ({ linkToken, clientId, scopes, redirectUri, onSuccess }: UseOAuthProps) => {

const useOAuth = ({ providerName, returnUrl, projectId, linkedUserId, onSuccess }: UseOAuthProps) => {
const [isReady, setIsReady] = useState(false);

useEffect(() => {
// Perform any setup logic here
setTimeout(() => setIsReady(true), 1000); // Simulating async operation
}, []);

const constructAuthUrl = () => {
const encodedRedirectUrl = encodeURIComponent(`http://localhost:3000/oauth/callback`);
const state = encodeURIComponent(JSON.stringify({ projectId, linkedUserId, providerName, returnUrl }));

const vertical = 'CRM'; //TODO when multiple verticals

const config = providersConfig[vertical][providerName];
if (!config) {
throw new Error(`Unsupported provider: ${providerName}`);
}

const { clientId, scopes } = config;

const baseUrl = providerAuthBaseUrls[providerName];
if (!baseUrl) {
throw new Error(`Unsupported provider: ${providerName}`);
}

return `${baseUrl}?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodedRedirectUrl}&scope=${encodeURIComponent(scopes)}&state=${state}`;
};

const openModal = () => {
//const authUrl = `https://app-eu1.hubspot.com/oauth/authorize?client_id=${encodeURIComponent(clientId)}&scope=${encodeURIComponent(scopes)}&redirect_uri=${encodeURIComponent(redirectUri)}`;
const authUrl = `https://app-eu1.hubspot.com/oauth/authorize?client_id=${clientId}&redirect_uri=http://localhost:3000/oauth/callback&scope=crm.lists.read%20crm.lists.write`
const authUrl = constructAuthUrl();
const width = 600, height = 600;
const left = (window.innerWidth - width) / 2;
const top = (window.innerHeight - height) / 2;
Expand Down
24 changes: 24 additions & 0 deletions packages/frontend-snippet/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,27 @@ button:focus-visible {
background-color: #f9f9f9;
}
}

.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}

.modal-content {
background-color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
}

.modal-content button {
margin: 10px;
padding: 10px 20px;
}
29 changes: 29 additions & 0 deletions packages/frontend-snippet/src/lib/ProviderModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

type ProviderModalProps = {
isOpen: boolean;
onSelectProvider: (providerName: string) => void;
onClose: () => void;
};

const PROVIDERS = ['hubspot', 'zoho', 'pipedrive', 'freshsales', 'zendesk'];

const ProviderModal: React.FC<ProviderModalProps> = ({ isOpen, onSelectProvider, onClose }) => {
if (!isOpen) return null;

return (
<div className="modal">
<div className="modal-content">
<h2>Select a CRM Provider</h2>
{PROVIDERS.map(provider => (
<button key={provider} onClick={() => onSelectProvider(provider)}>
Connect to {provider}
</button>
))}
<button onClick={onClose}>Close</button>
</div>
</div>
);
};

export default ProviderModal;

0 comments on commit 0618745

Please sign in to comment.