From 3774cf8b87f4cac54ca66e9ab203ee5f76cfe523 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Thu, 24 Apr 2025 16:49:30 +0300 Subject: [PATCH] feat: separate constraint edit/view screens (#9836) Separating constraing edit and view screens. Next PR is to start using these. --- .../ConstraintsList/ConstraintsListUtils.ts | 50 +++++++++ .../EditableConstraintsList.tsx | 101 ++++++++++++++++++ .../ViewableConstraintsList.tsx | 72 +++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ConstraintsListUtils.ts create mode 100644 frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx create mode 100644 frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ViewableConstraintsList.tsx diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ConstraintsListUtils.ts b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ConstraintsListUtils.ts new file mode 100644 index 0000000000..3ea2568643 --- /dev/null +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ConstraintsListUtils.ts @@ -0,0 +1,50 @@ +import type React from 'react'; +import { useImperativeHandle } from 'react'; +import type { IConstraint } from 'interfaces/strategy'; +import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; +import { useWeakMap } from 'hooks/useWeakMap'; +import { createEmptyConstraint } from 'component/common/LegacyConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; + +export interface IConstraintsListProps { + constraints: IConstraint[]; + setConstraints?: React.Dispatch>; +} + +export interface IConstraintsListRef { + addConstraint?: (contextName: string) => void; +} + +export interface IConstraintsListItemState { + new?: boolean; + editing?: boolean; +} + +export const useConstraintsList = ( + setConstraints: + | React.Dispatch> + | undefined, + ref: React.RefObject, +) => { + const state = useWeakMap(); + const { context } = useUnleashContext(); + + const addConstraint = + setConstraints && + ((contextName: string) => { + const constraint = createEmptyConstraint(contextName); + state.set(constraint, { editing: true, new: true }); + setConstraints((prev) => [...prev, constraint]); + }); + + useImperativeHandle(ref, () => ({ + addConstraint, + })); + + const onAdd = + addConstraint && + (() => { + addConstraint(context[0].name); + }); + + return { onAdd, state, context }; +}; diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx new file mode 100644 index 0000000000..744ceae626 --- /dev/null +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx @@ -0,0 +1,101 @@ +import { forwardRef } from 'react'; +import { styled } from '@mui/material'; +import type { IConstraint } from 'interfaces/strategy'; +import produce from 'immer'; +import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; +import { useWeakMap } from 'hooks/useWeakMap'; +import { constraintId } from 'component/common/LegacyConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; +import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList'; +import { EditableConstraintWrapper } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraintWrapper'; +import type { + IConstraintsListProps as IEditableConstraintsListProps, + IConstraintsListRef as IEditableConstraintsListRef, + IConstraintsListItemState as IEditableConstraintsListItemState, +} from './ConstraintsListUtils'; +import { useConstraintsList } from './ConstraintsListUtils'; + +export type { IEditableConstraintsListProps, IEditableConstraintsListRef }; + +export const editableConstraintsListId = 'editableConstraintsListId'; + +export const useEditableConstraintsList = useConstraintsList; + +const StyledContainer = styled('div')({ + width: '100%', + display: 'flex', + flexDirection: 'column', +}); + +export const EditableConstraintsList = forwardRef< + IEditableConstraintsListRef | undefined, + IEditableConstraintsListProps +>(({ constraints, setConstraints }, ref) => { + const { context } = useUnleashContext(); + const state = useWeakMap(); + + const onRemove = + setConstraints && + ((index: number) => { + const constraint = constraints[index]; + state.set(constraint, {}); + setConstraints( + produce((draft) => { + draft.splice(index, 1); + }), + ); + }); + + const onSave = + setConstraints && + ((index: number, constraint: IConstraint) => { + state.set(constraint, {}); + setConstraints( + produce((draft) => { + draft[index] = constraint; + }), + ); + }); + + const onAutoSave = + setConstraints && + ((id: string | undefined) => (constraint: IConstraint) => { + state.set(constraint, { editing: true }); + setConstraints( + produce((draft) => { + return draft.map((oldConstraint) => { + if (oldConstraint[constraintId] === id) { + return constraint; + } + return oldConstraint; + }); + }), + ); + }); + + const onCancel = (index: number) => { + const constraint = constraints[index]; + state.get(constraint)?.new && onRemove?.(index); + state.set(constraint, {}); + }; + + if (context.length === 0) { + return null; + } + + return ( + + + {constraints.map((constraint, index) => ( + + ))} + + + ); +}); diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ViewableConstraintsList.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ViewableConstraintsList.tsx new file mode 100644 index 0000000000..5eaca8b8e5 --- /dev/null +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/ViewableConstraintsList.tsx @@ -0,0 +1,72 @@ +import { forwardRef } from 'react'; +import { styled } from '@mui/material'; +import type { IConstraint } from 'interfaces/strategy'; +import produce from 'immer'; +import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; +import { useWeakMap } from 'hooks/useWeakMap'; +import { constraintId } from 'component/common/LegacyConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; +import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList'; +import { ConstraintAccordionView } from 'component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView'; +import type { + IConstraintsListProps as IViewableConstraintsListProps, + IConstraintsListRef as IViewableConstraintsListRef, + IConstraintsListItemState as IViewableConstraintsListItemState, +} from './ConstraintsListUtils'; +import { useConstraintsList } from './ConstraintsListUtils'; + +export type { IViewableConstraintsListProps, IViewableConstraintsListRef }; + +export const viewableConstraintsListId = 'viewableConstraintsListId'; + +export const useViewableConstraintsList = useConstraintsList; + +const StyledContainer = styled('div')({ + width: '100%', + display: 'flex', + flexDirection: 'column', +}); + +export const ViewableConstraintsList = forwardRef< + IViewableConstraintsListRef | undefined, + IViewableConstraintsListProps +>(({ constraints, setConstraints }, ref) => { + const { context } = useUnleashContext(); + const state = useWeakMap(); + + const onEdit = + setConstraints && + ((constraint: IConstraint) => { + state.set(constraint, { editing: true }); + }); + + const onRemove = + setConstraints && + ((index: number) => { + const constraint = constraints[index]; + state.set(constraint, {}); + setConstraints( + produce((draft) => { + draft.splice(index, 1); + }), + ); + }); + + if (context.length === 0) { + return null; + } + + return ( + + + {constraints.map((constraint, index) => ( + + ))} + + + ); +});