mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-15 01:16:22 +02:00
Fix pro project role descriptions (#2612)
https://linear.app/unleash/issue/2-485/bug-pro-access-page-the-tooltip-on-the-roles-is-not-working Fixes an issue where we would not show anything for project role permissions since we're not serving that specific endpoint below Enterprise level. This way we fallback to the access role descriptions, which are also nice and informative:  PR also adds support for SWR options in ConditionalSWR and EnterpriseSWR.
This commit is contained in:
parent
c66daa0ba2
commit
9accbcfa8b
@ -392,7 +392,12 @@ export const ProjectAccessAssign = ({
|
||||
</StyledAutocompleteWrapper>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(role?.id)}
|
||||
show={<ProjectRoleDescription roleId={role?.id!} />}
|
||||
show={
|
||||
<ProjectRoleDescription
|
||||
roleId={role?.id!}
|
||||
projectId={projectId}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -2,6 +2,9 @@ import { styled, SxProps, Theme } from '@mui/material';
|
||||
import { ForwardedRef, forwardRef, useMemo, VFC } from 'react';
|
||||
import useProjectRole from 'hooks/api/getters/useProjectRole/useProjectRole';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import useProjectAccess from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
||||
import { ProjectRoleDescriptionProjectPermissions } from './ProjectRoleDescriptionProjectPermissions/ProjectRoleDescriptionProjectPermissions';
|
||||
import { ProjectRoleDescriptionEnvironmentPermissions } from './ProjectRoleDescriptionEnvironmentPermissions/ProjectRoleDescriptionEnvironmentPermissions';
|
||||
|
||||
const StyledDescription = styled('div', {
|
||||
shouldForwardProp: prop =>
|
||||
@ -46,15 +49,24 @@ interface IProjectRoleDescriptionStyleProps {
|
||||
interface IProjectRoleDescriptionProps
|
||||
extends IProjectRoleDescriptionStyleProps {
|
||||
roleId: number;
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
export const ProjectRoleDescription: VFC<IProjectRoleDescriptionProps> =
|
||||
forwardRef(
|
||||
(
|
||||
{ roleId, className, sx, ...props }: IProjectRoleDescriptionProps,
|
||||
{
|
||||
roleId,
|
||||
projectId,
|
||||
className,
|
||||
sx,
|
||||
...props
|
||||
}: IProjectRoleDescriptionProps,
|
||||
ref: ForwardedRef<HTMLDivElement>
|
||||
) => {
|
||||
const { role } = useProjectRole(roleId.toString());
|
||||
const { access } = useProjectAccess(projectId);
|
||||
const accessRole = access?.roles.find(role => role.id === roleId);
|
||||
|
||||
const environments = useMemo(() => {
|
||||
const environments = new Set<string>();
|
||||
@ -80,26 +92,24 @@ export const ProjectRoleDescription: VFC<IProjectRoleDescriptionProps> =
|
||||
ref={ref}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(projectPermissions?.length)}
|
||||
condition={role.permissions?.length > 0}
|
||||
show={
|
||||
<>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
projectPermissions?.length
|
||||
)}
|
||||
show={
|
||||
<>
|
||||
<StyledDescriptionHeader>
|
||||
Project permissions
|
||||
</StyledDescriptionHeader>
|
||||
<StyledDescriptionBlock>
|
||||
{role.permissions
|
||||
?.filter(
|
||||
(permission: any) =>
|
||||
!permission.environment
|
||||
)
|
||||
.map(
|
||||
(permission: any) =>
|
||||
permission.displayName
|
||||
)
|
||||
.sort()
|
||||
.map((permission: any) => (
|
||||
<p key={permission}>{permission}</p>
|
||||
))}
|
||||
<ProjectRoleDescriptionProjectPermissions
|
||||
permissions={
|
||||
role.permissions
|
||||
}
|
||||
/>
|
||||
</StyledDescriptionBlock>
|
||||
</>
|
||||
}
|
||||
@ -111,34 +121,39 @@ export const ProjectRoleDescription: VFC<IProjectRoleDescriptionProps> =
|
||||
<StyledDescriptionHeader>
|
||||
Environment permissions
|
||||
</StyledDescriptionHeader>
|
||||
{environments.map((environment: any) => (
|
||||
{environments.map(environment => (
|
||||
<div key={environment}>
|
||||
<StyledDescriptionSubHeader>
|
||||
{environment}
|
||||
</StyledDescriptionSubHeader>
|
||||
<StyledDescriptionBlock>
|
||||
{role.permissions
|
||||
.filter(
|
||||
(permission: any) =>
|
||||
permission.environment ===
|
||||
<ProjectRoleDescriptionEnvironmentPermissions
|
||||
environment={
|
||||
environment
|
||||
)
|
||||
.map(
|
||||
(permission: any) =>
|
||||
permission.displayName
|
||||
)
|
||||
.sort()
|
||||
.map((permission: any) => (
|
||||
<p key={permission}>
|
||||
{permission}
|
||||
</p>
|
||||
))}
|
||||
}
|
||||
permissions={
|
||||
role.permissions
|
||||
}
|
||||
/>
|
||||
</StyledDescriptionBlock>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
elseShow={
|
||||
<>
|
||||
<StyledDescriptionSubHeader>
|
||||
{accessRole?.name}
|
||||
</StyledDescriptionSubHeader>
|
||||
<StyledDescriptionBlock>
|
||||
{accessRole?.description}
|
||||
</StyledDescriptionBlock>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</StyledDescription>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
interface IProjectRoleDescriptionEnvironmentPermissionsProps {
|
||||
environment: string;
|
||||
permissions: any[];
|
||||
}
|
||||
|
||||
export const ProjectRoleDescriptionEnvironmentPermissions = ({
|
||||
environment,
|
||||
permissions,
|
||||
}: IProjectRoleDescriptionEnvironmentPermissionsProps) => (
|
||||
<>
|
||||
{permissions
|
||||
.filter((permission: any) => permission.environment === environment)
|
||||
.map((permission: any) => permission.displayName)
|
||||
.sort()
|
||||
.map((permission: any) => (
|
||||
<p key={permission}>{permission}</p>
|
||||
))}
|
||||
</>
|
||||
);
|
@ -0,0 +1,17 @@
|
||||
interface IProjectRoleDescriptionProjectPermissionsProps {
|
||||
permissions: any[];
|
||||
}
|
||||
|
||||
export const ProjectRoleDescriptionProjectPermissions = ({
|
||||
permissions,
|
||||
}: IProjectRoleDescriptionProjectPermissionsProps) => (
|
||||
<>
|
||||
{permissions
|
||||
?.filter((permission: any) => !permission.environment)
|
||||
.map((permission: any) => permission.displayName)
|
||||
.sort()
|
||||
.map((permission: any) => (
|
||||
<p key={permission}>{permission}</p>
|
||||
))}
|
||||
</>
|
||||
);
|
@ -17,12 +17,14 @@ const StyledPopover = styled(Popover)(() => ({
|
||||
|
||||
interface IProjectAccessRoleCellProps {
|
||||
roleId: number;
|
||||
projectId: string;
|
||||
value?: string;
|
||||
emptyText?: string;
|
||||
}
|
||||
|
||||
export const ProjectAccessRoleCell: VFC<IProjectAccessRoleCellProps> = ({
|
||||
roleId,
|
||||
projectId,
|
||||
value,
|
||||
emptyText,
|
||||
}) => {
|
||||
@ -63,7 +65,11 @@ export const ProjectAccessRoleCell: VFC<IProjectAccessRoleCellProps> = ({
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<ProjectRoleDescription roleId={roleId} popover />
|
||||
<ProjectRoleDescription
|
||||
roleId={roleId}
|
||||
projectId={projectId}
|
||||
popover
|
||||
/>
|
||||
</StyledPopover>
|
||||
</>
|
||||
);
|
||||
|
@ -170,6 +170,7 @@ export const ProjectAccessTable: VFC = () => {
|
||||
Cell: ({ value, row: { original: row } }: any) => (
|
||||
<ProjectAccessRoleCell
|
||||
roleId={row.entity.roleId}
|
||||
projectId={projectId}
|
||||
value={value}
|
||||
/>
|
||||
),
|
||||
|
@ -7,9 +7,9 @@ export const useChangeRequestConfig = (projectId: string) => {
|
||||
const { data, error, mutate } = useEnterpriseSWR<
|
||||
IChangeRequestEnvironmentConfig[]
|
||||
>(
|
||||
[],
|
||||
formatApiPath(`api/admin/projects/${projectId}/change-requests/config`),
|
||||
fetcher,
|
||||
[]
|
||||
fetcher
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -0,0 +1,23 @@
|
||||
import useSWR, { BareFetcher, Key, SWRConfiguration, SWRResponse } from 'swr';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useConditionalSWR = <Data = any, Error = any, T = boolean>(
|
||||
condition: T,
|
||||
fallback: Data,
|
||||
key: Key,
|
||||
fetcher: BareFetcher<Data>,
|
||||
options: SWRConfiguration = {}
|
||||
): SWRResponse<Data, Error> => {
|
||||
const result = useSWR(
|
||||
key,
|
||||
(path: string) =>
|
||||
condition ? fetcher(path) : Promise.resolve(fallback),
|
||||
options
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
result.mutate();
|
||||
}, [condition]);
|
||||
|
||||
return result;
|
||||
};
|
@ -1,33 +1,21 @@
|
||||
import useSWR, { BareFetcher, Key, SWRResponse } from 'swr';
|
||||
import { useEffect } from 'react';
|
||||
import { BareFetcher, Key, SWRConfiguration } from 'swr';
|
||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||
|
||||
export const useConditionalSWR = <Data = any, Error = any, T = boolean>(
|
||||
key: Key,
|
||||
fetcher: BareFetcher<Data>,
|
||||
condition: T
|
||||
): SWRResponse<Data, Error> => {
|
||||
const result = useSWR(key, fetcher);
|
||||
|
||||
useEffect(() => {
|
||||
result.mutate();
|
||||
}, [condition]);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const useEnterpriseSWR = <Data = any, Error = any>(
|
||||
fallback: Data,
|
||||
key: Key,
|
||||
fetcher: BareFetcher<Data>,
|
||||
fallback: Data
|
||||
options: SWRConfiguration = {}
|
||||
) => {
|
||||
const { isEnterprise } = useUiConfig();
|
||||
|
||||
const result = useConditionalSWR(
|
||||
isEnterprise(),
|
||||
fallback,
|
||||
key,
|
||||
(path: string) =>
|
||||
isEnterprise() ? fetcher(path) : Promise.resolve(fallback),
|
||||
isEnterprise()
|
||||
fetcher,
|
||||
options
|
||||
);
|
||||
|
||||
return result;
|
||||
|
@ -11,9 +11,9 @@ const fetcher = (path: string) => {
|
||||
|
||||
export const usePendingChangeRequests = (project: string) => {
|
||||
const { data, error, mutate } = useEnterpriseSWR<IChangeRequest[]>(
|
||||
[],
|
||||
formatApiPath(`api/admin/projects/${project}/change-requests/pending`),
|
||||
fetcher,
|
||||
[]
|
||||
fetcher
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -14,11 +14,11 @@ export const usePendingChangeRequestsForFeature = (
|
||||
featureName: string
|
||||
) => {
|
||||
const { data, error, mutate } = useEnterpriseSWR<IChangeRequest[]>(
|
||||
[],
|
||||
formatApiPath(
|
||||
`api/admin/projects/${project}/change-requests/pending/${featureName}`
|
||||
),
|
||||
fetcher,
|
||||
[]
|
||||
fetcher
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import useSWR, { mutate, SWRConfiguration } from 'swr';
|
||||
import { mutate, SWRConfiguration } from 'swr';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR';
|
||||
|
||||
const useProjectRole = (id: string, options: SWRConfiguration = {}) => {
|
||||
const fetcher = () => {
|
||||
@ -13,7 +14,12 @@ const useProjectRole = (id: string, options: SWRConfiguration = {}) => {
|
||||
.then(res => res.json());
|
||||
};
|
||||
|
||||
const { data, error } = useSWR(`api/admin/roles/${id}`, fetcher, options);
|
||||
const { data, error } = useEnterpriseSWR(
|
||||
{},
|
||||
`api/admin/roles/${id}`,
|
||||
fetcher,
|
||||
options
|
||||
);
|
||||
const [loading, setLoading] = useState(!error && !data);
|
||||
|
||||
const refetch = () => {
|
||||
|
Loading…
Reference in New Issue
Block a user