Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9379dbf

Browse files
committedMar 1, 2024·
Add editor warning; add workflow fetching on cold reload
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>
1 parent 412dc71 commit 9379dbf

File tree

5 files changed

+65
-22
lines changed

5 files changed

+65
-22
lines changed
 

‎common/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ export const DELETE_WORKFLOW_NODE_API_PATH = `${BASE_WORKFLOW_NODE_API_PATH}/del
3434
* MISCELLANEOUS
3535
*/
3636
export const NEW_WORKFLOW_ID_URL = 'new';
37+
export const START_FROM_SCRATCH_WORKFLOW_NAME = 'Start From Scratch';
38+
export const DEFAULT_NEW_WORKFLOW_NAME = 'new_workflow';

‎public/pages/workflow_detail/components/header.tsx

+15-5
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
import React, { useContext } from 'react';
77
import { useDispatch, useSelector } from 'react-redux';
8-
import { EuiPageHeader, EuiButton } from '@elastic/eui';
9-
import { Workflow } from '../../../../common';
8+
import { EuiPageHeader, EuiButton, EuiLoadingSpinner } from '@elastic/eui';
9+
import { DEFAULT_NEW_WORKFLOW_NAME, Workflow } from '../../../../common';
1010
import { saveWorkflow } from '../utils';
1111
import { rfContext, AppState, removeDirty } from '../../../store';
1212

1313
interface WorkflowDetailHeaderProps {
1414
tabs: any[];
15-
formattedWorkflowName: string;
15+
isNewWorkflow: boolean;
1616
workflow?: Workflow;
1717
}
1818

@@ -23,14 +23,24 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
2323

2424
return (
2525
<EuiPageHeader
26-
pageTitle={props.formattedWorkflowName}
26+
pageTitle={
27+
props.workflow ? (
28+
props.workflow.name
29+
) : props.isNewWorkflow && !props.workflow ? (
30+
DEFAULT_NEW_WORKFLOW_NAME
31+
) : (
32+
<EuiLoadingSpinner size="xl" />
33+
)
34+
}
2735
rightSideItems={[
36+
// TODO: add launch logic
2837
<EuiButton fill={false} onClick={() => {}}>
29-
Prototype
38+
Launch
3039
</EuiButton>,
3140
<EuiButton
3241
fill={false}
3342
disabled={!props.workflow || !isDirty}
43+
// TODO: if isNewWorkflow is true, clear the workflow cache if saving is successful.
3444
onClick={() => {
3545
// @ts-ignore
3646
saveWorkflow(props.workflow, reactFlowInstance);

‎public/pages/workflow_detail/workflow_detail.tsx

+27-10
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ import { EuiPage, EuiPageBody } from '@elastic/eui';
1212
import { BREADCRUMBS } from '../../utils';
1313
import { getCore } from '../../services';
1414
import { WorkflowDetailHeader } from './components';
15-
import { AppState } from '../../store';
15+
import { AppState, searchWorkflows } from '../../store';
1616
import { ResizableWorkspace } from './workspace';
1717
import { Launches } from './launches';
1818
import { Prototype } from './prototype';
19-
import { NEW_WORKFLOW_ID_URL } from '../../../common';
19+
import {
20+
DEFAULT_NEW_WORKFLOW_NAME,
21+
NEW_WORKFLOW_ID_URL,
22+
} from '../../../common';
2023

2124
export interface WorkflowDetailRouterProps {
2225
workflowId: string;
@@ -49,20 +52,21 @@ function replaceActiveTab(activeTab: string, props: WorkflowDetailProps) {
4952
* New, unsaved workflows are cached in the redux store and displayed here.
5053
*/
5154

52-
// TODO: if exiting the page, or if saving, clear the cached workflow. Can use redux clearCachedWorkflow()
5355
export function WorkflowDetail(props: WorkflowDetailProps) {
56+
const dispatch = useDispatch();
5457
const { workflows, cachedWorkflow } = useSelector(
5558
(state: AppState) => state.workflows
5659
);
60+
const { isDirty } = useSelector((state: AppState) => state.workspace);
5761

58-
const isNewWorkflow = props.match?.params?.workflowId === NEW_WORKFLOW_ID_URL;
59-
const workflow = isNewWorkflow
60-
? cachedWorkflow
61-
: workflows[props.match?.params?.workflowId];
62+
// selected workflow state
63+
const workflowId = props.match?.params?.workflowId;
64+
const isNewWorkflow = workflowId === NEW_WORKFLOW_ID_URL;
65+
const workflow = isNewWorkflow ? cachedWorkflow : workflows[workflowId];
6266
const workflowName = workflow
6367
? workflow.name
64-
: isNewWorkflow && !cachedWorkflow
65-
? 'new_workflow'
68+
: isNewWorkflow && !workflow
69+
? DEFAULT_NEW_WORKFLOW_NAME
6670
: '';
6771

6872
// tab state
@@ -92,6 +96,19 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
9296
]);
9397
});
9498

99+
// On initial load:
100+
// - fetch workflow, if there is an existing workflow ID
101+
// - add a window listener to warn users if they exit/refresh
102+
// without saving latest changes
103+
useEffect(() => {
104+
if (!isNewWorkflow) {
105+
// TODO: can optimize to only fetch a single workflow
106+
dispatch(searchWorkflows({ query: { match_all: {} } }));
107+
}
108+
window.onbeforeunload = (e) =>
109+
isDirty || isNewWorkflow ? true : undefined;
110+
}, []);
111+
95112
const tabs = [
96113
{
97114
id: WORKFLOW_DETAILS_TAB.EDITOR,
@@ -128,7 +145,7 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
128145
<EuiPageBody>
129146
<WorkflowDetailHeader
130147
workflow={workflow}
131-
formattedWorkflowName={workflowName}
148+
isNewWorkflow={isNewWorkflow}
132149
tabs={tabs}
133150
/>
134151
{selectedTabId === WORKFLOW_DETAILS_TAB.EDITOR && (

‎public/pages/workflows/new_workflow/new_workflow.tsx

+15-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
import { useDispatch } from 'react-redux';
1515
import { UseCase } from './use_case';
1616
import { getPresetWorkflows } from './presets';
17-
import { Workflow } from '../../../../common';
17+
import {
18+
DEFAULT_NEW_WORKFLOW_NAME,
19+
START_FROM_SCRATCH_WORKFLOW_NAME,
20+
Workflow,
21+
} from '../../../../common';
1822
import { cacheWorkflow } from '../../../store';
1923

2024
interface NewWorkflowProps {}
@@ -66,7 +70,7 @@ export function NewWorkflow(props: NewWorkflowProps) {
6670
dispatch(
6771
cacheWorkflow({
6872
...workflow,
69-
name: toSnakeCase(workflow.name),
73+
name: processWorkflowName(workflow.name),
7074
})
7175
)
7276
}
@@ -92,9 +96,15 @@ function fetchFilteredWorkflows(
9296
);
9397
}
9498

95-
// Utility fn to convert to snakecase. Used when caching the workflow
96-
// to make a valid name and cause less friction if users decide
97-
// to save it later on.
99+
// Utility fn to process workflow names from their presentable/readable titles
100+
// on the UI, to a valid name format.
101+
// This leads to less friction if users decide to save the name later on.
102+
function processWorkflowName(workflowName: string): string {
103+
return workflowName === START_FROM_SCRATCH_WORKFLOW_NAME
104+
? DEFAULT_NEW_WORKFLOW_NAME
105+
: toSnakeCase(workflowName);
106+
}
107+
98108
function toSnakeCase(text: string): string {
99109
return text
100110
.replace(/\W+/g, ' ')

‎public/pages/workflows/new_workflow/presets.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { Workflow, WorkspaceFlowState } from '../../../../common';
6+
import {
7+
START_FROM_SCRATCH_WORKFLOW_NAME,
8+
Workflow,
9+
WorkspaceFlowState,
10+
} from '../../../../common';
711

812
// TODO: fetch from the backend when the workflow library is complete.
913
/**
@@ -34,7 +38,7 @@ export function getPresetWorkflows(): Workflow[] {
3438
} as WorkspaceFlowState,
3539
},
3640
{
37-
name: 'Start From Scratch',
41+
name: START_FROM_SCRATCH_WORKFLOW_NAME,
3842
description:
3943
'Build your workflow from scratch according to your specific use cases. Start by adding components for your ingest or query needs.',
4044
useCase: '',

0 commit comments

Comments
 (0)
Please sign in to comment.