diff --git a/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx index ed4221c83..db37ebaae 100644 --- a/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx @@ -25,7 +25,6 @@ type ApplicationItemProps = { entity: Application; }; -// eslint-disable-next-line import/no-unused-modules -- will use in the next pull request export function ApplicationItem({ entity }: ApplicationItemProps) { return ( <> diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntitiesBox/index.module.scss b/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntitiesBox/index.module.scss deleted file mode 100644 index b6084458b..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntitiesBox/index.module.scss +++ /dev/null @@ -1,7 +0,0 @@ -.search { - width: 100%; -} - -.icon { - color: var(--color-text-secondary); -} diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntitiesBox/index.tsx b/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntitiesBox/index.tsx deleted file mode 100644 index fd78779c6..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntitiesBox/index.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import type { Application, User } from '@logto/schemas'; -import { ApplicationType, RoleType } from '@logto/schemas'; -import { conditional } from '@silverhand/essentials'; -import classNames from 'classnames'; -import type { ChangeEvent } from 'react'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import useSWR from 'swr'; - -import Search from '@/assets/icons/search.svg'; -import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; -import { defaultPageSize } from '@/consts'; -import Pagination from '@/ds-components/Pagination'; -import TextInput from '@/ds-components/TextInput'; -import type { RequestError } from '@/hooks/use-api'; -import useDebounce from '@/hooks/use-debounce'; -import * as transferLayout from '@/scss/transfer.module.scss'; -import { buildUrl, formatSearchKeyword } from '@/utils/url'; - -import SourceEntityItem from '../SourceEntityItem'; - -import * as styles from './index.module.scss'; - -type Props = { - roleId: string; - roleType: RoleType; - onChange: (value: T[]) => void; - selectedEntities: T[]; -}; - -const pageSize = defaultPageSize; - -function SourceEntitiesBox({ - roleId, - roleType, - selectedEntities, - onChange, -}: Props) { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const [page, setPage] = useState(1); - const [keyword, setKeyword] = useState(''); - const debounce = useDebounce(); - - const commonSearchParams = { - page: String(page), - page_size: String(pageSize), - ...conditional(keyword && { search: formatSearchKeyword(keyword) }), - }; - - const { data, error } = useSWR<[Props['selectedEntities'], number], RequestError>( - roleType === RoleType.User - ? buildUrl('api/users', { - excludeRoleId: roleId, - ...commonSearchParams, - }) - : buildUrl(`api/applications`, { - excludeRoleId: roleId, - ...commonSearchParams, - 'search.type': ApplicationType.MachineToMachine, - 'mode.type': 'exact', - }) - ); - - const isLoading = !data && !error; - - const [dataSource = [], totalCount] = data ?? []; - - const handleSearchInput = (event: ChangeEvent) => { - debounce(() => { - setPage(1); - setKeyword(event.target.value); - }); - }; - - const isEntityAdded = (entity: User | Application) => - selectedEntities.findIndex(({ id }) => id === entity.id) >= 0; - - const isEmpty = !isLoading && !error && dataSource.length === 0; - - return ( -
-
- } - placeholder={t('general.search_placeholder')} - onChange={handleSearchInput} - /> -
-
- {isEmpty ? ( - - ) : ( - dataSource.map((entity) => { - const isSelected = isEntityAdded(entity); - - return ( - { - onChange( - isSelected - ? selectedEntities.filter(({ id }) => entity.id !== id) - : [entity, ...selectedEntities] - ); - }} - /> - ); - }) - )} -
- { - setPage(page); - }} - /> -
- ); -} -export default SourceEntitiesBox; diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntityItem/index.module.scss b/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntityItem/index.module.scss deleted file mode 100644 index 1f4ccdfe2..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntityItem/index.module.scss +++ /dev/null @@ -1,31 +0,0 @@ -@use '@/scss/underscore' as _; - -.item { - display: flex; - align-items: center; - padding: _.unit(2.5) _.unit(4); - cursor: pointer; - user-select: none; - - .icon { - width: 20px; - height: 20px; - border-radius: 6px; - } - - .title { - flex: 1 1 0; - font: var(--font-body-2); - @include _.text-ellipsis; - margin-left: _.unit(2); - max-width: fit-content; - } - - .suspended { - margin-left: _.unit(1); - } - - &:hover { - background: var(--color-hover); - } -} diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntityItem/index.tsx b/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntityItem/index.tsx deleted file mode 100644 index 2dcc6209f..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/SourceEntityItem/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import type { User, Application } from '@logto/schemas'; -import { ApplicationType } from '@logto/schemas'; - -import ApplicationIcon from '@/components/ApplicationIcon'; -import UserAvatar from '@/components/UserAvatar'; -import Checkbox from '@/ds-components/Checkbox'; -import SuspendedTag from '@/pages/Users/components/SuspendedTag'; -import { onKeyDownHandler } from '@/utils/a11y'; -import { getUserTitle } from '@/utils/user'; - -import { isUser } from '../../utils'; - -import * as styles from './index.module.scss'; - -type Props = { - entity: T; - isSelected: boolean; - onSelect: () => void; -}; - -function SourceEntityItem({ - entity, - isSelected, - onSelect, -}: Props) { - return ( -
{ - onSelect(); - })} - onClick={() => { - onSelect(); - }} - > - { - onSelect(); - }} - /> - {isUser(entity) ? ( - - ) : ( - - )} -
{isUser(entity) ? getUserTitle(entity) : entity.name}
- {isUser(entity) && entity.isSuspended && } -
- ); -} - -export default SourceEntityItem; diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntitiesBox/index.module.scss b/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntitiesBox/index.module.scss deleted file mode 100644 index f4233af9b..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntitiesBox/index.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.added { - font: var(--font-label-2); -} diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntitiesBox/index.tsx b/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntitiesBox/index.tsx deleted file mode 100644 index 356e5ac23..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntitiesBox/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import type { User, Application } from '@logto/schemas'; -import { useTranslation } from 'react-i18next'; - -import * as transferLayout from '@/scss/transfer.module.scss'; - -import TargetEntityItem from '../TargetEntityItem'; - -import * as styles from './index.module.scss'; - -type Props = { - selectedEntities: T[]; - onChange: (value: T[]) => void; -}; - -function TargetEntitiesBox({ selectedEntities, onChange }: Props) { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - - return ( -
-
- - {`${selectedEntities.length} `} - {t('general.added')} - -
-
- {selectedEntities.map((entity) => ( - { - onChange(selectedEntities.filter(({ id }) => id !== entity.id)); - }} - /> - ))} -
-
- ); -} - -export default TargetEntitiesBox; diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntityItem/index.module.scss b/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntityItem/index.module.scss deleted file mode 100644 index 0c01be48e..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntityItem/index.module.scss +++ /dev/null @@ -1,37 +0,0 @@ -@use '@/scss/underscore' as _; - -.item { - display: flex; - align-items: center; - padding: _.unit(2) _.unit(3) _.unit(2) _.unit(4); - user-select: none; - - .meta { - flex: 1; - display: flex; - align-items: center; - overflow: hidden; - - .icon { - width: 20px; - height: 20px; - border-radius: 6px; - } - - .title { - flex: 1 1 0; - font: var(--font-body-2); - @include _.text-ellipsis; - margin-left: _.unit(2); - max-width: fit-content; - } - - .suspended { - margin: 0 _.unit(1); - } - } - - &:hover { - background: var(--color-hover); - } -} diff --git a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntityItem/index.tsx b/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntityItem/index.tsx deleted file mode 100644 index 561c00fdb..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/components/TargetEntityItem/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import type { User, Application } from '@logto/schemas'; -import { ApplicationType } from '@logto/schemas'; - -import Close from '@/assets/icons/close.svg'; -import ApplicationIcon from '@/components/ApplicationIcon'; -import UserAvatar from '@/components/UserAvatar'; -import IconButton from '@/ds-components/IconButton'; -import SuspendedTag from '@/pages/Users/components/SuspendedTag'; -import { getUserTitle } from '@/utils/user'; - -import { isUser } from '../../utils'; - -import * as styles from './index.module.scss'; - -type Props = { - entity: T; - onDelete: () => void; -}; - -function TargetEntityItem({ entity, onDelete }: Props) { - return ( -
-
- {isUser(entity) ? ( - - ) : ( - - )} -
{isUser(entity) ? getUserTitle(entity) : entity.name}
- {isUser(entity) && entity.isSuspended && } -
- { - onDelete(); - }} - > - - -
- ); -} - -export default TargetEntityItem; diff --git a/packages/console/src/components/RoleEntitiesTransfer/index.module.scss b/packages/console/src/components/RoleEntitiesTransfer/index.module.scss deleted file mode 100644 index 7fe601a01..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/index.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -@use '@/scss/underscore' as _; - -.rolesTransfer { - height: 360px; -} diff --git a/packages/console/src/components/RoleEntitiesTransfer/index.tsx b/packages/console/src/components/RoleEntitiesTransfer/index.tsx deleted file mode 100644 index 1f81cfd2b..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { Application, User, RoleType } from '@logto/schemas'; -import classNames from 'classnames'; - -import * as transferLayout from '@/scss/transfer.module.scss'; - -import SourceEntitiesBox from './components/SourceEntitiesBox'; -import TargetEntitiesBox from './components/TargetEntitiesBox'; -import * as styles from './index.module.scss'; - -type Props = { - roleId: string; - roleType: RoleType; - value: T[]; - onChange: (value: T[]) => void; -}; - -function RoleEntitiesTransfer({ - roleId, - roleType, - value, - onChange, -}: Props) { - return ( -
- -
- -
- ); -} - -export default RoleEntitiesTransfer; diff --git a/packages/console/src/components/RoleEntitiesTransfer/utils.ts b/packages/console/src/components/RoleEntitiesTransfer/utils.ts deleted file mode 100644 index 9af97e22e..000000000 --- a/packages/console/src/components/RoleEntitiesTransfer/utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { User, Application } from '@logto/schemas'; - -export const isUser = (entity: User | Application): entity is User => - 'customData' in entity || 'identities' in entity; diff --git a/packages/console/src/pages/Roles/components/AssignRoleModal/index.tsx b/packages/console/src/pages/Roles/components/AssignRoleModal/index.tsx index c25184144..85a30b59c 100644 --- a/packages/console/src/pages/Roles/components/AssignRoleModal/index.tsx +++ b/packages/console/src/pages/Roles/components/AssignRoleModal/index.tsx @@ -1,16 +1,20 @@ -import { type Application, RoleType, type User } from '@logto/schemas'; +import { type Application, RoleType, type User, ApplicationType } from '@logto/schemas'; import { useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import ReactModal from 'react-modal'; -import RoleEntitiesTransfer from '@/components/RoleEntitiesTransfer'; +import EntitiesTransfer from '@/components/EntitiesTransfer'; +import { ApplicationItem, UserItem } from '@/components/EntitiesTransfer/components/EntityItem'; import Button from '@/ds-components/Button'; import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import useApi from '@/hooks/use-api'; import * as modalStyles from '@/scss/modal.module.scss'; +const isUserEntity = (entity: User | Application): entity is User => + 'customData' in entity || 'identities' in entity; + type Props = { roleId: string; roleType: RoleType; @@ -27,6 +31,7 @@ function AssignRoleModal({ const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const [isLoading, setIsLoading] = useState(false); const [entities, setEntities] = useState([]); + const isUser = roleType === RoleType.User; const api = useApi(); @@ -38,7 +43,7 @@ function AssignRoleModal({ setIsLoading(true); try { - await (roleType === RoleType.User + await (isUser ? api.post(`api/roles/${roleId}/users`, { json: { userIds: entities.map(({ id }) => id) }, }) @@ -47,7 +52,7 @@ function AssignRoleModal({ })); toast.success( t( - roleType === RoleType.User + isUser ? 'role_details.users.users_assigned' : 'role_details.applications.applications_assigned' ) @@ -70,12 +75,10 @@ function AssignRoleModal({ > ({ disabled={entities.length === 0} htmlType="submit" title={ - roleType === RoleType.User + isUser ? 'role_details.users.confirm_assign' : 'role_details.applications.confirm_assign' } @@ -111,18 +114,33 @@ function AssignRoleModal({ > - { - setEntities(value); + + isUserEntity(entity) ? ( + + ) : ( + + ) + } + onChange={setEntities} /> diff --git a/packages/core/src/utils/search.ts b/packages/core/src/utils/search.ts index 6a526db38..524558329 100644 --- a/packages/core/src/utils/search.ts +++ b/packages/core/src/utils/search.ts @@ -233,7 +233,6 @@ const showLowercase = ( * * @param search The search config object. * @param searchFields Allowed and default search fields (columns). - * @param isCaseSensitive Should perform case sensitive search or not. * @returns The SQL token that includes the all condition checks. * @throws TypeError error if fields in `search` do not match the `searchFields`, or invalid condition found (e.g. the value is empty). */