mirror of https://github.com/boxyhq/jackson.git
Add missing translations (#2089)
* Add missing translations * Add eslint-plugin-i18next plugin * Add missing translation * Update translations * Update ESLint rules and improve UI text * Update WellKnownURLs locales * Add server-side translations in SetupLinkIndexPage
This commit is contained in:
parent
1525035092
commit
fde4e59fa6
|
@ -9,18 +9,20 @@ module.exports = {
|
|||
},
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
plugins: ['@typescript-eslint', 'i18next'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'next/core-web-vitals',
|
||||
'plugin:i18next/recommended',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'import/no-anonymous-default-export': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export const PoweredBy = () => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<p className='text-center text-xs text-gray-500 py-5'>
|
||||
<a href='https://boxyhq.com/' target='_blank' rel='noopener noreferrer'>
|
||||
Powered by BoxyHQ
|
||||
{t('boxyhq_powered_by')}
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
|
|
|
@ -165,7 +165,7 @@ export const Sidebar = ({ isOpen, setIsOpen }: SidebarProps) => {
|
|||
<div className='flex flex-shrink-0 items-center px-4'>
|
||||
<Link href='/' className='flex items-center'>
|
||||
<Image src={Logo} alt='BoxyHQ' width={36} height={36} className='h-8 w-auto' />
|
||||
<span className='ml-4 text-xl font-bold text-gray-900'>BoxyHQ Admin Portal</span>
|
||||
<span className='ml-4 text-xl font-bold text-gray-900'>{t('boxyhq_admin_portal')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='mt-5 h-0 flex-1 overflow-y-auto'>
|
||||
|
@ -182,7 +182,7 @@ export const Sidebar = ({ isOpen, setIsOpen }: SidebarProps) => {
|
|||
<div className='flex flex-shrink-0 items-center px-4'>
|
||||
<Link href='/' className='flex items-center'>
|
||||
<Image src={Logo} alt='BoxyHQ' width={36} height={36} className='h-8 w-auto' />
|
||||
<span className='ml-4 text-lg font-bold text-gray-900'>BoxyHQ Admin Portal</span>
|
||||
<span className='ml-4 text-lg font-bold text-gray-900'>{t('boxyhq_admin_portal')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='mt-5 flex flex-1 flex-col'>
|
||||
|
|
|
@ -13,49 +13,49 @@ const WellKnownURLs = () => {
|
|||
|
||||
const links = [
|
||||
{
|
||||
title: 'SP Metadata',
|
||||
title: t('sp_metadata'),
|
||||
description: t('sp_metadata_description'),
|
||||
href: '/.well-known/sp-metadata',
|
||||
buttonText: viewText,
|
||||
type: 'idp-config',
|
||||
},
|
||||
{
|
||||
title: 'SAML Configuration',
|
||||
title: t('saml_configuration'),
|
||||
description: t('sp_config_description'),
|
||||
href: '/.well-known/saml-configuration',
|
||||
buttonText: viewText,
|
||||
type: 'idp-config',
|
||||
},
|
||||
{
|
||||
title: 'SAML Public Certificate',
|
||||
title: t('saml_public_cert'),
|
||||
description: t('saml_public_cert_description'),
|
||||
href: '/.well-known/saml.cer',
|
||||
buttonText: downloadText,
|
||||
type: 'idp-config',
|
||||
},
|
||||
{
|
||||
title: 'OpenID Configuration',
|
||||
title: t('oidc_configuration'),
|
||||
description: t('oidc_config_description'),
|
||||
href: '/.well-known/oidc-configuration',
|
||||
buttonText: viewText,
|
||||
type: 'idp-config',
|
||||
},
|
||||
{
|
||||
title: 'OpenID Connect Discovery',
|
||||
title: t('oidc_discovery'),
|
||||
description: t('oidc_discovery_description'),
|
||||
href: '/.well-known/openid-configuration',
|
||||
buttonText: viewText,
|
||||
type: 'auth',
|
||||
},
|
||||
{
|
||||
title: 'IdP Metadata',
|
||||
title: t('idp_metadata'),
|
||||
description: t('idp_metadata_description'),
|
||||
href: '/.well-known/idp-metadata',
|
||||
buttonText: viewText,
|
||||
type: 'saml-fed',
|
||||
},
|
||||
{
|
||||
title: 'IdP Configuration',
|
||||
title: t('idp_configuration'),
|
||||
description: t('idp_config_description'),
|
||||
href: '/.well-known/idp-configuration',
|
||||
buttonText: viewText,
|
||||
|
@ -74,23 +74,23 @@ const WellKnownURLs = () => {
|
|||
<Tab
|
||||
isActive={view === 'idp-config'}
|
||||
setIsActive={() => setView('idp-config')}
|
||||
title='Identity Provider Configuration'
|
||||
description='Links for SAML/OIDC IdP setup'
|
||||
label='Identity Provider Configuration links'
|
||||
title={t('idp_configuration_title')}
|
||||
description={t('idp_configuration_description')}
|
||||
label={t('idp_configuration_label')}
|
||||
/>
|
||||
<Tab
|
||||
isActive={view === 'auth'}
|
||||
setIsActive={() => setView('auth')}
|
||||
title='Auth integration'
|
||||
description='Links for OAuth 2.0/OpenID Connect auth'
|
||||
label='Auth integration links'
|
||||
title={t('auth_integration_title')}
|
||||
description={t('auth_integration_description')}
|
||||
label={t('auth_integration_label')}
|
||||
/>
|
||||
<Tab
|
||||
isActive={view === 'saml-fed'}
|
||||
setIsActive={() => setView('saml-fed')}
|
||||
title='SAML Federation'
|
||||
description='Links for SAML Federation app setup'
|
||||
label='SAML Federation links'
|
||||
title={t('saml_fed_configuration_title')}
|
||||
description={t('saml_fed_configuration_description')}
|
||||
label={t('saml_fed_configuration_label')}
|
||||
/>
|
||||
</div>
|
||||
<div className='space-y-3 mt-8'>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Link from 'next/link';
|
||||
import type { Directory } from '@boxyhq/saml-jackson';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const DirectoryTab = ({
|
||||
directory,
|
||||
|
@ -11,32 +12,34 @@ const DirectoryTab = ({
|
|||
activeTab: string;
|
||||
setupLinkToken?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const menus = setupLinkToken
|
||||
? [
|
||||
{
|
||||
name: 'Directory',
|
||||
name: t('directory'),
|
||||
href: `/setup/${setupLinkToken}/directory-sync/${directory.id}`,
|
||||
active: activeTab === 'directory',
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: 'Directory',
|
||||
name: t('directory'),
|
||||
href: `/admin/directory-sync/${directory.id}`,
|
||||
active: activeTab === 'directory',
|
||||
},
|
||||
{
|
||||
name: 'Users',
|
||||
name: t('users'),
|
||||
href: `/admin/directory-sync/${directory.id}/users`,
|
||||
active: activeTab === 'users',
|
||||
},
|
||||
{
|
||||
name: 'Groups',
|
||||
name: t('groups'),
|
||||
href: `/admin/directory-sync/${directory.id}/groups`,
|
||||
active: activeTab === 'groups',
|
||||
},
|
||||
{
|
||||
name: 'Webhook Events',
|
||||
name: t('webhook_events'),
|
||||
href: `/admin/directory-sync/${directory.id}/events`,
|
||||
active: activeTab === 'events',
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@ export const AccountLayout = ({ children }: { children: React.ReactNode }) => {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Admin Portal | BoxyHQ</title>
|
||||
<title>{t('boxyhq_admin_portal')}</title>
|
||||
<link rel='icon' href='/favicon.ico' />
|
||||
</Head>
|
||||
<Sidebar isOpen={isOpen} setIsOpen={setIsOpen} />
|
||||
|
|
|
@ -3,9 +3,11 @@ import classNames from 'classnames';
|
|||
import { useRouter } from 'next/router';
|
||||
import { successToast, errorToast } from '@components/Toaster';
|
||||
import { LinkBack } from '@components/LinkBack';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const AddProject = () => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [project, setProject] = useState({
|
||||
|
@ -49,7 +51,7 @@ const AddProject = () => {
|
|||
}
|
||||
|
||||
if (data && data.project) {
|
||||
successToast('Project created successfully.');
|
||||
successToast(t('retraced_project_created'));
|
||||
router.replace(`/admin/retraced/projects/${data.project.id}`);
|
||||
}
|
||||
};
|
||||
|
@ -58,13 +60,15 @@ const AddProject = () => {
|
|||
<>
|
||||
<LinkBack href='/admin/retraced/projects' />
|
||||
<div className='mt-5'>
|
||||
<h2 className='mb-5 mt-5 font-bold text-gray-700 dark:text-white md:text-xl'>Create Project</h2>
|
||||
<h2 className='mb-5 mt-5 font-bold text-gray-700 dark:text-white md:text-xl'>
|
||||
{t('create_project')}
|
||||
</h2>
|
||||
<div className='min-w-[28rem] rounded border border-gray-200 bg-white p-3 dark:border-gray-700 dark:bg-gray-800 md:w-3/4 md:max-w-lg'>
|
||||
<form onSubmit={createProject}>
|
||||
<div className='flex flex-col space-y-3'>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label'>
|
||||
<span className='label-text'>Project name</span>
|
||||
<span className='label-text'>{t('project_name')}</span>
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
|
@ -76,7 +80,7 @@ const AddProject = () => {
|
|||
</div>
|
||||
<div>
|
||||
<button className={classNames('btn-primary btn', loading ? 'loading' : '')}>
|
||||
Create Project
|
||||
{t('create_project')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import RetracedEventsBrowser from '@retracedhq/logs-viewer';
|
||||
import useSWR from 'swr';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import type { ApiError, ApiSuccess } from 'types';
|
||||
import type { Project } from 'types/retraced';
|
||||
|
@ -8,6 +9,8 @@ import Loading from '@components/Loading';
|
|||
import { fetcher } from '@lib/ui/utils';
|
||||
|
||||
const LogsViewer = (props: { project: Project; environmentId: string; groupId: string; host: string }) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const { project, environmentId, groupId, host } = props;
|
||||
|
||||
const token = project.tokens.filter((token) => token.environment_id === environmentId)[0];
|
||||
|
@ -36,7 +39,7 @@ const LogsViewer = (props: { project: Project; environmentId: string; groupId: s
|
|||
<RetracedEventsBrowser
|
||||
host={`${host}/viewer/v1`}
|
||||
auditLogToken={viewerToken}
|
||||
header='Audit Logs'
|
||||
header={t('audit_logs')}
|
||||
customClass={'text-primary dark:text-white'}
|
||||
skipViewLogEvent={true}
|
||||
/>
|
||||
|
|
|
@ -19,7 +19,7 @@ const ProjectDetails = (props: { project: Project; host?: string }) => {
|
|||
<>
|
||||
<div className='form-control mb-5 max-w-xs'>
|
||||
<label className='label pl-0'>
|
||||
<span className='label-text'>Environment</span>
|
||||
<span className='label-text'>{t('environment')}</span>
|
||||
</label>
|
||||
<Select
|
||||
value={selectedIndex}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useRouter } from 'next/router';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ButtonPrimary } from '@components/ButtonPrimary';
|
||||
|
||||
const NextButton = () => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const onClick = () => {
|
||||
const { idp, step, token } = router.query as { idp: string; step: string; token: string };
|
||||
|
@ -20,7 +21,7 @@ const NextButton = () => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<ButtonPrimary onClick={onClick}>Next Step</ButtonPrimary>
|
||||
<ButtonPrimary onClick={onClick}>{t('next_step')}</ButtonPrimary>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useRouter } from 'next/router';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ButtonOutline } from '@components/ButtonOutline';
|
||||
|
||||
const PreviousButton = () => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const onClick = () => {
|
||||
const { idp, step, token } = router.query as { idp: string; step: string; token: string };
|
||||
|
@ -20,7 +21,7 @@ const PreviousButton = () => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<ButtonOutline onClick={onClick}>Previous Step</ButtonOutline>
|
||||
<ButtonOutline onClick={onClick}>{t('previous_step')}</ButtonOutline>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -196,7 +196,7 @@ const CreateSetupLink = ({ service }: { service: SetupLinkService }) => {
|
|||
<textarea
|
||||
id={'redirectUrl'}
|
||||
name='redirectUrl'
|
||||
placeholder={'Allowed redirect URLs (newline separated)'}
|
||||
placeholder={t('allowed_redirect_url')}
|
||||
value={formObj['redirectUrl']}
|
||||
required
|
||||
onChange={handleChange}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const InvalidSetupLinkAlert = ({ message }: { message: string }) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-3 rounded border border-error p-4'>
|
||||
<h3 className='text-base font-medium'>{message}</h3>
|
||||
<p className='leading-6'>
|
||||
Please contact your administrator to get a new setup link. If you are the administrator, visit the
|
||||
Admin Portal to create a new setup link for the service.
|
||||
</p>
|
||||
<p className='leading-6'>{t('invalid_setup_link_alert')}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -23,13 +23,10 @@ export const SetupLinkInfo = ({ setupLink, visible, onClose }: SetupLinkInfoProp
|
|||
title={`Setup link info: tenant '${setupLink.tenant}', product '${setupLink.product}'`}>
|
||||
<div className='mt-2 flex flex-col gap-3'>
|
||||
<div>
|
||||
<InputWithCopyButton
|
||||
text={setupLink.url}
|
||||
label='Share this link with your customer to setup their service'
|
||||
/>
|
||||
<InputWithCopyButton text={setupLink.url} label={t('share_setup_link')} />
|
||||
</div>
|
||||
<p className='text-sm'>
|
||||
This link is valid till{' '}
|
||||
{t('setup_link_valid_till')}{' '}
|
||||
<span className={new Date(setupLink.validTill) < new Date() ? 'text-red-400' : ''}>
|
||||
{new Date(setupLink.validTill).toString()}
|
||||
</span>
|
||||
|
|
|
@ -113,11 +113,11 @@ function BlocklyComponent(props) {
|
|||
/>
|
||||
</div>
|
||||
<div className='mb-6 w-full px-3 md:mb-0 md:w-1/3'>
|
||||
<ButtonPrimary onClick={uploadModel}>Publish Model</ButtonPrimary>
|
||||
<ButtonPrimary onClick={uploadModel}>{t('publish_model')}</ButtonPrimary>
|
||||
</div>
|
||||
<div className='mb-6 w-full px-3 md:mb-0 md:w-1/3'>
|
||||
<ButtonBase color='secondary' onClick={toggleRetrieveConfirm}>
|
||||
Retrieve Model
|
||||
{t('retrieve_model')}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -162,10 +162,7 @@ const UpdateApp = ({ hasValidLicense }: { hasValidLicense: boolean }) => {
|
|||
/>
|
||||
</div>
|
||||
<div className='pt-4'>
|
||||
<p className='text-base leading-6 text-gray-500'>
|
||||
You can customize the look and feel Identity Provider selection page by setting following
|
||||
options:
|
||||
</p>
|
||||
<p className='text-base leading-6 text-gray-500'>{t('customize_branding')}:</p>
|
||||
</div>
|
||||
<div className='form-control w-full md:w-1/2'>
|
||||
<label className='label'>
|
||||
|
|
|
@ -171,6 +171,8 @@
|
|||
"assertion_encryption": "Assertion Encryption",
|
||||
"sp_saml_config_title": "Service Provider (SP) SAML Configuration",
|
||||
"sp_saml_config_description": "Your Identity Provider (IdP) will ask for the following information while configuring the SAML application. Share this information with your IT administrator.",
|
||||
"refer_to_provider_instructions": "Refer to our <guideLink>guides</guideLink> for provider specific instructions.",
|
||||
"sp_download_our_public_cert": "If you want to encrypt the assertion, you can <downloadLink>download our public certificate.</downloadLink> Otherwise select the Unencrypted option.",
|
||||
"sp_oidc_config_title": "Service Provider (SP) OIDC Configuration",
|
||||
"sp_oidc_config_description": "Your Identity Provider (IdP) will ask for the following information while configuring the OIDC application. Share this information with your IT administrator.",
|
||||
"password": "Password",
|
||||
|
@ -214,5 +216,52 @@
|
|||
"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"
|
||||
"hide_secret": "Hide secret",
|
||||
"directory": "Directory",
|
||||
"users": "Users",
|
||||
"groups": "Groups",
|
||||
"webhook_events": "Webhook Events",
|
||||
"sp_metadata": "SP Metadata",
|
||||
"saml_configuration": "SAML Configuration",
|
||||
"saml_public_cert": "SAML Public Certificate",
|
||||
"oidc_configuration": "OpenID Configuration",
|
||||
"oidc_discovery": "OpenID Connect Discovery",
|
||||
"idp_metadata": "IdP Metadata",
|
||||
"idp_configuration": "IdP Configuration",
|
||||
"idp_configuration_title": "Identity Provider Configuration",
|
||||
"idp_configuration_description": "Links for SAML/OIDC IdP setup",
|
||||
"idp_configuration_label": "Identity Provider Configuration links",
|
||||
"auth_integration_title": "Auth integration",
|
||||
"auth_integration_description": "Links for OAuth 2.0/OpenID Connect auth",
|
||||
"auth_integration_label": "Auth integration links",
|
||||
"saml_fed_configuration_title": "SAML Federation",
|
||||
"saml_fed_configuration_description": "Links for SAML Federation app setup",
|
||||
"saml_fed_configuration_label": "SAML Federation links",
|
||||
"retraced_project_created": "Project created successfully",
|
||||
"project_name": "Project name",
|
||||
"create_project": "Create Project",
|
||||
"share_setup_link": "Share this link with your customer to setup their service",
|
||||
"setup_link_valid_till": "This link is valid till",
|
||||
"invalid_setup_link_alert": "Please contact your administrator to get a new setup link. If you are the administrator, visit the Admin Portal to create a new setup link for the service.",
|
||||
"boxyhq_admin_portal": "BoxyHQ Admin Portal",
|
||||
"environment:": "Environment",
|
||||
"group_or_tenant": "Group (Tenant)",
|
||||
"id": "Id",
|
||||
"created_at": "Created At",
|
||||
"previous_step": "Previous Step",
|
||||
"next_step": "Next Step",
|
||||
"boxyhq_powered_by": "Powered by BoxyHQ",
|
||||
"publish_model": "Publish Model",
|
||||
"retrieve_model": "Retrieve Model",
|
||||
"guides": "guides",
|
||||
"learn_to_enable_auth_methods": "Please visit <docLink>BoxyHQ documentation</docLink> to learn how to enable the Magic Link or Email/Password authentication methods.",
|
||||
"advanced_sp_saml_configuration": "Advanced Service Provider (SP) SAML Configuration",
|
||||
"select_identity_provider": "Select Identity Provider",
|
||||
"configure_identity_provider": "Configure {{provider}}",
|
||||
"change_identity_provider": "Change Identity Provider",
|
||||
"invalid_request_try_again": "Invalid request. Please try again.",
|
||||
"choose_an_identity_provider_to_continue": "Choose an Identity Provider to continue. If you don't see your Identity Provider, please contact your administrator.",
|
||||
"choose_an_app_to_continue": "Choose an app to continue. If you don't see your app, please contact your administrator.",
|
||||
"no_saml_response_try_again": "No SAMLResponse found. Please try again.",
|
||||
"customize_branding": "You can customize the look and feel Identity Provider selection page by setting following options"
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"eslint": "8.56.0",
|
||||
"eslint-config-next": "14.0.4",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-i18next": "6.0.3",
|
||||
"postcss": "8.4.32",
|
||||
"prettier": "3.1.1",
|
||||
"prettier-plugin-tailwindcss": "0.5.9",
|
||||
|
@ -7748,6 +7749,19 @@
|
|||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-i18next": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-6.0.3.tgz",
|
||||
"integrity": "sha512-RtQXYfg6PZCjejIQ/YG+dUj/x15jPhufJ9hUDGH0kCpJ6CkVMAWOQ9exU1CrbPmzeykxLjrXkjAaOZF/V7+DOA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
"requireindex": "~1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-import": {
|
||||
"version": "2.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz",
|
||||
|
@ -16871,6 +16885,15 @@
|
|||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/requireindex": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz",
|
||||
"integrity": "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.5"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
"eslint": "8.56.0",
|
||||
"eslint-config-next": "14.0.4",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-i18next": "6.0.3",
|
||||
"postcss": "8.4.32",
|
||||
"prettier": "3.1.1",
|
||||
"prettier-plugin-tailwindcss": "0.5.9",
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
|
|||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { useSession, getCsrfToken, signIn, SessionProvider } from 'next-auth/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation, Trans } from 'next-i18next';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { errorToast, successToast } from '@components/Toaster';
|
||||
import { ButtonOutline } from '@components/ButtonOutline';
|
||||
|
@ -114,7 +114,9 @@ const Login = ({
|
|||
<div className='flex justify-center'>
|
||||
<Image src='/logo.png' alt='BoxyHQ logo' width={50} height={50} />
|
||||
</div>
|
||||
<h2 className='text-center text-3xl font-extrabold text-gray-900'>BoxyHQ Admin Portal</h2>
|
||||
<h2 className='text-center text-3xl font-extrabold text-gray-900'>
|
||||
{t('boxyhq_admin_portal')}
|
||||
</h2>
|
||||
<p className='text-center text-sm text-gray-600'>{t('boxyhq_tagline')}</p>
|
||||
</div>
|
||||
|
||||
|
@ -170,17 +172,21 @@ const Login = ({
|
|||
{/* No login methods enabled */}
|
||||
{!isEmailPasswordEnabled && !isMagicLinkEnabled && (
|
||||
<div className='mt-10 text-center font-medium text-gray-600'>
|
||||
<p>
|
||||
Please visit
|
||||
<a
|
||||
href='https://boxyhq.com/docs/admin-portal/overview'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='underline underline-offset-2'>
|
||||
BoxyHQ documentation
|
||||
</a>
|
||||
to learn how to enable the Magic Link or Email/Password authentication methods.
|
||||
</p>
|
||||
<Trans
|
||||
i18nKey='learn_to_enable_auth_methods'
|
||||
t={t}
|
||||
components={{
|
||||
docLink: (
|
||||
<a
|
||||
href='https://boxyhq.com/docs/admin-portal/overview'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='underline underline-offset-2'>
|
||||
{t('documentation')}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import ErrorMessage from '@components/Error';
|
|||
import { LinkBack } from '@components/LinkBack';
|
||||
import { Select } from 'react-daisyui';
|
||||
import { retracedOptions } from '@lib/env';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const LogsViewer = dynamic(() => import('@components/retraced/LogsViewer'), {
|
||||
ssr: false,
|
||||
|
@ -20,6 +21,7 @@ export interface Props {
|
|||
|
||||
const Events: NextPage<Props> = ({ host }: Props) => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const [environment, setEnvironment] = useState('');
|
||||
const [group, setGroup] = useState('');
|
||||
|
@ -62,7 +64,7 @@ const Events: NextPage<Props> = ({ host }: Props) => {
|
|||
<div className='flex space-x-2'>
|
||||
<div className='form-control max-w-xs'>
|
||||
<label className='label pl-0'>
|
||||
<span className='label-text'>Environment</span>
|
||||
<span className='label-text'>{t('environment')}</span>
|
||||
</label>
|
||||
{project ? (
|
||||
<Select
|
||||
|
@ -81,7 +83,7 @@ const Events: NextPage<Props> = ({ host }: Props) => {
|
|||
</div>
|
||||
<div className='form-control max-w-xs'>
|
||||
<label className='label pl-0'>
|
||||
<span className='label-text'>Group (Tenant)</span>
|
||||
<span className='label-text'>{t('group_or_tenant')}</span>
|
||||
</label>
|
||||
{groups ? (
|
||||
<Select
|
||||
|
|
|
@ -32,7 +32,7 @@ const ProjectList: NextPage = () => {
|
|||
return (
|
||||
<div>
|
||||
<div className='mb-5 flex items-center justify-between'>
|
||||
<h2 className='font-bold text-gray-700 dark:text-white md:text-xl'>Projects</h2>
|
||||
<h2 className='font-bold text-gray-700 dark:text-white md:text-xl'>{t('projects')}</h2>
|
||||
<LinkPrimary Icon={PlusIcon} href={'/admin/retraced/projects/new'}>
|
||||
{t('new_project')}
|
||||
</LinkPrimary>
|
||||
|
@ -46,16 +46,16 @@ const ProjectList: NextPage = () => {
|
|||
<thead className='bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400'>
|
||||
<tr>
|
||||
<th scope='col' className='px-6 py-3'>
|
||||
Name
|
||||
{t('name')}
|
||||
</th>
|
||||
<th scope='col' className='px-6 py-3'>
|
||||
Id
|
||||
{t('id')}
|
||||
</th>
|
||||
<th scope='col' className='px-6 py-3'>
|
||||
Created At
|
||||
{t('created_at')}
|
||||
</th>
|
||||
<th scope='col' className='px-6 py-3'>
|
||||
Actions
|
||||
{t('actions')}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -58,7 +58,7 @@ const SAMLTraceInspector: NextPage = () => {
|
|||
<h3 className='text-base font-semibold leading-6 text-gray-900'>{t('trace_details')}</h3>
|
||||
<p className='mt-1 flex max-w-2xl gap-6 text-sm text-gray-500'>
|
||||
<span className='whitespace-nowrap'>
|
||||
<span className='font-medium text-gray-500'>TraceID:</span>
|
||||
<span className='font-medium text-gray-500'>{t('trace_id')}</span>
|
||||
<span className='ml-2 font-bold text-gray-700'> {traceId}</span>
|
||||
</span>
|
||||
<span className='whitespace-nowrap'>
|
||||
|
|
|
@ -53,7 +53,7 @@ export default function ChooseIdPConnection({
|
|||
{authFlow in selectors ? (
|
||||
selectors[authFlow]
|
||||
) : (
|
||||
<p className='text-center text-sm text-slate-600'>Invalid request. Please try again.</p>
|
||||
<p className='text-center text-sm text-slate-600'>{t('invalid_request_try_again')}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className='my-4'>
|
||||
|
@ -98,10 +98,7 @@ const IdpSelector = ({ connections }: { connections: Connection[] }) => {
|
|||
);
|
||||
})}
|
||||
</ul>
|
||||
<p className='text-center text-sm text-slate-600'>
|
||||
Choose an Identity Provider to continue. If you don't see your Identity Provider, please contact
|
||||
your administrator.
|
||||
</p>
|
||||
<p className='text-center text-sm text-slate-600'>{t('choose_an_identity_provider_to_continue')}</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -143,7 +140,7 @@ const AppSelector = ({
|
|||
};
|
||||
|
||||
if (!SAMLResponse) {
|
||||
return <p className='text-center text-sm text-slate-600'>No SAMLResponse found. Please try again.</p>;
|
||||
return <p className='text-center text-sm text-slate-600'>{t('no_saml_response_try_again')}</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -171,9 +168,7 @@ const AppSelector = ({
|
|||
})}
|
||||
</ul>
|
||||
</form>
|
||||
<p className='text-center text-sm text-slate-600'>
|
||||
Choose an app to continue. If you don't see your app, please contact your administrator.
|
||||
</p>
|
||||
<p className='text-center text-sm text-slate-600'>{t('choose_an_app_to_continue')}</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { NextPage } from 'next';
|
|||
import { useRouter } from 'next/router';
|
||||
import Loading from '@components/Loading';
|
||||
import useSetupLink from '@lib/ui/hooks/useSetupLink';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
|
||||
const SetupLinkIndexPage: NextPage = () => {
|
||||
const router = useRouter();
|
||||
|
@ -29,4 +30,14 @@ const SetupLinkIndexPage: NextPage = () => {
|
|||
return null;
|
||||
};
|
||||
|
||||
export const getServerSideProps = async (context) => {
|
||||
const { locale } = context;
|
||||
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default SetupLinkIndexPage;
|
||||
|
|
|
@ -10,6 +10,7 @@ import type { GetServerSidePropsContext, InferGetServerSidePropsType } from 'nex
|
|||
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
||||
import { ArrowsRightLeftIcon } from '@heroicons/react/24/outline';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import jackson from '@lib/jackson';
|
||||
import { jacksonOptions } from '@lib/env';
|
||||
|
@ -24,10 +25,12 @@ import SelectIdentityProviders from '@components/setup-link-instructions/SelectI
|
|||
type NewConnectionProps = InferGetServerSidePropsType<typeof getServerSideProps>;
|
||||
|
||||
const AdvancedSPConfigLink = () => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<div className='py-2'>
|
||||
<Link href='/.well-known/saml-configuration' target='_blank' className='underline-offset-4'>
|
||||
<span className='text-xs'>Advanced Service Provider (SP) SAML Configuration</span>
|
||||
<span className='text-xs'>{t('advanced_sp_saml_configuration')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
@ -72,6 +75,8 @@ const NewConnection = ({
|
|||
publicCertUrl,
|
||||
oidcCallbackUrl,
|
||||
}: NewConnectionProps) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const linkSelectIdp = { pathname: '/setup/[token]/sso-connection/new', query: { token: setupLinkToken } };
|
||||
|
||||
const scope = {
|
||||
|
@ -108,9 +113,9 @@ const NewConnection = ({
|
|||
}
|
||||
|
||||
progress = (100 / selectedIdP.stepCount) * parseInt(step);
|
||||
heading = `Configure ${selectedIdP?.name}`;
|
||||
heading = t('configure_identity_provider', { provider: selectedIdP.name });
|
||||
} else {
|
||||
heading = 'Select Identity Provider';
|
||||
heading = t('select_identity_provider');
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -121,7 +126,7 @@ const NewConnection = ({
|
|||
{source && (
|
||||
<Link className='btn btn-xs h-0' href={linkSelectIdp}>
|
||||
<ArrowsRightLeftIcon className='w-5 h-5' />
|
||||
Change Identity Provider
|
||||
{t('change_identity_provider')}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { NextPage, InferGetStaticPropsType } from 'next';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation, Trans } from 'next-i18next';
|
||||
import jackson from '@lib/jackson';
|
||||
import { InputWithCopyButton } from '@components/ClipboardButton';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
|
@ -18,15 +18,21 @@ const SPConfig: NextPage<InferGetStaticPropsType<typeof getServerSideProps>> = (
|
|||
<h2 className='font-bold text-gray-700 md:text-xl'>{t('sp_oidc_config_title')}</h2>
|
||||
<p className='text-sm leading-6 text-gray-800'>{t('sp_oidc_config_description')}</p>
|
||||
<p className='text-sm leading-6 text-gray-600'>
|
||||
Refer to our
|
||||
<a
|
||||
href='https://boxyhq.com/docs/jackson/sso-providers'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className='underline underline-offset-4'>
|
||||
guides
|
||||
</a>
|
||||
for provider specific instructions.
|
||||
<Trans
|
||||
i18nKey='refer_to_provider_instructions'
|
||||
t={t}
|
||||
components={{
|
||||
guideLink: (
|
||||
<a
|
||||
href='https://boxyhq.com/docs/jackson/sso-providers'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className='underline underline-offset-4'>
|
||||
{t('guides')}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className='mt-6 flex flex-col gap-6'>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { NextPage, InferGetStaticPropsType } from 'next';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useTranslation, Trans } from 'next-i18next';
|
||||
import jackson from '@lib/jackson';
|
||||
import { InputWithCopyButton } from '@components/ClipboardButton';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
|
@ -19,15 +19,21 @@ const SPConfig: NextPage<InferGetStaticPropsType<typeof getServerSideProps>> = (
|
|||
<h2 className='font-bold text-gray-700 md:text-xl'>{t('sp_saml_config_title')}</h2>
|
||||
<p className='text-sm leading-6 text-gray-800'>{t('sp_saml_config_description')}</p>
|
||||
<p className='text-sm leading-6 text-gray-600'>
|
||||
Refer to our
|
||||
<a
|
||||
href='https://boxyhq.com/docs/jackson/sso-providers'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className='underline underline-offset-4'>
|
||||
guides
|
||||
</a>
|
||||
for provider specific instructions.
|
||||
<Trans
|
||||
i18nKey='refer_to_provider_instructions'
|
||||
t={t}
|
||||
components={{
|
||||
guideLink: (
|
||||
<a
|
||||
href='https://boxyhq.com/docs/jackson/sso-providers'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className='underline underline-offset-4'>
|
||||
{t('guides')}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className='mt-6 flex flex-col gap-6'>
|
||||
|
@ -67,11 +73,20 @@ const SPConfig: NextPage<InferGetStaticPropsType<typeof getServerSideProps>> = (
|
|||
{t('assertion_encryption')}
|
||||
</label>
|
||||
<p className='text-sm'>
|
||||
If you want to encrypt the assertion, you can
|
||||
<Link href='/.well-known/saml.cer' className='underline underline-offset-4' target='_blank'>
|
||||
download our public certificate.
|
||||
</Link>
|
||||
Otherwise select the Unencrypted option.
|
||||
<Trans
|
||||
i18nKey='sp_download_our_public_cert'
|
||||
t={t}
|
||||
components={{
|
||||
downloadLink: (
|
||||
<Link
|
||||
href='/.well-known/saml.cer'
|
||||
className='underline underline-offset-4'
|
||||
target='_blank'>
|
||||
{t('download')}
|
||||
</Link>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue