mirror of
https://github.com/logto-io/logto.git
synced 2025-02-24 22:05:56 -05:00
feat(console,phrases): add third party app list page (#5149)
* feat(console,phrases): add new third-party applicaiton to the guide library add new third-party applicaiton to the guide library * feat(console,phrases): add third party app list page add third party app list page * fix(console): fix page size fix page size * fix(console): fix rebase issue fix rebase issue * fix(test): fix rebase issue fix rebase issue
This commit is contained in:
parent
a85266284b
commit
1c3ada0123
22 changed files with 463 additions and 32 deletions
|
@ -11,7 +11,7 @@ import {
|
|||
ApplicationDetailsTabs,
|
||||
EnterpriseSsoDetailsTabs,
|
||||
} from '@/consts';
|
||||
import { isCloud } from '@/consts/env';
|
||||
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
|
||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
|
||||
import useUserPreferences from '@/hooks/use-user-preferences';
|
||||
|
@ -85,6 +85,12 @@ function ConsoleContent() {
|
|||
<Route path="dashboard" element={<Dashboard />} />
|
||||
<Route path="applications">
|
||||
<Route index element={<Applications />} />
|
||||
{isDevFeaturesEnabled && (
|
||||
<Route
|
||||
path="third-party-applications"
|
||||
element={<Applications tab="thirdPartyApplications" />}
|
||||
/>
|
||||
)}
|
||||
<Route path="create" element={<Applications />} />
|
||||
<Route path=":id/guide/:guideId" element={<ApplicationDetails />} />
|
||||
<Route path=":id">
|
||||
|
|
|
@ -19,6 +19,7 @@ import { addSupportQuotaToPlan } from '@/utils/subscription';
|
|||
*/
|
||||
const useSubscriptionPlans = () => {
|
||||
const cloudApi = useCloudApi();
|
||||
|
||||
const useSwrResponse = useSWRImmutable<SubscriptionPlanResponse[], Error>(
|
||||
isCloud && '/api/subscription-plans',
|
||||
async () => cloudApi.get('/api/subscription-plans')
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
useLocation,
|
||||
useNavigate,
|
||||
useHref,
|
||||
type NavigateFunction,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { isCloud } from '@/consts/env';
|
||||
|
@ -43,7 +44,7 @@ type TenantPathname = {
|
|||
*/
|
||||
getTo: (to: To) => To;
|
||||
/** Navigate to the given pathname in the current tenant. */
|
||||
navigate: (to: To, options?: NavigateOptions) => void;
|
||||
navigate: NavigateFunction;
|
||||
/** Returns the full URL with the current tenant ID prepended. */
|
||||
getUrl: (pathname: string) => URL;
|
||||
};
|
||||
|
@ -112,7 +113,13 @@ function useTenantPathname(): TenantPathname {
|
|||
const data = useMemo(
|
||||
() => ({
|
||||
match,
|
||||
navigate: (to: To, options?: NavigateOptions) => {
|
||||
navigate: (to: To | number, options?: NavigateOptions) => {
|
||||
// Navigate to the given index in the history stack
|
||||
if (typeof to === 'number') {
|
||||
navigate(to);
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(getTo(to), options);
|
||||
},
|
||||
getPathname,
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import { type Application } from '@logto/schemas';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { defaultPageSize } from '@/consts';
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import { buildUrl } from '@/utils/url';
|
||||
|
||||
const pageSize = defaultPageSize;
|
||||
const applicationsEndpoint = 'api/applications';
|
||||
|
||||
/**
|
||||
* @typeof {Object} ApplicationData
|
||||
*
|
||||
* @property data - The application data of the current active tab, returned from useSWR.
|
||||
* @property error - The request error of the current active tab returned from useSWR.
|
||||
* @property mutate - The mutate function of the current active tab returned from useSWR.
|
||||
* @property pagination - The pagination data of the current active tab. It contains the current page and page size.
|
||||
* @property paginationRecords - Returns the global pagination records of the first party and third party applications.
|
||||
* This is used to keep track of the pagination when switching between tabs by passing the page records to the tab navigation.
|
||||
* @property updatePagination - The function to update the pagination of the current active tab.
|
||||
* @property showThirdPartyApplicationTab - The flag to show the third party application tab. Hide the tab if there is no third party applications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This hook is used to keep track of the first party and third party applications data with pagination.
|
||||
*
|
||||
* @param isThirdParty
|
||||
* @returns {ApplicationData}
|
||||
*/
|
||||
|
||||
const useApplicationsData = (isThirdParty = false) => {
|
||||
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const [firstPartyApplicationPage, setFirstPartyApplicationPage] = useState(
|
||||
isThirdParty ? 1 : page
|
||||
);
|
||||
|
||||
const [thirdPartyApplicationPage, setThirdPartyApplicationPage] = useState(
|
||||
isThirdParty ? page : 1
|
||||
);
|
||||
|
||||
const updatePagination = useCallback(
|
||||
(page: number) => {
|
||||
updateSearchParameters({ page });
|
||||
},
|
||||
[updateSearchParameters]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Update the pagination records based on the current active tab.
|
||||
if (isThirdParty) {
|
||||
setThirdPartyApplicationPage(page);
|
||||
} else {
|
||||
setFirstPartyApplicationPage(page);
|
||||
}
|
||||
}, [page, isThirdParty]);
|
||||
|
||||
const firstPartyApplicationsFetchUrl = useMemo(
|
||||
() =>
|
||||
buildUrl(applicationsEndpoint, {
|
||||
page: String(firstPartyApplicationPage),
|
||||
page_size: String(pageSize),
|
||||
}),
|
||||
[firstPartyApplicationPage]
|
||||
);
|
||||
|
||||
const thirdPartyApplicationsFetchUrl = useMemo(
|
||||
() =>
|
||||
buildUrl(applicationsEndpoint, {
|
||||
page: String(thirdPartyApplicationPage),
|
||||
page_size: String(pageSize),
|
||||
isThirdParty: 'true',
|
||||
}),
|
||||
[thirdPartyApplicationPage]
|
||||
);
|
||||
|
||||
const firstPartyApplicationsData = useSWR<[Application[], number], RequestError>(
|
||||
firstPartyApplicationsFetchUrl
|
||||
);
|
||||
|
||||
const thirdPartyApplicationsData = useSWR<[Application[], number], RequestError>(
|
||||
thirdPartyApplicationsFetchUrl
|
||||
);
|
||||
|
||||
const { data } = thirdPartyApplicationsData;
|
||||
const [_, totalCount] = data ?? [];
|
||||
const hasThirdPartyApplications = totalCount && totalCount > 0;
|
||||
|
||||
return {
|
||||
...(isThirdParty ? thirdPartyApplicationsData : firstPartyApplicationsData),
|
||||
pagination: {
|
||||
page: isThirdParty ? thirdPartyApplicationPage : firstPartyApplicationPage,
|
||||
pageSize,
|
||||
},
|
||||
paginationRecords: {
|
||||
firstPartyApplicationPage,
|
||||
thirdPartyApplicationPage,
|
||||
},
|
||||
showThirdPartyApplicationTab: hasThirdPartyApplications,
|
||||
updatePagination,
|
||||
};
|
||||
};
|
||||
|
||||
export default useApplicationsData;
|
|
@ -0,0 +1,60 @@
|
|||
import { type Application } from '@logto/schemas';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { defaultPageSize } from '@/consts';
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import { buildUrl } from '@/utils/url';
|
||||
|
||||
const pageSize = defaultPageSize;
|
||||
const applicationsEndpoint = 'api/applications';
|
||||
|
||||
/**
|
||||
* This hook is the forked version of useApplicationsData. @see {@link (./use-application-data.ts)}
|
||||
* But have all the third party application related request and code removed.
|
||||
* This will be applied directly on the current application page.
|
||||
* To prevent the third party api request and code from being triggered.
|
||||
*
|
||||
* We use the isDevFeatureEnabled to determine if we should use this legacy hook or the new one.
|
||||
* This hook will be removed once we have the third-party application feature ready for production.
|
||||
*/
|
||||
const useLegacyApplicationsData = () => {
|
||||
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const updatePagination = useCallback(
|
||||
(page: number) => {
|
||||
updateSearchParameters({ page });
|
||||
},
|
||||
[updateSearchParameters]
|
||||
);
|
||||
|
||||
const url = useMemo(
|
||||
() =>
|
||||
buildUrl(applicationsEndpoint, {
|
||||
page: String(page),
|
||||
page_size: String(pageSize),
|
||||
}),
|
||||
[page]
|
||||
);
|
||||
|
||||
const data = useSWR<[Application[], number], RequestError>(url);
|
||||
|
||||
return {
|
||||
...data,
|
||||
pagination: {
|
||||
page,
|
||||
pageSize,
|
||||
},
|
||||
paginationRecords: {
|
||||
firstPartyApplicationPage: page,
|
||||
thirdPartyApplicationPage: page,
|
||||
},
|
||||
showThirdPartyApplicationTab: false,
|
||||
updatePagination,
|
||||
};
|
||||
};
|
||||
|
||||
export default useLegacyApplicationsData;
|
|
@ -12,6 +12,10 @@
|
|||
width: 360px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
|
||||
.guideLibraryContainer {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react';
|
||||
import type { Application } from '@logto/schemas';
|
||||
import { joinPath } from '@silverhand/essentials';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Plus from '@/assets/icons/plus.svg';
|
||||
import ApplicationIcon from '@/components/ApplicationIcon';
|
||||
import ChargeNotification from '@/components/ChargeNotification';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import { defaultPageSize } from '@/consts';
|
||||
import { isCloud } from '@/consts/env';
|
||||
import { isDevFeaturesEnabled, isCloud } from '@/consts/env';
|
||||
import Button from '@/ds-components/Button';
|
||||
import CardTitle from '@/ds-components/CardTitle';
|
||||
import CopyToClipboard from '@/ds-components/CopyToClipboard';
|
||||
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
|
||||
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
|
||||
import Table from '@/ds-components/Table';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApplicationsUsage from '@/hooks/use-applications-usage';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import * as pageLayout from '@/scss/page-layout.module.scss';
|
||||
import { applicationTypeI18nKey } from '@/types/applications';
|
||||
|
@ -26,29 +23,53 @@ import { buildUrl } from '@/utils/url';
|
|||
|
||||
import GuideLibrary from './components/GuideLibrary';
|
||||
import GuideLibraryModal from './components/GuideLibraryModal';
|
||||
import useApplicationsData from './hooks/use-application-data';
|
||||
import useLegacyApplicationsData from './hooks/use-legacy-application-data';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const pageSize = defaultPageSize;
|
||||
const tabs = Object.freeze({
|
||||
thirdPartyApplications: 'third-party-applications',
|
||||
});
|
||||
|
||||
const applicationsPathname = '/applications';
|
||||
const createApplicationPathname = `${applicationsPathname}/create`;
|
||||
const buildDetailsPathname = (id: string) => `${applicationsPathname}/${id}`;
|
||||
|
||||
function Applications() {
|
||||
// Build the path with pagination query param for the tabs
|
||||
const buildTabPathWithPagePagination = (page: number, tab?: keyof typeof tabs) => {
|
||||
const pathname = tab
|
||||
? joinPath(applicationsPathname, tabs.thirdPartyApplications)
|
||||
: applicationsPathname;
|
||||
|
||||
return page > 1 ? buildUrl(pathname, { page: String(page) }) : pathname;
|
||||
};
|
||||
|
||||
// @simeng-li FIXME: Remove this when the third party applications is production ready
|
||||
const useApplicationDataHook = isDevFeaturesEnabled
|
||||
? useApplicationsData
|
||||
: useLegacyApplicationsData;
|
||||
|
||||
type Props = {
|
||||
tab?: keyof typeof tabs;
|
||||
};
|
||||
|
||||
function Applications({ tab }: Props) {
|
||||
const { search } = useLocation();
|
||||
const { match, navigate } = useTenantPathname();
|
||||
const isCreating = match(createApplicationPathname);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const isCreating = match(createApplicationPathname);
|
||||
const { hasMachineToMachineAppsSurpassedLimit } = useApplicationsUsage();
|
||||
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const url = buildUrl('api/applications', {
|
||||
page: String(page),
|
||||
page_size: String(pageSize),
|
||||
});
|
||||
|
||||
const { data, error, mutate } = useSWR<[Application[], number], RequestError>(url);
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
mutate,
|
||||
pagination,
|
||||
updatePagination,
|
||||
paginationRecords,
|
||||
showThirdPartyApplicationTab,
|
||||
} = useApplicationDataHook(tab === 'thirdPartyApplications');
|
||||
|
||||
const isLoading = !data && !error;
|
||||
const [applications, totalCount] = data ?? [];
|
||||
|
@ -81,6 +102,27 @@ function Applications() {
|
|||
checkedFlagKey="machineToMachineApp"
|
||||
/>
|
||||
)}
|
||||
|
||||
{showThirdPartyApplicationTab && (
|
||||
<TabNav className={styles.tabs}>
|
||||
<TabNavItem
|
||||
href={buildTabPathWithPagePagination(paginationRecords.firstPartyApplicationPage)}
|
||||
isActive={!tab}
|
||||
>
|
||||
{t('applications.tab.my_applications')}
|
||||
</TabNavItem>
|
||||
<TabNavItem
|
||||
href={buildTabPathWithPagePagination(
|
||||
paginationRecords.thirdPartyApplicationPage,
|
||||
'thirdPartyApplications'
|
||||
)}
|
||||
isActive={tab === 'thirdPartyApplications'}
|
||||
>
|
||||
{t('applications.tab.third_party_applications')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
)}
|
||||
|
||||
{!isLoading && !applications?.length && (
|
||||
<OverlayScrollbar className={styles.guideLibraryContainer}>
|
||||
<CardTitle
|
||||
|
@ -103,10 +145,14 @@ function Applications() {
|
|||
title: t('applications.application_name'),
|
||||
dataIndex: 'name',
|
||||
colSpan: 6,
|
||||
render: ({ id, name, type }) => (
|
||||
render: ({ id, name, type, isThirdParty }) => (
|
||||
<ItemPreview
|
||||
title={name}
|
||||
subtitle={t(`${applicationTypeI18nKey[type]}.title`)}
|
||||
subtitle={
|
||||
isThirdParty
|
||||
? t('applications.type.third_party.title')
|
||||
: t(`${applicationTypeI18nKey[type]}.title`)
|
||||
}
|
||||
icon={<ApplicationIcon className={styles.icon} type={type} />}
|
||||
to={buildDetailsPathname(id)}
|
||||
/>
|
||||
|
@ -123,12 +169,9 @@ function Applications() {
|
|||
navigate(buildDetailsPathname(id));
|
||||
}}
|
||||
pagination={{
|
||||
page,
|
||||
...pagination,
|
||||
totalCount,
|
||||
pageSize,
|
||||
onChange: (page) => {
|
||||
updateSearchParameters({ page });
|
||||
},
|
||||
onChange: updatePagination,
|
||||
}}
|
||||
onRetry={async () => mutate(undefined, true)}
|
||||
/>
|
||||
|
@ -136,10 +179,7 @@ function Applications() {
|
|||
<GuideLibraryModal
|
||||
isOpen={isCreating}
|
||||
onClose={() => {
|
||||
navigate({
|
||||
pathname: applicationsPathname,
|
||||
search,
|
||||
});
|
||||
navigate(-1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Wähle einen Anwendungstyp',
|
||||
no_application_type_selected: 'Du hast noch keinen Anwendungstyp ausgewählt',
|
||||
application_created: 'Die Anwendung wurde erfolgreich erstellt.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -35,6 +41,14 @@ const applications = {
|
|||
subtitle: 'Eine Anwendung (normalerweise ein Dienst), die direkt mit Ressourcen kommuniziert',
|
||||
description: 'z.B. Backend Dienst',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Wähle einen Anwendungstyp, um fortzufahren',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,10 @@ const applications = {
|
|||
select_application_type: 'Select an application type',
|
||||
no_application_type_selected: 'You haven’t selected any application type yet',
|
||||
application_created: 'Application created successfully.',
|
||||
tab: {
|
||||
my_applications: 'My apps',
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -34,6 +38,11 @@ const applications = {
|
|||
subtitle: 'An app (usually a service) that directly talks to resources',
|
||||
description: 'E.g., Backend service',
|
||||
},
|
||||
third_party: {
|
||||
title: 'Third-party app',
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Select an application type to continue',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Seleccionar un tipo de aplicación',
|
||||
no_application_type_selected: 'Aún no has seleccionado ningún tipo de aplicación',
|
||||
application_created: '¡La aplicación se ha creado correctamente.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -35,6 +41,14 @@ const applications = {
|
|||
subtitle: 'Una aplicación (generalmente un servicio) que habla directamente con recursos',
|
||||
description: 'Por ejemplo, servicio backend',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Selecciona un tipo de aplicación para continuar',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: "Sélectionner un type d'application",
|
||||
no_application_type_selected: "Vous n'avez pas encore sélectionné de type d'application",
|
||||
application_created: "L'application a été créée avec succès.",
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -36,6 +42,14 @@ const applications = {
|
|||
'Une application (généralement un service) qui communique directement avec les ressources',
|
||||
description: 'Par exemple, un service backend',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: "Sélectionnez un type d'application pour continuer",
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Seleziona un tipo di applicazione',
|
||||
no_application_type_selected: 'Non hai ancora selezionato alcun tipo di applicazione',
|
||||
application_created: "L'applicazione è stata creata con successo.",
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -35,6 +41,14 @@ const applications = {
|
|||
subtitle: "Un'app (solitamente un servizio) che comunica direttamente con le risorse",
|
||||
description: 'E.g., servizio backend',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Seleziona un tipo di applicazione per continuare',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'アプリケーションタイプを選択してください',
|
||||
no_application_type_selected: 'まだアプリケーションタイプが選択されていません',
|
||||
application_created: 'アプリケーションが正常に作成されました。',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -34,6 +40,14 @@ const applications = {
|
|||
subtitle: 'リソースに直接アクセスするアプリケーション(通常はサービス)',
|
||||
description: '例:バックエンドサービス',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: '続行するにはアプリケーションタイプを選択してください',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: '어플리케이션 종류 선택',
|
||||
no_application_type_selected: '어플리케이션 종류를 선택하지 않았어요.',
|
||||
application_created: '어플리케이션이 성공적으로 생성되었어요.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -34,6 +40,14 @@ const applications = {
|
|||
subtitle: '직접 리소스에 접근하는 엡(서비스)',
|
||||
description: '예) 백엔드 서비스',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: '애플리케이션 유형을 선택하여 계속하세요',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Wybierz typ aplikacji',
|
||||
no_application_type_selected: 'Nie wybrałeś jeszcze żadnego typu aplikacji',
|
||||
application_created: 'Aplikacja ostała pomyślnie utworzona.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -35,6 +41,14 @@ const applications = {
|
|||
subtitle: 'Aplikacja (zazwyczaj usługa), która bezpośrednio komunikuje się z zasobami',
|
||||
description: 'Na przykład usługa backendowa',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Wybierz typ aplikacji, aby kontynuować',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Selecione um tipo de aplicativo',
|
||||
no_application_type_selected: 'Você ainda não selecionou nenhum tipo de aplicativo',
|
||||
application_created: 'O aplicativo foi criado com sucesso.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -35,6 +41,14 @@ const applications = {
|
|||
subtitle: 'Um aplicativo (geralmente um serviço) que fala diretamente com os recursos',
|
||||
description: 'Ex: serviço de backend',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Selecione um tipo de aplicativo para continuar',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Selecione o tipo de aplicação',
|
||||
no_application_type_selected: 'Ainda não selecionou nenhum tipo de aplicação',
|
||||
application_created: 'A aplicação foi criada com sucesso.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -34,6 +40,14 @@ const applications = {
|
|||
subtitle: 'Uma aplicação (normalmente um serviço) que se comunica diretamente com recursos',
|
||||
description: 'Ex., serviço back-end',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Selecione um tipo de aplicação para continuar',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Выбрать тип приложения',
|
||||
no_application_type_selected: 'Вы еще не выбрали тип приложения',
|
||||
application_created: 'Приложение успешно создано.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -34,6 +40,14 @@ const applications = {
|
|||
subtitle: 'Приложение (обычно сервис), которое напрямую общается с ресурсами',
|
||||
description: 'Например, backend-сервис',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Выберите тип приложения, чтобы продолжить',
|
||||
placeholder_description:
|
||||
|
|
|
@ -12,6 +12,12 @@ const applications = {
|
|||
select_application_type: 'Uygulama tipi seçiniz',
|
||||
no_application_type_selected: 'Henüz bir uygulama tipi seçmediniz',
|
||||
application_created: 'Uygulaması başarıyla oluşturuldu.',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -35,6 +41,14 @@ const applications = {
|
|||
subtitle: 'Kaynaklarla doğrudan iletişim kuran bir uygulama (genellikle bir servis)',
|
||||
description: 'Örneğin, Backend servisi',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: 'Devam etmek için bir uygulama tipi seçin',
|
||||
placeholder_description:
|
||||
|
|
|
@ -11,6 +11,12 @@ const applications = {
|
|||
select_application_type: '选择应用类型',
|
||||
no_application_type_selected: '你还没有选择应用类型',
|
||||
application_created: '创建应用成功。',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -33,6 +39,14 @@ const applications = {
|
|||
subtitle: '直接与资源对话的应用程序(通常是服务)',
|
||||
description: '例如,后端服务',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: '选择应用程序类型以继续',
|
||||
placeholder_description:
|
||||
|
|
|
@ -11,6 +11,12 @@ const applications = {
|
|||
select_application_type: '選擇應用類型',
|
||||
no_application_type_selected: '你還沒有選擇應用類型',
|
||||
application_created: '應用創建成功。',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -33,6 +39,14 @@ const applications = {
|
|||
subtitle: '直接與資源對話的應用程序(通常是服務)',
|
||||
description: '例如,後端服務',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: '選擇應用程序類型以繼續',
|
||||
placeholder_description:
|
||||
|
|
|
@ -11,6 +11,12 @@ const applications = {
|
|||
select_application_type: '選擇應用類型',
|
||||
no_application_type_selected: '你還沒有選擇應用類型',
|
||||
application_created: '應用創建成功。',
|
||||
tab: {
|
||||
/** UNTRANSLATED */
|
||||
my_applications: 'My apps',
|
||||
/** UNTRANSLATED */
|
||||
third_party_applications: 'Third party apps',
|
||||
},
|
||||
app_id: 'App ID',
|
||||
type: {
|
||||
native: {
|
||||
|
@ -33,6 +39,14 @@ const applications = {
|
|||
subtitle: '直接與資源對話的應用程序(通常是服務)',
|
||||
description: '例如,後端服務',
|
||||
},
|
||||
third_party: {
|
||||
/** UNTRANSLATED */
|
||||
title: 'Third-party app',
|
||||
/** UNTRANSLATED */
|
||||
subtitle: 'An app that is used as a third-party IdP connector',
|
||||
/** UNTRANSLATED */
|
||||
description: 'E.g., OIDC, SAML',
|
||||
},
|
||||
},
|
||||
placeholder_title: '選擇應用程序類型以繼續',
|
||||
placeholder_description:
|
||||
|
|
Loading…
Add table
Reference in a new issue