Display the Google dsync auth button on Setup Link (#2361)

* Display the Google auth button

* Delete DirectoryInfo and DirectoryTab components
This commit is contained in:
Kiran K 2024-02-29 17:15:37 +05:30 committed by GitHub
parent 503940c122
commit 952bfe360f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 126 additions and 2282 deletions

View File

@ -95,7 +95,7 @@ const CreateDirectory = ({ setupLinkToken, defaultWebhookEndpoint }: CreateDirec
<div>
<LinkBack href={backUrl} />
<h2 className='mb-5 mt-5 font-bold text-gray-700 md:text-xl'>{t('new_directory')}</h2>
<div className='min-w-[28rem] rounded border border-gray-200 bg-white p-6 dark:border-gray-700 dark:bg-gray-800 md:w-3/4 md:max-w-lg'>
<div className='min-w-[28rem] rounded border border-gray-200 bg-white p-6 dark:border-gray-700 dark:bg-gray-800'>
<form onSubmit={onSubmit}>
<div className='flex flex-col space-y-3'>
<div className='form-control w-full'>

View File

@ -1,98 +0,0 @@
import DirectoryTab from './DirectoryTab';
import { useTranslation } from 'next-i18next';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { LinkBack } from '@components/LinkBack';
import React from 'react';
import useDirectory from '@lib/ui/hooks/useDirectory';
import Loading from '@components/Loading';
import { errorToast } from '@components/Toaster';
import { dsyncGoogleAuthURL } from '@lib/env';
const DirectoryInfo = ({ directoryId, setupLinkToken }: { directoryId: string; setupLinkToken?: string }) => {
const { t } = useTranslation('common');
const { directory, isLoading, error } = useDirectory(directoryId, setupLinkToken);
if (isLoading) {
return <Loading />;
}
if (error) {
errorToast(error.message);
return null;
}
if (!directory) {
return null;
}
const displayWebhook = directory.webhook.endpoint && directory.webhook.secret;
const displayTenantProduct = setupLinkToken ? false : true;
const backUrl = setupLinkToken ? `/setup/${setupLinkToken}/directory-sync` : '/admin/directory-sync';
return (
<>
<LinkBack href={backUrl} />
<h2 className='mt-5 font-bold text-gray-700 md:text-xl'>{directory.name}</h2>
<div className='w-full md:w-3/4'>
<DirectoryTab directory={directory} activeTab='directory' setupLinkToken={setupLinkToken} />
<div className='my-3 rounded border'>
<dl className='divide-y'>
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('directory_id')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>{directory.id}</dd>
</div>
{displayTenantProduct && (
<>
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('tenant')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>{directory.tenant}</dd>
</div>
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('product')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>{directory.product}</dd>
</div>
</>
)}
{displayWebhook && (
<>
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('webhook_endpoint')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{directory.webhook.endpoint}
</dd>
</div>
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('webhook_secret')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{directory.webhook.secret}
</dd>
</div>
</>
)}
</dl>
</div>
{directory.scim.endpoint && directory.scim.secret && (
<div className='mt-4 space-y-4 rounded border p-6'>
<div className='form-control'>
<InputWithCopyButton text={directory.scim.endpoint as string} label={t('scim_endpoint')} />
</div>
<div className='form-control'>
<InputWithCopyButton text={directory.scim.secret} label={t('scim_token')} />
</div>
</div>
)}
{directory.type === 'google' && (
<div className='form-control mt-10'>
<InputWithCopyButton
text={`${dsyncGoogleAuthURL}?directoryId=${directory.id}`}
label={t('dsync_google_auth_url')}
/>
</div>
)}
</div>
</>
);
};
export default DirectoryInfo;

View File

@ -1,69 +0,0 @@
import Link from 'next/link';
import type { Directory } from '@boxyhq/saml-jackson';
import classNames from 'classnames';
import { useTranslation } from 'next-i18next';
const DirectoryTab = ({
directory,
activeTab,
setupLinkToken,
}: {
directory: Directory;
activeTab: string;
setupLinkToken?: string;
}) => {
const { t } = useTranslation('common');
const menus = setupLinkToken
? [
{
name: t('directory'),
href: `/setup/${setupLinkToken}/directory-sync/${directory.id}`,
active: activeTab === 'directory',
},
]
: [
{
name: t('directory'),
href: `/admin/directory-sync/${directory.id}`,
active: activeTab === 'directory',
},
{
name: t('users'),
href: `/admin/directory-sync/${directory.id}/users`,
active: activeTab === 'users',
},
{
name: t('groups'),
href: `/admin/directory-sync/${directory.id}/groups`,
active: activeTab === 'groups',
},
{
name: t('webhook_events'),
href: `/admin/directory-sync/${directory.id}/events`,
active: activeTab === 'events',
},
];
return (
<nav className='-mb-px flex space-x-5 border-b' aria-label='Tabs'>
{menus.map((menu) => {
return (
<Link
href={menu.href}
key={menu.href}
className={classNames(
'inline-flex items-center border-b-2 py-4 text-sm font-medium',
menu.active
? 'border-gray-700 text-gray-700'
: 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'
)}>
{menu.name}
</Link>
);
})}
</nav>
);
};
export default DirectoryTab;

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,10 @@
import { useTranslation } from 'next-i18next';
import ArrowTopRightOnSquareIcon from '@heroicons/react/24/outline/ArrowTopRightOnSquareIcon';
import { useDirectory } from '../hooks';
import { DirectoryTab } from '../dsync';
import { Loading, Error, PageHeader, Badge } from '../shared';
import { useTranslation } from 'next-i18next';
import { InputWithCopyButton } from '../shared/InputWithCopyButton';
import type { Directory } from '../types';
import { Loading, Error, PageHeader, Badge, Alert, InputWithCopyButton, LinkPrimary } from '../shared';
type ExcludeFields = keyof Pick<Directory, 'tenant' | 'product' | 'webhook'>;
@ -13,9 +14,13 @@ type ExcludeFields = keyof Pick<Directory, 'tenant' | 'product' | 'webhook'>;
export const DirectoryInfo = ({
urls,
excludeFields = [],
hideTabs = false,
displayGoogleAuthButton = false,
}: {
urls: { getDirectory: string; tabBase: string; googleAuth: string };
excludeFields?: ExcludeFields[];
hideTabs?: boolean;
displayGoogleAuthButton?: boolean;
}) => {
const { t } = useTranslation('common');
const { directory, isLoadingDirectory, directoryError } = useDirectory(urls.getDirectory);
@ -32,11 +37,30 @@ export const DirectoryInfo = ({
return null;
}
const authorizedGoogle = directory.google_access_token && directory.google_refresh_token;
return (
<>
<PageHeader title={directory.name} />
<DirectoryTab activeTab='directory' baseUrl={urls.tabBase} />
<div className='rounded border'>
{!hideTabs && <DirectoryTab activeTab='directory' baseUrl={urls.tabBase} />}
{!authorizedGoogle && displayGoogleAuthButton && (
<div className='mt-5'>
<Alert variant='success' className='bg-white border-error'>
<div className='space-y-3'>
<p className='text-sm text-gray-600'>{t('bui-dsync-authorization-google-desc')}</p>
<LinkPrimary
href={`${urls.googleAuth}?directoryId=${directory.id}`}
target='_blank'
className='btn-md'
Icon={ArrowTopRightOnSquareIcon}
rel='noopener noreferrer'>
{t('bui-dsync-authorization-google')}
</LinkPrimary>
</div>
</Alert>
</div>
)}
<div className={`rounded border ${hideTabs ? 'mt-5' : ''}`}>
<dl className='divide-y'>
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('bui-dsync-directory-id')}</dt>
@ -74,7 +98,7 @@ export const DirectoryInfo = ({
<div className='px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>{t('bui-dsync-authorized-status')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{directory.google_access_token && directory.google_refresh_token ? (
{authorizedGoogle ? (
<Badge color='success'>{t('bui-dsync-authorized')}</Badge>
) : (
<Badge color='warning'>{t('bui-dsync-not-authorized')}</Badge>

View File

@ -3,7 +3,8 @@ import { useTranslation } from 'next-i18next';
import { useContext } from 'react';
import { BUIContext } from '../provider';
type Tabs = 'directory' | 'users' | 'groups' | 'events';
export const allTabs = ['directory', 'users', 'groups', 'events'] as const;
export type Tabs = (typeof allTabs)[number];
export const DirectoryTab = ({ activeTab, baseUrl }: { activeTab: Tabs; baseUrl: string }) => {
const { t } = useTranslation('common');

View File

@ -0,0 +1,23 @@
type AlertType = 'error' | 'success' | 'warning';
const variants = {
error: 'alert-error',
success: 'alert-success',
warning: 'alert-warning',
} as const;
export const Alert = ({
variant,
children,
className,
}: {
variant: AlertType;
children: React.ReactNode;
className?: string;
}) => {
return (
<div role='alert' className={`rounded alert ${className} ${variants[variant]}`}>
<span>{children}</span>
</div>
);
};

View File

@ -15,3 +15,5 @@ export { LinkPrimary } from './LinkPrimary';
export { LinkBack } from './LinkBack';
export { pageLimit } from './Pagination';
export { ButtonPrimary } from './ButtonPrimary';
export { Alert } from './Alert';
export { InputWithCopyButton } from './InputWithCopyButton';

View File

@ -18,7 +18,6 @@
"deleted": "Deleted",
"delete_the_connection": "Delete the Connection?",
"delete_this_connection": "Delete this Connection",
"directory_id": "Directory ID",
"directory_name": "Directory name",
"directory_provider": "Directory provider",
"directory_sync": "Directory Sync",
@ -56,15 +55,12 @@
"sign_out": "Sign out",
"saml": "SAML",
"sso_error": "SSO error",
"scim_endpoint": "SCIM endpoint",
"scim_token": "SCIM token",
"select_sso_type": "Select SSO type",
"select_an_app": "Select an App to continue",
"send_magic_link": "Send Magic Link",
"setup_links": "Setup Links",
"tenant": "Tenant",
"update_directory": "Update Directory",
"webhook_endpoint": "Webhook Endpoint",
"webhook_secret": "Webhook secret",
"webhook_url": "Webhook URL",
"download": "Download",
@ -178,13 +174,8 @@
"delete_this_directory_desc": "This action cannot be undone. This will permanently delete the directory connection, users, and groups.",
"directory_connection_deleted_successfully": "Directory connection deleted successfully",
"directory_domain": "Directory Domain",
"dsync_google_auth_url": "The URL that you will need to authorize the application to access your Google Directory.",
"show_secret": "Show secret",
"hide_secret": "Hide secret",
"directory": "Directory",
"users": "Users",
"groups": "Groups",
"webhook_events": "Webhook Events",
"retraced_project_created": "Project created successfully",
"project_name": "Project name",
"create_project": "Create Project",
@ -319,5 +310,7 @@
"bui-dsync-delete-events-desc": "This action will permanently delete all webhook events log. Are you sure you want to proceed?",
"bui-dsync-authorized-status": "Status",
"bui-dsync-authorized": "Authorized",
"bui-dsync-not-authorized": "Not Authorized"
"bui-dsync-not-authorized": "Not Authorized",
"bui-dsync-authorization-google": "Authorize Google Workspace",
"bui-dsync-authorization-google-desc": "You should authorize Google Workspace to sync your directory. Click the button below start the authorization process. Make sure you have the necessary permissions to authorize Google Workspace."
}

4
package-lock.json generated
View File

@ -76,6 +76,7 @@
}
},
"internal-ui": {
"name": "@boxyhq/internal-ui",
"version": "0.0.0",
"dependencies": {
"vite-tsconfig-paths": "4.3.1"
@ -85,6 +86,8 @@
"@types/react": "18.2.56",
"@vitejs/plugin-react": "4.2.1",
"autoprefixer": "10.4.17",
"rollup": "4.12.0",
"tslib": "2.6.2",
"typescript": "5.3.3",
"vite": "5.1.0"
},
@ -23579,6 +23582,7 @@
}
},
"npm": {
"name": "@boxyhq/saml-jackson",
"version": "0.0.0",
"license": "Apache 2.0",
"dependencies": {

View File

@ -1,15 +1,33 @@
import type { NextPage } from 'next';
import React from 'react';
import type { NextPage } from 'next';
import { useRouter } from 'next/router';
import DirectoryInfo from '@components/dsync/DirectoryInfo';
import { DirectoryInfo, LinkBack } from '@boxyhq/internal-ui';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { dsyncGoogleAuthURL } from '@lib/env';
const DirectoryDetailsPage: NextPage = () => {
const router = useRouter();
const { token, directoryId } = router.query as { token: string; directoryId: string };
return <DirectoryInfo directoryId={directoryId} setupLinkToken={token} />;
return (
<div className='flex flex-col gap-4'>
<div>
<LinkBack href={`/setup/${token}/directory-sync`} />
</div>
<div>
<DirectoryInfo
urls={{
getDirectory: `/api/setup/${token}/directory-sync/${directoryId}`,
tabBase: '',
googleAuth: dsyncGoogleAuthURL,
}}
hideTabs={true}
displayGoogleAuthButton={true}
/>
</div>
</div>
);
};
export const getServerSideProps = async (context) => {