diff --git a/apps/client/src/entities/flow/store.ts b/apps/client/src/entities/flow/store.ts index 5ef524e..4bd0be1 100644 --- a/apps/client/src/entities/flow/store.ts +++ b/apps/client/src/entities/flow/store.ts @@ -25,6 +25,7 @@ interface FlowState { createNewFlow: (name: string) => Promise; saveGraph: (comment?: string) => Promise; loadGraph: () => Promise; + resetFlowState: () => void; } export const useFlowStore = create((set, get) => ({ @@ -134,4 +135,5 @@ export const useFlowStore = create((set, get) => ({ console.error(error); } }, + resetFlowState: () => set({ nodes: [], edges: [], currentFlowId: null }), })); diff --git a/apps/client/src/features/auth/index.tsx b/apps/client/src/features/auth/index.tsx index 52948c7..aac4518 100644 --- a/apps/client/src/features/auth/index.tsx +++ b/apps/client/src/features/auth/index.tsx @@ -7,6 +7,10 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useNavigate } from "react-router"; +import { Badge } from "@/components/ui/badge"; + +const RECENT_URLS_COOKIE = "recent_server_urls"; +const MAX_RECENT_URLS = 5; export function LoginForm({ className, @@ -17,12 +21,16 @@ export function LoginForm({ const [password, setPassword] = useState(""); const [showAuthFields, setShowAuthFields] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [recentUrls, setRecentUrls] = useState([]); const navigate = useNavigate(); - const handleConnect = async (e: React.FormEvent) => { - e.preventDefault(); + const connectToServer = async (targetUrl: string) => { + if (!targetUrl) { + toast.error("Server URL cannot be empty."); + return; + } setIsLoading(true); - const processedUrl = url.replace(/\/$/, ""); + const processedUrl = targetUrl.replace(/\/$/, ""); try { const response = await fetch(`${processedUrl}/info`, { @@ -44,7 +52,17 @@ export function LoginForm({ data.status === "success" ) { toast.success("Connected to server successfully. Please authenticate."); - Cookies.set("server_url", processedUrl, { expires: 1 / 24 }); + + const updatedUrls = [ + processedUrl, + ...recentUrls.filter((u) => u !== processedUrl), + ].slice(0, MAX_RECENT_URLS); + + setRecentUrls(updatedUrls); + Cookies.set(RECENT_URLS_COOKIE, JSON.stringify(updatedUrls), { + expires: 365, + }); + Cookies.set("server_url", processedUrl, { expires: 1 }); setShowAuthFields(true); } else { @@ -61,6 +79,11 @@ export function LoginForm({ } }; + const handleConnect = async (e: React.FormEvent) => { + e.preventDefault(); + await connectToServer(url); + }; + const handleAuth = async (e: React.FormEvent) => { e.preventDefault(); setIsLoading(true); @@ -89,6 +112,7 @@ export function LoginForm({ if (data.token) { Cookies.set("token", data.token, { expires: 1 / 24 }); + Cookies.set("server_url", processedUrl, { expires: 1 / 24 }); toast.success("Successfully authenticated."); navigate("/dashboard"); } else { @@ -107,6 +131,20 @@ export function LoginForm({ const token = Cookies.get("token"); if (token) { navigate("/dashboard"); + return; + } + + const storedUrls = Cookies.get(RECENT_URLS_COOKIE); + if (storedUrls) { + try { + const parsedUrls = JSON.parse(storedUrls); + if (Array.isArray(parsedUrls)) { + setRecentUrls(parsedUrls); + } + } catch (error) { + console.error("Failed to parse recent URLs from cookies.", error); + Cookies.remove(RECENT_URLS_COOKIE); + } } }, [navigate]); @@ -141,6 +179,26 @@ export function LoginForm({ onChange={(e) => setUrl(e.target.value)} disabled={isLoading} /> + {recentUrls.length > 0 && ( +
+ + Recent: + + {recentUrls.map((recentUrl) => ( + { + setUrl(recentUrl); + await connectToServer(recentUrl); + }} + > + {recentUrl} + + ))} +
+ )} ) : ( <> diff --git a/apps/client/src/features/code/FileTree.tsx b/apps/client/src/features/code/FileTree.tsx index 212d84d..f0dce30 100644 --- a/apps/client/src/features/code/FileTree.tsx +++ b/apps/client/src/features/code/FileTree.tsx @@ -140,7 +140,7 @@ const TreeNode: React.FC = ({ entry, onDeleteRequest }) => { > {entry.isDir ? ( @@ -171,7 +171,7 @@ const TreeNode: React.FC = ({ entry, onDeleteRequest }) => { onDeleteRequest(entry)} - className='text-destructive focus:text-destructive' + className='text-red-500 focus:text-red-500' > Delete @@ -283,7 +283,7 @@ export const FileTree = () => { return (
-

Project

+

Project

diff --git a/apps/client/src/features/flow/Flow.tsx b/apps/client/src/features/flow/Flow.tsx index 470fb3a..fd9a5d4 100644 --- a/apps/client/src/features/flow/Flow.tsx +++ b/apps/client/src/features/flow/Flow.tsx @@ -39,8 +39,16 @@ import { RunFlowButton } from "./RunFlow"; import { FlowLog } from "../flow-log/FlowLog"; export default function FlowPage() { - const { nodes, edges, setNodes, setEdges, flows, fetchFlows, currentFlowId } = - useFlowStore(); + const { + nodes, + edges, + setNodes, + setEdges, + flows, + fetchFlows, + currentFlowId, + resetFlowState, + } = useFlowStore(); // const saveAndRunFlow = async () => { // await saveGraph(); @@ -49,7 +57,11 @@ export default function FlowPage() { useEffect(() => { fetchFlows(); - }, [fetchFlows]); + + return () => { + resetFlowState(); + }; + }, [fetchFlows, resetFlowState]); useEffect(() => { console.log("Fetched flows from API:", flows); @@ -178,7 +190,7 @@ export function FlowSidebar() { }; return ( -