1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-04 01:18:20 +02:00
unleash.unleash/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx
2023-06-19 11:31:08 +03:00

236 lines
8.6 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useNavigate } from 'react-router-dom';
import useToast from 'hooks/useToast';
import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy';
import {
featureStrategyDocsLink,
featureStrategyHelp,
formatFeaturePath,
createStrategyPayload,
featureStrategyDocsLinkLabel,
} from '../FeatureStrategyEdit/FeatureStrategyEdit';
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
import { ISegment } from 'interfaces/segment';
import { formatStrategyName } from 'utils/strategyNames';
import { useFormErrors } from 'hooks/useFormErrors';
import { createFeatureStrategy } from 'utils/createFeatureStrategy';
import { useStrategy } from 'hooks/api/getters/useStrategy/useStrategy';
import { useCollaborateData } from 'hooks/useCollaborateData';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { IFeatureToggle } from 'interfaces/featureToggle';
import { comparisonModerator } from '../featureStrategy.utils';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import useQueryParams from 'hooks/useQueryParams';
import useProject from 'hooks/api/getters/useProject/useProject';
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import { DEFAULT_STRATEGY } from '../../../project/Project/ProjectSettings/ProjectDefaultStrategySettings/ProjectEnvironment/ProjectEnvironmentDefaultStrategy/EditDefaultStrategy';
export const FeatureStrategyCreate = () => {
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const environmentId = useRequiredQueryParam('environmentId');
const strategyName = useRequiredQueryParam('strategyName');
const useDefaultStrategy: boolean = JSON.parse(
useQueryParams().get('defaultStrategy') || 'false'
);
const { project } = useProject(projectId);
const defaultStrategy = project.environments.find(
env => env.environment === environmentId
)?.defaultStrategy;
const { segments: allSegments } = useSegments();
const strategySegments = (allSegments || []).filter(segment => {
return defaultStrategy?.segments?.includes(segment.id);
});
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({});
const [segments, setSegments] = useState<ISegment[]>(strategySegments);
const { strategyDefinition } = useStrategy(strategyName);
const errors = useFormErrors();
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
const { addChange } = useChangeRequestApi();
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const { unleashUrl } = uiConfig;
const navigate = useNavigate();
const { feature, refetchFeature } = useFeature(projectId, featureId);
const ref = useRef<IFeatureToggle>(feature);
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const { refetch: refetchChangeRequests } =
usePendingChangeRequests(projectId);
const { trackEvent } = usePlausibleTracker();
const { data, staleDataNotification, forceRefreshCache } =
useCollaborateData<IFeatureToggle>(
{
unleashGetter: useFeature,
params: [projectId, featureId],
dataKey: 'feature',
refetchFunctionKey: 'refetchFeature',
options: {},
},
feature,
{
afterSubmitAction: refetchFeature,
},
comparisonModerator
);
useEffect(() => {
if (ref.current.name === '' && feature.name) {
forceRefreshCache(feature);
ref.current = feature;
}
}, [feature.name]);
useEffect(() => {
if (useDefaultStrategy) {
setStrategy((defaultStrategy as any) || DEFAULT_STRATEGY);
} else if (strategyDefinition) {
setStrategy(createFeatureStrategy(featureId, strategyDefinition));
}
}, [featureId, strategyDefinition, useDefaultStrategy]);
const onAddStrategy = async (payload: IFeatureStrategyPayload) => {
await addStrategyToFeature(
projectId,
featureId,
environmentId,
payload
);
setToastData({
title: 'Strategy created',
type: 'success',
confetti: true,
});
};
const onStrategyRequestAdd = async (payload: IFeatureStrategyPayload) => {
await addChange(projectId, environmentId, {
action: 'addStrategy',
feature: featureId,
payload,
});
// FIXME: segments in change requests
setToastData({
title: 'Strategy added to draft',
type: 'success',
confetti: true,
});
refetchChangeRequests();
};
const payload = createStrategyPayload(strategy, segments);
const onSubmit = async () => {
trackEvent('strategyTitle', {
props: {
hasTitle: Boolean(strategy.title),
on: 'create',
},
});
try {
if (isChangeRequestConfigured(environmentId)) {
await onStrategyRequestAdd(payload);
} else {
await onAddStrategy(payload);
}
refetchFeature();
navigate(formatFeaturePath(projectId, featureId));
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
const emptyFeature = !data || !data.project;
if (emptyFeature) return null;
return (
<FormTemplate
modal
title={formatStrategyName(strategyName)}
description={featureStrategyHelp}
documentationLink={featureStrategyDocsLink}
documentationLinkLabel={featureStrategyDocsLinkLabel}
formatApiCode={() =>
formatAddStrategyApiCode(
projectId,
featureId,
environmentId,
payload,
unleashUrl
)
}
>
<FeatureStrategyForm
projectId={projectId}
feature={data}
strategy={strategy}
setStrategy={setStrategy}
segments={segments}
setSegments={setSegments}
environmentId={environmentId}
onSubmit={onSubmit}
loading={loading}
permission={CREATE_FEATURE_STRATEGY}
errors={errors}
isChangeRequest={isChangeRequestConfigured(environmentId)}
/>
{staleDataNotification}
</FormTemplate>
);
};
export const formatCreateStrategyPath = (
projectId: string,
featureId: string,
environmentId: string,
strategyName: string,
defaultStrategy: boolean = false
): string => {
const params = new URLSearchParams({
environmentId,
strategyName,
defaultStrategy: String(defaultStrategy),
});
return `/projects/${projectId}/features/${featureId}/strategies/create?${params}`;
};
export const formatAddStrategyApiCode = (
projectId: string,
featureId: string,
environmentId: string,
strategy: Partial<IFeatureStrategy>,
unleashUrl?: string
): string => {
if (!unleashUrl) {
return '';
}
const url = `${unleashUrl}/api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies`;
const payload = JSON.stringify(strategy, undefined, 2);
return `curl --location --request POST '${url}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${payload}'`;
};