Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
fd9bc10
59876 refactor(template): migrate template edit form to Formik
aicarma-artyom-maslov Jun 29, 2026
c94187b
59876 fix(template): render task form widgets as components
aicarma-artyom-maslov Jun 29, 2026
bcc0aca
59876 fix(template): clean up task form widget types
aicarma-artyom-maslov Jun 29, 2026
c9217f7
59876 refactor(template): bind task form fields to Formik and central…
aicarma-artyom-maslov Jul 1, 2026
0d7a4b6
Merge branch 'master' into frontend/template/59876__migrate_edit_form…
aicarma-artyom-maslov Jul 1, 2026
1be3808
59876 refactor(template): stabilize Formik task persistence
aicarma-artyom-maslov Jul 1, 2026
f00a36a
59876 fix(template): flush pending task patches on unmount and fix up…
aicarma-artyom-maslov Jul 1, 2026
2d2c587
59876 fix(template): flush pending edits to previous task on early ta…
aicarma-artyom-maslov Jul 1, 2026
c672017
59876 fix(template): detect task switch by uuid instead of object ref…
aicarma-artyom-maslov Jul 1, 2026
7c57948
59876 fix(template): fix task form stale field updates
aicarma-artyom-maslov Jul 1, 2026
f645756
59876 refactor(template): migrate Edit Template form to Formik
aicarma-artyom-maslov Jul 1, 2026
db15abd
59876 fix(template): keep Formik in sync with saga deactivation and t…
aicarma-artyom-maslov Jul 1, 2026
36832b0
59876 fix(template): preserve pending template edits on activation
aicarma-artyom-maslov Jul 1, 2026
c9bcf34
59876 fix(template): prevent stale autosave and lost Formik edits
aicarma-artyom-maslov Jul 1, 2026
b267cc9
59876 fix(template): preserve pending template edits during activation
aicarma-artyom-maslov Jul 1, 2026
32d055c
59876 fix(template): resolve nested task paths in pending Formik edits
aicarma-artyom-maslov Jul 1, 2026
5b1ff14
59876 fix(template): clean stale references synchronously on tasks/ki…
aicarma-artyom-maslov Jul 1, 2026
cb6fe05
59876 fix(template): run reference cleanup on nested task field edits
aicarma-artyom-maslov Jul 1, 2026
92f8499
59876 fix(template): preserve flushed Formik edits during reference c…
aicarma-artyom-maslov Jul 1, 2026
94befcd
59876 fix(template): deactivate active templates synchronously on edit
aicarma-artyom-maslov Jul 1, 2026
481e26c
59876 fix(template): prevent autosave on unmount after discarding edits
aicarma-artyom-maslov Jul 1, 2026
3594286
59876 fix(template): flush unmount edits and preserve server-added tasks
aicarma-artyom-maslov Jul 1, 2026
0f894d9
59876 fix(template): fix task merge on reinit and autosave after fail…
aicarma-artyom-maslov Jul 2, 2026
169a009
59876 fix(template): interleave server tasks on reinit and fix persis…
aicarma-artyom-maslov Jul 2, 2026
1a75351
59876 fix(template): harden form persist edge cases and split useTemp…
aicarma-artyom-maslov Jul 2, 2026
53cba7d
59876 refactor(template): split TemplateEdit and sync variables from …
aicarma-artyom-maslov Jul 2, 2026
e49f342
59876 fix(template): harden Formik persist retry, revert, and init ed…
aicarma-artyom-maslov Jul 2, 2026
28a33f3
59876 fix(template): defer autosave commit and fix init/retry failures
aicarma-artyom-maslov Jul 2, 2026
9ffaa62
59876 fix(template): harden autosave supersede, owners, and reference…
aicarma-artyom-maslov Jul 2, 2026
79d793f
59876 fix(template): cancel stale autosave on discard and sync persis…
aicarma-artyom-maslov Jul 2, 2026
2c41ba5
59876 fix(template): reset Saving status when patch saga is cancelled…
aicarma-artyom-maslov Jul 2, 2026
c582cd0
59876 fix(template): keep Saving status until the latest autosave com…
aicarma-artyom-maslov Jul 2, 2026
6edbc46
59876 fix(template): preserve create id from superseded autosaves
aicarma-artyom-maslov Jul 2, 2026
423e48c
59876 fix(template): keep create-session edits and sync Formik on act…
aicarma-artyom-maslov Jul 2, 2026
db682cb
59876 fix(template): isolate create flows and re-queue autosave after…
aicarma-artyom-maslov Jul 2, 2026
0b7b49e
59876 fix(template): mark Saving as soon as Formik has uncommitted edits
aicarma-artyom-maslov Jul 2, 2026
db4b80f
59876 fix(template): read live Formik tasks for create open and delay…
aicarma-artyom-maslov Jul 2, 2026
25aed62
Fix Formik values reset on patchTemplate failure in handleChangeIsAct…
macroscopeapp[bot] Jul 2, 2026
c10784a
59876 fix(template): stabilize create task open and defer discard aba…
aicarma-artyom-maslov Jul 2, 2026
7a06b45
Merge branch 'frontend/template/59876__migrate_edit_form_to_formik' o…
aicarma-artyom-maslov Jul 2, 2026
f0e60d9
59876 fix(template): preserve in-flight edits when activation toggle …
aicarma-artyom-maslov Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import { IntlShape } from 'react-intl';
import { useDispatch } from 'react-redux';

