mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-24 17:51:14 +02:00
* minor archive table updates * archived date cell * archive import paths * move project health table files * fix: align actions cells * simplify health table row mapping * fix project pages browser tab title * initial draft of virtualized table component * refactor: virtualized table common component * fix: health report name cell width * refactor: report cell paths
238 lines
8.9 KiB
TypeScript
238 lines
8.9 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||
import { useStyles } from './ProjectEnvironment.styles';
|
||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||
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 { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||
import { FormControlLabel, FormGroup, Alert } from '@mui/material';
|
||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||
import EnvironmentDisableConfirm from './EnvironmentDisableConfirm/EnvironmentDisableConfirm';
|
||
import { Link } from 'react-router-dom';
|
||
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
||
import { IProjectEnvironment } from 'interfaces/environments';
|
||
import { getEnabledEnvs } from './helpers';
|
||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||
import { useThemeStyles } from 'themes/themeStyles';
|
||
import { usePageTitle } from 'hooks/usePageTitle';
|
||
|
||
interface IProjectEnvironmentListProps {
|
||
projectId: string;
|
||
projectName: string;
|
||
}
|
||
|
||
const ProjectEnvironmentList = ({
|
||
projectId,
|
||
projectName,
|
||
}: IProjectEnvironmentListProps) => {
|
||
usePageTitle(`Project environments – ${projectName}`);
|
||
// api state
|
||
const [envs, setEnvs] = useState<IProjectEnvironment[]>([]);
|
||
const { setToastData, setToastApiError } = useToast();
|
||
const { uiConfig } = useUiConfig();
|
||
const { environments, loading, error, refetchEnvironments } =
|
||
useEnvironments();
|
||
const { project, refetch: refetchProject } = useProject(projectId);
|
||
const { removeEnvironmentFromProject, addEnvironmentToProject } =
|
||
useProjectApi();
|
||
const { classes: themeStyles } = useThemeStyles();
|
||
|
||
// local state
|
||
const [selectedEnv, setSelectedEnv] = useState<IProjectEnvironment>();
|
||
const [confirmName, setConfirmName] = useState('');
|
||
const { classes: styles } = useStyles();
|
||
const { isOss } = useUiConfig();
|
||
|
||
useEffect(() => {
|
||
const envs = environments.map(e => ({
|
||
name: e.name,
|
||
enabled: project?.environments.includes(e.name),
|
||
}));
|
||
|
||
setEnvs(envs);
|
||
}, [environments, project?.environments]);
|
||
|
||
const refetch = () => {
|
||
refetchEnvironments();
|
||
refetchProject();
|
||
};
|
||
|
||
const renderError = () => {
|
||
return (
|
||
<ApiError
|
||
onClick={refetch}
|
||
className={styles.apiError}
|
||
text="Error fetching environments"
|
||
/>
|
||
);
|
||
};
|
||
|
||
const errorMsg = (enable: boolean): string => {
|
||
return `Got an API error when trying to ${
|
||
enable ? 'enable' : 'disable'
|
||
} the environment.`;
|
||
};
|
||
|
||
const toggleEnv = async (env: IProjectEnvironment) => {
|
||
if (env.enabled) {
|
||
const enabledEnvs = getEnabledEnvs(envs);
|
||
|
||
if (enabledEnvs > 1) {
|
||
setSelectedEnv(env);
|
||
return;
|
||
}
|
||
setToastData({
|
||
title: 'One environment must be active',
|
||
text: 'You must always have at least one active environment per project',
|
||
type: 'error',
|
||
});
|
||
} else {
|
||
try {
|
||
await addEnvironmentToProject(projectId, env.name);
|
||
setToastData({
|
||
title: 'Environment enabled',
|
||
text: 'Environment successfully enabled. You can now use it to segment strategies in your feature toggles.',
|
||
type: 'success',
|
||
});
|
||
} catch (error) {
|
||
setToastApiError(errorMsg(true));
|
||
}
|
||
}
|
||
refetch();
|
||
};
|
||
|
||
const handleDisableEnvironment = async () => {
|
||
if (selectedEnv && confirmName === selectedEnv.name) {
|
||
try {
|
||
await removeEnvironmentFromProject(projectId, selectedEnv.name);
|
||
setSelectedEnv(undefined);
|
||
setConfirmName('');
|
||
setToastData({
|
||
title: 'Environment disabled',
|
||
text: 'Environment successfully disabled.',
|
||
type: 'success',
|
||
});
|
||
} catch (e) {
|
||
setToastApiError(errorMsg(false));
|
||
}
|
||
|
||
refetch();
|
||
}
|
||
};
|
||
|
||
const handleCancelDisableEnvironment = () => {
|
||
setSelectedEnv(undefined);
|
||
setConfirmName('');
|
||
};
|
||
|
||
const genLabel = (env: IProjectEnvironment) => (
|
||
<div className={themeStyles.flexRow}>
|
||
<code>
|
||
<StringTruncator
|
||
text={env.name}
|
||
maxLength={50}
|
||
maxWidth="150"
|
||
/>
|
||
</code>
|
||
{/* This is ugly - but regular {" "} doesn't work here*/}
|
||
<p>
|
||
environment is{' '}
|
||
<strong>{env.enabled ? 'enabled' : 'disabled'}</strong>
|
||
</p>
|
||
</div>
|
||
);
|
||
|
||
const envIsDisabled = (projectName: string) => {
|
||
return isOss() && projectName === 'default';
|
||
};
|
||
|
||
const renderEnvironments = () => {
|
||
return (
|
||
<FormGroup>
|
||
{envs.map(env => (
|
||
<FormControlLabel
|
||
key={env.name}
|
||
label={genLabel(env)}
|
||
control={
|
||
<PermissionSwitch
|
||
tooltip={`${
|
||
env.enabled ? 'Disable' : 'Enable'
|
||
} environment`}
|
||
size="medium"
|
||
disabled={envIsDisabled(env.name)}
|
||
projectId={projectId}
|
||
permission={UPDATE_PROJECT}
|
||
checked={env.enabled}
|
||
onChange={() => toggleEnv(env)}
|
||
/>
|
||
}
|
||
/>
|
||
))}
|
||
</FormGroup>
|
||
);
|
||
};
|
||
|
||
return (
|
||
<PageContent
|
||
header={
|
||
<PageHeader
|
||
titleElement={`Configure environments for "${project?.name}" project`}
|
||
/>
|
||
}
|
||
isLoading={loading}
|
||
>
|
||
<ConditionallyRender
|
||
condition={uiConfig.flags.E}
|
||
show={
|
||
<div className={styles.container}>
|
||
<ConditionallyRender
|
||
condition={Boolean(error)}
|
||
show={renderError()}
|
||
/>
|
||
<Alert severity="info" style={{ marginBottom: '20px' }}>
|
||
<b>Important!</b> In order for your application to
|
||
retrieve configured activation strategies for a
|
||
specific environment, the application
|
||
<br /> must use an environment specific API key. You
|
||
can look up the environment-specific API keys{' '}
|
||
<Link to="/admin/api">here.</Link>
|
||
<br />
|
||
<br />
|
||
Your administrator can configure an
|
||
environment-specific API key to be used in the SDK.
|
||
If you are an administrator you can{' '}
|
||
<Link to="/admin/api">create a new API key.</Link>
|
||
</Alert>
|
||
<ConditionallyRender
|
||
condition={environments.length < 1 && !loading}
|
||
show={<div>No environments available.</div>}
|
||
elseShow={renderEnvironments()}
|
||
/>
|
||
<EnvironmentDisableConfirm
|
||
env={selectedEnv}
|
||
open={Boolean(selectedEnv)}
|
||
handleDisableEnvironment={handleDisableEnvironment}
|
||
handleCancelDisableEnvironment={
|
||
handleCancelDisableEnvironment
|
||
}
|
||
confirmName={confirmName}
|
||
setConfirmName={setConfirmName}
|
||
/>
|
||
</div>
|
||
}
|
||
elseShow={
|
||
<Alert security="success">
|
||
This feature has not been Unleashed for you yet.
|
||
</Alert>
|
||
}
|
||
/>
|
||
</PageContent>
|
||
);
|
||
};
|
||
|
||
export default ProjectEnvironmentList;
|