🔨 refactor(flows.py): change response model of update_flow endpoint to FlowRead

🔨 refactor(parameterComponent): remove unused imports and refactor onChange function to handleOnNewValue
 feat(tabsContext): add tabsState and setTabsState to TabsContextType and TabsProvider
🔨 refactor(flowSettingsModal): refactor handleSaveFlow function to update flow and setTabsState with isPending false
The update_flow endpoint now returns a FlowRead response model instead of FlowReadWithStyle. The parameterComponent file has been refactored to remove unused imports and to use a handleOnNewValue function to handle onChange events. The TabsContextType and TabsProvider have been updated to include tabsState and setTabsState. The flowSettingsModal has been refactored to update the flow and setTabsState with isPending false.

🔨 refactor(extraSidebarComponent): add tabsState and setTabsState to TabsContextType
🐛 fix(extraSidebarComponent): disable save button when flow is not pending
🐛 fix(extraSidebarComponent): update flow state after saving
The TabsContextType now includes tabsState and setTabsState to allow for the management of the state of each tab. The save button is now disabled when the flow is not pending. The flow state is now updated after saving to reflect the changes made.
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-06-15 15:06:52 -03:00
commit 03076b3577
6 changed files with 78 additions and 59 deletions

View file

@ -49,7 +49,7 @@ def read_flow(*, session: Session = Depends(get_session), flow_id: UUID):
raise HTTPException(status_code=404, detail="Flow not found")
@router.patch("/{flow_id}", response_model=FlowReadWithStyle, status_code=200)
@router.patch("/{flow_id}", response_model=FlowRead, status_code=200)
def update_flow(
*, session: Session = Depends(get_session), flow_id: UUID, flow: FlowUpdate
):

View file

