-
-
- Stickiness
+
+ Stickiness
+
-
-
+
+
-
-
- GroupId
+
+ GroupId
+
-
-
+
+
{
+ const { uiConfig } = useUiConfig();
+ const { setToastData, setToastApiError } = useToast();
+ const history = useHistory();
+ const { createSegment, loading } = useSegmentsApi();
+ const { refetchSegments } = useSegments();
+
+ const {
+ name,
+ setName,
+ description,
+ setDescription,
+ constraints,
+ setConstraints,
+ getSegmentPayload,
+ errors,
+ clearErrors,
+ } = useSegmentForm();
+
+ const hasValidConstraints = useConstraintsValidation(constraints);
+
+ const formatApiCode = () => {
+ return `curl --location --request POST '${
+ uiConfig.unleashUrl
+ }/api/admin/segments' \\
+--header 'Authorization: INSERT_API_KEY' \\
+--header 'Content-Type: application/json' \\
+--data-raw '${JSON.stringify(getSegmentPayload(), undefined, 2)}'`;
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ clearErrors();
+ try {
+ await createSegment(getSegmentPayload());
+ await refetchSegments();
+ history.push('/segments/');
+ setToastData({
+ title: 'Segment created',
+ confetti: true,
+ type: 'success',
+ });
+ } catch (error: unknown) {
+ setToastApiError(formatUnknownError(error));
+ }
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+export const segmentsFormDescription = `
+ Segments make it easy for you to define which of your users should get access to a feature.
+ A segment is a reusable collection of constraints.
+ You can create and apply a segment when configuring activation strategies for a feature toggle or at any time from the segments page in the navigation menu.
+`;
+
+// TODO(olav): Update link when the segments docs are ready.
+export const segmentsFormDocsLink = 'https://docs.getunleash.io';
diff --git a/frontend/src/component/segments/EditSegment/EditSegment.tsx b/frontend/src/component/segments/EditSegment/EditSegment.tsx
new file mode 100644
index 0000000000..62c961bffd
--- /dev/null
+++ b/frontend/src/component/segments/EditSegment/EditSegment.tsx
@@ -0,0 +1,103 @@
+import FormTemplate from 'component/common/FormTemplate/FormTemplate';
+import { UPDATE_SEGMENT } from 'component/providers/AccessProvider/permissions';
+import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
+import { useConstraintsValidation } from 'hooks/api/getters/useConstraintsValidation/useConstraintsValidation';
+import { useSegment } from 'hooks/api/getters/useSegment/useSegment';
+import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import useToast from 'hooks/useToast';
+import React from 'react';
+import { useHistory } from 'react-router-dom';
+import { formatUnknownError } from 'utils/formatUnknownError';
+import { useSegmentForm } from '../hooks/useSegmentForm';
+import { SegmentForm } from '../SegmentForm/SegmentForm';
+import {
+ segmentsFormDocsLink,
+ segmentsFormDescription,
+} from 'component/segments/CreateSegment/CreateSegment';
+import { UpdateButton } from 'component/common/UpdateButton/UpdateButton';
+
+export const EditSegment = () => {
+ const segmentId = useRequiredPathParam('segmentId');
+ const { segment } = useSegment(Number(segmentId));
+ const { uiConfig } = useUiConfig();
+ const { setToastData, setToastApiError } = useToast();
+ const history = useHistory();
+ const { updateSegment, loading } = useSegmentsApi();
+ const { refetchSegments } = useSegments();
+
+ const {
+ name,
+ setName,
+ description,
+ setDescription,
+ constraints,
+ setConstraints,
+ getSegmentPayload,
+ errors,
+ clearErrors,
+ } = useSegmentForm(
+ segment?.name,
+ segment?.description,
+ segment?.constraints
+ );
+
+ const hasValidConstraints = useConstraintsValidation(constraints);
+
+ const formatApiCode = () => {
+ return `curl --location --request PUT '${
+ uiConfig.unleashUrl
+ }/api/admin/segments/${segmentId}' \\
+--header 'Authorization: INSERT_API_KEY' \\
+--header 'Content-Type: application/json' \\
+--data-raw '${JSON.stringify(getSegmentPayload(), undefined, 2)}'`;
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ if (segment) {
+ e.preventDefault();
+ clearErrors();
+ try {
+ await updateSegment(segment.id, getSegmentPayload());
+ await refetchSegments();
+ history.push('/segments/');
+ setToastData({
+ title: 'Segment updated',
+ type: 'success',
+ });
+ } catch (error: unknown) {
+ setToastApiError(formatUnknownError(error));
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/component/segments/SegmentDelete/SegmentDelete.tsx b/frontend/src/component/segments/SegmentDelete/SegmentDelete.tsx
new file mode 100644
index 0000000000..a276ab8927
--- /dev/null
+++ b/frontend/src/component/segments/SegmentDelete/SegmentDelete.tsx
@@ -0,0 +1,43 @@
+import ConditionallyRender from 'component/common/ConditionallyRender';
+import { useStrategiesBySegment } from 'hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment';
+import { ISegment } from 'interfaces/segment';
+import React from 'react';
+import { SegmentDeleteConfirm } from './SegmentDeleteConfirm/SegmentDeleteConfirm';
+import { SegmentDeleteUsedSegment } from './SegmentDeleteUsedSegment/SegmentDeleteUsedSegment';
+
+interface ISegmentDeleteProps {
+ segment: ISegment;
+ open: boolean;
+ setDeldialogue: React.Dispatch
>;
+ handleDeleteSegment: (id: number) => Promise;
+}
+export const SegmentDelete = ({
+ segment,
+ open,
+ setDeldialogue,
+ handleDeleteSegment,
+}: ISegmentDeleteProps) => {
+ const { strategies } = useStrategiesBySegment(segment.id);
+ const canDeleteSegment = strategies?.length === 0;
+ return (
+
+ }
+ elseShow={
+
+ }
+ />
+ );
+};
diff --git a/frontend/src/component/segments/SegmentDeleteConfirm/SegmentDeleteConfirm.styles.ts b/frontend/src/component/segments/SegmentDelete/SegmentDeleteConfirm/SegmentDeleteConfirm.styles.ts
similarity index 61%
rename from frontend/src/component/segments/SegmentDeleteConfirm/SegmentDeleteConfirm.styles.ts
rename to frontend/src/component/segments/SegmentDelete/SegmentDeleteConfirm/SegmentDeleteConfirm.styles.ts
index 6c4bba514d..3faefca510 100644
--- a/frontend/src/component/segments/SegmentDeleteConfirm/SegmentDeleteConfirm.styles.ts
+++ b/frontend/src/component/segments/SegmentDelete/SegmentDeleteConfirm/SegmentDeleteConfirm.styles.ts
@@ -7,4 +7,9 @@ export const useStyles = makeStyles(theme => ({
deleteInput: {
marginTop: '1rem',
},
+ link: {
+ textDecoration: 'none',
+ color: theme.palette.primary.main,
+ fontWeight: theme.fontWeight.bold,
+ },
}));
diff --git a/frontend/src/component/segments/SegmentDeleteConfirm/SegmentDeleteConfirm.tsx b/frontend/src/component/segments/SegmentDelete/SegmentDeleteConfirm/SegmentDeleteConfirm.tsx
similarity index 88%
rename from frontend/src/component/segments/SegmentDeleteConfirm/SegmentDeleteConfirm.tsx
rename to frontend/src/component/segments/SegmentDelete/SegmentDeleteConfirm/SegmentDeleteConfirm.tsx
index ecfe5a61da..6f3605f0ed 100644
--- a/frontend/src/component/segments/SegmentDeleteConfirm/SegmentDeleteConfirm.tsx
+++ b/frontend/src/component/segments/SegmentDelete/SegmentDeleteConfirm/SegmentDeleteConfirm.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState } from 'react';
import Dialogue from 'component/common/Dialogue';
import Input from 'component/common/Input/Input';
import { useStyles } from './SegmentDeleteConfirm.styles';
@@ -9,8 +9,6 @@ interface ISegmentDeleteConfirmProps {
open: boolean;
setDeldialogue: React.Dispatch>;
handleDeleteSegment: (id: number) => Promise;
- confirmName: string;
- setConfirmName: React.Dispatch>;
}
export const SegmentDeleteConfirm = ({
@@ -18,10 +16,9 @@ export const SegmentDeleteConfirm = ({
open,
setDeldialogue,
handleDeleteSegment,
- confirmName,
- setConfirmName,
}: ISegmentDeleteConfirmProps) => {
const styles = useStyles();
+ const [confirmName, setConfirmName] = useState('');
const handleChange = (e: React.ChangeEvent) =>
setConfirmName(e.currentTarget.value);
@@ -37,7 +34,10 @@ export const SegmentDeleteConfirm = ({
open={open}
primaryButtonText="Delete segment"
secondaryButtonText="Cancel"
- onClick={() => handleDeleteSegment(segment.id)}
+ onClick={() => {
+ handleDeleteSegment(segment.id);
+ setConfirmName('');
+ }}
disabledPrimaryButton={segment?.name !== confirmName}
onClose={handleCancel}
formId={formId}
diff --git a/frontend/src/component/segments/SegmentDelete/SegmentDeleteUsedSegment/SegmentDeleteUsedSegment.tsx b/frontend/src/component/segments/SegmentDelete/SegmentDeleteUsedSegment/SegmentDeleteUsedSegment.tsx
new file mode 100644
index 0000000000..0cfbe278ab
--- /dev/null
+++ b/frontend/src/component/segments/SegmentDelete/SegmentDeleteUsedSegment/SegmentDeleteUsedSegment.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import Dialogue from 'component/common/Dialogue';
+import { useStyles } from '../SegmentDeleteConfirm/SegmentDeleteConfirm.styles';
+import { ISegment } from 'interfaces/segment';
+import { IFeatureStrategy } from 'interfaces/strategy';
+import { Link } from 'react-router-dom';
+import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
+import { formatStrategyName } from 'utils/strategyNames';
+
+interface ISegmentDeleteUsedSegmentProps {
+ segment: ISegment;
+ open: boolean;
+ setDeldialogue: React.Dispatch>;
+ strategies: IFeatureStrategy[] | undefined;
+}
+
+export const SegmentDeleteUsedSegment = ({
+ segment,
+ open,
+ setDeldialogue,
+ strategies,
+}: ISegmentDeleteUsedSegmentProps) => {
+ const styles = useStyles();
+
+ const handleCancel = () => {
+ setDeldialogue(false);
+ };
+
+ return (
+
+
+ The following feature toggles are using the{' '}
+ {segment.name} segment for their strategies:
+
+
+ {strategies?.map(strategy => (
+ -
+
+ {strategy.featureName!}{' '}
+ {formatStrategyNameParens(strategy)}
+
+
+ ))}
+
+
+ );
+};
+
+const formatStrategyNameParens = (strategy: IFeatureStrategy): string => {
+ if (!strategy.strategyName) {
+ return '';
+ }
+
+ return `(${formatStrategyName(strategy.strategyName)})`;
+};
diff --git a/frontend/src/component/segments/SegmentForm/SegmentForm.styles.ts b/frontend/src/component/segments/SegmentForm/SegmentForm.styles.ts
new file mode 100644
index 0000000000..9d9a66a241
--- /dev/null
+++ b/frontend/src/component/segments/SegmentForm/SegmentForm.styles.ts
@@ -0,0 +1,53 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles(theme => ({
+ container: {
+ maxWidth: '400px',
+ },
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%',
+ },
+ input: { width: '100%', marginBottom: '1rem' },
+ label: {
+ minWidth: '300px',
+ [theme.breakpoints.down(600)]: {
+ minWidth: 'auto',
+ },
+ },
+ buttonContainer: {
+ marginTop: 'auto',
+ display: 'flex',
+ justifyContent: 'flex-end',
+ },
+ cancelButton: {
+ marginLeft: '1.5rem',
+ },
+ inputDescription: {
+ marginBottom: '0.5rem',
+ },
+ formHeader: {
+ fontWeight: 'normal',
+ marginTop: '0',
+ },
+ header: {
+ fontWeight: 'normal',
+ },
+ errorMessage: {
+ fontSize: theme.fontSizes.smallBody,
+ color: theme.palette.error.main,
+ position: 'absolute',
+ top: '-8px',
+ },
+ userInfoContainer: {
+ margin: '-20px 0',
+ },
+ errorAlert: {
+ marginBottom: '1rem',
+ },
+ flexRow: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+}));
diff --git a/frontend/src/component/segments/SegmentForm/SegmentForm.tsx b/frontend/src/component/segments/SegmentForm/SegmentForm.tsx
new file mode 100644
index 0000000000..5c169df69a
--- /dev/null
+++ b/frontend/src/component/segments/SegmentForm/SegmentForm.tsx
@@ -0,0 +1,72 @@
+import { IConstraint } from 'interfaces/strategy';
+import { useStyles } from './SegmentForm.styles';
+import { SegmentFormStepOne } from '../SegmentFormStepOne/SegmentFormStepOne';
+import { SegmentFormStepTwo } from '../SegmentFormStepTwo/SegmentFormStepTwo';
+import React, { useState } from 'react';
+import { SegmentFormStepList } from 'component/segments/SegmentFormStepList/SegmentFormStepList';
+import ConditionallyRender from 'component/common/ConditionallyRender';
+
+export type SegmentFormStep = 1 | 2;
+interface ISegmentProps {
+ name: string;
+ description: string;
+ constraints: IConstraint[];
+ setName: React.Dispatch>;
+ setDescription: React.Dispatch>;
+ setConstraints: React.Dispatch>;
+ handleSubmit: (e: any) => void;
+ errors: { [key: string]: string };
+ mode: 'Create' | 'Edit';
+ clearErrors: () => void;
+}
+
+export const SegmentForm: React.FC = ({
+ children,
+ name,
+ description,
+ constraints,
+ setName,
+ setDescription,
+ setConstraints,
+ handleSubmit,
+ errors,
+ clearErrors,
+}) => {
+ const styles = useStyles();
+ const totalSteps = 2;
+ const [currentStep, setCurrentStep] = useState(1);
+
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/frontend/src/component/segments/SegmentFormStepList/SegmentFormStepList.styles.ts b/frontend/src/component/segments/SegmentFormStepList/SegmentFormStepList.styles.ts
new file mode 100644
index 0000000000..d31272e625
--- /dev/null
+++ b/frontend/src/component/segments/SegmentFormStepList/SegmentFormStepList.styles.ts
@@ -0,0 +1,40 @@
+import { makeStyles } from '@material-ui/core/styles';
+import { formTemplateSidebarWidth } from 'component/common/FormTemplate/FormTemplate.styles';
+
+export const useStyles = makeStyles(theme => ({
+ container: {
+ display: 'flex',
+ position: 'absolute',
+ alignItems: 'center',
+ justifyContent: 'center',
+ top: 30,
+ left: 0,
+ right: formTemplateSidebarWidth,
+ [theme.breakpoints.down(1100)]: {
+ right: 0,
+ },
+ },
+ steps: {
+ position: 'relative',
+ borderRadius: 10,
+ background: '#fff',
+ padding: '0.6rem 1.5rem',
+ margin: 'auto',
+ display: 'flex',
+ alignItems: 'center',
+ },
+ stepsText: {
+ marginRight: 15,
+ fontSize: theme.fontSizes.smallBody,
+ },
+ circle: {
+ fill: theme.palette.primary.main,
+ fontSize: 17,
+ opacity: 0.4,
+ transition: 'opacity 0.4s ease',
+ },
+ filledCircle: {
+ opacity: 1,
+ fontSize: 20,
+ },
+}));
diff --git a/frontend/src/component/segments/SegmentFormStepList/SegmentFormStepList.tsx b/frontend/src/component/segments/SegmentFormStepList/SegmentFormStepList.tsx
new file mode 100644
index 0000000000..7c416be4af
--- /dev/null
+++ b/frontend/src/component/segments/SegmentFormStepList/SegmentFormStepList.tsx
@@ -0,0 +1,40 @@
+import { FiberManualRecord } from '@material-ui/icons';
+import { useStyles } from './SegmentFormStepList.styles';
+import React from 'react';
+import classNames from 'classnames';
+
+interface ISegmentFormStepListProps {
+ total: number;
+ current: number;
+}
+
+export const SegmentFormStepList: React.FC = ({
+ total,
+ current,
+}) => {
+ const styles = useStyles();
+
+ // Create a list with all the step numbers, e.g. [1, 2, 3].
+ const steps: number[] = Array.from({ length: total }).map((_, i) => {
+ return i + 1;
+ });
+
+ return (
+
+
+
+ Step {current} of {total}
+
+ {steps.map(step => (
+
+ ))}
+
+
+ );
+};
diff --git a/frontend/src/component/segments/SegmentFormStepOne/SegmentFormStepOne.styles.ts b/frontend/src/component/segments/SegmentFormStepOne/SegmentFormStepOne.styles.ts
new file mode 100644
index 0000000000..33f994c199
--- /dev/null
+++ b/frontend/src/component/segments/SegmentFormStepOne/SegmentFormStepOne.styles.ts
@@ -0,0 +1,50 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles(theme => ({
+ container: {
+ maxWidth: '400px',
+ },
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%',
+ },
+ input: { width: '100%', marginBottom: '1rem' },
+ label: {
+ minWidth: '300px',
+ [theme.breakpoints.down(600)]: {
+ minWidth: 'auto',
+ },
+ },
+ buttonContainer: {
+ marginTop: 'auto',
+ display: 'flex',
+ justifyContent: 'flex-end',
+ },
+ cancelButton: {
+ marginLeft: '1.5rem',
+ color: theme.palette.primary.light,
+ },
+ inputDescription: {
+ marginBottom: '0.5rem',
+ },
+ header: {
+ fontWeight: 'normal',
+ },
+ errorMessage: {
+ fontSize: theme.fontSizes.smallBody,
+ color: theme.palette.error.main,
+ position: 'absolute',
+ top: '-8px',
+ },
+ userInfoContainer: {
+ margin: '-20px 0',
+ },
+ errorAlert: {
+ marginBottom: '1rem',
+ },
+ flexRow: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+}));
diff --git a/frontend/src/component/segments/SegmentFormStepOne/SegmentFormStepOne.tsx b/frontend/src/component/segments/SegmentFormStepOne/SegmentFormStepOne.tsx
new file mode 100644
index 0000000000..21180f1b6e
--- /dev/null
+++ b/frontend/src/component/segments/SegmentFormStepOne/SegmentFormStepOne.tsx
@@ -0,0 +1,83 @@
+import { Button } from '@material-ui/core';
+import Input from 'component/common/Input/Input';
+import React from 'react';
+import { useHistory } from 'react-router-dom';
+import { useStyles } from 'component/segments/SegmentFormStepOne/SegmentFormStepOne.styles';
+import { SegmentFormStep } from '../SegmentForm/SegmentForm';
+
+interface ISegmentFormPartOneProps {
+ name: string;
+ description: string;
+ setName: React.Dispatch>;
+ setDescription: React.Dispatch>;
+ errors: { [key: string]: string };
+ clearErrors: () => void;
+ setCurrentStep: React.Dispatch>;
+}
+
+export const SegmentFormStepOne: React.FC = ({
+ children,
+ name,
+ description,
+ setName,
+ setDescription,
+ errors,
+ clearErrors,
+ setCurrentStep,
+}) => {
+ const history = useHistory();
+ const styles = useStyles();
+
+ return (
+
+
+
+ What is the segment name?
+
+
setName(e.target.value)}
+ error={Boolean(errors.name)}
+ errorText={errors.name}
+ onFocus={() => clearErrors()}
+ autoFocus
+ required
+ />
+
+ What is the segment description?
+
+
setDescription(e.target.value)}
+ error={Boolean(errors.description)}
+ errorText={errors.description}
+ onFocus={() => clearErrors()}
+ />
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.styles.ts b/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.styles.ts
new file mode 100644
index 0000000000..dfbeaf045b
--- /dev/null
+++ b/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.styles.ts
@@ -0,0 +1,96 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles(theme => ({
+ container: {},
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%',
+ },
+ input: { width: '100%', marginBottom: '1rem' },
+ label: {
+ minWidth: '300px',
+ [theme.breakpoints.down(600)]: {
+ minWidth: 'auto',
+ },
+ },
+ buttonContainer: {
+ marginTop: 'auto',
+ display: 'flex',
+ justifyContent: 'flex-end',
+ borderTop: `1px solid ${theme.palette.grey[300]}`,
+ paddingTop: 15,
+ },
+ cancelButton: {
+ marginLeft: '1.5rem',
+ color: theme.palette.primary.light,
+ },
+ inputDescription: {
+ marginBottom: '1rem',
+ },
+ formHeader: {
+ fontWeight: 'normal',
+ marginTop: '0',
+ },
+ header: {
+ fontWeight: 'normal',
+ },
+ errorMessage: {
+ fontSize: theme.fontSizes.smallBody,
+ color: theme.palette.error.main,
+ position: 'absolute',
+ top: '-8px',
+ },
+ userInfoContainer: {
+ margin: '-20px 0',
+ },
+ errorAlert: {
+ marginBottom: '1rem',
+ },
+ flexRow: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ backButton: {
+ marginRight: 'auto',
+ color: theme.palette.primary.light,
+ },
+ addContextContainer: {
+ marginTop: '1rem',
+ borderBottom: `1px solid ${theme.palette.grey[300]}`,
+ paddingBottom: '2rem',
+ },
+ addContextButton: {
+ color: theme.palette.primary.dark,
+ background: 'transparent',
+ boxShadow: 'none',
+ border: '1px solid',
+ '&:hover': {
+ background: 'transparent',
+ boxShadow: 'none',
+ },
+ },
+ divider: {
+ borderStyle: 'solid',
+ borderColor: `${theme.palette.grey[300]}`,
+ marginTop: '1rem !important',
+ },
+ noConstraintText: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ marginTop: '6rem',
+ },
+ subtitle: {
+ fontSize: theme.fontSizes.bodySize,
+ color: theme.palette.grey[600],
+ maxWidth: 515,
+ marginBottom: 20,
+ wordBreak: 'break-word',
+ whiteSpace: 'normal',
+ textAlign: 'center',
+ },
+ constraintContainer: {
+ marginBlock: '2rem',
+ },
+}));
diff --git a/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx b/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx
new file mode 100644
index 0000000000..54c9d86cb9
--- /dev/null
+++ b/frontend/src/component/segments/SegmentFormStepTwo/SegmentFormStepTwo.tsx
@@ -0,0 +1,134 @@
+import React, { useRef, useState } from 'react';
+import { Button } from '@material-ui/core';
+import { Add } from '@material-ui/icons';
+import ConditionallyRender from 'component/common/ConditionallyRender';
+import PermissionButton from 'component/common/PermissionButton/PermissionButton';
+import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
+import { CreateUnleashContext } from 'component/context/CreateUnleashContext/CreateUnleashContext';
+import { CREATE_CONTEXT_FIELD } from 'component/providers/AccessProvider/permissions';
+import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
+import { IConstraint } from 'interfaces/strategy';
+import { useHistory } from 'react-router-dom';
+import { useStyles } from 'component/segments/SegmentFormStepTwo/SegmentFormStepTwo.styles';
+import {
+ ConstraintAccordionList,
+ IConstraintAccordionListRef,
+} from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
+import { SegmentFormStep } from '../SegmentForm/SegmentForm';
+import {
+ AutocompleteBox,
+ IAutocompleteBoxOption,
+} from 'component/common/AutocompleteBox/AutocompleteBox';
+
+interface ISegmentFormPartTwoProps {
+ constraints: IConstraint[];
+ setConstraints: React.Dispatch>;
+ setCurrentStep: React.Dispatch>;
+}
+
+export const SegmentFormStepTwo: React.FC = ({
+ children,
+ constraints,
+ setConstraints,
+ setCurrentStep,
+}) => {
+ const constraintsAccordionListRef = useRef();
+ const history = useHistory();
+ const styles = useStyles();
+ const { context = [] } = useUnleashContext();
+ const [open, setOpen] = useState(false);
+
+ const autocompleteOptions = context.map(c => ({
+ value: c.name,
+ label: c.name,
+ }));
+
+ const onChange = ([option]: IAutocompleteBoxOption[]) => {
+ constraintsAccordionListRef.current?.addConstraint?.(option.value);
+ };
+
+ return (
+
+
+
+
+ Select the context fields you want to include in the
+ segment.
+
+
+ Use a predefined context field:
+
+
+
+
+
+ ...or add a new context field:
+
+
setOpen(false)}
+ open={open}
+ >
+ setOpen(false)}
+ onCancel={() => setOpen(false)}
+ modal
+ />
+
+
}
+ onClick={() => setOpen(true)}
+ >
+ Add context field
+
+
+
+
+
+ Start adding context fields by selecting an
+ option from above, or you can create a new
+ context field and use it right away
+
+
+ }
+ />
+
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+};
diff --git a/frontend/src/component/segments/SegmentList/SegmentList.styles.ts b/frontend/src/component/segments/SegmentList/SegmentList.styles.ts
index 1aa31752ff..1788c1678d 100644
--- a/frontend/src/component/segments/SegmentList/SegmentList.styles.ts
+++ b/frontend/src/component/segments/SegmentList/SegmentList.styles.ts
@@ -1,33 +1,31 @@
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
- main: {
- paddingBottom: '2rem',
- },
- container: {
+ empty: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
- marginTop: '5rem',
+ marginBlock: '5rem',
},
title: {
fontSize: theme.fontSizes.mainHeader,
- marginBottom: 12,
+ marginBottom: '1.25rem',
},
subtitle: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.grey[600],
maxWidth: 515,
marginBottom: 20,
- wordBreak: 'break-all',
- whiteSpace: 'normal',
+ textAlign: 'center',
},
tableRow: {
background: '#F6F6FA',
borderRadius: '8px',
},
paramButton: {
- color: theme.palette.primary.dark,
+ textDecoration: 'none',
+ color: theme.palette.primary.main,
+ fontWeight: theme.fontWeight.bold,
},
cell: {
borderBottom: 'none',
diff --git a/frontend/src/component/segments/SegmentList/SegmentList.tsx b/frontend/src/component/segments/SegmentList/SegmentList.tsx
index 32cd92f9ef..291703a195 100644
--- a/frontend/src/component/segments/SegmentList/SegmentList.tsx
+++ b/frontend/src/component/segments/SegmentList/SegmentList.tsx
@@ -18,7 +18,6 @@ import { SegmentListItem } from './SegmentListItem/SegmentListItem';
import { ISegment } from 'interfaces/segment';
import { useStyles } from './SegmentList.styles';
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
-import { SegmentDeleteConfirm } from '../SegmentDeleteConfirm/SegmentDeleteConfirm';
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
@@ -27,17 +26,17 @@ import ConditionallyRender from 'component/common/ConditionallyRender';
import HeaderTitle from 'component/common/HeaderTitle';
import PageContent from 'component/common/PageContent';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
+import { SegmentDelete } from '../SegmentDelete/SegmentDelete';
export const SegmentsList = () => {
const history = useHistory();
const { hasAccess } = useContext(AccessContext);
- const { segments, refetchSegments } = useSegments();
+ const { segments = [], refetchSegments } = useSegments();
const { deleteSegment } = useSegmentsApi();
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
usePagination(segments, 10);
const [currentSegment, setCurrentSegment] = useState