diff --git a/skyvern-frontend/src/routes/credentials/CredentialsPage.tsx b/skyvern-frontend/src/routes/credentials/CredentialsPage.tsx index 66d6124f..5fe51f5c 100644 --- a/skyvern-frontend/src/routes/credentials/CredentialsPage.tsx +++ b/skyvern-frontend/src/routes/credentials/CredentialsPage.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect } from "react"; import { Button } from "@/components/ui/button"; import { CardStackIcon, PlusIcon } from "@radix-ui/react-icons"; import { @@ -16,13 +16,36 @@ import { import { KeyIcon } from "@/components/icons/KeyIcon"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { CredentialsTotpTab } from "./CredentialsTotpTab"; +import { useSearchParams } from "react-router-dom"; const subHeaderText = "Securely store your passwords, credit cards, and manage incoming 2FA codes for your workflows."; +const TAB_VALUES = ["passwords", "creditCards", "twoFactor"] as const; +type TabValue = (typeof TAB_VALUES)[number]; +const DEFAULT_TAB: TabValue = "passwords"; + function CredentialsPage() { const { setIsOpen, setType } = useCredentialModalState(); - const [activeTab, setActiveTab] = useState("passwords"); + const [searchParams, setSearchParams] = useSearchParams(); + const tabParam = searchParams.get("tab"); + const matchedTab = TAB_VALUES.find((tab) => tab === tabParam); + const activeTab: TabValue = matchedTab ?? DEFAULT_TAB; + + useEffect(() => { + if (tabParam && !matchedTab) { + const params = new URLSearchParams(searchParams); + params.set("tab", DEFAULT_TAB); + setSearchParams(params, { replace: true }); + } + }, [tabParam, matchedTab, searchParams, setSearchParams]); + + function handleTabChange(value: string) { + const nextTab = TAB_VALUES.find((tab) => tab === value) ?? DEFAULT_TAB; + const params = new URLSearchParams(searchParams); + params.set("tab", nextTab); + setSearchParams(params, { replace: true }); + } return (
@@ -60,9 +83,9 @@ function CredentialsPage() {
Passwords diff --git a/skyvern/forge/sdk/db/client.py b/skyvern/forge/sdk/db/client.py index 57fd236e..3176a022 100644 --- a/skyvern/forge/sdk/db/client.py +++ b/skyvern/forge/sdk/db/client.py @@ -3290,7 +3290,7 @@ class AgentDB: self, organization_id: str, limit: int = 50, - valid_lifespan_minutes: int = settings.TOTP_LIFESPAN_MINUTES, + valid_lifespan_minutes: int | None = None, otp_type: OTPType | None = None, workflow_run_id: str | None = None, ) -> list[TOTPCode]: @@ -3304,11 +3304,13 @@ class AgentDB: TOTPCodeModel.workflow_run_id.is_(None), ) async with self.Session() as session: - query = ( - select(TOTPCodeModel) - .filter_by(organization_id=organization_id) - .filter(TOTPCodeModel.created_at > datetime.utcnow() - timedelta(minutes=valid_lifespan_minutes)) - ) + query = select(TOTPCodeModel).filter_by(organization_id=organization_id) + + if valid_lifespan_minutes is not None: + query = query.filter( + TOTPCodeModel.created_at > datetime.utcnow() - timedelta(minutes=valid_lifespan_minutes) + ) + if otp_type: query = query.filter(TOTPCodeModel.otp_type == otp_type) if workflow_run_id is not None: diff --git a/skyvern/forge/sdk/routes/credentials.py b/skyvern/forge/sdk/routes/credentials.py index 7302e194..6d3df64a 100644 --- a/skyvern/forge/sdk/routes/credentials.py +++ b/skyvern/forge/sdk/routes/credentials.py @@ -177,6 +177,7 @@ async def get_totp_codes( codes = await app.DATABASE.get_recent_otp_codes( organization_id=curr_org.organization_id, limit=limit, + valid_lifespan_minutes=None, otp_type=otp_type, workflow_run_id=workflow_run_id, )