0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-24 22:41:28 -05:00

refactor(console,phrases,core): add roles and logs tabs for m2m app details page (#4548)

This commit is contained in:
Darcy Ye 2023-09-21 16:54:17 +08:00 committed by GitHub
parent fc252b56f0
commit 352ca03177
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 961 additions and 56 deletions

View file

@ -1,5 +1,5 @@
import type { Log } from '@logto/schemas';
import { LogResult } from '@logto/schemas';
import type { Log, ApplicationResponse } from '@logto/schemas';
import { LogResult, ApplicationType } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr';
@ -27,24 +27,33 @@ const auditLogEventOptions = Object.entries(auditLogEventTitle).map(([value, tit
}));
type Props = {
applicationId?: string;
userId?: string;
className?: string;
};
function AuditLogTable({ userId, className }: Props) {
function AuditLogTable({ applicationId, userId, className }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const pageSize = defaultPageSize;
const [{ page, event, applicationId }, updateSearchParameters] = useSearchParametersWatcher({
page: 1,
event: '',
applicationId: '',
});
const [{ page, event, applicationId: applicationIdFromSearch }, updateSearchParameters] =
useSearchParametersWatcher({
page: 1,
event: '',
...conditional(applicationId && { applicationId: '' }),
});
// TODO: LOG-7135, revisit this fallback logic and see whether this should be done outside of this component.
const searchApplicationId = applicationId ?? applicationIdFromSearch;
const { data: specifiedApplication } = useSWR<ApplicationResponse>(
applicationId && `api/applications/${applicationId}`
);
const url = buildUrl('api/logs', {
page: String(page),
page_size: String(pageSize),
...conditional(event && { logKey: event }),
...conditional(applicationId && { applicationId }),
...conditional(searchApplicationId && { applicationId: searchApplicationId }),
...conditional(userId && { userId }),
});
@ -52,7 +61,8 @@ function AuditLogTable({ userId, className }: Props) {
const isLoading = !data && !error;
const { navigate } = useTenantPathname();
const [logs, totalCount] = data ?? [];
const isUserColumnVisible = !userId;
const isUserColumnVisible =
!userId && specifiedApplication?.type !== ApplicationType.MachineToMachine;
const eventColumn: Column<Log> = {
title: t('logs.event'),
@ -114,14 +124,16 @@ function AuditLogTable({ userId, className }: Props) {
}}
/>
</div>
<div className={styles.applicationSelector}>
<ApplicationSelector
value={applicationId}
onChange={(applicationId) => {
updateSearchParameters({ applicationId, page: undefined });
}}
/>
</div>
{!applicationId && (
<div className={styles.applicationSelector}>
<ApplicationSelector
value={applicationId}
onChange={(applicationId) => {
updateSearchParameters({ applicationId, page: undefined });
}}
/>
</div>
)}
</div>
}
placeholder={<EmptyDataPlaceholder />}

View file

@ -1,4 +1,5 @@
import type { RoleResponse } from '@logto/schemas';
import { RoleType } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import classNames from 'classnames';
import type { ChangeEvent } from 'react';
@ -21,14 +22,15 @@ import SourceRoleItem from '../SourceRoleItem';
import * as styles from './index.module.scss';
type Props = {
userId: string;
entityId: string;
type: RoleType;
selectedRoles: RoleResponse[];
onChange: (value: RoleResponse[]) => void;
};
const pageSize = defaultPageSize;
function SourceRolesBox({ userId, selectedRoles, onChange }: Props) {
function SourceRolesBox({ entityId, type, selectedRoles, onChange }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [page, setPage] = useState(1);
@ -37,9 +39,11 @@ function SourceRolesBox({ userId, selectedRoles, onChange }: Props) {
const debounce = useDebounce();
const url = buildUrl('api/roles', {
excludeUserId: userId,
page: String(page),
page_size: String(pageSize),
'search.type': type,
'mode.type': 'exact',
[type === RoleType.User ? 'excludeUserId' : 'excludeApplicationId']: entityId,
...conditional(keyword && { search: `%${keyword}%` }),
});
@ -75,7 +79,14 @@ function SourceRolesBox({ userId, selectedRoles, onChange }: Props) {
className={classNames(transferLayout.boxContent, isEmpty && transferLayout.emptyBoxContent)}
>
{isEmpty ? (
<EmptyDataPlaceholder size="small" title={t('user_details.roles.empty')} />
<EmptyDataPlaceholder
size="small"
title={t(
type === RoleType.User
? 'user_details.roles.empty'
: 'application_details.roles.empty'
)}
/>
) : (
dataSource.map((role) => {
const isSelected = isRoleSelected(role);

View file

@ -1,4 +1,4 @@
import type { RoleResponse } from '@logto/schemas';
import type { RoleResponse, RoleType } from '@logto/schemas';
import classNames from 'classnames';
import * as transferLayout from '@/scss/transfer.module.scss';
@ -8,19 +8,20 @@ import TargetRolesBox from './components/TargetRolesBox';
import * as styles from './index.module.scss';
type Props = {
userId: string;
entityId: string;
type: RoleType;
value: RoleResponse[];
onChange: (value: RoleResponse[]) => void;
};
function UserRolesTransfer({ userId, value, onChange }: Props) {
function RolesTransfer({ entityId, type, value, onChange }: Props) {
return (
<div className={classNames(transferLayout.container, styles.rolesTransfer)}>
<SourceRolesBox userId={userId} selectedRoles={value} onChange={onChange} />
<SourceRolesBox entityId={entityId} type={type} selectedRoles={value} onChange={onChange} />
<div className={transferLayout.verticalBar} />
<TargetRolesBox selectedRoles={value} onChange={onChange} />
</div>
);
}
export default UserRolesTransfer;
export default RolesTransfer;

View file

@ -1,6 +1,8 @@
export enum ApplicationDetailsTabs {
Settings = 'settings',
AdvancedSettings = 'advanced-settings',
Roles = 'roles',
Logs = 'logs',
}
export enum ApiResourceDetailsTabs {

View file

@ -0,0 +1,7 @@
@use '@/scss/underscore' as _;
.logs {
flex: 1;
margin-bottom: _.unit(6);
overflow-y: auto;
}

View file

@ -0,0 +1,11 @@
import AuditLogTable from '@/components/AuditLogTable';
import * as styles from './index.module.scss';
type Props = { applicationId: string };
function MachineLogs({ applicationId }: Props) {
return <AuditLogTable applicationId={applicationId} className={styles.logs} />;
}
export default MachineLogs;

View file

@ -0,0 +1,26 @@
@use '@/scss/underscore' as _;
.rolesTable {
flex: 1;
margin-bottom: _.unit(6);
color: var(--color-text);
.filter {
display: flex;
justify-content: space-between;
align-items: center;
.searchInput {
width: 306px;
}
}
.name {
display: block;
@include _.text-ellipsis;
}
.description {
@include _.text-ellipsis;
}
}

View file

@ -0,0 +1,183 @@
import type { Application, Role } from '@logto/schemas';
import { RoleType } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr';
import Delete from '@/assets/icons/delete.svg';
import Plus from '@/assets/icons/plus.svg';
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
import { defaultPageSize } from '@/consts';
import Button from '@/ds-components/Button';
import ConfirmModal from '@/ds-components/ConfirmModal';
import IconButton from '@/ds-components/IconButton';
import Search from '@/ds-components/Search';
import Table from '@/ds-components/Table';
import TextLink from '@/ds-components/TextLink';
import { Tooltip } from '@/ds-components/Tip';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
import AssignToRoleModal from '@/pages/Roles/components/AssignToRoleModal';
import { buildUrl, formatSearchKeyword } from '@/utils/url';
import * as styles from './index.module.scss';
const pageSize = defaultPageSize;
type Props = {
application: Application;
};
function MachineToMachineApplicationRoles({ application }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [{ page, keyword }, updateSearchParameters] = useSearchParametersWatcher({
page: 1,
keyword: '',
});
const { data, error, mutate } = useSWR<[Role[], number], RequestError>(
buildUrl(`api/applications/${application.id}/roles`, {
page: String(page),
page_size: String(pageSize),
...conditional(keyword && { search: formatSearchKeyword(keyword) }),
})
);
const isLoading = !data && !error;
const [roles, totalCount] = data ?? [];
const [isAssignRolesModalOpen, setIsAssignRolesModalOpen] = useState(false);
const [roleToBeDeleted, setRoleToBeDeleted] = useState<Role>();
const [isDeleting, setIsDeleting] = useState(false);
const api = useApi();
const handleDelete = async () => {
if (!roleToBeDeleted || isDeleting) {
return;
}
setIsDeleting(true);
try {
await api.delete(`api/applications/${application.id}/roles/${roleToBeDeleted.id}`);
toast.success(t('application_details.roles.deleted', { name: roleToBeDeleted.name }));
await mutate();
setRoleToBeDeleted(undefined);
} finally {
setIsDeleting(false);
}
};
return (
<>
<Table
className={styles.rolesTable}
isLoading={isLoading}
rowGroups={[{ key: 'roles', data: roles }]}
rowIndexKey="id"
columns={[
{
title: t('application_details.roles.name_column'),
dataIndex: 'name',
colSpan: 6,
render: ({ id, name }) => (
<TextLink className={styles.name} to={`/roles/${id}`}>
{name}
</TextLink>
),
},
{
title: t('application_details.roles.description_column'),
dataIndex: 'description',
colSpan: 9,
render: ({ description }) => <div className={styles.description}>{description}</div>,
},
{
title: null,
dataIndex: 'delete',
colSpan: 1,
render: (role) => (
<Tooltip content={t('general.remove')}>
<IconButton
onClick={() => {
setRoleToBeDeleted(role);
}}
>
<Delete />
</IconButton>
</Tooltip>
),
},
]}
filter={
<div className={styles.filter}>
<Search
inputClassName={styles.searchInput}
defaultValue={keyword}
isClearable={Boolean(keyword)}
placeholder={t('application_details.roles.search')}
onSearch={(keyword) => {
updateSearchParameters({ keyword, page: 1 });
}}
onClearSearch={() => {
updateSearchParameters({ keyword: '', page: 1 });
}}
/>
<Button
title="application_details.roles.assign_button"
type="primary"
size="large"
icon={<Plus />}
onClick={() => {
setIsAssignRolesModalOpen(true);
}}
/>
</div>
}
pagination={{
page,
pageSize,
totalCount,
onChange: (page) => {
updateSearchParameters({ page });
},
}}
placeholder={<EmptyDataPlaceholder />}
errorMessage={error?.body?.message ?? error?.message}
onRetry={async () => mutate(undefined, true)}
/>
{roleToBeDeleted && (
<ConfirmModal
isOpen
isLoading={isDeleting}
confirmButtonText="general.remove"
onCancel={() => {
setRoleToBeDeleted(undefined);
}}
onConfirm={handleDelete}
>
{t('application_details.roles.delete_description')}
</ConfirmModal>
)}
{isAssignRolesModalOpen && (
<AssignToRoleModal
entity={application}
type={RoleType.MachineToMachine}
onClose={(success) => {
if (success) {
void mutate();
}
setIsAssignRolesModalOpen(false);
}}
/>
)}
</>
);
}
export default MachineToMachineApplicationRoles;

View file

@ -4,6 +4,7 @@ import {
type ApplicationResponse,
type SnakeCaseOidcConfig,
customClientMetadataDefault,
ApplicationType,
} from '@logto/schemas';
import { useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
@ -21,6 +22,7 @@ import Drawer from '@/components/Drawer';
import PageMeta from '@/components/PageMeta';
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import { ApplicationDetailsTabs } from '@/consts';
import { isDevFeaturesEnabled } from '@/consts/env';
import { openIdProviderConfigPath } from '@/consts/oidc';
import ActionMenu, { ActionMenuItem } from '@/ds-components/ActionMenu';
import Button from '@/ds-components/Button';
@ -39,6 +41,8 @@ import { trySubmitSafe } from '@/utils/form';
import AdvancedSettings from './components/AdvancedSettings';
import GuideDrawer from './components/GuideDrawer';
import GuideModal from './components/GuideModal';
import MachineLogs from './components/MachineLogs';
import MachineToMachineApplicationRoles from './components/MachineToMachineApplicationRoles';
import Settings from './components/Settings';
import * as styles from './index.module.scss';
@ -234,6 +238,18 @@ function ApplicationDetails() {
>
{t('application_details.advanced_settings')}
</TabNavItem>
{isDevFeaturesEnabled && (
<>
{data.type === ApplicationType.MachineToMachine && (
<TabNavItem href={`/applications/${data.id}/${ApplicationDetailsTabs.Roles}`}>
{t('application_details.application_roles')}
</TabNavItem>
)}
<TabNavItem href={`/applications/${data.id}/${ApplicationDetailsTabs.Logs}`}>
{t('application_details.machine_logs')}
</TabNavItem>
</>
)}
</TabNav>
<FormProvider {...formMethods}>
<DetailsForm
@ -248,6 +264,18 @@ function ApplicationDetails() {
<TabWrapper isActive={tab === ApplicationDetailsTabs.AdvancedSettings}>
<AdvancedSettings app={data} oidcConfig={oidcConfig} />
</TabWrapper>
{isDevFeaturesEnabled && (
<>
{data.type === ApplicationType.MachineToMachine && (
<TabWrapper isActive={tab === ApplicationDetailsTabs.Roles}>
<MachineToMachineApplicationRoles application={data} />
</TabWrapper>
)}
<TabWrapper isActive={tab === ApplicationDetailsTabs.Logs}>
<MachineLogs applicationId={data.id} />
</TabWrapper>
</>
)}
</DetailsForm>
</FormProvider>
</>

View file

@ -1,10 +1,11 @@
import type { RoleResponse, User } from '@logto/schemas';
import type { RoleResponse, User, Application } from '@logto/schemas';
import { RoleType } from '@logto/schemas';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import UserRolesTransfer from '@/components/UserRolesTransfer';
import RolesTransfer from '@/components/RolesTransfer';
import Button from '@/ds-components/Button';
import DangerousRaw from '@/ds-components/DangerousRaw';
import ModalLayout from '@/ds-components/ModalLayout';
@ -12,16 +13,21 @@ import useApi from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import { getUserTitle } from '@/utils/user';
type Props = {
user: User;
onClose: (success?: boolean) => void;
};
type Props =
| {
entity: User;
onClose: (success?: boolean) => void;
type: RoleType.User;
}
| {
entity: Application;
onClose: (success?: boolean) => void;
type: RoleType.MachineToMachine;
};
function AssignRolesModal({ user, onClose }: Props) {
function AssignToRoleModal({ entity, onClose, type }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const userName = getUserTitle(user);
const [isSubmitting, setIsSubmitting] = useState(false);
const [roles, setRoles] = useState<RoleResponse[]>([]);
@ -35,9 +41,12 @@ function AssignRolesModal({ user, onClose }: Props) {
setIsSubmitting(true);
try {
await api.post(`api/users/${user.id}/roles`, {
json: { roleIds: roles.map(({ id }) => id) },
});
await api.post(
`api/${type === RoleType.User ? 'users' : 'applications'}/${entity.id}/roles`,
{
json: { roleIds: roles.map(({ id }) => id) },
}
);
toast.success(t('user_details.roles.role_assigned'));
onClose(true);
} finally {
@ -57,10 +66,24 @@ function AssignRolesModal({ user, onClose }: Props) {
>
<ModalLayout
title={
<DangerousRaw>{t('user_details.roles.assign_title', { name: userName })}</DangerousRaw>
<DangerousRaw>
{t(
type === RoleType.User
? 'user_details.roles.assign_title'
: 'application_details.roles.assign_title',
{ name: type === RoleType.User ? getUserTitle(entity) : entity.name }
)}
</DangerousRaw>
}
subtitle={
<DangerousRaw>{t('user_details.roles.assign_subtitle', { name: userName })}</DangerousRaw>
<DangerousRaw>
{t(
type === RoleType.User
? 'user_details.roles.assign_subtitle'
: 'application_details.roles.assign_subtitle',
{ name: type === RoleType.User ? getUserTitle(entity) : entity.name }
)}
</DangerousRaw>
}
size="large"
footer={
@ -68,7 +91,11 @@ function AssignRolesModal({ user, onClose }: Props) {
isLoading={isSubmitting}
disabled={roles.length === 0}
htmlType="submit"
title="user_details.roles.confirm_assign"
title={
type === RoleType.User
? 'user_details.roles.confirm_assign'
: 'application_details.roles.confirm_assign'
}
size="large"
type="primary"
onClick={handleAssign}
@ -76,8 +103,9 @@ function AssignRolesModal({ user, onClose }: Props) {
}
onClose={onClose}
>
<UserRolesTransfer
userId={user.id}
<RolesTransfer
entityId={entity.id}
type={type}
value={roles}
onChange={(value) => {
setRoles(value);
@ -88,4 +116,4 @@ function AssignRolesModal({ user, onClose }: Props) {
);
}
export default AssignRolesModal;
export default AssignToRoleModal;

View file

@ -1,4 +1,5 @@
import type { Role } from '@logto/schemas';
import { RoleType } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useState } from 'react';
import { toast } from 'react-hot-toast';
@ -20,11 +21,11 @@ import { Tooltip } from '@/ds-components/Tip';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
import AssignToRoleModal from '@/pages/Roles/components/AssignToRoleModal';
import { buildUrl, formatSearchKeyword } from '@/utils/url';
import type { UserDetailsOutletContext } from '../types';
import AssignRolesModal from './components/AssignRolesModal';
import * as styles from './index.module.scss';
const pageSize = defaultPageSize;
@ -166,8 +167,9 @@ function UserRoles() {
</ConfirmModal>
)}
{isAssignRolesModalOpen && (
<AssignRolesModal
user={user}
<AssignToRoleModal
entity={user}
type={RoleType.User}
onClose={(success) => {
if (success) {
void mutate();

View file

@ -4,6 +4,7 @@ import type { OmitAutoSetFields } from '@logto/shared';
import { conditionalArraySql, conditionalSql, convertToIdentifiers } from '@logto/shared';
import type { CommonQueryMethods } from 'slonik';
import { sql } from 'slonik';
import { snakeCase } from 'snake-case';
import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
@ -16,11 +17,19 @@ const { table, fields } = convertToIdentifiers(Roles);
const buildRoleConditions = (search: Search) => {
const hasSearch = search.matches.length > 0;
const searchFields = [Roles.fields.id, Roles.fields.name, Roles.fields.description];
const searchFields = [
Roles.fields.id,
Roles.fields.name,
Roles.fields.description,
Roles.fields.type,
];
return conditionalSql(
hasSearch,
() => sql`and ${buildConditionsFromSearch(search, searchFields)}`
() =>
sql`and ${buildConditionsFromSearch(search, searchFields, {
[Roles.fields.type]: snakeCase('RoleType'),
})}`
);
};

View file

@ -36,7 +36,11 @@ export default function roleRoutes<T extends AuthedRouter>(...[router, tenant]:
users: { findUsersByIds },
usersRoles: { countUsersRolesByRoleId, findUsersRolesByRoleId, findUsersRolesByUserId },
applications: { findApplicationsByIds },
applicationsRoles: { countApplicationsRolesByRoleId, findApplicationsRolesByRoleId },
applicationsRoles: {
countApplicationsRolesByRoleId,
findApplicationsRolesByRoleId,
findApplicationsRolesByApplicationId,
},
} = queries;
router.use('/roles(/.*)?', koaRoleRlsErrorHandler());
@ -75,9 +79,19 @@ export default function roleRoutes<T extends AuthedRouter>(...[router, tenant]:
return tryThat(
async () => {
const search = parseSearchParamsForSearch(searchParams);
const excludeUserId = searchParams.get('excludeUserId');
const usersRoles = excludeUserId ? await findUsersRolesByUserId(excludeUserId) : [];
const excludeRoleIds = usersRoles.map(({ roleId }) => roleId);
const excludeApplicationId = searchParams.get('excludeApplicationId');
const applicationsRoles = excludeApplicationId
? await findApplicationsRolesByApplicationId(excludeApplicationId)
: [];
const excludeRoleIds = [
...usersRoles.map(({ roleId }) => roleId),
...applicationsRoles.map(({ roleId }) => roleId),
];
const [{ count }, roles] = await Promise.all([
countRoles(search, { excludeRoleIds }),

View file

@ -5,6 +5,8 @@ import { generateRoleName } from '#src/utils.js';
import { authedAdminApi } from './api.js';
export type GetRoleOptions = { excludeUserId?: string; excludeApplicationId?: string };
export const createRole = async ({
name,
description,
@ -27,7 +29,8 @@ export const createRole = async ({
})
.json<Role>();
export const getRoles = async () => authedAdminApi.get('roles').json<Role[]>();
export const getRoles = async (options?: GetRoleOptions) =>
authedAdminApi.get('roles', { searchParams: new URLSearchParams(options) }).json<Role[]>();
export const getRole = async (roleId: string) => authedAdminApi.get(`roles/${roleId}`).json<Role>();

View file

@ -7,11 +7,12 @@ import {
assignApplicationsToRole,
createRole,
deleteApplicationFromRole,
getRoles,
getRoleApplications,
} from '#src/api/role.js';
describe('roles applications', () => {
it('should get role applications successfully', async () => {
it('should get role applications successfully and get roles correctly (specifying exclude application)', async () => {
const role = await createRole({ type: RoleType.MachineToMachine });
const m2mApp = await createApplication(generateStandardId(), ApplicationType.MachineToMachine);
await assignApplicationsToRole([m2mApp.id], role.id);
@ -19,6 +20,9 @@ describe('roles applications', () => {
expect(applications.length).toBe(1);
expect(applications[0]).toHaveProperty('id', m2mApp.id);
const allRolesWithoutAppsRoles = await getRoles({ excludeApplicationId: m2mApp.id });
expect(allRolesWithoutAppsRoles.find(({ id }) => id === role.id)).toBeUndefined();
});
it('should return 404 if role not found', async () => {

View file

@ -2,12 +2,18 @@ import { RoleType } from '@logto/schemas';
import { HTTPError } from 'got';
import { createUser } from '#src/api/index.js';
import { assignUsersToRole, createRole, deleteUserFromRole, getRoleUsers } from '#src/api/role.js';
import {
assignUsersToRole,
createRole,
deleteUserFromRole,
getRoles,
getRoleUsers,
} from '#src/api/role.js';
import { expectRejects } from '#src/helpers/index.js';
import { generateNewUserProfile } from '#src/helpers/user.js';
describe('roles users', () => {
it('should get role users successfully', async () => {
it('should get role users successfully and can get roles correctly (specifying exclude user)', async () => {
const role = await createRole({});
const user = await createUser(generateNewUserProfile({}));
await assignUsersToRole([user.id], role.id);
@ -15,6 +21,9 @@ describe('roles users', () => {
expect(users.length).toBe(1);
expect(users[0]).toHaveProperty('id', user.id);
const allRolesWithoutUsersRoles = await getRoles({ excludeUserId: user.id });
expect(allRolesWithoutUsersRoles.find(({ id }) => id === role.id)).toBeUndefined();
});
it('should return 404 if role not found', async () => {

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Erweiterte Einstellungen',
advanced_settings_description:
'Erweiterte Einstellungen beinhalten OIDC-bezogene Begriffe. Sie können den Token-Endpunkt für weitere Informationen überprüfen.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Anwendungsname',
application_name_placeholder: 'Meine App',
description: 'Beschreibung',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Gib einen Anwendungsnamen ein',
application_deleted: 'Anwendung {{name}} wurde erfolgreich gelöscht',
redirect_uri_required: 'Gib mindestens eine Umleitungs-URI an',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Advanced Settings',
advanced_settings_description:
'Advanced settings include OIDC related terms. You can check out the Token Endpoint for more information.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Application name',
application_name_placeholder: 'My App',
description: 'Description',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Enter your application name',
application_deleted: 'Application {{name}} has been successfully deleted',
redirect_uri_required: 'You must enter at least one redirect URI',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Configuraciones Avanzadas',
advanced_settings_description:
'Las configuraciones avanzadas incluyen términos relacionados con OIDC. Puedes revisar el Endpoint del Token para obtener más información.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Nombre de Aplicación',
application_name_placeholder: 'Mi App',
description: 'Descripción',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Ingresa el nombre de tu aplicación',
application_deleted: 'Se ha eliminado exitosamente la aplicación {{name}}',
redirect_uri_required: 'Debes ingresar al menos un URI de Redireccionamiento',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Paramètres avancés',
advanced_settings_description:
"Les paramètres avancés comprennent des termes liés à OIDC. Vous pouvez consulter le point de terminaison de jeton pour plus d'informations.",
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: "Nom de l'application",
application_name_placeholder: 'Mon App',
description: 'Description',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Entrez le nom de votre application',
application_deleted: "L'application {{name}} a été supprimée avec succès.",
redirect_uri_required: 'Vous devez entrer au moins un URI de redirection.',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Impostazioni avanzate',
advanced_settings_description:
"Le impostazioni avanzate includono termini correlati all'OIDC. Puoi consultare il Endpoint Token per ulteriori informazioni.",
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: "Nome dell'applicazione",
application_name_placeholder: 'La mia app',
description: 'Descrizione',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Inserisci il nome della tua applicazione',
application_deleted: "L'applicazione {{name}} è stata eliminata con successo",
redirect_uri_required: 'Devi inserire almeno un URI di reindirizzamento',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: '高度な設定',
advanced_settings_description:
'高度な設定にはOIDC関連用語が含まれます。詳細については、トークンエンドポイントを確認してください。',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'アプリケーション名',
application_name_placeholder: '私のアプリ',
description: '説明',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'アプリケーション名を入力してください',
application_deleted: 'アプリケーション{{name}}が正常に削除されました',
redirect_uri_required: 'リダイレクトURIを少なくとも1つ入力する必要があります',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: '고급 설정',
advanced_settings_description:
'고급 설정에는 OIDC 관련 용어가 포함돼요. 자세한 내용은 토큰 엔드포인트에서 확인할 수 있어요.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: '어플리케이션 이름',
application_name_placeholder: '나의 앱',
description: '설명',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: '어플리케이션 이름을 입력해 주세요.',
application_deleted: '{{name}} 어플리케이션이 성공적으로 삭제되었어요.',
redirect_uri_required: '반드시 최소 하나의 Redirect URI 를 입력해야 해요.',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Zaawansowane ustawienia',
advanced_settings_description:
'Zaawansowane ustawienia obejmują związane z OIDC terminy. Możesz sprawdzić punkt końcowy Token dla bardziej szczegółowych informacji.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Nazwa aplikacji',
application_name_placeholder: 'Moja aplikacja',
description: 'Opis',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Wpisz nazwę swojej aplikacji',
application_deleted: 'Aplikacja {{name}} została pomyślnie usunięta',
redirect_uri_required: 'Musisz wpisać co najmniej jeden adres URL przekierowania',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Configurações avançadas',
advanced_settings_description:
'As configurações avançadas incluem termos relacionados ao OIDC. Você pode conferir o Token Endpoint para obter mais informações.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Nome do aplicativo',
application_name_placeholder: 'Meu aplicativo',
description: 'Descrição',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Digite o nome do seu aplicativo',
application_deleted: 'O aplicativo {{name}} foi excluído com sucesso',
redirect_uri_required: 'Você deve inserir pelo menos um URI de redirecionamento',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Configurações avançadas',
advanced_settings_description:
'As configurações avançadas incluem termos relacionados com OIDC. Pode consultar o Endpoint do Token para obter mais informações.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Nome da aplicação',
application_name_placeholder: 'Ex: Site da Empresa',
description: 'Descrição',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Insira o nome da aplicação',
application_deleted: 'Aplicação {{name}} eliminada com sucesso',
redirect_uri_required: 'Deve inserir pelo menos um URI de redirecionamento',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Расширенные настройки',
advanced_settings_description:
'Расширенные настройки включают связанные с OIDC термины. Вы можете проверить конечную точку токена для получения дополнительной информации.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Название приложения',
application_name_placeholder: 'Мое приложение',
description: 'Описание',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Введите название своего приложения',
application_deleted: 'Приложение {{name}} успешно удалено',
redirect_uri_required: 'Вы должны ввести по крайней мере один URI перенаправления',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -8,6 +8,10 @@ const application_details = {
advanced_settings: 'Gelişmiş Ayarlar',
advanced_settings_description:
'Gelişmiş ayarlar, OIDC ile ilgili terimleri içerir. Daha fazla bilgi için Token Bitiş Noktasına bakabilirsiniz.',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: 'Uygulama Adı',
application_name_placeholder: 'Uygulamam',
description: 'Açıklama',
@ -55,6 +59,39 @@ const application_details = {
enter_your_application_name: 'Uygulama adı giriniz',
application_deleted: '{{name}} Uygulaması başarıyla silindi',
redirect_uri_required: 'En az 1 yönlendirme URIı girmelisiniz',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -7,6 +7,10 @@ const application_details = {
advanced_settings: '高级设置',
advanced_settings_description:
'高级设置包括 OIDC 相关术语。你可以查看 Token Endpoint 以获取更多信息。',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: '应用名称',
application_name_placeholder: '我的应用',
description: '描述',
@ -52,6 +56,39 @@ const application_details = {
enter_your_application_name: '输入你的应用名称',
application_deleted: '应用 {{name}} 成功删除。',
redirect_uri_required: '至少需要输入一个重定向 URI。',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -7,6 +7,10 @@ const application_details = {
advanced_settings: '高級設定',
advanced_settings_description:
'高級設定包括 OIDC 相關術語。你可以查看 Token Endpoint 以獲取更多資訊。',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: '應用程式名稱',
application_name_placeholder: '我的應用程式',
description: '描述',
@ -52,6 +56,39 @@ const application_details = {
enter_your_application_name: '輸入你的應用程式名稱',
application_deleted: '應用 {{name}} 成功刪除。',
redirect_uri_required: '至少需要輸入一個重定向 URL。',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);

View file

@ -7,6 +7,10 @@ const application_details = {
advanced_settings: '高級設置',
advanced_settings_description:
'高級設置包括 OIDC 相關術語。你可以查看 Token Endpoint 以獲取更多信息。',
/** UNTRANSLATED */
application_roles: 'Roles',
/** UNTRANSLATED */
machine_logs: 'Machine logs',
application_name: '應用程式姓名',
application_name_placeholder: '我的應用程式',
description: '說明',
@ -53,6 +57,39 @@ const application_details = {
enter_your_application_name: '輸入你的應用程式姓名',
application_deleted: '應用 {{name}} 成功刪除。',
redirect_uri_required: '至少需要輸入一個重定向 URL。',
roles: {
/** UNTRANSLATED */
name_column: 'Role',
/** UNTRANSLATED */
description_column: 'Description',
/** UNTRANSLATED */
assign_button: 'Assign Roles',
/** UNTRANSLATED */
delete_description:
'This action will remove this role from this user. The role itself will still exist, but it will no longer be associated with this user.',
/** UNTRANSLATED */
deleted: '{{name}} was successfully removed from this user.',
/** UNTRANSLATED */
assign_title: 'Assign roles to {{name}}',
/** UNTRANSLATED */
assign_subtitle: 'Authorize {{name}} one or more roles',
/** UNTRANSLATED */
assign_role_field: 'Assign roles',
/** UNTRANSLATED */
role_search_placeholder: 'Search by role name',
/** UNTRANSLATED */
added_text: '{{value, number}} added',
/** UNTRANSLATED */
assigned_user_count: '{{value, number}} users',
/** UNTRANSLATED */
confirm_assign: 'Assign roles',
/** UNTRANSLATED */
role_assigned: 'Successfully assigned role(s)',
/** UNTRANSLATED */
search: 'Search by role name, description or ID',
/** UNTRANSLATED */
empty: 'No role available',
},
};
export default Object.freeze(application_details);