Skip to content

Commit

Permalink
Merge pull request wso2#4366 from DonOmalVindula/fix/17240
Browse files Browse the repository at this point in the history
Improve roles UI in sub organizations
  • Loading branch information
DonOmalVindula authored Oct 27, 2023
2 parents 8612f0e + 917be49 commit ea8855c
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 122 deletions.
11 changes: 7 additions & 4 deletions apps/console/src/features/roles/api/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,8 @@ export const useRolesList = <Data = RoleListInterface, Error = RequestErrorInter
* @deprecated This is a temporary hook until the API resource feature moved to the features folder.
*/
export const useAPIResourcesList = <Data = APIResourceListInterface, Error = RequestErrorInterface>(
filter?: string
filter?: string,
shouldFetch: boolean = true
): RequestResultInterface<Data, Error> => {

const requestConfig: RequestConfigInterface = {
Expand All @@ -499,7 +500,7 @@ export const useAPIResourcesList = <Data = APIResourceListInterface, Error = Req
isValidating,
mutate,
response
} = useRequest<Data, Error>(requestConfig);
} = useRequest<Data, Error>(shouldFetch ? requestConfig : null);

return {
data,
Expand All @@ -519,7 +520,8 @@ export const useAPIResourcesList = <Data = APIResourceListInterface, Error = Req
* @deprecated This is a temporary hook until the API resource feature moved to the features folder.
*/
export const useAPIResourceDetails = <Data = APIResourceInterface, Error = RequestErrorInterface>(
apiResourceId: string
apiResourceId: string,
shouldFetch: boolean = true
): RequestResultInterface<Data, Error> => {

const requestConfig: RequestConfigInterface = {
Expand All @@ -534,7 +536,8 @@ export const useAPIResourceDetails = <Data = APIResourceInterface, Error = Reque
/**
* Pass `null` if the `apiResourceId` is not available. This will prevent the request from being called.
*/
const { data, error, isValidating, mutate } = useRequest<Data, Error>(apiResourceId ? requestConfig : null);
const { data, error, isValidating, mutate } = useRequest<Data, Error>((apiResourceId && shouldFetch)
? requestConfig : null);

return {
data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
* under the License.
*/

import Autocomplete, {
AutocompleteRenderGetTagProps,
AutocompleteRenderInputParams
} from "@oxygen-ui/react/Autocomplete";
import Button from "@oxygen-ui/react/Button";
import Grid from "@oxygen-ui/react/Grid";
import TextField from "@oxygen-ui/react/TextField";
import {
AlertInterface,
AlertLevels,
Expand All @@ -29,11 +34,20 @@ import { addAlert } from "@wso2is/core/store";
import { Field, Form } from "@wso2is/form";
import { EmphasizedSegment, Heading } from "@wso2is/react-components";
import debounce, { DebouncedFunc } from "lodash-es/debounce";
import React, { FunctionComponent, ReactElement, SyntheticEvent, useCallback, useEffect, useState } from "react";
import React, {
FunctionComponent,
ReactElement,
ReactNode,
SyntheticEvent,
useCallback,
useEffect,
useState
} from "react";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { Dispatch } from "redux";
import { DropdownProps } from "semantic-ui-react";
import { RenderChip } from "./edit-role-common/render-chip";
import { RoleAPIResourcesListItem } from "./edit-role-common/role-api-resources-list-item";
import { getAPIResourceDetailsBulk, updateRoleDetails, useAPIResourceDetails, useAPIResourcesList } from "../../api";
import { RoleConstants } from "../../constants/role-constants";
Expand Down Expand Up @@ -94,7 +108,7 @@ export const UpdatedRolePermissionDetails: FunctionComponent<RolePermissionDetai
isLoading: isAPIResourcesListFetchRequestLoading,
error: apiResourcsListFetchRequestError,
mutate: mutateAPIResourcesListFetchRequest
} = useAPIResourcesList(apiResourceSearchQuery);
} = useAPIResourcesList(apiResourceSearchQuery, !isReadOnly);

const {
data: selectedAPIResource,
Expand All @@ -104,7 +118,9 @@ export const UpdatedRolePermissionDetails: FunctionComponent<RolePermissionDetai
} = useAPIResourceDetails(selectedAPIResourceId);

useEffect(() => {
getExistingAPIResources();
!isReadOnly
? getExistingAPIResources()
: null;
}, [ role ]);

/**
Expand Down Expand Up @@ -341,105 +357,155 @@ export const UpdatedRolePermissionDetails: FunctionComponent<RolePermissionDetai
}));
};

const editablePermissionList = (): ReactNode => (
<Grid container direction="column" justifyContent="center" alignItems="flex-start" spacing={ 2 }>
<Grid xs={ 8 }>
<Form
id={ componentId }
uncontrolledForm={ false }
onSubmit={ undefined }
>
<Field.Dropdown
ariaLabel="assignedApplication"
name="assignedApplication"
label={ t("console:manage.features.roles.addRoleWizard.forms.rolePermission." +
"apiResource.label") }
options={ apiResourcesListOptions }
search
data-componentid={ `${componentId}-typography-font-family-dropdown` }
placeholder={ t("console:manage.features.roles.addRoleWizard." +
"forms.rolePermission.apiResource.placeholder") }
noResultsMessage={
isAPIResourcesListFetchRequestLoading
? t("common:searching")
: t("common:noResultsFound")
}
loading={ isAPIResourcesSearching }
onSearchChange={ onSearchChangeAPIResources }
onChange={ onAPIResourceSelected }
/>
</Form>
</Grid>
<Grid xs={ 12 }>
{
selectedAPIResources?.length > 0
? (
<div className="role-permission-list field">
<label className="form-label">
{ t("console:manage.features.roles.addRoleWizard." +
"forms.rolePermission.permissions.label") }
</label>
<EmphasizedSegment
className="mt-2"
data-componentid={ componentId }
basic
loading={
selectedAPIResourceId &&
(isSelectedAPIResourceFetchRequestLoading
|| isSelectedAPIResourceFetchRequestValidating)
}
>
{
selectedAPIResources?.map((apiResource: APIResourceInterface) => {
return (
<RoleAPIResourcesListItem
key={ apiResource?.id }
apiResource={ apiResource }
onChangeScopes={ onChangeScopes }
onRemoveAPIResource={ onRemoveAPIResource }
initialSelectedPermissions={ initialSelectedPermissions?.find(
(selectedPermission: SelectedPermissionsInterface) =>
selectedPermission.apiResourceId === apiResource?.id)
?.scopes
}
selectedPermissions={ selectedPermissions?.find(
(selectedPermission: SelectedPermissionsInterface) =>
selectedPermission.apiResourceId === apiResource?.id)
?.scopes
}
/>
);
})
}
</EmphasizedSegment>
</div>
) : null
}
</Grid>
</Grid>
);

const readOnlyPermissionList = (): ReactNode => (
<Autocomplete
readOnly
multiple
options={ role?.permissions ?? [] }
defaultValue={ role?.permissions ?? [] }
getOptionLabel={ (scope: RolePermissionInterface) => scope.display }
renderInput={ (params: AutocompleteRenderInputParams) => (
<TextField
{ ...params }
data-componentid={ `${componentId}-textfield` }
/>
) }
renderTags={ (
value: RolePermissionInterface[],
getTagProps: AutocompleteRenderGetTagProps
) => value.map((option: RolePermissionInterface, index: number) => (
<RenderChip
{ ...getTagProps({ index }) }
key={ index }
className="pt-5 m-1"
primaryText={ option.display }
secondaryText={ option.value }
option={ option }
activeOption={ null }
setActiveOption={ () => null }
variant="solid"
/>
)) }
/>
);

return (
<EmphasizedSegment padded="very">
<Grid xs={ 8 }>
<Heading as="h4">
{ t("console:manage.features.roles.edit.permissions.heading") }
</Heading>
<Heading as="h6" color="grey" subHeading className="mb-5">
{ t("console:manage.features.roles.edit.permissions.subHeading") }
</Heading>
{
isReadOnly ? (
<Heading as="h6" color="grey" subHeading className="mb-5">
{ t("console:manage.features.roles.edit.permissions.readOnlySubHeading") }
</Heading>
) : (
<Heading as="h6" color="grey" subHeading className="mb-5">
{ t("console:manage.features.roles.edit.permissions.subHeading") }
</Heading>
)
}
</Grid>
<Grid container direction="column" justifyContent="center" alignItems="flex-start" spacing={ 2 }>
<Grid xs={ 8 }>
<Form
id={ componentId }
uncontrolledForm={ false }
onSubmit={ undefined }
{
isReadOnly ? readOnlyPermissionList() : editablePermissionList()
}
{
!isReadOnly && (
<Button
color="primary"
variant="contained"
size="small"
className="mt-5"
loading={ isSubmitting }
disabled={ isReadOnly }
onClick={ () => {
updateRolePermissions();
} }
data-componentid={ `${ componentId }-update-button` }
>
<Field.Dropdown
ariaLabel="assignedApplication"
name="assignedApplication"
label={ t("console:manage.features.roles.addRoleWizard.forms.rolePermission." +
"apiResource.label") }
options={ apiResourcesListOptions }
search
data-componentid={ `${componentId}-typography-font-family-dropdown` }
placeholder={ t("console:manage.features.roles.addRoleWizard.forms.rolePermission." +
"apiResource.placeholder") }
noResultsMessage={
isAPIResourcesListFetchRequestLoading
? t("common:searching")
: t("common:noResultsFound")
}
loading={ isAPIResourcesSearching }
onSearchChange={ onSearchChangeAPIResources }
onChange={ onAPIResourceSelected }
/>
</Form>
</Grid>
<Grid xs={ 12 }>
{
selectedAPIResources?.length > 0
? (
<div className="role-permission-list field">
<label className="form-label">
{ t("console:manage.features.roles.addRoleWizard.forms.rolePermission." +
"permissions.label") }
</label>
<EmphasizedSegment
className="mt-2"
data-componentid={ componentId }
basic
loading={
selectedAPIResourceId &&
(isSelectedAPIResourceFetchRequestLoading
|| isSelectedAPIResourceFetchRequestValidating)
}
>
{
selectedAPIResources?.map((apiResource: APIResourceInterface) => {
return (
<RoleAPIResourcesListItem
key={ apiResource?.id }
apiResource={ apiResource }
onChangeScopes={ onChangeScopes }
onRemoveAPIResource={ onRemoveAPIResource }
initialSelectedPermissions={ initialSelectedPermissions?.find(
(selectedPermission: SelectedPermissionsInterface) =>
selectedPermission.apiResourceId === apiResource?.id)
?.scopes
}
selectedPermissions={ selectedPermissions?.find(
(selectedPermission: SelectedPermissionsInterface) =>
selectedPermission.apiResourceId === apiResource?.id)
?.scopes
}
/>
);
})
}
</EmphasizedSegment>
</div>
) : null
}
</Grid>
</Grid>
<Button
color="primary"
variant="contained"
size="small"
className="mt-5"
loading={ isSubmitting }
disabled={ isReadOnly }
onClick={ () => {
updateRolePermissions();
} }
data-componentid={ `${ componentId }-update-button` }
>
{ t("common:update") }
</Button>
{ t("common:update") }
</Button>
)
}
</EmphasizedSegment>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* under the License.
*/

import { OrganizationType } from "@wso2is/common";
import { RoleConstants } from "@wso2is/core/constants";
import { hasRequiredScopes } from "@wso2is/core/helpers";
import { RolesInterface, SBACInterface } from "@wso2is/core/models";
Expand All @@ -29,6 +30,7 @@ import { RoleGroupsList } from "./edit-role-groups";
import { UpdatedRolePermissionDetails } from "./edit-role-permission";
import { RoleUsersList } from "./edit-role-users";
import { AppState, FeatureConfigInterface } from "../../../core";
import { useGetOrganizationType } from "../../../organizations/hooks/use-get-organization-type";

/**
* Captures props needed for edit role component
Expand Down Expand Up @@ -67,12 +69,15 @@ export const EditRole: FunctionComponent<EditRoleProps> = (props: EditRoleProps)
} = props;

const { t } = useTranslation();
const orgType: OrganizationType = useGetOrganizationType();

const featureConfig: FeatureConfigInterface = useSelector((state: AppState) => state?.config?.ui?.features);
const allowedScopes: string = useSelector((state: AppState) => state?.auth?.allowedScopes);

const [ isAdminRole, setIsAdminRole ] = useState<boolean>(false);

const isSubOrg: boolean = orgType === OrganizationType.SUBORGANIZATION;

/**
* Set the if the role is `Internal/admin`.
*/
Expand All @@ -90,7 +95,7 @@ export const EditRole: FunctionComponent<EditRoleProps> = (props: EditRoleProps)
render: () => (
<ResourceTab.Pane controlledSegmentation attached={ false }>
<BasicRoleDetails
isReadOnly={ isAdminRole
isReadOnly={ isSubOrg || isAdminRole
|| !hasRequiredScopes(
featureConfig?.roles, featureConfig?.roles?.scopes?.update, allowedScopes) }
role={ roleObject }
Expand All @@ -105,7 +110,7 @@ export const EditRole: FunctionComponent<EditRoleProps> = (props: EditRoleProps)
render: () => (
<ResourceTab.Pane controlledSegmentation attached={ false }>
<UpdatedRolePermissionDetails
isReadOnly={ isAdminRole
isReadOnly={ isSubOrg || isAdminRole
|| !hasRequiredScopes(
featureConfig?.roles, featureConfig?.roles?.scopes?.update, allowedScopes) }
role={ roleObject }
Expand Down
Loading

0 comments on commit ea8855c

Please sign in to comment.