@@ -129,7 +129,7 @@ export default function Header(): JSX.Element {
data-testid="button-store"
>
-
Store
+
Store
)}
diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts
index 7e6e58abc..452830133 100644
--- a/src/frontend/src/constants/constants.ts
+++ b/src/frontend/src/constants/constants.ts
@@ -709,7 +709,7 @@ export const CREATE_API_KEY = `Don’t have an API key? Sign up at`;
export const STATUS_BUILD = "Build to validate status.";
export const STATUS_INACTIVE = "Execution blocked";
export const STATUS_BUILDING = "Building...";
-export const SAVED_HOVER = "Last saved at ";
+export const SAVED_HOVER = "Last saved: ";
export const RUN_TIMESTAMP_PREFIX = "Last Run: ";
export const STARTER_FOLDER_NAME = "Starter Projects";
export const PRIORITY_SIDEBAR_ORDER = [
diff --git a/src/frontend/src/hooks/flows/use-add-flow.ts b/src/frontend/src/hooks/flows/use-add-flow.ts
index b3e827bc5..56bbd0f46 100644
--- a/src/frontend/src/hooks/flows/use-add-flow.ts
+++ b/src/frontend/src/hooks/flows/use-add-flow.ts
@@ -64,11 +64,10 @@ const useAddFlow = () => {
newFlow.folder_id = folder_id;
postAddFlow(newFlow, {
- onSuccess: ({ id }) => {
- newFlow.id = id;
+ onSuccess: (createdFlow) => {
// Add the new flow to the list of flows.
const { data, flows: myFlows } = processFlows([
- newFlow,
+ createdFlow,
...(flows ?? []),
]);
setFlows(myFlows);
@@ -79,7 +78,7 @@ const useAddFlow = () => {
["saved_components"]: data,
}),
}));
- resolve(id);
+ resolve(createdFlow.id);
},
onError: (error) => {
if (error.response?.data?.detail) {
diff --git a/src/frontend/src/hooks/flows/use-autosave-flow.ts b/src/frontend/src/hooks/flows/use-autosave-flow.ts
index 62fe8562d..3e48e518f 100644
--- a/src/frontend/src/hooks/flows/use-autosave-flow.ts
+++ b/src/frontend/src/hooks/flows/use-autosave-flow.ts
@@ -5,7 +5,7 @@ import useSaveFlow from "./use-save-flow";
const useAutoSaveFlow = () => {
const saveFlow = useSaveFlow();
- const shouldAutosave = process.env.LANGFLOW_AUTO_SAVE !== "false";
+ const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
const autoSaveFlow = shouldAutosave
? useDebounce((flow?: FlowType) => {
diff --git a/src/frontend/src/modals/confirmationModal/index.tsx b/src/frontend/src/modals/confirmationModal/index.tsx
index f1e0c894c..e22f1d162 100644
--- a/src/frontend/src/modals/confirmationModal/index.tsx
+++ b/src/frontend/src/modals/confirmationModal/index.tsx
@@ -7,7 +7,6 @@ import {
ContentProps,
TriggerProps,
} from "../../types/components";
-import { nodeIconsLucide } from "../../utils/styleUtils";
import BaseModal from "../baseModal";
const Content: React.FC
= ({ children }) => {
@@ -36,6 +35,7 @@ function ConfirmationModal({
destructive = false,
destructiveCancel = false,
icon,
+ loading,
data,
index,
onConfirm,
@@ -71,11 +71,13 @@ function ConfirmationModal({
{triggerChild}
{title}
-
+ {icon && (
+
+ )}
{modalContentTitle && modalContentTitle != "" && (
@@ -96,22 +98,24 @@ function ConfirmationModal({
setModalOpen(false);
onConfirm(index, data);
}}
+ loading={loading}
data-testid="replace-button"
>
{confirmationText}
-
-
+ {cancelText && onCancel && (
+
+ )}
);
diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx
index e7a9b9078..8a97594ad 100644
--- a/src/frontend/src/modals/flowSettingsModal/index.tsx
+++ b/src/frontend/src/modals/flowSettingsModal/index.tsx
@@ -33,7 +33,7 @@ export default function FlowSettingsModal({
);
const [isSaving, setIsSaving] = useState(false);
const [disableSave, setDisableSave] = useState(true);
- const shouldAutosave = process.env.LANGFLOW_AUTO_SAVE !== "false";
+ const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
function handleClick(): void {
setIsSaving(true);
if (!currentFlow) return;
diff --git a/src/frontend/src/modals/saveChangesModal/index.tsx b/src/frontend/src/modals/saveChangesModal/index.tsx
index b783e7c7c..716fc9571 100644
--- a/src/frontend/src/modals/saveChangesModal/index.tsx
+++ b/src/frontend/src/modals/saveChangesModal/index.tsx
@@ -1,21 +1,61 @@
+import ForwardedIconComponent from "@/components/genericIconComponent";
+import { truncate } from "lodash";
import ConfirmationModal from "../confirmationModal";
-export function SaveChangesModal({ onSave, onProceed, onCancel }) {
+export function SaveChangesModal({
+ onSave,
+ onProceed,
+ onCancel,
+ flowName,
+ unsavedChanges,
+ lastSaved,
+ autoSave,
+}: {
+ onSave: () => void;
+ onProceed: () => void;
+ onCancel: () => void;
+ flowName: string;
+ unsavedChanges: boolean;
+ lastSaved: string | undefined;
+ autoSave: boolean;
+}): JSX.Element {
return (
- You have unsaved changes. Would you like to save them before exiting?
+ {autoSave ? (
+ unsavedChanges ? (
+ "Saving flow automatically..."
+ ) : (
+ "Flow saved! Click 'Exit' to leave the page."
+ )
+ ) : (
+ <>
+
+
+ Last saved: {lastSaved ?? "Never"}
+
+ Unsaved changes will be permanently lost.{" "}
+
+ Enable auto-saving
+ {" "}
+ to avoid losing progress.
+ >
+ )}
);
diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx
index 9be5720b7..75132fc83 100644
--- a/src/frontend/src/pages/FlowPage/index.tsx
+++ b/src/frontend/src/pages/FlowPage/index.tsx
@@ -20,7 +20,8 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
const changesNotSaved =
- customStringify(currentFlow) !== customStringify(currentSavedFlow);
+ customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
+ (currentFlow?.data?.nodes?.length ?? 0) > 0;
const blocker = useBlocker(changesNotSaved);
const version = useDarkStore((state) => state.version);
@@ -36,6 +37,10 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const getTypes = useTypesStore((state) => state.getTypes);
+ const updatedAt = currentSavedFlow?.updated_at;
+
+ const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
+
const handleSave = () => {
saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null));
};
@@ -113,11 +118,25 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
⛓️ v{version}
- {blocker.state === "blocked" && (
+ {blocker.state === "blocked" && currentSavedFlow && (