Cleanup duplicate locale and components (#2493)

* Tweak sdk style import order

* WIP

* Override SDK styles

* Cleanup and pass props to component

* Cleanup setup link related code as it's handled via setup-link instructions

* Cleanup locale

* Fix e2e tests

* Fix selectors in e2e test

* Add select dropdown style override

* Use component from SDK

* Cleanup locale

* Use Edit DSync from SDK

* Remove default webhook props from setup token page

* Ability to set default webhook secret

* Tweak header text

* Revert sdk style import order - app styles should be latest

* Override default SDK focus style

* Update locale

* Use Edit component from SDK

* Allow patching oidcMetadata fields

* Tweak return data format

* Route change on edit success and other fixes

* Fix button styles

* Fix data access from API

* Fix focus styling for error btn

* Sync lock file

* Cleanup unused files

* Set `displayInfo` to false for setup link and fix exclude fields for SAML under setup link

* Allow forceAuthn in setup links

* Only update forceAuthn if its a boolean value coming from body

* Cleanup and hideSave only for setup link

* Update UI SDK

* Cleanup locales

* Fix failing e2e

* cleaned up dups

* cleaned up dups

* cleanup of components

* more cleanup

* cleanup

* locale cleanup

* dup cleanup

* Reuse styles

* Set min value for expiry field to 1

* Validate expiry before using

* Update SDK and set idpMetadata display to true

* cleaned up unused code, added formik as dep

* clean unused locale strings

* cleaned up ErrorMessage component

---------

Co-authored-by: Aswin V <vaswin91@gmail.com>
This commit is contained in:
Deepak Prabhakara 2024-03-28 13:21:59 +00:00 committed by GitHub
parent 67f111711a
commit 742f9b0676
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
80 changed files with 138 additions and 931 deletions

View File

@ -1,15 +0,0 @@
import { Badge as BaseBadge, BadgeProps } from 'react-daisyui';
const Badge = (props: BadgeProps) => {
const { children, className } = props;
return (
<>
<BaseBadge {...props} className={`rounded-md py-2 text-white ${className}`}>
{children}
</BaseBadge>
</>
);
};
export default Badge;

View File

@ -1,15 +0,0 @@
import classNames from 'classnames';
import { Button, type ButtonProps } from 'react-daisyui';
export interface ButtonBaseProps extends ButtonProps {
Icon?: any;
}
export const ButtonBase = ({ Icon, children, ...others }: ButtonBaseProps) => {
return (
<Button {...others}>
{Icon && <Icon className={classNames('h-4 w-4', children ? 'mr-1' : '')} aria-hidden />}
{children}
</Button>
);
};

View File

@ -1,9 +0,0 @@
import { ButtonBase, type ButtonBaseProps } from './ButtonBase';
export const ButtonDanger = ({ children, ...other }: ButtonBaseProps) => {
return (
<ButtonBase color='error' {...other}>
{children}
</ButtonBase>
);
};

View File

@ -1,9 +0,0 @@
import { ButtonBase, type ButtonBaseProps } from './ButtonBase';
export const ButtonLink = ({ children, ...other }: ButtonBaseProps) => {
return (
<ButtonBase variant='link' {...other}>
{children}
</ButtonBase>
);
};

View File

@ -1,9 +0,0 @@
import { ButtonBase, type ButtonBaseProps } from './ButtonBase';
export const ButtonOutline = ({ children, ...other }: ButtonBaseProps) => {
return (
<ButtonBase variant='outline' {...other}>
{children}
</ButtonBase>
);
};

View File

@ -1,9 +0,0 @@
import { ButtonBase, type ButtonBaseProps } from './ButtonBase';
export const ButtonPrimary = ({ children, ...other }: ButtonBaseProps) => {
return (
<ButtonBase color='primary' {...other}>
{children}
</ButtonBase>
);
};

View File

@ -1,39 +0,0 @@
import { useTranslation } from 'next-i18next';
import { copyToClipboard } from '@lib/ui/utils';
import ClipboardDocumentIcon from '@heroicons/react/24/outline/ClipboardDocumentIcon';
import { successToast } from '@components/Toaster';
import { IconButton } from './IconButton';
export const CopyToClipboardButton = ({ text }: { text: string }) => {
const { t } = useTranslation('common');
return (
<IconButton
tooltip={t('copy')}
Icon={ClipboardDocumentIcon}
className='hover:text-primary'
onClick={() => {
copyToClipboard(text);
successToast(t('copied'));
}}
/>
);
};
export const InputWithCopyButton = ({ text, label }: { text: string; label: string }) => {
return (
<>
<div className='flex justify-between'>
<label className='mb-2 block text-sm font-medium text-gray-900 dark:text-gray-300'>{label}</label>
<CopyToClipboardButton text={text} />
</div>
<input
type='text'
defaultValue={text}
key={text}
readOnly
className='input-bordered input w-full text-sm'
/>
</>
);
};

View File

@ -1,52 +0,0 @@
import Modal from './Modal';
import { useTranslation } from 'next-i18next';
import { ButtonOutline } from './ButtonOutline';
import { ButtonDanger } from './ButtonDanger';
import { ButtonBase } from './ButtonBase';
interface Props {
visible: boolean;
title: string;
description: string;
onConfirm: () => void | Promise<void>;
onCancel: () => void;
actionButtonText?: string;
overrideDeleteButton?: boolean;
dataTestId?: string;
}
const ConfirmationModal = (props: Props) => {
const {
visible,
title,
description,
onConfirm,
onCancel,
actionButtonText,
dataTestId = 'confirm-delete',
overrideDeleteButton = false,
} = props;
const { t } = useTranslation('common');
const buttonText = actionButtonText || t('delete');
return (
<Modal visible={visible} title={title} description={description}>
<div className='modal-action'>
<ButtonOutline onClick={onCancel}>{t('cancel')}</ButtonOutline>
{overrideDeleteButton ? (
<ButtonBase color='secondary' onClick={onConfirm} data-testid={dataTestId}>
{buttonText}
</ButtonBase>
) : (
<ButtonDanger onClick={onConfirm} data-testid={dataTestId}>
{buttonText}
</ButtonDanger>
)}
</div>
</Modal>
);
};
export default ConfirmationModal;

View File

@ -1,74 +0,0 @@
import { FC, useEffect, useState } from 'react';
import { useTranslation } from 'next-i18next';
import ConfirmationModal from '@components/ConfirmationModal';
interface Props {
onChange: (active: boolean) => void;
connection: {
active: boolean;
type: 'sso' | 'dsync';
};
}
export const ConnectionToggle: FC<Props> = (props) => {
const { onChange, connection } = props;
const { t } = useTranslation('common');
const [isModalVisible, setModalVisible] = useState(false);
const [active, setActive] = useState(connection.active);
useEffect(() => {
setActive(connection.active);
}, [connection]);
const askForConfirmation = () => {
setModalVisible(true);
};
const onConfirm = () => {
setModalVisible(false);
setActive(!active);
onChange(!active);
};
const onCancel = () => {
setModalVisible(false);
};
const confirmationModalTitle = active ? t('deactivate_connection') : t('activate_connection');
const confirmationModalDescription = {
sso: {
activate: t('activate_sso_connection_description'),
deactivate: t('deactivate_sso_connection_description'),
},
dsync: {
activate: t('activate_dsync_connection_description'),
deactivate: t('deactivate_dsync_connection_description'),
},
}[connection.type][active ? 'deactivate' : 'activate'];
return (
<>
<label className='label cursor-pointer'>
<span className='label-text mr-2'>{active ? t('active') : t('inactive')}</span>
<input
type='checkbox'
className='toggle-success toggle'
onChange={askForConfirmation}
checked={active}
/>
</label>
<ConfirmationModal
title={confirmationModalTitle}
description={confirmationModalDescription}
actionButtonText={t('yes_proceed')}
overrideDeleteButton={true}
visible={isModalVisible}
onConfirm={onConfirm}
onCancel={onCancel}
dataTestId='confirm-connection-toggle'
/>
</>
);
};

View File

@ -1,29 +0,0 @@
import InformationCircleIcon from '@heroicons/react/24/outline/InformationCircleIcon';
import { useTranslation } from 'next-i18next';
import { LinkPrimary } from '@components/LinkPrimary';
const EmptyState = ({
title,
href,
className,
description,
}: {
title: string;
href?: string;
className?: string;
description?: string;
}) => {
const { t } = useTranslation('common');
return (
<div
className={`my-3 flex flex-col items-center justify-center space-y-3 rounded border py-32 ${className}`}>
<InformationCircleIcon className='h-10 w-10' />
<h4 className='text-center'>{title}</h4>
{description && <p className='text-center text-gray-500'>{description}</p>}
{href && <LinkPrimary href={href}>{t('create_new')}</LinkPrimary>}
</div>
);
};
export default EmptyState;

View File

@ -1,5 +0,0 @@
const ErrorMessage = () => {
return <p>{`Unable to load this page. Maybe you don't have enough rights.`}</p>;
};
export default ErrorMessage;

View File

@ -1,11 +0,0 @@
import classNames from 'classnames';
export const IconButton = ({ Icon, tooltip, onClick, className, ...other }) => {
return (
<div className='tooltip' data-tip={tooltip}>
<button onClick={onClick} type='button' {...other}>
<Icon className={classNames('hover:scale-115 h-5 w-5 cursor-pointer text-secondary', className)} />
</button>
</div>
);
};

View File

@ -1,4 +1,4 @@
import EmptyState from './EmptyState';
import { EmptyState } from '@boxyhq/internal-ui';
const LicenseRequired = () => {
return (

View File

@ -1,26 +0,0 @@
import ArrowLeftIcon from '@heroicons/react/24/outline/ArrowLeftIcon';
import { useTranslation } from 'next-i18next';
import { ButtonOutline } from './ButtonOutline';
import { LinkOutline } from './LinkOutline';
export const LinkBack = ({ href, onClick }: { href?: string; onClick?: () => void }) => {
const { t } = useTranslation('common');
if (href) {
return (
<LinkOutline href={href} Icon={ArrowLeftIcon}>
{t('back')}
</LinkOutline>
);
}
if (onClick) {
return (
<ButtonOutline onClick={onClick} Icon={ArrowLeftIcon}>
{t('back')}
</ButtonOutline>
);
}
return null;
};

View File

@ -1,17 +0,0 @@
import Link from 'next/link';
import classNames from 'classnames';
import type { LinkProps } from 'react-daisyui';
export interface LinkBaseProps extends LinkProps {
href: string;
Icon?: any;
}
export const LinkBase = ({ children, href, className, Icon, ...others }: LinkBaseProps) => {
return (
<Link href={href} className={classNames('btn', className)} {...others}>
{Icon && <Icon className='mr-1 h-4 w-4' aria-hidden />}
{children}
</Link>
);
};

View File

@ -1,10 +0,0 @@
import classNames from 'classnames';
import { LinkBase, type LinkBaseProps } from './LinkBase';
export const LinkOutline = ({ children, className, ...others }: LinkBaseProps) => {
return (
<LinkBase className={classNames('btn-outline', className)} {...others}>
{children}
</LinkBase>
);
};

View File

@ -1,10 +0,0 @@
import classNames from 'classnames';
import { LinkBase, type LinkBaseProps } from './LinkBase';
export const LinkPrimary = ({ children, className, ...others }: LinkBaseProps) => {
return (
<LinkBase className={classNames('btn-primary', className)} {...others}>
{children}
</LinkBase>
);
};

View File

@ -1,32 +0,0 @@
const Loading = () => {
return (
<div className='flex items-center justify-center p-5'>
<div role='status'>
<Spinner />
<span className='sr-only'>Loading...</span>
</div>
</div>
);
};
const Spinner = () => {
return (
<svg
aria-hidden='true'
className='h-10 w-10 animate-spin fill-primary text-gray-200'
viewBox='0 0 100 101'
fill='none'
xmlns='http://www.w3.org/2000/svg'>
<path
d='M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z'
fill='currentColor'
/>
<path
d='M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z'
fill='currentFill'
/>
</svg>
);
};
export default Loading;

View File

@ -1,31 +0,0 @@
import React from 'react';
import { useState, useEffect } from 'react';
type ModalProps = {
visible: boolean;
title: string;
description?: string;
children?: React.ReactNode;
};
const Modal = ({ visible, title, description, children }: ModalProps) => {
const [open, setOpen] = useState(visible ? visible : false);
useEffect(() => {
setOpen(visible);
}, [visible]);
return (
<div className={`modal ${open ? 'modal-open' : ''}`}>
<div className='modal-box'>
<div className='flex flex-col gap-2'>
<h3 className='text-lg font-bold'>{title}</h3>
{description && <p className='text-sm'>{description}</p>}
<div>{children}</div>
</div>
</div>
</div>
);
};
export default Modal;

View File

@ -60,7 +60,7 @@ export const Sidebar = ({ isOpen, setIsOpen }: SidebarProps) => {
},
{
href: '/admin/sso-tracer',
text: t('sso_tracer'),
text: t('bui-tracer-title'),
active: asPath.includes('/admin/sso-tracer'),
},
],

View File

@ -1,10 +1,8 @@
import LinkIcon from '@heroicons/react/24/outline/LinkIcon';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { LinkPrimary } from '@components/LinkPrimary';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { ConnectionList } from '@boxyhq/react-ui/sso';
import { pageLimit } from '@boxyhq/internal-ui';
import { InputWithCopyButton, pageLimit, LinkPrimary } from '@boxyhq/internal-ui';
const SSOConnectionList = ({
setupLinkToken,
@ -45,7 +43,7 @@ const SSOConnectionList = ({
Icon={LinkIcon}
href='/admin/sso-connection/setup-link/new'
data-testid='create-setup-link'>
{t('new_setup_link')}
{t('bui-sl-new-link')}
</LinkPrimary>
)}
<LinkPrimary href={createConnectionUrl} data-testid='create-connection'>

View File

@ -1,7 +1,7 @@
import { useRouter } from 'next/router';
import { errorToast } from '@components/Toaster';
import { useTranslation } from 'next-i18next';
import { LinkBack } from '@components/LinkBack';
import { LinkBack } from '@boxyhq/internal-ui';
import { CreateSSOConnection } from '@boxyhq/react-ui/sso';
import { BOXYHQ_UI_CSS } from '@components/styles';
import { AdminPortalSSODefaults } from '@lib/utils';

View File

@ -1,7 +1,7 @@
import { useRouter } from 'next/router';
import { errorToast, successToast } from '@components/Toaster';
import { useTranslation } from 'next-i18next';
import { LinkBack } from '@components/LinkBack';
import { LinkBack } from '@boxyhq/internal-ui';
import type { OIDCSSORecord, SAMLSSORecord } from '@boxyhq/saml-jackson';
import { EditSAMLConnection, EditOIDCConnection } from '@boxyhq/react-ui/sso';
import { BOXYHQ_UI_CSS } from '@components/styles';

View File

@ -2,7 +2,7 @@ import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import React from 'react';
import { errorToast, successToast } from '@components/Toaster';
import { LinkBack } from '@components/LinkBack';
import { LinkBack } from '@boxyhq/internal-ui';
import { CreateDirectory as CreateDSync } from '@boxyhq/react-ui/dsync';
import { BOXYHQ_UI_CSS } from '@components/styles';

View File

@ -1,74 +0,0 @@
import { useRouter } from 'next/router';
import React, { useState } from 'react';
import { useTranslation } from 'next-i18next';
import type { Directory } from '@boxyhq/saml-jackson';
import type { ApiResponse } from 'types';
import { errorToast, successToast } from '@components/Toaster';
import ConfirmationModal from '@components/ConfirmationModal';
import { ButtonDanger } from '@components/ButtonDanger';
export const DeleteDirectory = ({
directoryId,
setupLinkToken,
}: {
directoryId: Directory['id'];
setupLinkToken?: string;
}) => {
const { t } = useTranslation('common');
const router = useRouter();
const [delModalVisible, setDelModalVisible] = useState(false);
const deleteDirectory = async () => {
const deleteUrl = setupLinkToken
? `/api/setup/${setupLinkToken}/directory-sync/${directoryId}`
: `/api/admin/directory-sync/${directoryId}`;
const rawResponse = await fetch(deleteUrl, {
method: 'DELETE',
});
const response: ApiResponse<unknown> = await rawResponse.json();
if ('error' in response) {
errorToast(response.error.message);
return;
}
if ('data' in response) {
const redirectUrl = setupLinkToken
? `/setup/${setupLinkToken}/directory-sync`
: '/admin/directory-sync';
successToast(t('directory_connection_deleted_successfully'));
router.replace(redirectUrl);
}
};
return (
<>
<section className='mt-5 flex items-center rounded bg-red-100 p-6 text-red-900'>
<div className='flex-1'>
<h6 className='mb-1 font-medium'>{t('delete_this_directory')}</h6>
<p className='font-light'>{t('delete_this_directory_desc')}</p>
</div>
<ButtonDanger
type='button'
data-modal-toggle='popup-modal'
onClick={() => {
setDelModalVisible(true);
}}>
{t('delete')}
</ButtonDanger>
</section>
<ConfirmationModal
title={t('delete_this_directory')}
description={t('delete_this_directory_desc')}
visible={delModalVisible}
onConfirm={deleteDirectory}
onCancel={() => {
setDelModalVisible(false);
}}
/>
</>
);
};

View File

@ -1,9 +1,8 @@
import LinkIcon from '@heroicons/react/24/outline/LinkIcon';
import { useTranslation } from 'next-i18next';
import { LinkPrimary } from '@components/LinkPrimary';
import { useRouter } from 'next/router';
import { DirectoryList } from '@boxyhq/react-ui/dsync';
import { pageLimit } from '@boxyhq/internal-ui';
import { pageLimit, LinkPrimary } from '@boxyhq/internal-ui';
const DSyncDirectoryList = ({ setupLinkToken }: { setupLinkToken?: string }) => {
const { t } = useTranslation('common');
@ -24,7 +23,7 @@ const DSyncDirectoryList = ({ setupLinkToken }: { setupLinkToken?: string }) =>
<div className='flex gap-2'>
{!setupLinkToken && (
<LinkPrimary Icon={LinkIcon} href='/admin/directory-sync/setup-link/new'>
{t('new_setup_link')}
{t('bui-sl-new-link')}
</LinkPrimary>
)}
<LinkPrimary href={createDirectoryUrl}>{t('new_directory')}</LinkPrimary>

View File

@ -2,8 +2,7 @@ import { useRouter } from 'next/router';
import React from 'react';
import { useTranslation } from 'next-i18next';
import { errorToast, successToast } from '@components/Toaster';
import { LinkBack } from '@components/LinkBack';
import Loading from '@components/Loading';
import { LinkBack, Loading } from '@boxyhq/internal-ui';
import useDirectory from '@lib/ui/hooks/useDirectory';
import { EditDirectory as EditDSync } from '@boxyhq/react-ui/dsync';
import { BOXYHQ_UI_CSS } from '@components/styles';

View File

@ -1,62 +0,0 @@
import type { Directory } from '@boxyhq/saml-jackson';
import { errorToast, successToast } from '@components/Toaster';
import { FC, useState, useEffect } from 'react';
import type { ApiResponse } from 'types';
import { useTranslation } from 'next-i18next';
import { ConnectionToggle } from '@components/ConnectionToggle';
interface Props {
connection: Directory;
setupLinkToken?: string;
}
export const ToggleConnectionStatus: FC<Props> = (props) => {
const { connection, setupLinkToken } = props;
const { t } = useTranslation('common');
const [active, setActive] = useState(!connection.deactivated);
useEffect(() => {
setActive(!connection.deactivated);
}, [connection]);
const updateConnectionStatus = async (active: boolean) => {
setActive(active);
const body = {
directoryId: connection.id,
deactivated: !active,
};
const actionUrl = setupLinkToken
? `/api/setup/${setupLinkToken}/directory-sync/${connection.id}`
: `/api/admin/directory-sync/${connection.id}`;
const res = await fetch(actionUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const response: ApiResponse = await res.json();
if ('error' in response) {
errorToast(response.error.message);
return;
}
if (body.deactivated) {
successToast(t('connection_deactivated'));
} else {
successToast(t('connection_activated'));
}
};
return (
<>
<ConnectionToggle connection={{ active, type: 'dsync' }} onChange={updateConnectionStatus} />
</>
);
};

View File

@ -5,7 +5,7 @@ import Head from 'next/head';
import { Sidebar } from '@components/Sidebar';
import { Navbar } from '@components/Navbar';
import { useTranslation } from 'next-i18next';
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
export const AccountLayout = ({ children }: { children: React.ReactNode }) => {
const { t } = useTranslation('common');

View File

@ -4,7 +4,7 @@ import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
import InvalidSetupLinkAlert from '@components/setup-link/InvalidSetupLinkAlert';
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
import useSetupLink from '@lib/ui/hooks/useSetupLink';
import { useTranslation } from 'next-i18next';
import { hexToOklch } from '@lib/color';

View File

@ -2,7 +2,7 @@ import { useState } from 'react';
import classNames from 'classnames';
import { useRouter } from 'next/router';
import { successToast, errorToast } from '@components/Toaster';
import { LinkBack } from '@components/LinkBack';
import { LinkBack } from '@boxyhq/internal-ui';
import { useTranslation } from 'next-i18next';
const AddProject = () => {

View File

@ -1,4 +1,4 @@
import { CopyToClipboardButton } from '@components/ClipboardButton';
import { CopyToClipboardButton } from '@boxyhq/internal-ui';
import { useTranslation } from 'next-i18next';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter/dist/cjs';
import { materialOceanic } from 'react-syntax-highlighter/dist/cjs/styles/prism';

View File

@ -4,8 +4,7 @@ import { useTranslation } from 'next-i18next';
import type { ApiError, ApiSuccess } from 'types';
import type { Project } from 'types/retraced';
import ErrorMessage from '@components/Error';
import Loading from '@components/Loading';
import { Loading, Error } from '@boxyhq/internal-ui';
import { fetcher } from '@lib/ui/utils';
const LogsViewer = (props: { project: Project; environmentId: string; groupId: string; host: string }) => {
@ -28,7 +27,7 @@ const LogsViewer = (props: { project: Project; environmentId: string; groupId: s
}
if (error) {
return <ErrorMessage />;
return <Error message={t('error_loading_page')} />;
}
const viewerToken = data?.data?.viewerToken;

View File

@ -2,7 +2,7 @@ import type { Project } from 'types/retraced';
import CodeSnippet from '@components/retraced/CodeSnippet';
import { useState } from 'react';
import { Select } from 'react-daisyui';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { InputWithCopyButton } from '@boxyhq/internal-ui';
import { useTranslation } from 'next-i18next';
const ProjectDetails = (props: { project: Project; host?: string }) => {

View File

@ -1,6 +1,6 @@
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { ButtonPrimary } from '@components/ButtonPrimary';
import { ButtonPrimary } from '@boxyhq/internal-ui';
const NextButton = () => {
const router = useRouter();

View File

@ -1,6 +1,6 @@
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { ButtonOutline } from '@components/ButtonOutline';
import { ButtonOutline } from '@boxyhq/internal-ui';
const PreviousButton = () => {
const router = useRouter();

View File

@ -1,24 +0,0 @@
import { TableHeader } from './TableHeader';
import { TableBody, TableBodyType } from './TableBody';
const tableWrapperClass = 'rounder border';
const tableClass = 'w-full text-left text-sm text-gray-500 dark:text-gray-400';
export const Table = ({
cols,
body,
noMoreResults,
}: {
cols: string[];
body: TableBodyType[];
noMoreResults?: boolean;
}) => {
return (
<div className={tableWrapperClass}>
<table className={tableClass}>
<TableHeader cols={cols} />
<TableBody cols={cols} body={body} noMoreResults={noMoreResults} />
</table>
</div>
);
};

View File

@ -1,110 +0,0 @@
import { Button } from 'react-daisyui';
import Badge from '@components/Badge';
import { useTranslation } from 'next-i18next';
const trClass = 'border-b bg-white last:border-b-0 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800';
const tdClassBase = 'px-6 py-3 text-sm text-gray-500 dark:text-gray-400';
const tdClass = `whitespace-nowrap ${tdClassBase}`;
const tdClassWrap = `break-all ${tdClassBase}`;
interface TableBodyCell {
wrap?: boolean;
text?: string;
buttons?: {
text: string;
color?: string;
onClick: () => void;
}[];
badge?: {
text: string;
color: string;
};
element?: React.JSX.Element;
actions?: {
text: string;
icon: React.JSX.Element;
onClick: () => void;
destructive?: boolean;
}[];
}
export interface TableBodyType {
id: string;
cells: TableBodyCell[];
}
export const TableBody = ({
cols,
body,
noMoreResults,
}: {
cols: string[];
body: TableBodyType[];
noMoreResults?: boolean;
}) => {
const { t } = useTranslation('common');
if (noMoreResults) {
return (
<tbody>
<tr>
<td colSpan={cols.length} className='px-6 py-3 text-center text-sm text-gray-500'>
{t('no_more_results')}
</td>
</tr>
</tbody>
);
}
return (
<tbody>
{body.map((row) => {
return (
<tr key={row.id} className={trClass}>
{row.cells?.map((cell: any, index: number) => {
return (
<td key={row.id + '-td-' + index} className={cell.wrap ? tdClassWrap : tdClass}>
{!cell.buttons || cell.buttons?.length === 0 ? null : (
<div className='flex space-x-2'>
{cell.buttons?.map((button: any, index: number) => {
return (
<Button
key={row.id + '-button-' + index}
size='xs'
color={button.color}
variant='outline'
onClick={button.onClick}>
{button.text}
</Button>
);
})}
</div>
)}
{!cell.actions || cell.actions?.length === 0 ? null : (
<span className='flex gap-3'>
{cell.actions?.map((action: any, index: number) => {
return (
<div key={row.id + '-diva-' + index} className='tooltip' data-tip={action.text}>
<button
key={row.id + '-action-' + index}
className={`py-2 ${action.destructive ? 'text-red-500 hover:text-red-900' : 'hover:text-green-400'}`}
onClick={action.onClick}>
{action.icon}
</button>
</div>
);
})}
</span>
)}
{cell.badge ? <Badge color={cell.badge.color}>{cell.badge.text}</Badge> : null}
{cell.text ? cell.text : null}
{cell.element ? cell.element : null}
</td>
);
})}
</tr>
);
})}
</tbody>
);
};

View File

@ -1,17 +0,0 @@
const theadClass = 'bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400';
const trHeadClass = 'hover:bg-gray-50';
const thClass = 'px-6 py-3';
export const TableHeader = ({ cols }: { cols: string[] }) => {
return (
<thead className={theadClass}>
<tr className={trHeadClass}>
{cols.map((col, index) => (
<th key={index} scope='col' className={thClass}>
{col}
</th>
))}
</tr>
</thead>
);
};

View File

@ -9,11 +9,9 @@ import { maskSetup } from '@components/terminus/blocks/customblocks';
import locale from 'blockly/msg/en';
Blockly.setLocale(locale);
import { ButtonPrimary } from '@components/ButtonPrimary';
import { generateModel } from '@components/terminus/blocks/generator';
import { errorToast, successToast } from '@components/Toaster';
import ConfirmationModal from '@components/ConfirmationModal';
import { ButtonBase } from '@components/ButtonBase';
import { ButtonBase, ButtonPrimary, ConfirmationModal } from '@boxyhq/internal-ui';
function BlocklyComponent(props) {
const { t } = useTranslation('common');

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'next-i18next';
import { ButtonPrimary } from '@components/ButtonPrimary';
import { ButtonPrimary } from '@boxyhq/internal-ui';
import { errorToast, successToast } from '@components/Toaster';
import type { ApiResponse } from 'types';
import type { AdminPortalBranding } from '@boxyhq/saml-jackson';
@ -86,7 +86,7 @@ const Branding = ({ hasValidLicense }: { hasValidLicense: boolean }) => {
<div className='flex flex-col space-y-2'>
<div className='form-control w-full md:w-1/2'>
<label className='label'>
<span className='label-text'>{t('branding_logo_url_label')}</span>
<span className='label-text'>{t('bui-shared-logo-url')}</span>
</label>
<input
type='url'
@ -97,12 +97,12 @@ const Branding = ({ hasValidLicense }: { hasValidLicense: boolean }) => {
placeholder='https://company.com/logo.png'
/>
<label className='label'>
<span className='label-text-alt'>{t('branding_logo_url_alt')}</span>
<span className='label-text-alt'>{t('bui-shared-logo-url-desc')}</span>
</label>
</div>
<div className='form-control w-full md:w-1/2'>
<label className='label'>
<span className='label-text'>{t('branding_favicon_url_label')}</span>
<span className='label-text'>{t('bui-shared-favicon-url')}</span>
</label>
<input
type='url'
@ -113,7 +113,7 @@ const Branding = ({ hasValidLicense }: { hasValidLicense: boolean }) => {
placeholder='https://company.com/favicon.ico'
/>
<label className='label'>
<span className='label-text-alt'>{t('branding_favicon_url_alt')}</span>
<span className='label-text-alt'>{t('bui-shared-favicon-url-desc')}</span>
</label>
</div>
<div className='form-control w-full md:w-1/2'>
@ -134,15 +134,15 @@ const Branding = ({ hasValidLicense }: { hasValidLicense: boolean }) => {
</div>
<div className='form-control'>
<label className='label'>
<span className='label-text'>{t('branding_primary_color_label')}</span>
<span className='label-text'>{t('bui-shared-primary-color')}</span>
</label>
<input type='color' id='primaryColor' onChange={onChange} value={branding.primaryColor || ''} />
<label className='label'>
<span className='label-text-alt'>{t('branding_primary_color_alt')}</span>
<span className='label-text-alt'>{t('bui-shared-primary-color-desc')}</span>
</label>
</div>
<div className='mt-5'>
<ButtonPrimary loading={loading}>{t('save_changes')}</ButtonPrimary>
<ButtonPrimary loading={loading}>{t('bui-shared-save-changes')}</ButtonPrimary>
</div>
</div>
</form>

View File

@ -2,8 +2,7 @@ import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import type { SAMLFederationAppWithMetadata } from '@boxyhq/saml-jackson';
import { Toaster } from '@components/Toaster';
import { InputWithCopyButton, CopyToClipboardButton } from '@components/ClipboardButton';
import { LinkOutline } from '@components/LinkOutline';
import { InputWithCopyButton, CopyToClipboardButton, LinkOutline } from '@boxyhq/internal-ui';
import LicenseRequired from '@components/LicenseRequired';
type MetadataProps = {
@ -59,7 +58,7 @@ const Metadata = ({ metadata, hasValidLicense }: MetadataProps) => {
<InputWithCopyButton text={metadata.ssoUrl} label={t('sso_url')} />
</div>
<div className='form-control w-full'>
<InputWithCopyButton text={metadata.entityId} label={t('entity_id')} />
<InputWithCopyButton text={metadata.entityId} label={t('bui-fs-entity-id')} />
</div>
<div className='form-control w-full'>
<label className='label'>
@ -70,7 +69,7 @@ const Metadata = ({ metadata, hasValidLicense }: MetadataProps) => {
href='/.well-known/saml.cer'
target='_blank'
className='label-text font-bold text-gray-500 hover:link-primary'>
{t('download')}
{t('bui-wku-download')}
</Link>
<CopyToClipboardButton text={metadata.x509cert.trim()} />
</span>

View File

@ -54,13 +54,13 @@ export const DirectoryGroups = ({
const columns = [
{
key: 'name',
label: t('bui-dsync-name'),
label: t('bui-shared-name'),
wrap: true,
dataIndex: 'name',
},
{
key: 'actions',
label: t('bui-dsync-actions'),
label: t('bui-shared-actions'),
wrap: true,
dataIndex: null,
},
@ -78,7 +78,7 @@ export const DirectoryGroups = ({
return {
actions: [
{
text: t('bui-dsync-view'),
text: t('bui-shared-view'),
onClick: () => onView?.(group),
icon: <EyeIcon className='w-5' />,
},

View File

@ -70,13 +70,13 @@ export const DirectoryInfo = ({
)}
{!excludeFields.includes('tenant') && (
<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-tenant')}</dt>
<dt className='text-sm font-medium text-gray-500'>{t('bui-shared-tenant')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>{directory.tenant}</dd>
</div>
)}
{!excludeFields.includes('product') && (
<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-product')}</dt>
<dt className='text-sm font-medium text-gray-500'>{t('bui-shared-product')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>{directory.product}</dd>
</div>
)}
@ -89,7 +89,7 @@ export const DirectoryInfo = ({
</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('bui-dsync-webhook-secret')}</dt>
<dt className='text-sm font-medium text-gray-500'>{t('bui-shared-webhook-secret')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{directory.webhook.secret || '-'}
</dd>
@ -99,7 +99,7 @@ export const DirectoryInfo = ({
{directory.type === 'google' && (
<>
<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>
<dt className='text-sm font-medium text-gray-500'>{t('bui-shared-status')}</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{authorizedGoogle ? (
<Badge color='success'>{t('bui-dsync-authorized')}</Badge>

View File

@ -66,19 +66,19 @@ export const DirectoryUsers = ({
},
{
key: 'email',
label: t('bui-dsync-email'),
label: t('bui-shared-email'),
wrap: true,
dataIndex: 'email',
},
{
key: 'status',
label: t('bui-dsync-status'),
label: t('bui-shared-status'),
wrap: true,
dataIndex: 'active',
},
{
key: 'actions',
label: t('bui-dsync-actions'),
label: t('bui-shared-actions'),
wrap: true,
dataIndex: null,
},
@ -96,7 +96,7 @@ export const DirectoryUsers = ({
return {
actions: [
{
text: t('bui-dsync-view'),
text: t('bui-shared-view'),
onClick: () => onView?.(user),
icon: <EyeIcon className='w-5' />,
},
@ -107,7 +107,7 @@ export const DirectoryUsers = ({
if (dataIndex === 'active') {
return {
badge: {
text: user[dataIndex] ? t('bui-dsync-active') : t('bui-dsync-suspended'),
text: user[dataIndex] ? t('bui-shared-active') : t('bui-dsync-suspended'),
color: user[dataIndex] ? 'success' : 'warning',
},
};

View File

@ -86,7 +86,7 @@ export const DirectoryWebhookLogs = ({
},
{
key: 'actions',
label: t('bui-dsync-actions'),
label: t('bui-shared-actions'),
wrap: true,
dataIndex: null,
},
@ -104,7 +104,7 @@ export const DirectoryWebhookLogs = ({
return {
actions: [
{
text: t('bui-dsync-view'),
text: t('bui-shared-view'),
onClick: () => onView?.(event),
icon: <EyeIcon className='w-5' />,
},

View File

@ -164,7 +164,7 @@ export const Edit = ({
{connectionIsSAML && (
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-fs-acs-url')}</span>
<span className='label-text'>{t('bui-shared-acs-url')}</span>
</div>
<input
type='url'

View File

@ -55,7 +55,7 @@ export const EditBranding = ({
<div className='flex flex-col'>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-fs-logo-url')}</span>
<span className='label-text'>{t('bui-shared-logo-url')}</span>
</div>
<input
type='url'
@ -66,12 +66,12 @@ export const EditBranding = ({
onChange={formik.handleChange}
/>
<label className='label'>
<span className='label-text-alt'>{t('bui-fs-logo-url-desc')}</span>
<span className='label-text-alt'>{t('bui-shared-logo-url-desc')}</span>
</label>
</label>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-fs-favicon-url')}</span>
<span className='label-text'>{t('bui-shared-favicon-url')}</span>
</div>
<input
type='url'
@ -82,13 +82,13 @@ export const EditBranding = ({
onChange={formik.handleChange}
/>
<label className='label'>
<span className='label-text-alt'>{t('bui-fs-favicon-url-desc')}</span>
<span className='label-text-alt'>{t('bui-shared-favicon-url-desc')}</span>
</label>
</label>
<label className='form-control'>
<div className='flex'>
<label className='label pr-3'>
<span className='label-text'>{t('bui-fs-primary-color')}</span>
<span className='label-text'>{t('bui-shared-primary-color')}</span>
</label>
</div>
<div className='flex gap-3 border-[1px] border-gray-200 rounded-md p-2 w-fit'>
@ -101,7 +101,7 @@ export const EditBranding = ({
/>
</div>
<label className='label'>
<span className='label-text-alt'>{t('bui-fs-primary-color-desc')}</span>
<span className='label-text-alt'>{t('bui-shared-primary-color-desc')}</span>
</label>
</label>
</div>

View File

@ -132,7 +132,7 @@ export const FederatedSAMLApps = ({
{t('bui-fs-oidc-config')}
</LinkOutline>
<LinkOutline href={actions.samlConfiguration} target='_blank' className='btn-md'>
{t('bui-fs-saml-config')}
{t('bui-shared-saml-configuration')}
</LinkOutline>
<ButtonPrimary onClick={() => router?.push(actions.newApp)} className='btn-md'>
{t('bui-fs-new-app')}

View File

@ -109,7 +109,7 @@ export const NewFederatedSAMLApp = ({
checked={formik.values.type === 'oidc'}
onChange={formik.handleChange}
/>
<span className='label-text ml-1'>{t('bui-fs-oidc')}</span>
<span className='label-text ml-1'>{t('bui-shared-oidc')}</span>
</label>
</div>
</div>
@ -162,7 +162,7 @@ export const NewFederatedSAMLApp = ({
{connectionIsSAML && (
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-fs-acs-url')}</span>
<span className='label-text'>{t('bui-shared-acs-url')}</span>
</div>
<input
type='url'

View File

@ -74,7 +74,7 @@ export const DSyncForm = ({
<Card.Description>{t('bui-sl-dsync-desc')}</Card.Description>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-dsync-name')}</span>
<span className='label-text'>{t('bui-sl-name')}</span>
</div>
<input
type='text'
@ -87,7 +87,7 @@ export const DSyncForm = ({
</label>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-tenant')}</span>
<span className='label-text'>{t('bui-shared-tenant')}</span>
</div>
<input
type='text'
@ -102,7 +102,7 @@ export const DSyncForm = ({
{!excludeFields?.includes('product') && (
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-product')}</span>
<span className='label-text'>{t('bui-shared-product')}</span>
</div>
<input
type='text'
@ -131,7 +131,7 @@ export const DSyncForm = ({
</label>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-webhook-secret')}</span>
<span className='label-text'>{t('bui-shared-webhook-secret')}</span>
</div>
<input
type='password'
@ -165,7 +165,7 @@ export const DSyncForm = ({
className='btn btn-primary btn-md'
loading={formik.isSubmitting}
disabled={!formik.dirty || !formik.isValid}>
{t('bui-sl-create')}
{t('bui-sl-create-link')}
</Button>
</Card.Footer>
</Card>

View File

@ -78,7 +78,7 @@ export const SSOForm = ({
<Card.Description>{t('bui-sl-sso-desc')}</Card.Description>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-sso-name')}</span>
<span className='label-text'>{t('bui-sl-name')}</span>
</div>
<input
type='text'
@ -103,7 +103,7 @@ export const SSOForm = ({
</label>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-tenant')}</span>
<span className='label-text'>{t('bui-shared-tenant')}</span>
</div>
<input
type='text'
@ -118,7 +118,7 @@ export const SSOForm = ({
{!excludeFields?.includes('product') && (
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>{t('bui-sl-product')}</span>
<span className='label-text'>{t('bui-shared-product')}</span>
</div>
<input
type='text'
@ -180,7 +180,7 @@ export const SSOForm = ({
className='btn btn-primary btn-md'
loading={formik.isSubmitting}
disabled={!formik.dirty || !formik.isValid}>
{t('bui-sl-create')}
{t('bui-sl-create-link')}
</Button>
</Card.Footer>
</Card>

View File

@ -17,7 +17,7 @@ export const SetupLinkInfo = ({ setupLink, onClose }: { setupLink: SetupLink; on
</div>
<div>
<Button size='sm' color='primary' onClick={onClose}>
{t('bui-sl-btn-close')}
{t('bui-shared-close')}
</Button>
</div>
</Card.Body>

View File

@ -31,7 +31,7 @@ export const SetupLinkInfoModal = ({
)}
<div className='modal-action'>
<Button color='secondary' variant='outline' type='button' size='md' onClick={() => onClose()}>
{t('close')}
{t('bui-shared-close')}
</Button>
</div>
</Modal>

View File

@ -84,11 +84,11 @@ export const SetupLinks = ({
const noMoreResults = links.length === 0 && paginate.offset > 0;
let cols = [
t('bui-sl-tenant'),
t('bui-sl-product'),
t('bui-shared-tenant'),
t('bui-shared-product'),
t('bui-sl-validity'),
t('bui-sl-status'),
t('bui-sl-actions'),
t('bui-shared-status'),
t('bui-shared-actions'),
];
// Exclude fields
@ -118,7 +118,7 @@ export const SetupLinks = ({
wrap: false,
element:
new Date(setupLink.validTill) > new Date() ? (
<Badge color='primary'>{t('bui-sl-active')}</Badge>
<Badge color='primary'>{t('bui-shared-active')}</Badge>
) : (
<Badge color='warning'>{t('bui-sl-expired')}</Badge>
),
@ -126,7 +126,7 @@ export const SetupLinks = ({
{
actions: [
{
text: t('bui-sl-copy'),
text: t('bui-shared-copy'),
onClick: () => {
copyToClipboard(setupLink.url);
onCopy(setupLink);
@ -134,7 +134,7 @@ export const SetupLinks = ({
icon: <ClipboardDocumentIcon className='h-5 w-5' />,
},
{
text: t('bui-sl-view'),
text: t('bui-shared-view'),
onClick: () => {
setSetupLink(setupLink);
setShowSetupLink(true);
@ -151,7 +151,7 @@ export const SetupLinks = ({
},
{
destructive: true,
text: t('bui-sl-delete'),
text: t('bui-shared-delete'),
onClick: () => {
setSetupLink(setupLink);
setDelModal(true);

View File

@ -25,7 +25,7 @@ export const ConfirmationModal = ({
}) => {
const { t } = useTranslation('common');
const buttonText = actionButtonText || t('delete');
const buttonText = actionButtonText || t('bui-shared-delete');
return (
<Modal visible={visible} title={title} description={description}>

View File

@ -8,7 +8,7 @@ export const CopyToClipboardButton = ({ text }: { text: string }) => {
return (
<IconButton
tooltip={t('copy')}
tooltip={t('bui-shared-copy')}
Icon={ClipboardDocumentIcon}
className='hover:text-primary'
onClick={() => {

View File

@ -6,7 +6,6 @@ export { EmptyState } from './EmptyState';
export { Error } from './Error';
export { Badge } from './Badge';
export { Pagination } from './Pagination';
export { ButtonOutline } from './ButtonOutline';
export { Modal } from './Modal';
export { ConfirmationModal } from './ConfirmationModal';
export { PageHeader } from './PageHeader';
@ -14,6 +13,9 @@ export { LinkOutline } from './LinkOutline';
export { LinkPrimary } from './LinkPrimary';
export { LinkBack } from './LinkBack';
export { pageLimit } from './Pagination';
export { ButtonBase } from './ButtonBase';
export { ButtonPrimary } from './ButtonPrimary';
export { ButtonDanger } from './ButtonDanger';
export { ButtonOutline } from './ButtonOutline';
export { Alert } from './Alert';
export { InputWithCopyButton } from './InputWithCopyButton';
export { InputWithCopyButton, CopyToClipboardButton } from './InputWithCopyButton';

View File

@ -40,7 +40,7 @@ export const SSOTracerInfo = ({ urls }: { urls: { getTracer: string } }) => {
let badgeText = '';
if (trace.context.isOIDCFederated) {
if (trace.context.requestedOIDCFlow) {
badgeText = t('bui-tracer-oidc-federation');
badgeText = t('bui-shared-oidc-federation');
} else {
badgeText = t('bui-tracer-oauth2-federation');
}
@ -49,7 +49,7 @@ export const SSOTracerInfo = ({ urls }: { urls: { getTracer: string } }) => {
} else if (trace.context.isIdPFlow) {
badgeText = t('bui-tracer-idp-login');
} else if (trace.context.requestedOIDCFlow) {
badgeText = t('bui-tracer-oidc');
badgeText = t('bui-shared-oidc');
} else {
badgeText = t('bui-tracer-oauth2');
}
@ -81,9 +81,9 @@ export const SSOTracerInfo = ({ urls }: { urls: { getTracer: string } }) => {
<ListItem term={t('bui-tracer-error')} value={trace.error} />
{trace.context.tenant && <ListItem term={t('bui-tracer-tenant')} value={trace.context.tenant} />}
{trace.context.tenant && <ListItem term={t('bui-shared-tenant')} value={trace.context.tenant} />}
{trace.context.product && <ListItem term={t('bui-tracer-product')} value={trace.context.product} />}
{trace.context.product && <ListItem term={t('bui-shared-product')} value={trace.context.product} />}
{trace.context.relayState && (
<ListItem term={t('bui-tracer-relay-state')} value={trace.context.relayState} />
@ -104,7 +104,7 @@ export const SSOTracerInfo = ({ urls }: { urls: { getTracer: string } }) => {
{trace.context.issuer && <ListItem term={t('bui-tracer-issuer')} value={trace.context.issuer} />}
{trace.context.acsUrl && <ListItem term={t('bui-tracer-acs-url')} value={trace.context.acsUrl} />}
{trace.context.acsUrl && <ListItem term={t('bui-shared-acs-url')} value={trace.context.acsUrl} />}
{trace.context.entityId && (
<ListItem term={t('bui-tracer-entity-id')} value={trace.context.entityId} />

View File

@ -7,7 +7,7 @@ export const WellKnownURLs = ({ jacksonUrl }: { jacksonUrl?: string }) => {
const { t } = useTranslation('common');
const [view, setView] = useState<'idp-config' | 'auth' | 'identity-fed'>('idp-config');
const viewText = t('bui-wku-view');
const viewText = t('bui-shared-view');
const downloadText = t('bui-wku-download');
const baseUrl = jacksonUrl ?? '';
@ -20,7 +20,7 @@ export const WellKnownURLs = ({ jacksonUrl }: { jacksonUrl?: string }) => {
type: 'idp-config',
},
{
title: t('bui-wku-saml-configuration'),
title: t('bui-shared-saml-configuration'),
description: t('bui-wku-sp-config-desc'),
href: `${baseUrl}/.well-known/saml-configuration`,
buttonText: viewText,
@ -62,7 +62,7 @@ export const WellKnownURLs = ({ jacksonUrl }: { jacksonUrl?: string }) => {
type: 'identity-fed',
},
{
title: t('bui-wku-oidc-federation'),
title: t('bui-shared-oidc-federation'),
description: t('bui-wku-oidc-federation-desc'),
href: `${baseUrl}/.well-known/openid-configuration`,
buttonText: viewText,

View File

@ -1,20 +0,0 @@
import useSWR from 'swr';
import type { DirectorySyncProviders } from '@boxyhq/saml-jackson';
import type { ApiError, ApiSuccess } from 'types';
import { fetcher } from '@lib/ui/utils';
const useDirectoryProviders = (setupLinkToken?: string) => {
const url = setupLinkToken
? `/api/setup/${setupLinkToken}/directory-sync/providers`
: '/api/admin/directory-sync/providers';
const { data, error, isLoading } = useSWR<ApiSuccess<DirectorySyncProviders>, ApiError>(url, fetcher);
return {
providers: data?.data,
isLoading,
error,
};
};
export default useDirectoryProviders;

View File

@ -7,10 +7,6 @@ export function getErrorCookie() {
return matches ? decodeURIComponent(matches[1]) : undefined;
}
export function copyToClipboard(text: string) {
navigator.clipboard.writeText(text);
}
export const fetcher = async (url: string, queryParams = '') => {
const res = await fetch(`${url}${queryParams}`);
@ -36,11 +32,3 @@ export const fetcher = async (url: string, queryParams = '') => {
return resContent;
};
/** Check if object is empty ({}) https://stackoverflow.com/a/32108184 */
export const isObjectEmpty = (obj) =>
// because Object.keys(new Date()).length === 0;
// we have to do some additional check
obj && // 👈 null and undefined check
Object.keys(obj).length === 0 &&
Object.getPrototypeOf(obj) === Object.prototype;

View File

@ -1,34 +1,26 @@
{
"error_loading_page": "Unable to load this page. Maybe you don't have enough rights.",
"documentation": "Documentation",
"actions": "Actions",
"active": "Active",
"back": "Back",
"cancel": "Cancel",
"copy": "Copy",
"copied": "Copied",
"client_error": "Client error",
"close_sidebar": "Close sidebar",
"create_new": "Create New",
"create_sso_connection": "Create SSO Connection",
"create_dsync_connection": "Create DSync Connection",
"delete": "Delete",
"directory_sync": "Directory Sync",
"edit_sso_connection": "Edit SSO Connection",
"email": "Email",
"boxyhq_tagline": "Security Building Blocks for Developers.",
"enterprise_sso": "Enterprise SSO",
"idp_entity_id": "IdP Entity ID",
"login_with_sso": "Login with SSO",
"login_success_toast": "A sign in link has been sent to your email address.",
"name": "Name",
"new_directory": "New Directory",
"new_setup_link": "New Setup Link",
"connections": "Connections",
"new_connection": "New Connection",
"no_projects_found": "No projects found.",
"open_menu": "Open menu",
"open_sidebar": "Open sidebar",
"save_changes": "Save Changes",
"saved": "Saved",
"server_error": "Server error",
"sign_out": "Sign out",
@ -37,9 +29,7 @@
"send_magic_link": "Send Magic Link",
"setup_links": "Setup Links",
"edit_directory": "Edit Directory",
"download": "Download",
"saml_federation_new_success": "Identity Federation app created successfully.",
"entity_id": "Entity ID / Audience URI / Audience Restriction",
"saml_federation_update_success": "Identity Federation app updated successfully.",
"saml_federation_delete_success": "Identity federation app deleted successfully",
"saml_federation_app_info": "SAML Federation App Information",
@ -52,10 +42,8 @@
"directory_updated_successfully": "Directory updated successfully",
"dashboard": "Dashboard",
"saml_federation": "Identity Federation",
"sso_tracer": "SSO Tracer",
"settings": "Settings",
"admin_portal_sso": "SSO for Admin Portal",
"close": "Close",
"configuration": "Configuration",
"view_events": "View Events",
"new_project": "New Project",
@ -64,12 +52,10 @@
"publisher_api_base_url": "Publisher API Base URL",
"send_event_to_url": "Send your event to the following URL",
"curl_request": "cURL Request",
"no_more_results": "No more results found",
"unable_to_fetch_projects": "Unable to fetch the projects!",
"sp_acs_url": "ACS (Assertion Consumer Service) URL / Single Sign-On URL / Destination URL",
"sp_oidc_redirect_url": "Authorised redirect URI / Sign-in redirect URI",
"sp_entity_id": "SP Entity ID / Identifier / Audience URI / Audience Restriction",
"response": "Response",
"assertion_signature": "Assertion Signature",
"signature_algorithm": "Signature Algorithm",
"assertion_encryption": "Assertion Encryption",
@ -85,16 +71,10 @@
"settings_branding_description": "Customize the look and feel of your portal. These values will be used in the Setup Links and IdP selection page.",
"settings_updated_successfully": "Settings updated successfully",
"settings_branding_title": "Portal Customization",
"branding_logo_url_label": "Logo URL",
"branding_favicon_url_label": "Favicon URL",
"branding_company_name_label": "Company Name",
"branding_primary_color_label": "Primary Color",
"configure_sso": "Configure Single Sign-On",
"configure_dsync": "Configure Directory Sync",
"branding_logo_url_alt": "Provide a URL to your logo. Recommend PNG or SVG formats. The image will be capped to a maximum height of 56px.",
"branding_favicon_url_alt": "Provide a URL to your favicon. Recommend PNG, SVG, or ICO formats.",
"branding_company_name_alt": "Provide your company name or product name.",
"branding_primary_color_alt": "Primary color will be applied to buttons, links, and other elements.",
"select_an_idp": "Select an Identity Provider to continue",
"audit_logs": "Audit Logs",
"privacy_vault": "Privacy Vault",
@ -104,19 +84,6 @@
"discard_and_retrieve_model": "Retrieve Model?",
"discard_and_retrieve_model_desc": "Discard any unsaved changes and retrieve the Model from the server?",
"retrieve": "Retrieve",
"connection_activated": "Connection activated successfully",
"connection_deactivated": "Connection deactivated successfully",
"inactive": "Inactive",
"activate_connection": "Activate this connection?",
"deactivate_connection": "Deactivate this connection?",
"activate_sso_connection_description": "Activating this SSO connection will allow users to sign in with this connection.",
"deactivate_sso_connection_description": "Deactivating this SSO connection will prevent users from signing in with this connection.",
"activate_dsync_connection_description": "Activating this Directory connection will start sending webhook events to your configured webhook URL.",
"deactivate_dsync_connection_description": "Deactivating this Directory connection will stop sending webhook events to your configured webhook URL.",
"yes_proceed": "Yes, proceed",
"delete_this_directory": "Delete this directory connection?",
"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",
"retraced_project_created": "Project created successfully",
"project_name": "Project name",
"create_project": "Create Project",
@ -160,6 +127,23 @@
"bui-shared-delete": "Delete",
"bui-shared-hide": "Hide",
"bui-shared-show": "Show",
"bui-shared-status": "Status",
"bui-shared-webhook-secret": "Webhook Secret",
"bui-shared-acs-url": "ACS URL",
"bui-shared-oidc": "OIDC",
"bui-shared-view": "View",
"bui-shared-oidc-federation": "OIDC Federation",
"bui-shared-saml-configuration": "SAML Configuration",
"bui-shared-close": "Close",
"bui-shared-copy": "Copy",
"bui-shared-active": "Active",
"bui-shared-email": "Email",
"bui-shared-logo-url": "Logo URL",
"bui-shared-logo-url-desc": "Provide a URL to your logo. Recommend PNG or SVG formats. The image will be capped to a maximum height of 56px.",
"bui-shared-favicon-url": "Favicon URL",
"bui-shared-favicon-url-desc": "Provide a URL to your favicon. Recommend PNG, SVG, or ICO formats.",
"bui-shared-primary-color": "Primary Color",
"bui-shared-primary-color-desc": "Primary color will be applied to buttons, links, and other elements.",
"bui-wku-heading": "Here are the set of URIs you would need access to:",
"bui-wku-idp-configuration-links": "Identity Provider Configuration links",
"bui-wku-desc-idp-configuration": "Links for SAML/OIDC IdP setup",
@ -169,7 +153,6 @@
"bui-wku-desc-identity-federation": "Links for Identity Federation app setup",
"bui-wku-sp-metadata": "SP Metadata",
"bui-wku-sp-metadata-desc": "The metadata file that your customers who use federated management systems like OpenAthens and Shibboleth will need to configure your service.",
"bui-wku-saml-configuration": "SAML Configuration",
"bui-wku-sp-config-desc": "The configuration setup guide that your customers will need to refer to when setting up SAML application with their Identity Provider.",
"bui-wku-saml-public-cert": "SAML Public Certificate",
"bui-wku-saml-public-cert-desc": "The SAML Public Certificate if you want to enable encryption with your Identity Provider.",
@ -181,14 +164,11 @@
"bui-wku-saml-idp-metadata-desc": "The metadata file that your customers who use our SAML federation feature will need to set up SAML SP configuration on their application.",
"bui-wku-saml-idp-configuration": "SAML Federation IdP Configuration",
"bui-wku-saml-idp-config-desc": "The configuration setup guide that your customers who use our SAML federation feature will need to set up SAML SP configuration on their application.",
"bui-wku-oidc-federation": "OIDC Federation",
"bui-wku-oidc-federation-desc": "Our OIDC Federation well known URI which you will need if are configuring Identity Federation via OpenID Connect.",
"bui-wku-view": "View",
"bui-wku-download": "Download",
"bui-fs-client-id": "Client ID",
"bui-fs-client-secret": "Client Secret",
"bui-fs-saml": "SAML",
"bui-fs-oidc": "OIDC",
"bui-fs-select-app-type": "Select App Type",
"bui-fs-generate-sp-entity-id": "Generate Entity ID",
"bui-fs-entity-id-instruction": "Use the button to create a distinctive identifier if your service provider does not provide a unique Entity ID and set that in your provider's configuration. If your service provider does provide a unique ID, you can use that instead.",
@ -208,36 +188,22 @@
"bui-fs-add": "Add",
"bui-fs-no-apps": "No Identity Federation Apps found.",
"bui-fs-no-apps-desc": "Create a new App to configure Identity Federation.",
"bui-fs-acs-url": "ACS URL",
"bui-fs-entity-id": "Entity ID / Audience URI / Audience Restriction",
"bui-fs-branding-title": "Customize Look and Feel",
"bui-fs-branding-desc": "You can customize the look and feel of the Identity Provider selection page by setting the following options:",
"bui-fs-logo-url": "Logo URL",
"bui-fs-logo-url-desc": "Provide a URL to your logo. Recommend PNG or SVG formats. The image will be capped to a maximum height of 56px.",
"bui-fs-favicon-url": "Favicon URL",
"bui-fs-favicon-url-desc": "Provide a URL to your favicon. Recommend PNG, SVG, or ICO formats.",
"bui-fs-primary-color": "Primary Color",
"bui-fs-primary-color-desc": "Primary color will be applied to buttons, links, and other elements.",
"bui-fs-entity-id-edit-desc": "You can't change this value. Delete and create a new app if you need to change it.",
"bui-fs-delete-app-title": "Delete this Identity Federation app",
"bui-fs-delete-app-desc": "This action cannot be undone. This will permanently delete the Identity Federation app.",
"bui-fs-apps": "Identity Federation Apps",
"bui-fs-new-app": "New App",
"bui-fs-saml-config": "SAML Configuration",
"bui-fs-oidc-config": "OIDC Configuration",
"bui-fs-saml-attributes": "SAML Attributes",
"bui-fs-oidc-attributes": "OIDC Attributes",
"bui-dsync-name": "Name",
"bui-dsync-actions": "Actions",
"bui-dsync-view": "View",
"bui-dsync-no-groups": "No groups found for this directory.",
"bui-dsync-no-users": "No users found for this directory.",
"bui-dsync-no-events": "No webhook events found for this directory.",
"bui-dsync-directory-id": "Directory ID",
"bui-dsync-tenant": "Tenant",
"bui-dsync-product": "Product",
"bui-dsync-webhook-endpoint": "Webhook Endpoint",
"bui-dsync-webhook-secret": "Webhook Secret",
"bui-dsync-scim-endpoint": "SCIM Endpoint",
"bui-dsync-scim-token": "SCIM Token",
"bui-dsync-google-auth-url": "The URL that your tenant needs to authorize the application to access their Google Directory.",
@ -247,16 +213,12 @@
"bui-dsync-webhook-events": "Webhook Events",
"bui-dsync-first-name": "First Name",
"bui-dsync-last-name": "Last Name",
"bui-dsync-email": "Email",
"bui-dsync-status": "Status",
"bui-dsync-active": "Active",
"bui-dsync-suspended": "Suspended",
"bui-dsync-status-code": "Status Code",
"bui-dsync-sent-at": "Sent At",
"bui-dsync-remove-events": "Remove Events",
"bui-dsync-delete-events-title": "Remove Webhook Events Logs",
"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-authorization-google": "Authorize Google Workspace",
@ -272,20 +234,15 @@
"bui-tracer-no-traces": "No SSO Traces recorded yet.",
"bui-tracer-sp-protocol": "SP Protocol",
"bui-tracer-saml-federation": "SAML Federation",
"bui-tracer-oidc-federation": "OIDC Federation",
"bui-tracer-oauth2-federation": "OAuth 2.0 Federation",
"bui-tracer-idp-login": "IdP Login",
"bui-tracer-oauth2": "OAuth 2.0",
"bui-tracer-oidc": "OIDC",
"bui-tracer-error": "Error",
"bui-tracer-tenant": "Tenant",
"bui-tracer-product": "Product",
"bui-tracer-relay-state": "Relay State",
"bui-tracer-default-redirect-url": "Default Redirect URL",
"bui-tracer-redirect-uri": "Redirect URI",
"bui-tracer-sso-connection-client-id": "SSO Connection Client ID",
"bui-tracer-issuer": "Issuer",
"bui-tracer-acs-url": "ACS URL",
"bui-tracer-entity-id": "Entity ID",
"bui-tracer-provider": "Provider",
"bui-tracer-saml-response": "SAML Response",
@ -298,13 +255,9 @@
"bui-tracer-stack-trace": "Stack Trace",
"bui-tracer-session-state-from-oidc-idp": "Session State (from OIDC Provider)",
"bui-tracer-scope-from-op-error": "Scope (from OIDC Provider)",
"bui-sl-sso-name": "Name (Optional)",
"bui-sl-name": "Name (Optional)",
"bui-sl-sso-description": "Description (Optional)",
"bui-sl-create-link": "Create Setup Link",
"bui-sl-dsync-name": "Name (Optional)",
"bui-sl-tenant": "Tenant",
"bui-sl-product": "Product",
"bui-sl-create": "Create Setup Link",
"bui-sl-expiry-days": "Expiry in days",
"bui-sl-default-redirect-url": "Default redirect URL",
"bui-sl-allowed-redirect-urls": "Allowed redirect URLs (newline separated)",
@ -314,21 +267,14 @@
"bui-sl-dsync-name-placeholder": "Acme Directory",
"bui-sl-sso-name-placeholder": "Acme SSO",
"bui-sl-share-info": "Share this link with your customers to allow them to set up the integration",
"bui-sl-btn-close": "Close",
"bui-sl-dsync-title": "Setup Links (Directory Sync)",
"bui-sl-sso-title": "Setup Links (Enterprise SSO)",
"bui-sl-no-links": "No Setup Links found.",
"bui-sl-no-links-desc": "You have not created any Setup Links yet.",
"bui-sl-status": "Status",
"bui-sl-active": "Active",
"bui-sl-expired": "Expired",
"bui-sl-actions": "Actions",
"bui-sl-validity": "Valid till",
"bui-sl-new-link": "New Setup Link",
"bui-sl-view": "View",
"bui-sl-copy": "Copy",
"bui-sl-regenerate": "Regenerate",
"bui-sl-delete": "Delete",
"bui-sl-delete-link-title": "Delete Setup Link",
"bui-sl-delete-link-desc": "This action cannot be undone. This will permanently delete the Setup Link.",
"bui-sl-regen-link-title": "Regenerate Setup Link",
@ -336,6 +282,5 @@
"bui-sl-link-expired": "This link has expired",
"bui-sl-link-expire-on": "This link will expire on {{expiresAt}}.",
"bui-sl-share-link-info": "Share this link with your customer to setup their service",
"bui-sl-webhook-url": "Webhook URL",
"bui-sl-webhook-secret": "Webhook Secret"
"bui-sl-webhook-url": "Webhook URL"
}

View File

@ -20,7 +20,7 @@ export const keyFromParts = (...parts: string[]): string => {
export const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
export function isNumeric(num) {
function isNumeric(num) {
return !isNaN(num);
}
export const normalizeOffsetAndLimit = ({

12
package-lock.json generated
View File

@ -24,6 +24,7 @@
"classnames": "2.5.1",
"cors": "2.8.5",
"daisyui": "4.9.0",
"formik": "2.4.5",
"i18next": "23.10.1",
"medium-zoom": "1.1.0",
"micromatch": "4.0.5",
@ -8441,7 +8442,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -10293,7 +10293,6 @@
"url": "https://opencollective.com/formik"
}
],
"peer": true,
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.1",
"deepmerge": "^2.1.1",
@ -13459,8 +13458,7 @@
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"peer": true
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
@ -18423,8 +18421,7 @@
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
"peer": true
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"node_modules/react-i18next": {
"version": "14.1.0",
@ -21520,8 +21517,7 @@
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"peer": true
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"node_modules/tmp": {
"version": "0.0.33",

View File

@ -75,6 +75,7 @@
"classnames": "2.5.1",
"cors": "2.8.5",
"daisyui": "4.9.0",
"formik": "2.4.5",
"i18next": "23.10.1",
"medium-zoom": "1.1.0",
"micromatch": "4.0.5",

View File

@ -7,8 +7,7 @@ import { useSession, getCsrfToken, signIn, SessionProvider } from 'next-auth/rea
import { useTranslation, Trans } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { errorToast, successToast } from '@components/Toaster';
import { ButtonOutline } from '@components/ButtonOutline';
import Loading from '@components/Loading';
import { ButtonOutline, Loading } from '@boxyhq/internal-ui';
import { Login as SSOLogin } from '@boxyhq/react-ui/sso';
import { adminPortalSSODefaults } from '@lib/env';
@ -125,11 +124,11 @@ const Login = ({
<div className='mt-6'>
<div className='flex flex-col gap-3'>
<label className='block text-sm font-medium' htmlFor='email'>
{t('email')}
{t('bui-shared-email')}
<label>
<input
type='email'
placeholder={t('email')}
placeholder={t('bui-shared-email')}
className='input-bordered input mt-2 w-full rounded-md'
required
onChange={(e) => {

View File

@ -3,8 +3,7 @@ import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { retracedOptions } from '@lib/env';
import Loading from '@components/Loading';
import EmptyState from '@components/EmptyState';
import { EmptyState, Loading } from '@boxyhq/internal-ui';
export interface Props {
host?: string;

View File

@ -4,9 +4,7 @@ import dynamic from 'next/dynamic';
import { useEffect, useState } from 'react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useProject, useGroups } from '@lib/ui/retraced';
import Loading from '@components/Loading';
import ErrorMessage from '@components/Error';
import { LinkBack } from '@components/LinkBack';
import { LinkBack, Loading, Error } from '@boxyhq/internal-ui';
import { Select } from 'react-daisyui';
import { retracedOptions } from '@lib/env';
import { useTranslation } from 'next-i18next';
@ -50,7 +48,7 @@ const Events: NextPage<Props> = ({ host }: Props) => {
}
if (isError) {
return <ErrorMessage />;
return <Error message={t('error_loading_page')} />;
}
const displayLogsViewer = project && environment && group;

View File

@ -3,10 +3,9 @@ import { useRouter } from 'next/router';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import ProjectDetails from '@components/retraced/ProjectDetails';
import { useProject } from '@lib/ui/retraced';
import Loading from '@components/Loading';
import ErrorMessage from '@components/Error';
import { LinkBack } from '@components/LinkBack';
import { LinkBack, Loading, Error } from '@boxyhq/internal-ui';
import { retracedOptions } from '@lib/env';
import { useTranslation } from 'next-i18next';
export interface Props {
host?: string;
@ -14,6 +13,7 @@ export interface Props {
const ProjectInfo: NextPage<Props> = ({ host }: Props) => {
const router = useRouter();
const { t } = useTranslation('common');
const { id: projectId } = router.query;
@ -24,7 +24,7 @@ const ProjectInfo: NextPage<Props> = ({ host }: Props) => {
}
if (isError) {
return <ErrorMessage />;
return <Error message={t('error_loading_page')} />;
}
return (

View File

@ -2,16 +2,12 @@ import type { NextPage } from 'next';
import DocumentMagnifyingGlassIcon from '@heroicons/react/24/outline/DocumentMagnifyingGlassIcon';
import WrenchScrewdriverIcon from '@heroicons/react/24/outline/WrenchScrewdriverIcon';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import EmptyState from '@components/EmptyState';
import { useProjects } from '@lib/ui/retraced';
import Loading from '@components/Loading';
import { useTranslation } from 'next-i18next';
import router from 'next/router';
import { Pagination, pageLimit } from '@boxyhq/internal-ui';
import { EmptyState, Pagination, pageLimit, Table, LinkPrimary, Loading } from '@boxyhq/internal-ui';
import usePaginate from '@lib/ui/hooks/usePaginate';
import { LinkPrimary } from '@components/LinkPrimary';
import { errorToast } from '@components/Toaster';
import { Table } from '@components/table/Table';
const ProjectList: NextPage = () => {
const { t } = useTranslation('common');
@ -37,12 +33,12 @@ const ProjectList: NextPage = () => {
<LinkPrimary href={'/admin/retraced/projects/new'}>{t('new_project')}</LinkPrimary>
</div>
{noProjects ? (
<EmptyState title={t('no_projects_found')} href='/admin/retraced/projects/new' />
<EmptyState title={t('no_projects_found')} />
) : (
<>
<Table
noMoreResults={noMoreResults}
cols={[t('name'), t('id'), t('created_at'), t('actions')]}
cols={[t('bui-shared-name'), t('id'), t('created_at'), t('bui-shared-actions')]}
body={projects.map((project) => {
return {
id: project.id,

View File

@ -5,7 +5,7 @@ import { useRouter } from 'next/router';
import { fetcher } from '@lib/ui/utils';
import EditConnection from '@components/connection/EditConnection';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
import { errorToast } from '@components/Toaster';
import type { OIDCSSORecord, SAMLSSORecord } from '@boxyhq/saml-jackson';

View File

@ -5,7 +5,7 @@ import { useRouter } from 'next/router';
import { fetcher } from '@lib/ui/utils';
import EditConnection from '@components/connection/EditConnection';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
import { OIDCSSORecord, SAMLSSORecord } from '@boxyhq/saml-jackson';
import { errorToast } from '@components/Toaster';

View File

@ -4,7 +4,7 @@ import BlocklyComponent, { Block } from '@components/terminus/Blockly';
import '@components/terminus/blocks/customblocks';
import '@components/terminus/blocks/generator';
import EmptyState from '@components/EmptyState';
import { EmptyState } from '@boxyhq/internal-ui';
import { terminusOptions } from '@lib/env';
export interface Props {

View File

@ -1,4 +1,4 @@
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
import type { NextPage } from 'next';
import { useRouter } from 'next/router';
import { useEffect } from 'react';

View File

@ -1,7 +1,7 @@
import { useEffect } from 'react';
import type { NextPage } from 'next';
import { useRouter } from 'next/router';
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
import useSetupLink from '@lib/ui/hooks/useSetupLink';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

View File

@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { fetcher } from '@lib/ui/utils';
import EditConnection from '@components/connection/EditConnection';
import Loading from '@components/Loading';
import { Loading } from '@boxyhq/internal-ui';
import { errorToast } from '@components/Toaster';
const ConnectionEditPage: NextPage = () => {

View File

@ -17,7 +17,7 @@ import { jacksonOptions } from '@lib/env';
import { identityProviders } from '@lib/constants';
import Footer from '@components/setup-link-instructions/Footer';
import NextButton from '@components/setup-link-instructions/NextButton';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { InputWithCopyButton } from '@boxyhq/internal-ui';
import PreviousButton from '@components/setup-link-instructions/PreviousButton';
import CreateSSOConnection from '@components/setup-link-instructions/CreateSSOConnection';
import SelectIdentityProviders from '@components/setup-link-instructions/SelectIdentityProviders';

View File

@ -2,7 +2,7 @@ import type { NextPage, InferGetStaticPropsType } from 'next';
import React from 'react';
import { useTranslation, Trans } from 'next-i18next';
import jackson from '@lib/jackson';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { InputWithCopyButton } from '@boxyhq/internal-ui';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { Toaster } from '@components/Toaster';

View File

@ -3,7 +3,7 @@ import Link from 'next/link';
import React from 'react';
import { useTranslation, Trans } from 'next-i18next';
import jackson from '@lib/jackson';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { InputWithCopyButton } from '@boxyhq/internal-ui';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { Toaster } from '@components/Toaster';
@ -46,7 +46,7 @@ const SPConfig: NextPage<InferGetStaticPropsType<typeof getServerSideProps>> = (
<div className='form-control w-full'>
<div className='flex flex-col'>
<label className='mb-2 block text-sm font-medium text-gray-900 dark:text-gray-300'>
{t('response')}
{t('bui-tracer-response')}
</label>
<p className='text-sm'>{config.response}</p>
</div>
@ -82,7 +82,7 @@ const SPConfig: NextPage<InferGetStaticPropsType<typeof getServerSideProps>> = (
href='/.well-known/saml.cer'
className='underline underline-offset-4'
target='_blank'>
{t('download')}
{t('bui-wku-download')}
</Link>
),
}}