import { getEmptyField } from './utils/getEmptyField';
import { KickoffShareForm } from './KickoffShareForm';
Expand All @@ -9,48 +8,39 @@ import { isKickoffCleared } from './utils/isKickoffCleared';
import { KickoffMenu } from './KickoffMenu';
import { IntlMessages } from '../../IntlMessages';
import { EMoveDirections, EInputNameBackgroundColor } from '../../../types/workflow';
import { EExtraFieldMode, EExtraFieldType, IKickoff, IExtraField, ITemplate, ETemplateParts } from '../../../types/template';
import { EExtraFieldMode, EExtraFieldType, IKickoff, IExtraField, ETemplateParts } from '../../../types/template';
import { isArrayWithItems } from '../../../utils/helpers';
import { getNormalizeFieldsOrders, moveWorkflowField } from '../../../utils/workflows';
import { ExtraFieldsMap } from '../ExtraFields/utils/ExtraFieldsMap';
import { ExtraFieldIcon } from '../ExtraFields/utils/ExtraFieldIcon';
import { ExtraFieldIntl } from '../ExtraFields';
import { getEditedFields } from '../ExtraFields/utils/getEditedFields';
import { ETemplateStatus } from '../../../types/redux';
import { ExtraFieldsLabels } from '../ExtraFields/utils/ExtraFieldsLabels';
import { getEmptyKickoff } from '../../../utils/template';
import { useHashLink } from '../../../hooks/useHashLink';
import { useWorkflowNameVariables } from '../TaskForm/utils/getTaskVariables';

import styles from './KickoffRedux.css';
import { patchTemplate } from '../../../redux/actions';
import { InputWithVariables } from '../InputWithVariables';
import { useDatasetOptions } from '../ExtraFields/utils/useDatasetOptions';
import { useTemplateField } from '../useTemplateForm';

export interface IKickoffReduxProps {
template: ITemplate;
intl: IntlShape;
accountId: number;
templateStatus: ETemplateStatus;
setKickoff(value: IKickoff): void;
}

