0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

fix: fixup

This commit is contained in:
Charles Zhao 2024-03-12 19:43:31 +08:00
parent c1eec1c80f
commit 9a733bfbaa
No known key found for this signature in database
GPG key ID: 4858774754C92DF2
22 changed files with 660 additions and 29 deletions

View file

@ -1,13 +1,15 @@
import type router from '@logto/cloud/routes'; import type router from '@logto/cloud/routes';
import { type tenantAuthRouter } from '@logto/cloud/routes'; import { type tenantAuthRouter } from '@logto/cloud/routes';
import { useLogto } from '@logto/react'; import { useLogto } from '@logto/react';
import { getTenantOrganizationId } from '@logto/schemas';
import { conditional, trySafe } from '@silverhand/essentials'; import { conditional, trySafe } from '@silverhand/essentials';
import Client, { ResponseError } from '@withtyped/client'; import Client, { ResponseError } from '@withtyped/client';
import { useMemo } from 'react'; import { useContext, useMemo } from 'react';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
import { z } from 'zod'; import { z } from 'zod';
import { cloudApi } from '@/consts'; import { cloudApi } from '@/consts';
import { TenantsContext } from '@/contexts/TenantsProvider';
const responseErrorBodyGuard = z.object({ const responseErrorBodyGuard = z.object({
message: z.string(), message: z.string(),
@ -59,25 +61,32 @@ export const useCloudApi = ({ hideErrorToast = false }: UseCloudApiProps = {}):
return api; return api;
}; };
// TODO: @charles - Remove this hook when the `tenantAuthRouter` is merged into cloud `router`. /**
* This hook is used to request the cloud `tenantAuthRouter` endpoints, with an organization token.
*/
export const useAuthedCloudApi = ({ hideErrorToast = false }: UseCloudApiProps = {}): Client< export const useAuthedCloudApi = ({ hideErrorToast = false }: UseCloudApiProps = {}): Client<
typeof tenantAuthRouter typeof tenantAuthRouter
> => { > => {
const { isAuthenticated, getAccessToken } = useLogto(); const { currentTenantId } = useContext(TenantsContext);
const { isAuthenticated, getOrganizationToken } = useLogto();
const api = useMemo( const api = useMemo(
() => () =>
new Client<typeof tenantAuthRouter>({ new Client<typeof tenantAuthRouter>({
baseUrl: window.location.origin, baseUrl: window.location.origin,
headers: async () => { headers: async () => {
if (isAuthenticated) { if (isAuthenticated) {
return { Authorization: `Bearer ${(await getAccessToken(cloudApi.indicator)) ?? ''}` }; return {
Authorization: `Bearer ${
(await getOrganizationToken(getTenantOrganizationId(currentTenantId))) ?? ''
}`,
};
} }
}, },
before: { before: {
...conditional(!hideErrorToast && { error: toastResponseError }), ...conditional(!hideErrorToast && { error: toastResponseError }),
}, },
}), }),
[getAccessToken, hideErrorToast, isAuthenticated] [currentTenantId, getOrganizationToken, hideErrorToast, isAuthenticated]
); );
return api; return api;

View file

@ -11,16 +11,20 @@ import ItemPreview from '.';
type Props = { type Props = {
user: Pick<User, 'id' | 'avatar' | 'name' | 'primaryEmail' | 'primaryPhone' | 'username'> & user: Pick<User, 'id' | 'avatar' | 'name' | 'primaryEmail' | 'primaryPhone' | 'username'> &
Partial<Pick<User, 'isSuspended'>>; Partial<Pick<User, 'isSuspended'>>;
/**
* Whether to provide a link to user details page. Explicitly set to `false` to hide it.
*/
userDetailsLink?: false;
}; };
/** A component that renders a preview of a user. It's useful for displaying a user in a list. */ /** A component that renders a preview of a user. It's useful for displaying a user in a list. */
function UserPreview({ user }: Props) { function UserPreview({ user, userDetailsLink }: Props) {
return ( return (
<ItemPreview <ItemPreview
title={getUserTitle(user)} title={getUserTitle(user)}
subtitle={getUserSubtitle(user)} subtitle={getUserSubtitle(user)}
icon={<UserAvatar size="large" user={user} />} icon={<UserAvatar size="large" user={user} />}
to={`/users/${user.id}`} to={conditional(userDetailsLink !== false && `/users/${user.id}`)}
suffix={conditional(user.isSuspended && <SuspendedTag />)} suffix={conditional(user.isSuspended && <SuspendedTag />)}
/> />
); );

View file

@ -1,7 +1,9 @@
import { TenantRole } from '@logto/schemas'; import { TenantRole } from '@logto/schemas';
import { useContext, useState } from 'react'; import { getUserDisplayName } from '@logto/shared/universal';
import { useContext, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal'; import ReactModal from 'react-modal';
import { z } from 'zod';
import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api'; import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api';
import { type TenantMemberResponse } from '@/cloud/types/router'; import { type TenantMemberResponse } from '@/cloud/types/router';
@ -18,22 +20,28 @@ type Props = {
onClose: () => void; onClose: () => void;
}; };
const roles = Object.freeze([TenantRole.Admin, TenantRole.Member]);
function EditMemberModal({ user, isOpen, onClose }: Props) { function EditMemberModal({ user, isOpen, onClose }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.tenant_members' });
const { currentTenantId } = useContext(TenantsContext); const { currentTenantId } = useContext(TenantsContext);
const name = user.name ?? '';
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [role, setRole] = useState(TenantRole.Member);
const cloudApi = useAuthedCloudApi(); const cloudApi = useAuthedCloudApi();
const roleOptions = useMemo(
() => [
{ value: TenantRole.Admin, title: t('admin') },
{ value: TenantRole.Member, title: t('member') },
],
[t]
);
const onSubmit = async () => { const onSubmit = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
await cloudApi.put(`/api/tenants/:tenantId/members/:userId/roles`, { await cloudApi.put(`/api/tenants/:tenantId/members/:userId/roles`, {
params: { tenantId: currentTenantId, userId: user.id }, params: { tenantId: currentTenantId, userId: user.id },
body: { roleName: TenantRole.Admin }, body: { roleName: role },
}); });
onClose(); onClose();
} finally { } finally {
@ -49,14 +57,7 @@ function EditMemberModal({ user, isOpen, onClose }: Props) {
onRequestClose={onClose} onRequestClose={onClose}
> >
<ModalLayout <ModalLayout
title={ title={<>{t('edit_modal.title', { name: getUserDisplayName(user) })}</>}
<>
{t('organization_details.edit_organization_roles_of_user', {
name,
})}
</>
}
subtitle={<>{t('organization_details.authorize_to_roles', { name })}</>}
footer={ footer={
<Button <Button
size="large" size="large"
@ -68,8 +69,15 @@ function EditMemberModal({ user, isOpen, onClose }: Props) {
} }
onClose={onClose} onClose={onClose}
> >
<FormField title="organizations.organization_role_other"> <FormField title="tenant_members.roles">
<Select options={roles} /> <Select
options={roleOptions}
value={role}
onChange={(value) => {
const guardResult = z.nativeEnum(TenantRole).safeParse(value);
setRole(guardResult.success ? guardResult.data : TenantRole.Member);
}}
/>
</FormField> </FormField>
</ModalLayout> </ModalLayout>
</ReactModal> </ReactModal>

View file

@ -1,4 +1,5 @@
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr'; import useSWR from 'swr';
import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api'; import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api';
@ -17,6 +18,7 @@ import EditMemberModal from '../EditMemberModal';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
function Members() { function Members() {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.tenant_members' });
const cloudApi = useAuthedCloudApi(); const cloudApi = useAuthedCloudApi();
const { currentTenantId } = useContext(TenantsContext); const { currentTenantId } = useContext(TenantsContext);
@ -39,13 +41,13 @@ function Members() {
columns={[ columns={[
{ {
dataIndex: 'user', dataIndex: 'user',
title: 'User', title: t('user'),
colSpan: 4, colSpan: 4,
render: (user) => <UserPreview user={user} />, render: (user) => <UserPreview user={user} userDetailsLink={false} />,
}, },
{ {
dataIndex: 'roles', dataIndex: 'roles',
title: 'Organization roles', title: t('roles'),
colSpan: 6, colSpan: 6,
render: ({ organizationRoles }) => { render: ({ organizationRoles }) => {
if (organizationRoles.length === 0) { if (organizationRoles.length === 0) {
@ -77,7 +79,7 @@ function Members() {
deleteConfirmation: 'general.remove', deleteConfirmation: 'general.remove',
}} }}
onEdit={() => { onEdit={() => {
// SetUserToBeEdited(user); setUserToBeEdited(user);
}} }}
onDelete={async () => { onDelete={async () => {
await cloudApi.delete(`/api/tenants/:tenantId/members/:userId`, { await cloudApi.delete(`/api/tenants/:tenantId/members/:userId`, {

View file

@ -3,6 +3,7 @@
.container { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: _.unit(4);
.tabButtons { .tabButtons {
display: flex; display: flex;

View file

@ -1,5 +1,4 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { Route, Routes } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
import InvitationIcon from '@/assets/icons/invitation.svg'; import InvitationIcon from '@/assets/icons/invitation.svg';
@ -18,7 +17,6 @@ import * as styles from './index.module.scss';
const invitationsRoute = 'invitations'; const invitationsRoute = 'invitations';
function TenantMembers() { function TenantMembers() {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { navigate, match } = useTenantPathname(); const { navigate, match } = useTenantPathname();
const isInvitationTab = match( const isInvitationTab = match(

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -2,6 +2,31 @@ const tenant_members = {
members: 'Members', members: 'Members',
invitations: 'Invitations', invitations: 'Invitations',
new_member: 'New member', new_member: 'New member',
user: 'User',
roles: 'Roles',
admin: 'Admin',
member: 'Member',
invite_modal: {
title: 'Invite people to Silverhand',
subtitle: 'To invite members to an organization, they must accept the invitation.',
to: 'To',
added_as: 'Added as roles',
},
user_options: {
edit: 'Edit tenant role',
delete: 'Remove user from tenant',
},
edit_modal: {
title: 'Change roles of {{name}}',
},
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
user_exists: 'This user is already in this organization',
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -5,6 +5,47 @@ const tenant_members = {
invitations: 'Invitations', invitations: 'Invitations',
/** UNTRANSLATED */ /** UNTRANSLATED */
new_member: 'New member', new_member: 'New member',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
roles: 'Roles',
/** UNTRANSLATED */
admin: 'Admin',
/** UNTRANSLATED */
member: 'Member',
invite_modal: {
/** UNTRANSLATED */
title: 'Invite people to Silverhand',
/** UNTRANSLATED */
subtitle: 'To invite members to an organization, they must accept the invitation.',
/** UNTRANSLATED */
to: 'To',
/** UNTRANSLATED */
added_as: 'Added as roles',
},
user_options: {
/** UNTRANSLATED */
edit: 'Edit tenant role',
/** UNTRANSLATED */
delete: 'Remove user from tenant',
},
edit_modal: {
/** UNTRANSLATED */
title: 'Change roles of {{name}}',
},
/** UNTRANSLATED */
leave_tenant_confirm: 'Are you sure you want to leave this tenant?',
/** UNTRANSLATED */
delete_user_confirm: 'Are you sure you want to remove this user from this tenant?',
/** UNTRANSLATED */
assign_admin_confirm:
'Are you sure you want to make the selected user(s) admin? Granting admin access will give the user(s) the following permissions.<ul><li>Change the tenant billing plan</li><li>Add or remove collaborators</li><li>Delete the tenant</li></ul>',
errors: {
/** UNTRANSLATED */
user_exists: 'This user is already in this organization',
/** UNTRANSLATED */
invalid_email: 'Email address is invalid. Please make sure it is in the right format.',
},
}; };
export default Object.freeze(tenant_members); export default Object.freeze(tenant_members);

View file

@ -0,0 +1,10 @@
export const enumFromStringValue = <T extends Record<string, string>, K extends keyof T>(
enumObject: T,
value: string
): T[keyof T] | undefined =>
enumObject[
Object.keys(enumObject).find(
// eslint-disable-next-line no-restricted-syntax
(k) => enumObject[k as K].toString() === value
) as keyof typeof enumObject
];