1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx

309 lines
12 KiB
TypeScript
Raw Normal View History

import { useMemo, useState } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
2022-03-28 10:49:59 +02:00
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
import ApiError from 'component/common/ApiError/ApiError';
import useToast from 'hooks/useToast';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useProject from 'hooks/api/getters/useProject/useProject';
import { Alert, styled, TableBody, TableRow } from '@mui/material';
2022-03-28 10:49:59 +02:00
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
import { Link } from 'react-router-dom';
2022-03-28 10:49:59 +02:00
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
import type { IProjectEnvironment } from 'interfaces/environments';
import { getEnabledEnvs } from './helpers';
import { usePageTitle } from 'hooks/usePageTitle';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useGlobalFilter, useTable } from 'react-table';
import {
SortableTableHeader,
Table,
TableCell,
TablePlaceholder,
} from 'component/common/Table';
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { Search } from 'component/common/Search/Search';
import { EnvironmentNameCell } from 'component/environments/EnvironmentTable/EnvironmentNameCell/EnvironmentNameCell';
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
import { EnvironmentHideDialog } from './EnvironmentHideDialog/EnvironmentHideDialog';
import { useProjectEnvironments } from 'hooks/api/getters/useProjectEnvironments/useProjectEnvironments';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { useProjectOverviewNameOrId } from '../../../hooks/api/getters/useProjectOverview/useProjectOverview';
const StyledAlert = styled(Alert)(({ theme }) => ({
marginBottom: theme.spacing(4),
}));
const StyledDivContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexWrap: 'wrap',
[theme.breakpoints.down('sm')]: {
justifyContent: 'center',
},
}));
const StyledApiError = styled(ApiError)(({ theme }) => ({
maxWidth: '400px',
marginBottom: theme.spacing(2),
}));
const ProjectEnvironmentList = () => {
const projectId = useRequiredPathParam('projectId');
const projectName = useProjectOverviewNameOrId(projectId);
usePageTitle(`Project environments ${projectName}`);
// api state
feat/rbac roles (#562) * feat: create screen * fix: import accordion summary * feat: add accordions * fix: add codebox * feat: select permissions * fix: permission checker * fix: update permission checker * feat: wire up role list * fix: change icon color in project roles list * fix: add color to icon in project roles * add confirm dialog on role deletion * feat: add created screen * fix: cleanup * fix: update access permissions * fix: update admin panel * feat: add edit screen * fix: use color from palette and show toast when fails * fix: refactor * feat: validation * feat: implement checked all * fix: experimental toast * fix: error handling * fix: toast * feat: unique name validation * fix: update toasts * fix: remove toast * fix: reset flag * fix: remove unused vars * fix: update tests * feat: add error icon for toast * fix: replace wrong import for setToastData * feat: Patch keying on ui to handle uniqueness for permissions across multiple envs * fix: hasAccess handles * * fix: update permission switch * fix: use flag for environments rbac * fix: do not include check all keys in payload * fix: filter roles * fix: account for new permissions in variants list * fix: use effect on length property * fix: set polling interval on user * 4.5.0-beta.0 * fix: set initial permissions correctly to avoid race condition * fix: handle activeEnvironment when it is null * fix: remove unused imports * fix: unused imports * fix: Include missing project in hasAccess for deleteinng a tag * fix: Move add/delete tag to use update feature permissions * fix: use rest parameter * fix: remove sandbox from scripts * 4.6.0-beta.1 * fix: remove loading deduping * fix: disable editing on builtin roles * fix: check all * fix: feature overview environment * fix: refetch user on project create * fix: update snaphots * fix: frontend permissions * fix: delete create confirm * fix: remove unused permission * 4.6.0-beta.4 * fix: update permissions * fix: permissions * fix: set error to string * 4.6.0-beta.5 * fix: add permissions for project view * fix: add permissions to useEffect deps * fix: update permission for move feature toggle * fix: add permissions data to useEffect * fix: move settings * fix: key on confetti * fix: refetch project permissions on environment create/delete * fix: optional coalescing error object * fix: remove logging error * fix: reorder disable importance in permissionbutton * fix: add project roles to menu * fix: add disabled check to revive * fix: update snapshots * fix: change text to select all * fix: change text to select * 4.6.0-beta.6 Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: sighphyre <liquidwicked64@gmail.com>
2022-01-14 15:50:02 +01:00
const { setToastData, setToastApiError } = useToast();
const { environments, loading, error, refetchEnvironments } =
useProjectEnvironments(projectId);
const { project, refetch: refetchProject } = useProject(projectId);
const { removeEnvironmentFromProject, addEnvironmentToProject } =
useProjectApi();
// local state
const [selectedEnvironment, setSelectedEnvironment] =
useState<IProjectEnvironment>();
const [hideDialog, setHideDialog] = useState(false);
const { isOss } = useUiConfig();
const projectEnvironments = useMemo<IProjectEnvironment[]>(
() =>
environments.map((environment) => ({
...environment,
projectVisible: project?.environments
.map((projectEnvironment) => projectEnvironment.environment)
.includes(environment.name),
})),
[environments, project?.environments],
);
const refetch = () => {
refetchEnvironments();
refetchProject();
};
const renderError = () => {
return (
<StyledApiError
onClick={refetch}
text='Error fetching environments'
/>
);
};
const errorMsg = (enable: boolean): string => {
return `Got an API error when trying to set the environment as ${
enable ? 'visible' : 'hidden'
}`;
};
refactor: fix misc TS errors (#729) * refactor: update test deps * refactor: remove unused ts-expect-error annotations * refactor: add missing arg and return types * refactor: the loading prop is optional * refactor: add missing arg and return types * reafactor: fix value arg type * refactor: fix missing array type * refactor: the parameters field is an array * refactor: use undefined instead of null in state * refactor: add missing params type * refactor: add missing children prop * refactor: add missing array type * refactor: add missing React imports * refactor: use correct IProjectEnvironment type * refactor: type errors as unknown * refactor: the index prop is required * refactor: fix date prop type * refactor: fix tooltip placement prop type * refactor: fix environments state type * refactor: add missing arg types * refactor: add guard for undefined field * refactor: fix ChangePassword prop types * refactor: fix MUI import paths * refactor: add missing arg type * refactor: fix showDialog prop type * refactor: remove unused openUpdateDialog prop * refactor: add missing non-null assertion * refactor: remove unused types prop * refactor: stricten API error handler types * refactor: add missing undefined check * refactor: add missing IProject id field * refactor: fix ConditionallyRender condition prop types * refactor: remove unused args * refactor: add AddVariant prop types * refactor: add types to UIContext * refactor: fix event arg type * refactor: add missing default impressionData field * refactor: fix handleDeleteEnvironment prop args * refactor: fix IFeatureMetrics field requirements * refactor: add missing element types to ConditionallyRender * refactor: remove unused ProjectAccess projectId prop * refactor: add missing undefined check * refactor: fix getCreateTogglePath arg type * refactor: add missing IStrategyPayload import * refactor: remove unused user arg * refactor: add missing event arg type * refactor: add missing style object types * refactor: improve userApiErrors prop type * refactor: the Dialogue onClose prop is optional * refactor: fix the AddonEvents setEventValue prop type
2022-02-25 10:55:39 +01:00
const toggleEnv = async (env: IProjectEnvironment) => {
if (env.projectVisible) {
const enabledEnvs = getEnabledEnvs(projectEnvironments);
if (enabledEnvs > 1) {
setSelectedEnvironment(env);
setHideDialog(true);
return;
}
setToastData({
title: 'One environment must be visible',
text: 'You must always have at least one visible environment per project',
type: 'error',
});
} else {
try {
await addEnvironmentToProject(projectId, env.name);
refetch();
setToastData({
title: 'Environment set as visible',
text: 'Environment successfully set as visible.',
type: 'success',
});
} catch (error) {
feat/rbac roles (#562) * feat: create screen * fix: import accordion summary * feat: add accordions * fix: add codebox * feat: select permissions * fix: permission checker * fix: update permission checker * feat: wire up role list * fix: change icon color in project roles list * fix: add color to icon in project roles * add confirm dialog on role deletion * feat: add created screen * fix: cleanup * fix: update access permissions * fix: update admin panel * feat: add edit screen * fix: use color from palette and show toast when fails * fix: refactor * feat: validation * feat: implement checked all * fix: experimental toast * fix: error handling * fix: toast * feat: unique name validation * fix: update toasts * fix: remove toast * fix: reset flag * fix: remove unused vars * fix: update tests * feat: add error icon for toast * fix: replace wrong import for setToastData * feat: Patch keying on ui to handle uniqueness for permissions across multiple envs * fix: hasAccess handles * * fix: update permission switch * fix: use flag for environments rbac * fix: do not include check all keys in payload * fix: filter roles * fix: account for new permissions in variants list * fix: use effect on length property * fix: set polling interval on user * 4.5.0-beta.0 * fix: set initial permissions correctly to avoid race condition * fix: handle activeEnvironment when it is null * fix: remove unused imports * fix: unused imports * fix: Include missing project in hasAccess for deleteinng a tag * fix: Move add/delete tag to use update feature permissions * fix: use rest parameter * fix: remove sandbox from scripts * 4.6.0-beta.1 * fix: remove loading deduping * fix: disable editing on builtin roles * fix: check all * fix: feature overview environment * fix: refetch user on project create * fix: update snaphots * fix: frontend permissions * fix: delete create confirm * fix: remove unused permission * 4.6.0-beta.4 * fix: update permissions * fix: permissions * fix: set error to string * 4.6.0-beta.5 * fix: add permissions for project view * fix: add permissions to useEffect deps * fix: update permission for move feature toggle * fix: add permissions data to useEffect * fix: move settings * fix: key on confetti * fix: refetch project permissions on environment create/delete * fix: optional coalescing error object * fix: remove logging error * fix: reorder disable importance in permissionbutton * fix: add project roles to menu * fix: add disabled check to revive * fix: update snapshots * fix: change text to select all * fix: change text to select * 4.6.0-beta.6 Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: sighphyre <liquidwicked64@gmail.com>
2022-01-14 15:50:02 +01:00
setToastApiError(errorMsg(true));
}
}
};
const onHideConfirm = async () => {
if (selectedEnvironment) {
try {
await removeEnvironmentFromProject(
projectId,
selectedEnvironment.name,
);
refetch();
setToastData({
title: 'Environment set as hidden',
text: 'Environment successfully set as hidden.',
type: 'success',
});
} catch (e) {
feat/rbac roles (#562) * feat: create screen * fix: import accordion summary * feat: add accordions * fix: add codebox * feat: select permissions * fix: permission checker * fix: update permission checker * feat: wire up role list * fix: change icon color in project roles list * fix: add color to icon in project roles * add confirm dialog on role deletion * feat: add created screen * fix: cleanup * fix: update access permissions * fix: update admin panel * feat: add edit screen * fix: use color from palette and show toast when fails * fix: refactor * feat: validation * feat: implement checked all * fix: experimental toast * fix: error handling * fix: toast * feat: unique name validation * fix: update toasts * fix: remove toast * fix: reset flag * fix: remove unused vars * fix: update tests * feat: add error icon for toast * fix: replace wrong import for setToastData * feat: Patch keying on ui to handle uniqueness for permissions across multiple envs * fix: hasAccess handles * * fix: update permission switch * fix: use flag for environments rbac * fix: do not include check all keys in payload * fix: filter roles * fix: account for new permissions in variants list * fix: use effect on length property * fix: set polling interval on user * 4.5.0-beta.0 * fix: set initial permissions correctly to avoid race condition * fix: handle activeEnvironment when it is null * fix: remove unused imports * fix: unused imports * fix: Include missing project in hasAccess for deleteinng a tag * fix: Move add/delete tag to use update feature permissions * fix: use rest parameter * fix: remove sandbox from scripts * 4.6.0-beta.1 * fix: remove loading deduping * fix: disable editing on builtin roles * fix: check all * fix: feature overview environment * fix: refetch user on project create * fix: update snaphots * fix: frontend permissions * fix: delete create confirm * fix: remove unused permission * 4.6.0-beta.4 * fix: update permissions * fix: permissions * fix: set error to string * 4.6.0-beta.5 * fix: add permissions for project view * fix: add permissions to useEffect deps * fix: update permission for move feature toggle * fix: add permissions data to useEffect * fix: move settings * fix: key on confetti * fix: refetch project permissions on environment create/delete * fix: optional coalescing error object * fix: remove logging error * fix: reorder disable importance in permissionbutton * fix: add project roles to menu * fix: add disabled check to revive * fix: update snapshots * fix: change text to select all * fix: change text to select * 4.6.0-beta.6 Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: sighphyre <liquidwicked64@gmail.com>
2022-01-14 15:50:02 +01:00
setToastApiError(errorMsg(false));
} finally {
setHideDialog(false);
}
}
};
const envIsDisabled = (projectName: string) => {
return isOss() && projectName === 'default';
};
const COLUMNS = useMemo(
() => [
{
Header: 'Name',
accessor: 'name',
Cell: ({ row: { original } }: any) => (
<EnvironmentNameCell environment={original} />
),
},
{
Header: 'Type',
accessor: 'type',
Cell: HighlightCell,
},
{
Header: 'Project API tokens',
accessor: (row: IProjectEnvironment) =>
row.projectApiTokenCount === 1
? '1 token'
: `${row.projectApiTokenCount} tokens`,
Cell: TextCell,
},
{
Header: 'Visible in project',
accessor: 'enabled',
align: 'center',
width: 1,
Cell: ({ row: { original } }: any) => (
<ActionCell>
<PermissionSwitch
tooltip={
original.projectVisible
? 'Hide environment and disable feature toggles'
: 'Make it visible'
}
size='medium'
disabled={envIsDisabled(original.name)}
projectId={projectId}
permission={UPDATE_PROJECT}
checked={original.projectVisible}
onChange={() => toggleEnv(original)}
/>
</ActionCell>
),
disableGlobalFilter: true,
},
],
[projectEnvironments],
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state: { globalFilter },
setGlobalFilter,
} = useTable(
{
columns: COLUMNS as any,
data: projectEnvironments,
disableSortBy: true,
},
useGlobalFilter,
);
const header = (
<PageHeader
title={`Environments (${rows.length})`}
actions={
<Search
initialValue={globalFilter}
onChange={setGlobalFilter}
/>
}
/>
);
return (
<PageContent header={header} isLoading={loading}>
2023-01-11 08:00:26 +01:00
<StyledDivContainer>
<ConditionallyRender
condition={Boolean(error)}
show={renderError()}
/>
<StyledAlert severity='info'>
2023-01-11 08:00:26 +01:00
<strong>Important!</strong> In order for your application to
retrieve configured activation strategies for a specific
environment, the application must use an environment
specific API token. You can look up the environment-specific{' '}
<Link to='/admin/api'>API tokens here</Link>.
2023-01-11 08:00:26 +01:00
<br />
<br />
Your administrator can configure an environment-specific API
token to be used in the SDK. If you are an administrator you
can <Link to='/admin/api'>create a new API token here</Link>
2023-01-11 08:00:26 +01:00
.
</StyledAlert>
<SearchHighlightProvider value={globalFilter}>
<Table {...getTableProps()} rowHeight='compact'>
2023-01-11 08:00:26 +01:00
<SortableTableHeader
headerGroups={headerGroups as any}
/>
2023-01-11 08:00:26 +01:00
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
2023-01-11 08:00:26 +01:00
prepareRow(row);
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
2023-01-11 08:00:26 +01:00
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
</TableRow>
);
})}
</TableBody>
</Table>
</SearchHighlightProvider>
<ConditionallyRender
condition={rows.length === 0}
show={
<ConditionallyRender
2023-01-11 08:00:26 +01:00
condition={globalFilter?.length > 0}
show={
2023-01-11 08:00:26 +01:00
<TablePlaceholder>
No environments found matching &ldquo;
{globalFilter}
&rdquo;
</TablePlaceholder>
}
elseShow={
<TablePlaceholder>
No environments available. Get started by
adding one.
</TablePlaceholder>
}
/>
2023-01-11 08:00:26 +01:00
}
/>
<EnvironmentHideDialog
environment={selectedEnvironment}
open={hideDialog}
setOpen={setHideDialog}
onConfirm={onHideConfirm}
/>
</StyledDivContainer>
</PageContent>
);
};
export default ProjectEnvironmentList;