export function KickoffRedux({
template: { kickoff, wfNameTemplate },
intl: { formatMessage },
setKickoff,
accountId,
}: IKickoffReduxProps) {
const dispatch = useDispatch();
const { values, setFieldValue } = useTemplateField();
const { kickoff, wfNameTemplate } = values;
const [isOpen, setIsOpen] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement | null>(null);
const variables = useWorkflowNameVariables(kickoff);
const datasetOptions = useDatasetOptions(kickoff.fields);

const editTemplate = (templateFields: Partial<ITemplate>) => {
dispatch(patchTemplate({ changedFields: templateFields }));
};

useHashLink([
{
element: containerRef,
Expand All @@ -70,12 +60,12 @@ export function KickoffRedux({
};

const handleChangeKickoff = (newKickoff: IKickoff) => {
setKickoff(newKickoff);
setFieldValue('kickoff', newKickoff, false);
};

const handleClearKickoff = () => {
const newKickoff = { ...getEmptyKickoff() };
setKickoff(newKickoff);
handleChangeKickoff(newKickoff);
};

const handleCreateField = (type: EExtraFieldType) => {
Expand Down Expand Up @@ -125,9 +115,7 @@ export function KickoffRedux({
templateVariables={variables}
value={wfNameTemplate || ''}
onChange={(value: string) => {
editTemplate({
wfNameTemplate: value,
});
setFieldValue('wfNameTemplate', value, false);

return Promise.resolve(value);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import Switch from 'rc-switch';
import * as React from 'react';
import { useIntl } from 'react-intl';
import classnames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useSelector } from 'react-redux';
import { default as TextareaAutosize } from 'react-textarea-autosize';
import { useMemo, useState } from 'react';

import { patchTemplate } from '../../../../redux/actions';
import { getTemplateData } from '../../../../redux/selectors/template';
import { getIsUserSubsribed, getSubscriptionPlan } from '../../../../redux/selectors/user';
import { copyToClipboard } from '../../../../utils/helpers';
import { NotificationManager } from '../../../UI/Notifications';
Expand All @@ -21,6 +19,7 @@ import { trackShareKickoffForm } from '../../../../utils/analytics';
import { TPublicFormType } from '../../../../types/publicForms';
import { generateEmbedCode } from './utils/generateEmbedCode';
import { ESubscriptionPlan } from '../../../../types/account';
import { useTemplateField } from '../../useTemplateForm';

import styles from './KickoffShareForm.css';

Expand All @@ -29,12 +28,12 @@ interface IKickoffShareFormProps {
}

export function KickoffShareForm({ className }: IKickoffShareFormProps) {
const dispatch = useDispatch();
const { formatMessage } = useIntl();
const { values, setFieldValue } = useTemplateField();
const isSubscribed = useSelector(getIsUserSubsribed);
const subcriptionPlan = useSelector(getSubscriptionPlan);
const accessSharedForm = isSubscribed || subcriptionPlan === ESubscriptionPlan.Free;
const { publicUrl, isPublic, publicSuccessUrl, embedUrl, isEmbedded } = useSelector(getTemplateData);
const { publicUrl, isPublic, publicSuccessUrl, embedUrl, isEmbedded } = values;

const TABS: { id: TPublicFormType; label: string }[] = useMemo(
() => [
Expand Down Expand Up @@ -62,7 +61,9 @@ export function KickoffShareForm({ className }: IKickoffShareFormProps) {
}, [embedUrl]);

const editTemplate = (templateFields: Partial<ITemplate>) => {
dispatch(patchTemplate({ changedFields: templateFields }));
(Object.keys(templateFields) as (keyof ITemplate)[]).forEach((key) => {
setFieldValue(key as string, templateFields[key], false);
});
};

useDidUpdateEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@ import { IApplicationState } from '../../../types/redux';

import { IKickoffReduxProps, KickoffRedux } from './KickoffRedux';

type TStoreProps = Pick<IKickoffReduxProps, 'template' | 'accountId' | 'templateStatus'>;
type TStoreProps = Pick<IKickoffReduxProps, 'accountId'>;

export function mapStateToProps({
template: { data: template, status },
authUser: { account },
}: IApplicationState): TStoreProps {
return {
template,
templateStatus: status,
accountId: account.id || -1,
};
}

export const KickoffReduxContainer = injectIntl(connect(mapStateToProps)(KickoffRedux));
export const KickoffReduxContainer = injectIntl(connect<TStoreProps>(mapStateToProps)(KickoffRedux));
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import { useCheckDevice } from '../../../../hooks/useCheckDevice';

import { TTaskVariable } from '../../types';
import { ITemplateTask } from '../../../../types/template';
import { useTaskForm } from '../useTaskForm';

import styles from './ReturnTo.css';
import stylesTaskForm from '../TaskForm.css';

interface IReturnToProps {
variables: TTaskVariable[];
tasks: ITemplateTask[];
currentTaskRevertTask: string | null;
setCurrentTask(changedFields: Partial<ITemplateTask>): void;
taskAncestors: Set<string>;
}

Expand All @@ -29,8 +28,9 @@ interface IDropdownTask {
richLabel: ReactNode;
}

export function ReturnTo({ variables, tasks, currentTaskRevertTask, setCurrentTask, taskAncestors }: IReturnToProps) {
const [isReturnTo, setIsReturnTo] = useState(Boolean(currentTaskRevertTask));
export function ReturnTo({ variables, tasks, taskAncestors }: IReturnToProps) {
const { task, updateTask } = useTaskForm();
const [isReturnTo, setIsReturnTo] = useState(Boolean(task.revertTask));
const { formatMessage } = useIntl();
const { isMobile } = useCheckDevice();
const STYLES = {
Expand All @@ -57,7 +57,7 @@ export function ReturnTo({ variables, tasks, currentTaskRevertTask, setCurrentTa
const dropdownTaskList = useMemo(
() => [
...tasks
.filter((task: ITemplateTask) => taskAncestors.has(task.apiName))
.filter((ancestorTask: ITemplateTask) => taskAncestors.has(ancestorTask.apiName))
.map(({ name, apiName }) => {
return {
label: name,
Expand All @@ -74,25 +74,25 @@ export function ReturnTo({ variables, tasks, currentTaskRevertTask, setCurrentTa
);

const selectedTask =
currentTaskRevertTask && dropdownTaskList.find((task: IDropdownTask) => currentTaskRevertTask === task.apiName);
task.revertTask && dropdownTaskList.find((dropdownTask: IDropdownTask) => task.revertTask === dropdownTask.apiName);

const formatOptionLabel = (option: IDropdownTask, { context }: { context: string }) => {
return context === 'menu'
? getFormattedDropdownOption({
label: option.richLabel,
isSelected: option.apiName === currentTaskRevertTask,
isSelected: option.apiName === task.revertTask,
})
: option.richLabel;
};

const removeReturn = () => {
setIsReturnTo(false);
setCurrentTask({ revertTask: null });
updateTask({ revertTask: null });
};

const handleOptionChange = (option: IDropdownTask) => {
if (selectedTask && option.apiName === selectedTask.apiName) return;
setCurrentTask({ revertTask: option.apiName });
updateTask({ revertTask: option.apiName });
};

return (
Expand Down
Loading