import React, { useState, useCallback } from 'react'; import { connect } from 'react-redux'; import { Dispatch, compose } from 'redux'; import { requestUpdateStepSearchConfig, requestReplaceStep } from 'wdk-client/Actions/StrategyActions'; import { RootState } from 'wdk-client/Core/State/Types'; import { StepDetailProps, CombineUiStepTree } from 'wdk-client/Views/Strategy/Types'; import { useReviseOperatorConfigs, useCompatibleOperatorMetadata, OperatorMetadata, ReviseOperatorMenuGroup } from 'wdk-client/Utils/Operations'; import { Question } from 'wdk-client/Utils/WdkModel'; import Loading from 'wdk-client/Components/Loading'; import { StrategyDetails } from 'wdk-client/Utils/WdkUser'; import NotFound from 'wdk-client/Views/NotFound/NotFound'; import { emptyAction } from 'wdk-client/Core/WdkMiddleware'; import { Plugin } from 'wdk-client/Utils/ClientPlugin'; export interface ReviseOperatorMenuItem { display: React.ReactNode; value: string; iconClassName: string; } interface StateProps { questions?: Question[]; strategy?: StrategyDetails; } interface DispatchProps { dispatch: Dispatch; } type OwnProps = StepDetailProps; export type CombineStepDetailsProps = OwnProps & StateProps & DispatchProps; function CombineStepDetails(props: CombineStepDetailsProps) { return ( ); } function DefaultCombineStepDetails({ questions, strategy, stepTree, onClose, dispatch, }: CombineStepDetailsProps) { const { step } = stepTree; const compatibleOperatorMetadata = useCompatibleOperatorMetadata(questions, stepTree); const reviseOperatorConfigs = useReviseOperatorConfigs(questions, stepTree); const currentOperatorMetadata = compatibleOperatorMetadata && Object.values(compatibleOperatorMetadata).find( ({ searchName }) => searchName === step.searchName ); const currentOperatorParamName = currentOperatorMetadata && currentOperatorMetadata.paramName; const currentOperatorSearchName = currentOperatorMetadata && currentOperatorMetadata.searchName; const currentOperatorValue = currentOperatorMetadata && currentOperatorParamName && ( currentOperatorMetadata.reviseOperatorParamConfiguration.type === 'inline' ? step.searchConfig.parameters[currentOperatorParamName] : currentOperatorMetadata.paramValue ); return ( !questions || !strategy || !compatibleOperatorMetadata || !reviseOperatorConfigs || !currentOperatorMetadata || !currentOperatorSearchName || !currentOperatorValue ) ? : } type CombineStepDetailsFormProps = { questions: Question[]; strategy: StrategyDetails; onClose: () => void; dispatch: Dispatch; compatibleOperatorMetadata: Record; currentOperatorSearchName: string; currentOperatorValue: string; stepTree: CombineUiStepTree; reviseOperatorConfigs: ReviseOperatorMenuGroup[]; }; const CombineStepDetailsForm = ({ questions, strategy, onClose, dispatch, compatibleOperatorMetadata, currentOperatorSearchName, currentOperatorValue, stepTree, reviseOperatorConfigs }: CombineStepDetailsFormProps) => { const { step } = stepTree; const [ newOperatorValue, setNewOperatorValue ] = useState(currentOperatorValue); // If there is only one choice of operator value, and that operator requires a form, // jump directly to said form const [ isConfiguringOperatorParams, setIsConfiguringOperatorParams ] = useState( Object.values(compatibleOperatorMetadata).length === 1 && compatibleOperatorMetadata[newOperatorValue].reviseOperatorParamConfiguration.type === 'form' ); const { searchName: newOperatorSearchName, reviseOperatorParamConfiguration: reviseOperatorParamConfiguration, paramName: newOperatorParamName } = compatibleOperatorMetadata[newOperatorValue]; const ReviseOperatorForm = reviseOperatorParamConfiguration.type === 'form' && reviseOperatorParamConfiguration.FormComponent; return ( !isConfiguringOperatorParams ? (
{ e.preventDefault(); dispatch(() => { if (reviseOperatorParamConfiguration.type === 'inline') { onClose(); if (currentOperatorSearchName === newOperatorSearchName) { return requestUpdateStepSearchConfig(step.strategyId, step.id, { ...step.searchConfig, parameters: { ...step.searchConfig.parameters, [newOperatorParamName]: newOperatorValue } }); } else { const newOperatorQuestion = questions.find(({ urlSegment }) => urlSegment === newOperatorSearchName); if (!newOperatorQuestion || !newOperatorSearchName) { throw new Error(`Could not find "${newOperatorSearchName || 'Unknown'}" question.`); } // FIXME This is coupled to the order in which the answer parameters appear in the parameter list const answerParamValues = newOperatorQuestion.paramNames .slice(0, 2) .reduce( (memo, answerParamName) => ({ ...memo, [answerParamName]: '' }), {} as Record ); return requestReplaceStep( step.strategyId, step.id, { searchName: newOperatorSearchName, searchConfig: { ...step.searchConfig, parameters: { ...answerParamValues, [newOperatorParamName]: newOperatorValue } }, customName: newOperatorQuestion.displayName }, ); } } else { setIsConfiguringOperatorParams(true); return emptyAction; } }) }}>
{ reviseOperatorConfigs.map(config =>
{config.display}

) }
) : !ReviseOperatorForm ? : ); }; export default connect( (state: RootState, ownProps: OwnProps) => { const questions = state.globalData.questions; const strategyDetails = state.strategies.strategies[ownProps.stepTree.step.strategyId]; const strategy = strategyDetails && strategyDetails.strategy; return { questions, strategy }; })(CombineStepDetails); interface OperatorsProps { operators: ReviseOperatorMenuItem[]; currentValue: string; setValue: (newValue: string) => void; } function Operators({ currentValue, operators, setValue }: OperatorsProps) { const onChange = useCallback((e: React.ChangeEvent) => { setValue(e.target.value); }, [ setValue ]); return (
{operators.map(operator => { const id = `operator__${operator.value}`; return (
) })}
); }