@ -1,14 +1,11 @@
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import Tooltip from "../../../../components/TooltipComponent";
import {
classNames,
groupByFamily,
isValidConnection,
toFirstUpperCase,
} from "../../../../utils";
import { useContext, useEffect, useRef, useState } from "react";
import InputComponent from "../../../../components/inputComponent";
import ToggleComponent from "../../../../components/toggleComponent";
import InputListComponent from "../../../../components/inputListComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import { typesContext } from "../../../../contexts/typesContext";
@ -43,6 +40,7 @@ export default function ParameterComponent({
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
const { closePopUp } = useContext(PopUpContext);
const { setTabsState, tabId } = useContext(TabsContext);
useEffect(() => {
if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
@ -66,6 +64,19 @@ export default function ParameterComponent({
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
const [myData, setMyData] = useState(useContext(typesContext).data);
const handleOnNewValue = (newValue: any) => {
data.node.template[name].value = newValue;
// Set state to pending
setTabsState((prev) => {
return {
...prev,
[tabId]: {
isPending: true,
},
};
});
};
useEffect(() => {
const groupedObj = groupByFamily(myData, tooltipTitle);
@ -165,17 +176,13 @@ export default function ParameterComponent({
? [""]
: data.node.template[name].value
}
onChange={(t: string[]) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
) : data.node.template[name].multiline ? (
<TextAreaComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
) : (
<InputComponent
@ -183,9 +190,7 @@ export default function ParameterComponent({
disableCopyPaste={true}
password={data.node.template[name].password ?? false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
)}
</div>
@ -195,7 +200,7 @@ export default function ParameterComponent({
disabled={disabled}
enabled={enabled}
setEnabled={(t) => {
data.node.template[name].value = t;
handleOnNewValue(t);
setEnabled(t);
}}
size="large"
@ -207,9 +212,7 @@ export default function ParameterComponent({
disabled={disabled}
disableCopyPaste={true}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
</div>
) : left === true &&
@ -218,9 +221,7 @@ export default function ParameterComponent({
<div className="w-full">
<Dropdown
options={data.node.template[name].options}
onSelect={(newValue) =>
(data.node.template[name].value = newValue)
}
onSelect={handleOnNewValue}
value={data.node.template[name].value ?? "Choose an option"}
></Dropdown>
</div>
@ -228,17 +229,13 @@ export default function ParameterComponent({
<CodeAreaComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
) : left === true && type === "file" ? (
<InputFileComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
fileTypes={data.node.template[name].fileTypes}
suffixes={data.node.template[name].suffixes}
onFileChange={(t: string) => {
@ -251,18 +248,14 @@ export default function ParameterComponent({
disabled={disabled}
disableCopyPaste={true}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
</div>
) : left === true && type === "prompt" ? (
<PromptAreaComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
onChange={handleOnNewValue}
/>
) : (
<></>

View file

@ -7,7 +7,7 @@ import {
useContext,
} from "react";
import { FlowType, NodeType } from "../types/flow";
import { TabsContextType } from "../types/tabs";
import { TabsContextType, TabsState } from "../types/tabs";
import { updateIds, updateTemplate } from "../utils";
import { alertContext } from "./alertContext";
import { typesContext } from "./typesContext";
@ -43,7 +43,8 @@ const TabsContextInitialValue: TabsContextType = {
setDisableCopyPaste: (state: boolean) => {},
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
tabsState: {},
setTabsState: (state: TabsState) => {},
getNodeId: (nodeType: string) => "",
paste: (
selection: { nodes: any; edges: any },
@ -64,6 +65,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const [id, setId] = useState(uid());
const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
const [tabsState, setTabsState] = useState<TabsState>({});
const newNodeId = useRef(uid());
function incrementNodeId() {
@ -534,6 +536,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
description: flowData.description,
name: flow?.name ?? "New Flow",
data: flowData.data,
id: "",
});
const addFlowToLocalState = (newFlow) => {
@ -582,6 +585,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
uploadFlows,
uploadFlow,
getNodeId,
tabsState,
setTabsState,
paste,
}}
>

View file

@ -1,4 +1,3 @@
import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
import { useContext, useRef, useState } from "react";
import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
@ -14,16 +13,16 @@ import {
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants";
import { updateFlowInDatabase } from "../../controllers/API";
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
import { Settings2 } from "lucide-react";
import { updateFlowInDatabase } from "../../controllers/API";
export default function FlowSettingsModal() {
const [open, setOpen] = useState(true);
const { closePopUp } = useContext(PopUpContext);
const { setErrorData, setSuccessData } = useContext(alertContext);
const ref = useRef();
const { flows, tabId, updateFlow } = useContext(TabsContext);
const { flows, tabId, updateFlow, setTabsState } = useContext(TabsContext);
const maxLength = 50;
function setModalOpen(x: boolean) {
setOpen(x);
@ -34,22 +33,26 @@ export default function FlowSettingsModal() {
}
}
function handleSaveFlow(flow) {
if (name !== "") {
let newFlow = flows.find((f) => f.id === tabId);
if (newFlow) {
newFlow.name = name;
newFlow.description = description;
updateFlow(newFlow);
}
}
async function handleSaveFlow(flow) {
try {
updateFlowInDatabase(flow);
const updatedFlow = await updateFlowInDatabase(flow);
if (updatedFlow) {
updateFlow(updatedFlow);
setTabsState((prev) => {
return {
...prev,
[tabId]: {
isPending: false,
},
};
});
}
// updateFlowStyleInDataBase(flow);
} catch (err) {
setErrorData(err);
}
}
const [name, setName] = useState(flows.find((f) => f.id === tabId).name);
const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description

View file

@ -13,24 +13,23 @@ import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import { Code2, FileDown, FileUp, Save } from "lucide-react";
import { PopUpContext } from "../../../../contexts/popUpContext";
import ImportModal from "../../../../modals/importModal";
import ExportModal from "../../../../modals/exportModal";
import ApiModal from "../../../../modals/ApiModal";
import { TabsContext } from "../../../../contexts/tabsContext";
import { alertContext } from "../../../../contexts/alertContext";
import { updateFlowInDatabase } from "../../../../controllers/API";
import { INPUT_STYLE } from "../../../../constants";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
export default function ExtraSidebar() {
const { data } = useContext(typesContext);
const { openPopUp } = useContext(PopUpContext);
const { flows, tabId, uploadFlow } = useContext(TabsContext);
const { flows, tabId, uploadFlow, updateFlow, tabsState, setTabsState } =
useContext(TabsContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
const isPending = tabsState[tabId]?.isPending;
function onDragStart(
event: React.DragEvent<any>,
data: { type: string; node?: APIClassType }
@ -62,9 +61,21 @@ export default function ExtraSidebar() {
});
}
function handleSaveFlow(flow) {
async function handleSaveFlow(flow) {
try {
updateFlowInDatabase(flow);
const updatedFlow = await updateFlowInDatabase(flow);
if (updatedFlow) {
updateFlow(updatedFlow);
setTabsState((prev) => {
console.log(prev);
return {
...prev,
[tabId]: {
isPending: false,
},
};
});
}
// updateFlowStyleInDataBase(flow);
} catch (err) {
setErrorData(err);
@ -118,8 +129,13 @@ export default function ExtraSidebar() {
handleSaveFlow(flows.find((f) => f.id === tabId));
setSuccessData({ title: "Changes saved successfully" });
}}
disabled={!isPending}
>
<Save className="w-5 h-5 dark:text-gray-300"></Save>
<Save
className={
"w-5 h-5" + (isPending ? " text-muted-foreground" : " ")
}
></Save>
</button>
</ShadTooltip>
</div>

View file

@ -1,3 +1,4 @@
import { Dispatch, SetStateAction } from "react";
import { FlowType } from "../flow";
export type TabsContextType = {
@ -18,6 +19,8 @@ export type TabsContextType = {
disableCopyPaste: boolean;
setDisableCopyPaste: (value: boolean) => void;
getNodeId: (nodeType: string) => string;
tabsState: TabsState;
setTabsState: Dispatch<SetStateAction<TabsState>>;
paste: (
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
@ -26,9 +29,8 @@ export type TabsContextType = {
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
};
export type LangFlowState = {
tabIndex: number;
flows: FlowType[];
id: string;
nodeId: number;
export type TabsState = {
[key: string]: {
isPending: boolean;
};
};