Skip to content
Snippets Groups Projects
Unverified Commit 8f86eb00 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Handle basic/implicit actions in the action editor (#28327)

parent cbde44de
No related branches found
No related tags found
No related merge requests found
Showing
with 504 additions and 155 deletions
......@@ -150,9 +150,9 @@ export interface FieldSettings {
export type FieldSettingsMap = Record<ParameterId, FieldSettings>;
export interface ActionFormSettings {
name?: string;
type: ActionDisplayType;
type?: ActionDisplayType;
description?: string;
fields: FieldSettingsMap;
fields?: FieldSettingsMap;
submitButtonLabel?: string;
submitButtonColor?: string;
confirmMessage?: string;
......
......@@ -49,10 +49,10 @@ export const createMockQueryAction = ({
export const createMockImplicitQueryAction = ({
creator = createMockUserInfo(),
...opts
}: Partial<WritebackImplicitQueryAction>): WritebackImplicitQueryAction => ({
}: Partial<WritebackImplicitQueryAction> = {}): WritebackImplicitQueryAction => ({
id: 1,
kind: "row/create",
name: "",
name: "Implicit Query Action",
description: "",
model_id: 1,
parameters: [],
......
......@@ -87,7 +87,7 @@ export const ActionForm = ({
const newOrder = destination?.index ?? source.index;
const reorderedFields = reorderFields(
formSettings.fields,
formSettings.fields ?? {},
oldOrder,
newOrder,
);
......@@ -117,6 +117,7 @@ export const ActionForm = ({
) => onSubmit?.(formValidationSchema.cast(values), actions);
if (isSettings) {
const fieldSettings = formSettings.fields || {};
return (
<FormProvider
initialValues={initialValues}
......@@ -155,7 +156,7 @@ export const ActionForm = ({
</InputContainer>
{isEditable && (
<FieldSettingsButtons
fieldSettings={formSettings.fields[field.name]}
fieldSettings={fieldSettings[field.name]}
onChange={handleChangeFieldSettings}
/>
)}
......
......@@ -14,14 +14,16 @@ import type { VisualizationProps } from "metabase-types/types/Visualization";
import type { Dispatch, State } from "metabase-types/store";
import type { ParameterValueOrArray } from "metabase-types/types/Parameter";
import { generateFieldSettingsFromParameters } from "metabase/actions/utils";
import {
generateFieldSettingsFromParameters,
setNumericValues,
} from "metabase/actions/utils";
import { getEditingDashcardId } from "metabase/dashboard/selectors";
import {
getDashcardParamValues,
getNotProvidedActionParameters,
shouldShowConfirmation,
setNumericValues,
} from "./utils";
import ActionVizForm from "./ActionVizForm";
import ActionButtonView from "./ActionButtonView";
......
......@@ -11,7 +11,6 @@ import type {
ParametersForActionExecution,
WritebackAction,
WritebackParameter,
FieldSettingsMap,
} from "metabase-types/api";
import type { ParameterValueOrArray } from "metabase-types/types/Parameter";
......@@ -45,19 +44,6 @@ function prepareParameter(
return [actionParameter.id, formatParameterValue(parameterValue)];
}
export function setNumericValues(
params: ParametersForActionExecution,
fieldSettings: FieldSettingsMap,
) {
Object.entries(params).forEach(([key, value]) => {
if (fieldSettings[key]?.fieldType === "number" && !isEmpty(value)) {
params[key] = Number(value) ?? null;
}
});
return params;
}
export function getDashcardParamValues(
dashcard: ActionDashboardCard,
parameterValues: { [id: string]: ParameterValueOrArray },
......
import styled from "@emotion/styled";
import Icon from "metabase/components/Icon";
export const Root = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
`;
export const IconSmall = styled(Icon)`
margin-left: 24px;
`;
import React from "react";
import Icon from "metabase/components/Icon";
import { Root } from "./ImplicitActionIcon.styled";
interface ImplicitActionIconProps {
size?: number;
}
function ImplicitActionIcon({ size = 14 }: ImplicitActionIconProps) {
const sizeSmall = size * 0.375;
const marginLeft = size * 0.75;
return (
<Root>
<Icon name="insight" size={sizeSmall} style={{ marginLeft }} />
<Icon name="insight" size={size} />
</Root>
);
}
export default ImplicitActionIcon;
export { default } from "./ImplicitActionIcon";
import { createContext, useContext } from "react";
import _ from "underscore";
import type { ActionFormSettings, WritebackAction } from "metabase-types/api";
import { getDefaultFormSettings } from "../../../utils";
import type { ActionCreatorUIProps } from "../types";
import type { EditableActionParams, EditorBodyProps } from "./types";
import { createEmptyWritebackAction } from "./utils";
export type ActionContextType = {
action: Partial<WritebackAction>;
formSettings: ActionFormSettings;
canSave: boolean;
isNew: boolean;
ui: ActionCreatorUIProps;
handleActionChange: (action: EditableActionParams) => void;
handleFormSettingsChange: (formSettings: ActionFormSettings) => void;
handleSetupExample: () => void;
renderEditorBody: (props: EditorBodyProps) => React.ReactNode;
};
export const ActionContext = createContext<ActionContextType>({
action: createEmptyWritebackAction(),
formSettings: getDefaultFormSettings(),
canSave: false,
isNew: true,
ui: {
canRename: true,
canChangeFieldSettings: true,
},
handleActionChange: _.noop,
handleFormSettingsChange: _.noop,
handleSetupExample: _.noop,
renderEditorBody: () => null,
});
export const useActionContext = () => useContext(ActionContext);
import React from "react";
import type { WritebackAction } from "metabase-types/api";
import ImplicitActionContextProvider, {
ImplicitActionContextProviderProps,
} from "./ImplicitActionContextProvider";
import QueryActionContextProvider, {
QueryActionContextProviderProps,
} from "./QueryActionContextProvider";
type Props = Omit<ImplicitActionContextProviderProps, "initialAction"> &
Omit<QueryActionContextProviderProps, "initialAction"> & {
initialAction?: WritebackAction;
};
function ActionContextProvider({ initialAction, ...props }: Props) {
if (initialAction?.type === "query") {
return (
<QueryActionContextProvider {...props} initialAction={initialAction} />
);
}
if (initialAction?.type === "implicit") {
return (
<ImplicitActionContextProvider {...props} initialAction={initialAction} />
);
}
// Fallback to "new query action" mode when the action type is not supported
return <QueryActionContextProvider {...props} />;
}
export default ActionContextProvider;
import styled from "@emotion/styled";
export const EditorBodyRoot = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
`;
export const EditorTitle = styled.h3`
margin-top: 1rem;
`;
import React, { useCallback, useMemo, useState } from "react";
import { t } from "ttag";
import _ from "underscore";
import ImplicitActionIcon from "metabase/actions/components/ImplicitActionIcon";
import type {
ActionFormSettings,
WritebackImplicitQueryAction,
} from "metabase-types/api";
import { getDefaultFormSettings } from "../../../../utils";
import type { ActionContextProviderProps } from "../types";
import { ActionContext } from "../ActionContext";
import {
EditorBodyRoot,
EditorTitle,
} from "./ImplicitActionContextProvider.styled";
export type ImplicitActionContextProviderProps = Omit<
ActionContextProviderProps,
"initialAction"
> & {
initialAction: WritebackImplicitQueryAction;
};
function EditorBody() {
return (
<EditorBodyRoot>
<ImplicitActionIcon size={64} />
<EditorTitle>{t`Auto tracking schema`}</EditorTitle>
</EditorBodyRoot>
);
}
function cleanFormSettings(formSettings?: ActionFormSettings) {
const formSettingsWithDefaults = getDefaultFormSettings(formSettings);
// For implicit actions fields are generated dynamically according to the current schema
// For now, we don't let to customize the form to avoid dealing with fields getting out of sync
return _.omit(formSettingsWithDefaults, "fields");
}
function ImplicitActionContextProvider({
initialAction,
children,
}: ImplicitActionContextProviderProps) {
const [formSettings, setFormSettings] = useState(
getDefaultFormSettings(initialAction.visualization_settings),
);
const handleFormSettingsChange = useCallback(
(nextFormSettings: ActionFormSettings) => {
setFormSettings(cleanFormSettings(nextFormSettings));
},
[],
);
const canSave = useMemo(() => {
return !_.isEqual(
cleanFormSettings(formSettings),
cleanFormSettings(initialAction?.visualization_settings),
);
}, [formSettings, initialAction?.visualization_settings]);
const value = useMemo(
() => ({
action: initialAction,
formSettings,
isNew: false,
canSave,
ui: {
canRename: false,
canChangeFieldSettings: false,
},
handleFormSettingsChange,
handleActionChange: _.noop,
handleSetupExample: _.noop,
renderEditorBody: EditorBody,
}),
[initialAction, formSettings, canSave, handleFormSettingsChange],
);
return (
<ActionContext.Provider value={value}>{children}</ActionContext.Provider>
);
}
export default ImplicitActionContextProvider;
export { default } from "./ImplicitActionContextProvider";
export * from "./ImplicitActionContextProvider";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { CreateQueryActionParams } from "metabase/entities/actions";
import QueryActionEditor from "metabase/actions/containers/ActionCreator/QueryActionEditor";
import type { DatabaseId, WritebackQueryAction } from "metabase-types/api";
import type Metadata from "metabase-lib/metadata/Metadata";
import type NativeQuery from "metabase-lib/queries/NativeQuery";
import { getTemplateTagParametersFromCard } from "metabase-lib/parameters/utils/template-tags";
import { getDefaultFormSettings } from "../../../utils";
import {
newQuestion,
convertActionToQuestion,
convertQuestionToAction,
} from "../utils";
import { ActionContext } from "./ActionContext";
import type { ActionContextProviderProps, EditorBodyProps } from "./types";
export interface QueryActionContextProviderProps
extends ActionContextProviderProps<WritebackQueryAction> {
metadata: Metadata;
databaseId?: DatabaseId;
}
const EXAMPLE_QUERY =
"UPDATE products\nSET rating = {{ my_new_value }}\nWHERE id = {{ my_primary_key }}";
function resolveQuestion(
action: WritebackQueryAction | undefined,
{ metadata, databaseId }: { metadata: Metadata; databaseId?: DatabaseId },
) {
return action
? convertActionToQuestion(action, metadata)
: newQuestion(metadata, databaseId);
}
function QueryActionContextProvider({
initialAction,
metadata,
databaseId,
children,
}: QueryActionContextProviderProps) {
const [question, setQuestion] = useState(
resolveQuestion(initialAction, { metadata, databaseId }),
);
const query = useMemo(() => question.query() as NativeQuery, [question]);
const [formSettings, setFormSettings] = useState(
getDefaultFormSettings(initialAction?.visualization_settings),
);
const action = useMemo(() => {
const action = convertQuestionToAction(question, formSettings);
return {
...initialAction,
...action,
type: "query" as const,
};
}, [initialAction, question, formSettings]);
const isNew = !initialAction && !question.isSaved();
const canSave = !query.isEmpty();
useEffect(() => {
setQuestion(resolveQuestion(initialAction, { metadata, databaseId }));
// we do not want to update this any time
// the props or metadata change, only if action id changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialAction?.id]);
const handleActionChange = useCallback(
(values: Partial<CreateQueryActionParams>) => {
let nextQuestion = question.clone();
if (values.name) {
nextQuestion = nextQuestion.setDisplayName(values.name);
}
if (values.description) {
nextQuestion = nextQuestion.setDescription(values.description);
}
setQuestion(nextQuestion);
},
[question],
);
const handleSetupExample = useCallback(() => {
const nextQuery = query.setQueryText(query.queryText() + EXAMPLE_QUERY);
setQuestion(question.setQuery(nextQuery));
}, [question, query]);
const handleQueryChange = useCallback((nextQuery: NativeQuery) => {
const nextQuestion = nextQuery.question();
const parameters = getTemplateTagParametersFromCard(nextQuestion.card());
setQuestion(nextQuestion.setParameters(parameters));
}, []);
const renderEditorBody = useCallback(
({ isEditable }: EditorBodyProps) => (
<QueryActionEditor
query={query}
isEditable={isEditable}
onChangeQuestionQuery={handleQueryChange}
/>
),
[query, handleQueryChange],
);
const value = useMemo(
() => ({
action,
formSettings,
isNew,
canSave,
ui: {
canRename: true,
canChangeFieldSettings: true,
},
handleActionChange,
handleFormSettingsChange: setFormSettings,
handleSetupExample,
renderEditorBody,
}),
[
action,
formSettings,
isNew,
canSave,
handleActionChange,
setFormSettings,
handleSetupExample,
renderEditorBody,
],
);
return (
<ActionContext.Provider value={value}>{children}</ActionContext.Provider>
);
}
export default QueryActionContextProvider;
export { default } from "./ActionContextProvider";
export { useActionContext } from "./ActionContext";
import type { ReactNode } from "react";
import type { WritebackAction } from "metabase-types/api";
export type EditableActionParams = Pick<
Partial<WritebackAction>,
"name" | "description"
>;
export type EditorBodyProps = {
isEditable: boolean;
};
export interface ActionContextProviderProps<T = WritebackAction> {
initialAction?: T;
children: ReactNode;
}
import type { WritebackQueryAction } from "metabase-types/api";
import { getDefaultFormSettings } from "./../../../utils";
export function createEmptyWritebackAction(): Partial<WritebackQueryAction> {
return {
name: "",
description: null,
type: "query",
public_uuid: null,
parameters: [],
dataset_query: {
type: "native",
// @ts-expect-error — this is a valid unsaved query state
// We could allow nulls in the query type, but that'd require a lot of changes
database: null,
native: {
query: "",
},
},
visualization_settings: getDefaultFormSettings(),
};
}
import React, { useState, useEffect, useCallback } from "react";
import React, { useState } from "react";
import { t } from "ttag";
import _ from "underscore";
import { connect } from "react-redux";
......@@ -6,34 +6,26 @@ import { connect } from "react-redux";
import Modal from "metabase/components/Modal";
import Actions, {
CreateQueryActionParams,
UpdateQueryActionParams,
CreateActionParams,
UpdateActionParams,
} from "metabase/entities/actions";
import Database from "metabase/entities/databases";
import Questions from "metabase/entities/questions";
import { getMetadata } from "metabase/selectors/metadata";
import type {
ActionFormSettings,
Card,
DatabaseId,
WritebackActionId,
WritebackAction,
WritebackQueryAction,
} from "metabase-types/api";
import type { State } from "metabase-types/store";
import Question from "metabase-lib/Question";
import NativeQuery from "metabase-lib/queries/NativeQuery";
import Metadata from "metabase-lib/metadata/Metadata";
import type Metadata from "metabase-lib/metadata/Metadata";
import { getTemplateTagParametersFromCard } from "metabase-lib/parameters/utils/template-tags";
import { getDefaultFormSettings } from "../../utils";
import {
newQuestion,
convertActionToQuestion,
convertQuestionToAction,
} from "./utils";
import { isSavedAction } from "../../utils";
import ActionContext, { useActionContext } from "./ActionContext";
import ActionCreatorView from "./ActionCreatorView";
import CreateActionForm, {
FormValues as CreateActionFormValues,
......@@ -47,7 +39,7 @@ interface OwnProps {
}
interface ActionLoaderProps {
action?: WritebackQueryAction;
initialAction?: WritebackAction;
}
interface ModelLoaderProps {
......@@ -60,8 +52,8 @@ interface StateProps {
}
interface DispatchProps {
onCreateAction: (params: CreateQueryActionParams) => void;
onUpdateAction: (params: UpdateQueryActionParams) => void;
onCreateAction: (params: CreateActionParams) => void;
onUpdateAction: (params: UpdateActionParams) => void;
}
export type ActionCreatorProps = OwnProps;
......@@ -82,119 +74,90 @@ const mapDispatchToProps = {
onUpdateAction: Actions.actions.update,
};
const EXAMPLE_QUERY =
"UPDATE products\nSET rating = {{ my_new_value }}\nWHERE id = {{ my_primary_key }}";
function resolveQuestion(
action: WritebackQueryAction | undefined,
{ metadata, databaseId }: { metadata: Metadata; databaseId?: DatabaseId },
) {
return action
? convertActionToQuestion(action, metadata)
: newQuestion(metadata, databaseId);
}
function ActionCreator({
action,
model,
databaseId,
metadata,
onCreateAction,
onUpdateAction,
onClose,
}: Props) {
const [question, setQuestion] = useState(
resolveQuestion(action, { metadata, databaseId }),
);
const [formSettings, setFormSettings] = useState<ActionFormSettings>(
getDefaultFormSettings(action?.visualization_settings),
);
const {
action,
formSettings,
isNew,
canSave,
ui: UIProps,
handleActionChange,
handleFormSettingsChange,
handleSetupExample,
renderEditorBody,
} = useActionContext();
const [showSaveModal, setShowSaveModal] = useState(false);
const query = question.query() as NativeQuery;
const isNew = !action && !question.isSaved();
const isEditable = model.canWriteActions();
useEffect(() => {
setQuestion(resolveQuestion(action, { metadata, databaseId }));
// we do not want to update this any time the props or metadata change, only if action id changes
}, [action?.id]); // eslint-disable-line react-hooks/exhaustive-deps
const handleChangeQuestionQuery = useCallback(
(newQuery: NativeQuery) => {
const newQuestion = newQuery.question();
const newParams = getTemplateTagParametersFromCard(newQuestion.card());
setQuestion(newQuestion.setQuery(newQuery).setParameters(newParams));
},
[setQuestion],
);
const handleClickSave = () => {
if (isNew) {
setShowSaveModal(true);
} else {
const action = convertQuestionToAction(question, formSettings);
onUpdateAction({ ...action, model_id: model.id() });
onClose?.();
const handleCreate = async (values: CreateActionFormValues) => {
if (action.type !== "query") {
return; // only query action creation is supported now
}
};
const handleCreate = async (values: CreateActionFormValues) => {
const action = convertQuestionToAction(question, formSettings);
await onCreateAction({
...action,
...values,
type: "query",
});
visualization_settings: formSettings,
} as WritebackQueryAction);
const nextQuestion = question
.setDisplayName(values.name)
.setDescription(values.description);
setQuestion(nextQuestion);
// Sync the editor state with data from save modal form
handleActionChange(values);
setShowSaveModal(false);
onClose?.();
};
const handleCloseNewActionModal = () => setShowSaveModal(false);
const handleUpdate = () => {
if (isSavedAction(action)) {
onUpdateAction({
...action,
model_id: model.id(),
visualization_settings: formSettings,
});
}
};
const handleClickExample = () => {
setQuestion(
question.setQuery(query.setQueryText(query.queryText() + EXAMPLE_QUERY)),
);
const handleClickSave = () => {
if (isNew) {
setShowSaveModal(true);
} else {
handleUpdate();
onClose?.();
}
};
if (!question || !metadata) {
return null;
}
const handleCloseNewActionModal = () => setShowSaveModal(false);
return (
<>
<ActionCreatorView
isNew={isNew}
isEditable={isEditable}
canSave={query.isEmpty()}
{...UIProps}
action={action}
question={question}
formSettings={formSettings}
onChangeQuestionQuery={handleChangeQuestionQuery}
onChangeName={newName =>
setQuestion(question => question.setDisplayName(newName))
}
onCloseModal={onClose}
onChangeFormSettings={setFormSettings}
canSave={canSave}
isNew={isNew}
isEditable={isEditable}
onChangeAction={handleActionChange}
onChangeFormSettings={handleFormSettingsChange}
onClickSave={handleClickSave}
onClickExample={handleClickExample}
/>
onClickExample={handleSetupExample}
onCloseModal={onClose}
>
{renderEditorBody({ isEditable })}
</ActionCreatorView>
{showSaveModal && (
<Modal title={t`New Action`} onClose={handleCloseNewActionModal}>
<CreateActionForm
initialValues={{
name: question.displayName() ?? "",
description: question.description(),
name: action.name,
description: action.description,
model_id: model.id(),
}}
onCreate={handleCreate}
......@@ -206,9 +169,33 @@ function ActionCreator({
);
}
function ActionCreatorWithContext({
initialAction,
metadata,
databaseId,
...props
}: Props) {
return (
<ActionContext
initialAction={initialAction}
databaseId={databaseId}
metadata={metadata}
>
<ActionCreator
{...props}
initialAction={initialAction}
databaseId={databaseId}
metadata={metadata}
/>
</ActionContext>
);
}
export default _.compose(
Actions.load({
id: (state: State, props: OwnProps) => props.actionId,
loadingAndErrorWrapper: false,
entityAlias: "initialAction",
}),
Questions.load({
id: (state: State, props: OwnProps) => props.modelId,
......@@ -216,4 +203,4 @@ export default _.compose(
}),
Database.loadList(),
connect(mapStateToProps, mapDispatchToProps),
)(ActionCreator);
)(ActionCreatorWithContext);
......@@ -15,6 +15,7 @@ type Props = {
name: string;
type: WritebackActionType;
isEditable: boolean;
canRename: boolean;
onChangeName: (name: string) => void;
onChangeType?: (type: WritebackActionType) => void;
actionButtons: React.ReactElement[];
......@@ -25,6 +26,7 @@ const OPTS = [{ value: "query", name: t`Query`, disabled: true }];
const ActionCreatorHeader = ({
name = t`New Action`,
isEditable,
canRename,
type,
onChangeName,
onChangeType,
......@@ -36,7 +38,7 @@ const ActionCreatorHeader = ({
<EditableText
initialValue={name}
onChange={onChangeName}
isDisabled={!isEditable}
isDisabled={!isEditable || !canRename}
/>
{!!onChangeType && (
<CompactSelect options={OPTS} value={type} onChange={onChangeType} />
......
......@@ -3,7 +3,6 @@ import { t } from "ttag";
import Button from "metabase/core/components/Button";
import ActionCreatorHeader from "metabase/actions/containers/ActionCreator/ActionCreatorHeader";
import QueryActionEditor from "metabase/actions/containers/ActionCreator/QueryActionEditor";
import FormCreator from "metabase/actions/containers/ActionCreator/FormCreator";
import {
DataReferenceTriggerButton,
......@@ -19,46 +18,45 @@ import {
import { isNotNull } from "metabase/core/utils/types";
import type { ActionFormSettings, WritebackAction } from "metabase-types/api";
import type NativeQuery from "metabase-lib/queries/NativeQuery";
import type Question from "metabase-lib/Question";
import type { SideView } from "./types";
import type { ActionCreatorUIProps, SideView } from "./types";
import InlineActionSettings, {
ActionSettingsTriggerButton,
} from "./InlineActionSettings";
interface ActionCreatorProps {
isNew: boolean;
interface ActionCreatorProps extends ActionCreatorUIProps {
action: Partial<WritebackAction>;
formSettings: ActionFormSettings;
canSave: boolean;
isNew: boolean;
isEditable: boolean;
action?: WritebackAction;
question: Question;
formSettings: ActionFormSettings;
children: React.ReactNode;
onChangeQuestionQuery: (query: NativeQuery) => void;
onChangeName: (name: string) => void;
onCloseModal?: () => void;
onChangeAction: (action: Partial<WritebackAction>) => void;
onChangeFormSettings: (formSettings: ActionFormSettings) => void;
onClickSave: () => void;
onClickExample: () => void;
onCloseModal?: () => void;
}
const DEFAULT_SIDE_VIEW: SideView = "actionForm";
export default function ActionCreatorView({
isNew,
canSave,
isEditable,
action,
question,
formSettings,
onChangeQuestionQuery,
onChangeName,
onCloseModal,
canSave,
isNew,
isEditable,
canRename,
canChangeFieldSettings,
children,
onChangeAction,
onChangeFormSettings,
onClickSave,
onClickExample,
onCloseModal,
}: ActionCreatorProps) {
const [activeSideView, setActiveSideView] =
useState<SideView>(DEFAULT_SIDE_VIEW);
......@@ -93,9 +91,10 @@ export default function ActionCreatorView({
<ModalLeft>
<ActionCreatorHeader
type="query"
name={question.displayName() ?? t`New Action`}
name={action.name ?? t`New Action`}
canRename={canRename}
isEditable={isEditable}
onChangeName={onChangeName}
onChangeName={name => onChangeAction({ name })}
actionButtons={[
<DataReferenceTriggerButton
key="dataReference"
......@@ -107,19 +106,13 @@ export default function ActionCreatorView({
/>,
].filter(isNotNull)}
/>
<EditorContainer>
<QueryActionEditor
query={question.query() as NativeQuery}
isEditable={isEditable}
onChangeQuestionQuery={onChangeQuestionQuery}
/>
</EditorContainer>
<EditorContainer>{children}</EditorContainer>
<ModalActions>
<Button onClick={onCloseModal} borderless>
{t`Cancel`}
</Button>
{isEditable && (
<Button primary disabled={canSave} onClick={onClickSave}>
<Button primary disabled={!canSave} onClick={onClickSave}>
{isNew ? t`Save` : t`Update`}
</Button>
)}
......@@ -127,9 +120,9 @@ export default function ActionCreatorView({
</ModalLeft>
{activeSideView === "actionForm" ? (
<FormCreator
params={question.parameters() ?? []}
params={action.parameters ?? []}
formSettings={formSettings}
isEditable={isEditable}
isEditable={isEditable && canChangeFieldSettings}
onChange={onChangeFormSettings}
onExampleClick={onClickExample}
/>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment