mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-10 07:38:04 -05:00
refactor(frontend): Clean up React Query-related code (#11604)
- #11603 ### Changes 🏗️ Frontend: - Make `okData` infer the response data type instead of casting - Generalize infinite query utilities from `SidebarRunsList/helpers.ts` - Move to `@/app/api/helpers` and use wherever possible - Simplify/replace boilerplate checks and conditions with `okData` in many places - Add `useUserTimezone` hook to replace all the boilerplate timezone queries Backend: - Fix response type annotation of `GET /api/store/graph/{store_listing_version_id}` endpoint - Fix documentation and error behavior of `GET /api/review/execution/{graph_exec_id}` endpoint ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - CI passes - [x] Clicking around the app manually -> no obvious issues - [x] Test Onboarding step 5 (run) - [x] Library runs list loads normally
This commit is contained in:
committed by
GitHub
parent
de78d062a9
commit
08a60dcb9b
@@ -55,7 +55,7 @@ def sample_pending_review(test_user_id: str) -> PendingHumanReviewModel:
|
||||
|
||||
|
||||
def test_get_pending_reviews_empty(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
snapshot: Snapshot,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
@@ -73,7 +73,7 @@ def test_get_pending_reviews_empty(
|
||||
|
||||
|
||||
def test_get_pending_reviews_with_data(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
snapshot: Snapshot,
|
||||
test_user_id: str,
|
||||
@@ -95,7 +95,7 @@ def test_get_pending_reviews_with_data(
|
||||
|
||||
|
||||
def test_get_pending_reviews_for_execution_success(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
snapshot: Snapshot,
|
||||
test_user_id: str,
|
||||
@@ -122,9 +122,8 @@ def test_get_pending_reviews_for_execution_success(
|
||||
assert data[0]["graph_exec_id"] == "test_graph_exec_456"
|
||||
|
||||
|
||||
def test_get_pending_reviews_for_execution_access_denied(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
test_user_id: str,
|
||||
def test_get_pending_reviews_for_execution_not_available(
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
) -> None:
|
||||
"""Test access denied when user doesn't own the execution"""
|
||||
mock_get_graph_execution = mocker.patch(
|
||||
@@ -134,12 +133,12 @@ def test_get_pending_reviews_for_execution_access_denied(
|
||||
|
||||
response = client.get("/api/review/execution/test_graph_exec_456")
|
||||
|
||||
assert response.status_code == 403
|
||||
assert "Access denied" in response.json()["detail"]
|
||||
assert response.status_code == 404
|
||||
assert "not found" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_process_review_action_approve_success(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
@@ -203,7 +202,7 @@ def test_process_review_action_approve_success(
|
||||
|
||||
|
||||
def test_process_review_action_reject_success(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
@@ -263,7 +262,7 @@ def test_process_review_action_reject_success(
|
||||
|
||||
|
||||
def test_process_review_action_mixed_success(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
@@ -370,7 +369,7 @@ def test_process_review_action_mixed_success(
|
||||
|
||||
|
||||
def test_process_review_action_empty_request(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
"""Test error when no reviews provided"""
|
||||
@@ -387,7 +386,7 @@ def test_process_review_action_empty_request(
|
||||
|
||||
|
||||
def test_process_review_action_review_not_found(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
"""Test error when review is not found"""
|
||||
@@ -423,7 +422,7 @@ def test_process_review_action_review_not_found(
|
||||
|
||||
|
||||
def test_process_review_action_partial_failure(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
@@ -457,7 +456,7 @@ def test_process_review_action_partial_failure(
|
||||
|
||||
|
||||
def test_process_review_action_invalid_node_exec_id(
|
||||
mocker: pytest_mock.MockFixture,
|
||||
mocker: pytest_mock.MockerFixture,
|
||||
sample_pending_review: PendingHumanReviewModel,
|
||||
test_user_id: str,
|
||||
) -> None:
|
||||
|
||||
@@ -67,8 +67,7 @@ async def list_pending_reviews(
|
||||
response_model=List[PendingHumanReviewModel],
|
||||
responses={
|
||||
200: {"description": "List of pending reviews for the execution"},
|
||||
400: {"description": "Invalid graph execution ID"},
|
||||
403: {"description": "Access denied to graph execution"},
|
||||
404: {"description": "Graph execution not found"},
|
||||
500: {"description": "Server error", "content": {"application/json": {}}},
|
||||
},
|
||||
)
|
||||
@@ -91,7 +90,7 @@ async def list_pending_reviews_for_execution(
|
||||
|
||||
Raises:
|
||||
HTTPException:
|
||||
- 403: If user doesn't own the graph execution
|
||||
- 404: If the graph execution doesn't exist or isn't owned by this user
|
||||
- 500: If authentication fails or database error occurs
|
||||
|
||||
Note:
|
||||
@@ -105,8 +104,8 @@ async def list_pending_reviews_for_execution(
|
||||
)
|
||||
if not graph_exec:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Access denied to graph execution",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Graph execution #{graph_exec_id} not found",
|
||||
)
|
||||
|
||||
return await get_pending_reviews_for_execution(graph_exec_id, user_id)
|
||||
|
||||
@@ -173,7 +173,9 @@ async def get_agent(username: str, agent_name: str):
|
||||
tags=["store"],
|
||||
dependencies=[fastapi.Security(autogpt_libs.auth.requires_user)],
|
||||
)
|
||||
async def get_graph_meta_by_store_listing_version_id(store_listing_version_id: str):
|
||||
async def get_graph_meta_by_store_listing_version_id(
|
||||
store_listing_version_id: str,
|
||||
) -> backend.data.graph.GraphMeta:
|
||||
"""
|
||||
Get Agent Graph from Store Listing Version ID.
|
||||
"""
|
||||
|
||||
@@ -41,6 +41,12 @@ export default defineConfig({
|
||||
useInfiniteQueryParam: "page",
|
||||
},
|
||||
},
|
||||
"getV2List presets": {
|
||||
query: {
|
||||
useInfinite: true,
|
||||
useInfiniteQueryParam: "page",
|
||||
},
|
||||
},
|
||||
"getV1List graph executions": {
|
||||
query: {
|
||||
useInfinite: true,
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function Page() {
|
||||
ready,
|
||||
error,
|
||||
showInput,
|
||||
agent,
|
||||
agentGraph,
|
||||
onboarding,
|
||||
storeAgent,
|
||||
runningAgent,
|
||||
@@ -76,19 +76,19 @@ export default function Page() {
|
||||
<CardTitle className="font-poppins text-lg">Input</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
{Object.entries(agent?.input_schema.properties || {}).map(
|
||||
([key, inputSubSchema]) => (
|
||||
<RunAgentInputs
|
||||
key={key}
|
||||
schema={inputSubSchema}
|
||||
value={onboarding.state?.agentInput?.[key]}
|
||||
placeholder={inputSubSchema.description}
|
||||
onChange={(value) => handleSetAgentInput(key, value)}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{Object.entries(
|
||||
agentGraph?.input_schema.properties || {},
|
||||
).map(([key, inputSubSchema]) => (
|
||||
<RunAgentInputs
|
||||
key={key}
|
||||
schema={inputSubSchema}
|
||||
value={onboarding.state?.agentInput?.[key]}
|
||||
placeholder={inputSubSchema.description}
|
||||
onChange={(value) => handleSetAgentInput(key, value)}
|
||||
/>
|
||||
))}
|
||||
<AgentOnboardingCredentials
|
||||
agent={agent}
|
||||
agent={agentGraph}
|
||||
siblingInputs={
|
||||
(onboarding.state?.agentInput as Record<string, any>) ||
|
||||
undefined
|
||||
@@ -104,7 +104,7 @@ export default function Page() {
|
||||
className="mt-8 w-[136px]"
|
||||
loading={runningAgent}
|
||||
disabled={isRunDisabled({
|
||||
agent,
|
||||
agent: agentGraph,
|
||||
isRunning: runningAgent,
|
||||
agentInputs:
|
||||
(onboarding.state?.agentInput as unknown as InputValues) ||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { CredentialsMetaInput } from "@/app/api/__generated__/models/credentialsMetaInput";
|
||||
import { GraphMeta } from "@/app/api/__generated__/models/graphMeta";
|
||||
import { StoreAgentDetails } from "@/app/api/__generated__/models/storeAgentDetails";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
|
||||
@@ -8,20 +5,19 @@ import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { computeInitialAgentInputs } from "./helpers";
|
||||
import { InputValues } from "./types";
|
||||
import { okData, resolveResponse } from "@/app/api/helpers";
|
||||
import { postV2AddMarketplaceAgent } from "@/app/api/__generated__/endpoints/library/library";
|
||||
import {
|
||||
useGetV2GetAgentByVersion,
|
||||
useGetV2GetAgentGraph,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { resolveResponse } from "@/app/api/helpers";
|
||||
import { postV2AddMarketplaceAgent } from "@/app/api/__generated__/endpoints/library/library";
|
||||
import { CredentialsMetaInput } from "@/app/api/__generated__/models/credentialsMetaInput";
|
||||
import { GraphID } from "@/lib/autogpt-server-api";
|
||||
|
||||
export function useOnboardingRunStep() {
|
||||
const onboarding = useOnboarding(undefined, "AGENT_CHOICE");
|
||||
|
||||
const [showInput, setShowInput] = useState(false);
|
||||
const [agent, setAgent] = useState<GraphMeta | null>(null);
|
||||
const [storeAgent, setStoreAgent] = useState<StoreAgentDetails | null>(null);
|
||||
const [runningAgent, setRunningAgent] = useState(false);
|
||||
|
||||
const [inputCredentials, setInputCredentials] = useState<
|
||||
@@ -38,12 +34,26 @@ export function useOnboardingRunStep() {
|
||||
const currentAgentVersion =
|
||||
onboarding.state?.selectedStoreListingVersionId ?? "";
|
||||
|
||||
const storeAgentQuery = useGetV2GetAgentByVersion(currentAgentVersion, {
|
||||
query: { enabled: !!currentAgentVersion },
|
||||
const {
|
||||
data: storeAgent,
|
||||
error: storeAgentQueryError,
|
||||
isSuccess: storeAgentQueryIsSuccess,
|
||||
} = useGetV2GetAgentByVersion(currentAgentVersion, {
|
||||
query: {
|
||||
enabled: !!currentAgentVersion,
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
const graphMetaQuery = useGetV2GetAgentGraph(currentAgentVersion, {
|
||||
query: { enabled: !!currentAgentVersion },
|
||||
const {
|
||||
data: agentGraphMeta,
|
||||
error: agentGraphQueryError,
|
||||
isSuccess: agentGraphQueryIsSuccess,
|
||||
} = useGetV2GetAgentGraph(currentAgentVersion, {
|
||||
query: {
|
||||
enabled: !!currentAgentVersion,
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,29 +61,15 @@ export function useOnboardingRunStep() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (storeAgentQuery.data && storeAgentQuery.data.status === 200) {
|
||||
setStoreAgent(storeAgentQuery.data.data);
|
||||
}
|
||||
}, [storeAgentQuery.data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
graphMetaQuery.data &&
|
||||
graphMetaQuery.data.status === 200 &&
|
||||
onboarding.state
|
||||
) {
|
||||
const graphMeta = graphMetaQuery.data.data as GraphMeta;
|
||||
|
||||
setAgent(graphMeta);
|
||||
|
||||
const update = computeInitialAgentInputs(
|
||||
graphMeta,
|
||||
if (agentGraphMeta && onboarding.state) {
|
||||
const initialAgentInputs = computeInitialAgentInputs(
|
||||
agentGraphMeta,
|
||||
(onboarding.state.agentInput as unknown as InputValues) || null,
|
||||
);
|
||||
|
||||
onboarding.updateState({ agentInput: update });
|
||||
onboarding.updateState({ agentInput: initialAgentInputs });
|
||||
}
|
||||
}, [graphMetaQuery.data]);
|
||||
}, [agentGraphMeta]);
|
||||
|
||||
function handleNewRun() {
|
||||
if (!onboarding.state) return;
|
||||
@@ -95,7 +91,7 @@ export function useOnboardingRunStep() {
|
||||
}
|
||||
|
||||
async function handleRunAgent() {
|
||||
if (!agent || !storeAgent || !onboarding.state) {
|
||||
if (!agentGraphMeta || !storeAgent || !onboarding.state) {
|
||||
toast({
|
||||
title: "Error getting agent",
|
||||
description:
|
||||
@@ -142,12 +138,12 @@ export function useOnboardingRunStep() {
|
||||
}
|
||||
|
||||
return {
|
||||
ready: graphMetaQuery.isSuccess && storeAgentQuery.isSuccess,
|
||||
error: graphMetaQuery.error || storeAgentQuery.error,
|
||||
agent,
|
||||
ready: agentGraphQueryIsSuccess && storeAgentQueryIsSuccess,
|
||||
error: agentGraphQueryError || storeAgentQueryError,
|
||||
agentGraph: agentGraphMeta || null,
|
||||
onboarding,
|
||||
showInput,
|
||||
storeAgent,
|
||||
storeAgent: storeAgent || null,
|
||||
runningAgent,
|
||||
credentialsValid,
|
||||
credentialsLoaded,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { RunOutputs } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedRunView/components/RunOutputs";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useGetV1GetSharedExecution } from "@/app/api/__generated__/endpoints/default/default";
|
||||
import {
|
||||
Card,
|
||||
@@ -17,12 +18,11 @@ export default function SharePage() {
|
||||
const token = params.token as string;
|
||||
|
||||
const {
|
||||
data: response,
|
||||
data: executionData,
|
||||
isLoading: loading,
|
||||
error,
|
||||
} = useGetV1GetSharedExecution(token);
|
||||
} = useGetV1GetSharedExecution(token, { query: { select: okData } });
|
||||
|
||||
const executionData = response?.status === 200 ? response.data : undefined;
|
||||
const is404 = !loading && !executionData;
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -41,6 +41,7 @@ interface FormData extends Omit<ExecutionAnalyticsRequest, "created_after"> {
|
||||
// All other fields use the generated types as-is
|
||||
}
|
||||
import { AnalyticsResultsTable } from "./AnalyticsResultsTable";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
export function ExecutionAnalyticsForm() {
|
||||
const [results, setResults] = useState<ExecutionAnalyticsResponse | null>(
|
||||
@@ -178,7 +179,7 @@ export function ExecutionAnalyticsForm() {
|
||||
data: config,
|
||||
isLoading: configLoading,
|
||||
error: configError,
|
||||
} = useGetV2GetExecutionAnalyticsConfiguration();
|
||||
} = useGetV2GetExecutionAnalyticsConfiguration({ query: { select: okData } });
|
||||
|
||||
const generateAnalytics = usePostV2GenerateExecutionAnalytics({
|
||||
mutation: {
|
||||
@@ -231,10 +232,10 @@ export function ExecutionAnalyticsForm() {
|
||||
|
||||
// Update form defaults when config loads
|
||||
useEffect(() => {
|
||||
if (config?.data && config.status === 200 && !formData.model_name) {
|
||||
if (config && !formData.model_name) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
model_name: config.data.recommended_model,
|
||||
model_name: config.recommended_model,
|
||||
}));
|
||||
}
|
||||
}, [config, formData.model_name]);
|
||||
@@ -307,7 +308,7 @@ export function ExecutionAnalyticsForm() {
|
||||
}
|
||||
|
||||
// Show error state if config fails to load
|
||||
if (configError || !config?.data || config.status !== 200) {
|
||||
if (configError || !config) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<div className="text-red-500">Failed to load configuration</div>
|
||||
@@ -315,8 +316,6 @@ export function ExecutionAnalyticsForm() {
|
||||
);
|
||||
}
|
||||
|
||||
const configData = config.data;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
@@ -382,7 +381,7 @@ export function ExecutionAnalyticsForm() {
|
||||
<SelectValue placeholder="Select AI model" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{configData.available_models.map((model) => (
|
||||
{config.available_models.map((model) => (
|
||||
<SelectItem key={model.value} value={model.value}>
|
||||
{model.label}
|
||||
</SelectItem>
|
||||
@@ -442,7 +441,7 @@ export function ExecutionAnalyticsForm() {
|
||||
onChange={(e) =>
|
||||
handleInputChange("system_prompt", e.target.value)
|
||||
}
|
||||
placeholder={configData.default_system_prompt}
|
||||
placeholder={config.default_system_prompt}
|
||||
rows={6}
|
||||
className="resize-y"
|
||||
/>
|
||||
@@ -463,7 +462,7 @@ export function ExecutionAnalyticsForm() {
|
||||
onChange={(e) =>
|
||||
handleInputChange("user_prompt", e.target.value)
|
||||
}
|
||||
placeholder={configData.default_user_prompt}
|
||||
placeholder={config.default_user_prompt}
|
||||
rows={8}
|
||||
className="resize-y"
|
||||
/>
|
||||
@@ -490,7 +489,7 @@ export function ExecutionAnalyticsForm() {
|
||||
onClick={() => {
|
||||
handleInputChange(
|
||||
"system_prompt",
|
||||
configData.default_system_prompt,
|
||||
config.default_system_prompt,
|
||||
);
|
||||
}}
|
||||
>
|
||||
@@ -503,7 +502,7 @@ export function ExecutionAnalyticsForm() {
|
||||
onClick={() => {
|
||||
handleInputChange(
|
||||
"user_prompt",
|
||||
configData.default_user_prompt,
|
||||
config.default_user_prompt,
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -17,7 +17,6 @@ import type {
|
||||
import { CheckIcon, CircleIcon } from "@phosphor-icons/react";
|
||||
import { useGetOauthGetOauthAppInfo } from "@/app/api/__generated__/endpoints/oauth/oauth";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { OAuthApplicationPublicInfo } from "@/app/api/__generated__/models/oAuthApplicationPublicInfo";
|
||||
|
||||
// All credential types - we accept any type of credential
|
||||
const ALL_CREDENTIAL_TYPES: CredentialsType[] = [
|
||||
@@ -107,7 +106,7 @@ export default function IntegrationSetupWizardPage() {
|
||||
const state = searchParams.get("state");
|
||||
|
||||
const { data: appInfo } = useGetOauthGetOauthAppInfo(clientID || "", {
|
||||
query: { enabled: !!clientID, select: okData<OAuthApplicationPublicInfo> },
|
||||
query: { enabled: !!clientID, select: okData },
|
||||
});
|
||||
|
||||
// Parse providers from base64-encoded JSON
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import { usePostV1CreateExecutionSchedule } from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useUserTimezone } from "@/lib/hooks/useUserTimezone";
|
||||
import { getTimezoneDisplayName } from "@/lib/timezone-utils";
|
||||
import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -28,11 +28,7 @@ export const useCronSchedulerDialog = ({
|
||||
flowExecutionID: parseAsString,
|
||||
});
|
||||
|
||||
const { data: userTimezone } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : undefined),
|
||||
},
|
||||
});
|
||||
const userTimezone = useUserTimezone();
|
||||
const timezoneDisplay = getTimezoneDisplayName(userTimezone || "UTC");
|
||||
|
||||
const { mutateAsync: createSchedule, isPending: isCreatingSchedule } =
|
||||
|
||||
@@ -17,7 +17,6 @@ import { FloatingReviewsPanel } from "@/components/organisms/FloatingReviewsPane
|
||||
import { parseAsString, useQueryStates } from "nuqs";
|
||||
import { CustomControls } from "./components/CustomControl";
|
||||
import { useGetV1GetSpecificGraph } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { GraphModel } from "@/app/api/__generated__/models/graphModel";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { TriggerAgentBanner } from "./components/TriggerAgentBanner";
|
||||
import { resolveCollisions } from "./helpers/resolve-collision";
|
||||
@@ -34,7 +33,7 @@ export const Flow = () => {
|
||||
{},
|
||||
{
|
||||
query: {
|
||||
select: okData<GraphModel>,
|
||||
select: okData,
|
||||
enabled: !!flowID,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@ import { NoSearchResult } from "../NoSearchResult";
|
||||
|
||||
export const BlockMenuSearch = () => {
|
||||
const {
|
||||
allSearchData,
|
||||
searchResults,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
@@ -39,7 +39,7 @@ export const BlockMenuSearch = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (allSearchData.length === 0) {
|
||||
if (searchResults.length === 0) {
|
||||
return <NoSearchResult />;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export const BlockMenuSearch = () => {
|
||||
loader={<LoadingSpinner className="size-13" />}
|
||||
className="space-y-2.5"
|
||||
>
|
||||
{allSearchData.map((item: SearchResponseItemsItem, index: number) => {
|
||||
{searchResults.map((item: SearchResponseItemsItem, index: number) => {
|
||||
const { type, data } = getSearchItemType(item);
|
||||
// backend give support to these 3 types only [right now] - we need to give support to integration and ai agent types in follow up PRs
|
||||
switch (type) {
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import { useBlockMenuStore } from "../../../../stores/blockMenuStore";
|
||||
import { useGetV2BuilderSearchInfinite } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { SearchResponse } from "@/app/api/__generated__/models/searchResponse";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useBlockMenuStore } from "@/app/(platform)/build/stores/blockMenuStore";
|
||||
import { useAddAgentToBuilder } from "../hooks/useAddAgentToBuilder";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { getV2GetSpecificAgent } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import {
|
||||
getPaginationNextPageNumber,
|
||||
okData,
|
||||
unpaginate,
|
||||
} from "@/app/api/helpers";
|
||||
import {
|
||||
getGetV2GetBuilderItemCountsQueryKey,
|
||||
getGetV2GetBuilderSuggestionsQueryKey,
|
||||
} from "@/app/api/__generated__/endpoints/default/default";
|
||||
import {
|
||||
getGetV2ListLibraryAgentsQueryKey,
|
||||
getV2GetLibraryAgent,
|
||||
usePostV2AddMarketplaceAgent,
|
||||
} from "@/app/api/__generated__/endpoints/library/library";
|
||||
import {
|
||||
getGetV2GetBuilderItemCountsQueryKey,
|
||||
getGetV2GetBuilderSuggestionsQueryKey,
|
||||
} from "@/app/api/__generated__/endpoints/default/default";
|
||||
getV2GetSpecificAgent,
|
||||
useGetV2BuilderSearchInfinite,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
@@ -40,7 +46,7 @@ export const useBlockMenuSearch = () => {
|
||||
>(null);
|
||||
|
||||
const {
|
||||
data: searchData,
|
||||
data: searchQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -53,18 +59,7 @@ export const useBlockMenuSearch = () => {
|
||||
search_id: searchId,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
const response = lastPage.data as SearchResponse;
|
||||
const { pagination } = response;
|
||||
if (!pagination) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { current_page, total_pages } = pagination;
|
||||
return current_page < total_pages ? current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
@@ -93,16 +88,15 @@ export const useBlockMenuSearch = () => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchData?.pages?.length) {
|
||||
if (!searchQueryData?.pages?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const latestPage = searchData.pages[searchData.pages.length - 1];
|
||||
const response = latestPage?.data as SearchResponse;
|
||||
if (response?.search_id && response.search_id !== searchId) {
|
||||
setSearchId(response.search_id);
|
||||
const lastPage = okData(searchQueryData.pages.at(-1));
|
||||
if (lastPage?.search_id && lastPage.search_id !== searchId) {
|
||||
setSearchId(lastPage.search_id);
|
||||
}
|
||||
}, [searchData, searchId, setSearchId]);
|
||||
}, [searchQueryData, searchId, setSearchId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchId && !searchQuery) {
|
||||
@@ -110,11 +104,9 @@ export const useBlockMenuSearch = () => {
|
||||
}
|
||||
}, [resetSearchSession, searchId, searchQuery]);
|
||||
|
||||
const allSearchData =
|
||||
searchData?.pages?.flatMap((page) => {
|
||||
const response = page.data as SearchResponse;
|
||||
return response.items;
|
||||
}) ?? [];
|
||||
const searchResults = searchQueryData
|
||||
? unpaginate(searchQueryData, "items")
|
||||
: [];
|
||||
|
||||
const handleAddLibraryAgent = async (agent: LibraryAgent) => {
|
||||
setAddingLibraryAgentId(agent.id);
|
||||
@@ -177,7 +169,7 @@ export const useBlockMenuSearch = () => {
|
||||
};
|
||||
|
||||
return {
|
||||
allSearchData,
|
||||
searchResults,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import {
|
||||
getPaginatedTotalCount,
|
||||
getPaginationNextPageNumber,
|
||||
unpaginate,
|
||||
} from "@/app/api/helpers";
|
||||
import { useGetV2GetBuilderBlocksInfinite } from "@/app/api/__generated__/endpoints/default/default";
|
||||
import { BlockResponse } from "@/app/api/__generated__/models/blockResponse";
|
||||
import { useBlockMenuStore } from "../../../../stores/blockMenuStore";
|
||||
import { useBlockMenuStore } from "@/app/(platform)/build/stores/blockMenuStore";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
@@ -8,7 +12,7 @@ export const useIntegrationBlocks = () => {
|
||||
const { integration } = useBlockMenuStore();
|
||||
|
||||
const {
|
||||
data: blocks,
|
||||
data: blocksQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -22,30 +26,16 @@ export const useIntegrationBlocks = () => {
|
||||
provider: integration,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
const pagination = (lastPage.data as BlockResponse).pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
const allBlocks =
|
||||
blocks?.pages?.flatMap((page) => {
|
||||
const response = page.data as BlockResponse;
|
||||
return response.blocks;
|
||||
}) ?? [];
|
||||
const allBlocks = blocksQueryData
|
||||
? unpaginate(blocksQueryData, "blocks")
|
||||
: [];
|
||||
const totalBlocks = getPaginatedTotalCount(blocksQueryData);
|
||||
|
||||
const totalBlocks = blocks?.pages[0]
|
||||
? (blocks.pages[0].data as BlockResponse).pagination.total_items
|
||||
: 0;
|
||||
|
||||
const status = blocks?.pages[0]?.status;
|
||||
const status = blocksQueryData?.pages[0]?.status;
|
||||
|
||||
return {
|
||||
allBlocks,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getPaginationNextPageNumber, unpaginate } from "@/app/api/helpers";
|
||||
import { getGetV2GetBuilderItemCountsQueryKey } from "@/app/api/__generated__/endpoints/default/default";
|
||||
import {
|
||||
getGetV2ListLibraryAgentsQueryKey,
|
||||
@@ -8,13 +9,12 @@ import {
|
||||
getV2GetSpecificAgent,
|
||||
useGetV2ListStoreAgentsInfinite,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { StoreAgentsResponse } from "@/lib/autogpt-server-api";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { useState } from "react";
|
||||
import { useAddAgentToBuilder } from "../hooks/useAddAgentToBuilder";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
|
||||
export const useMarketplaceAgentsContent = () => {
|
||||
const { toast } = useToast();
|
||||
@@ -22,7 +22,7 @@ export const useMarketplaceAgentsContent = () => {
|
||||
const { addAgentToBuilder } = useAddAgentToBuilder();
|
||||
|
||||
const {
|
||||
data: listStoreAgents,
|
||||
data: storeAgentsQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -36,26 +36,14 @@ export const useMarketplaceAgentsContent = () => {
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
const pagination = (lastPage.data as StoreAgentsResponse).pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
const allAgents =
|
||||
listStoreAgents?.pages?.flatMap((page) => {
|
||||
const response = page.data as StoreAgentsResponse;
|
||||
return response.agents;
|
||||
}) ?? [];
|
||||
|
||||
const status = listStoreAgents?.pages[0]?.status;
|
||||
const allAgents = storeAgentsQueryData
|
||||
? unpaginate(storeAgentsQueryData, "agents")
|
||||
: [];
|
||||
const status = storeAgentsQueryData?.pages[0]?.status;
|
||||
|
||||
const { mutateAsync: addMarketplaceAgent } = usePostV2AddMarketplaceAgent({
|
||||
mutation: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getPaginationNextPageNumber, unpaginate } from "@/app/api/helpers";
|
||||
import { useGetV2ListLibraryAgentsInfinite } from "@/app/api/__generated__/endpoints/library/library";
|
||||
import { LibraryAgentResponse } from "@/app/api/__generated__/models/libraryAgentResponse";
|
||||
import { useState } from "react";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { useAddAgentToBuilder } from "../hooks/useAddAgentToBuilder";
|
||||
@@ -12,7 +12,7 @@ export const useMyAgentsContent = () => {
|
||||
const { toast } = useToast();
|
||||
|
||||
const {
|
||||
data: agents,
|
||||
data: agentsQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -26,26 +26,14 @@ export const useMyAgentsContent = () => {
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
const pagination = (lastPage.data as LibraryAgentResponse).pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
const allAgents =
|
||||
agents?.pages?.flatMap((page) => {
|
||||
const response = page.data as LibraryAgentResponse;
|
||||
return response.agents;
|
||||
}) ?? [];
|
||||
|
||||
const status = agents?.pages[0]?.status;
|
||||
const allAgents = agentsQueryData
|
||||
? unpaginate(agentsQueryData, "agents")
|
||||
: [];
|
||||
const status = agentsQueryData?.pages[0]?.status;
|
||||
|
||||
const handleAddBlock = async (agent: LibraryAgent) => {
|
||||
setSelectedAgentId(agent.id);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getPaginationNextPageNumber, unpaginate } from "@/app/api/helpers";
|
||||
import { useGetV2GetBuilderBlocksInfinite } from "@/app/api/__generated__/endpoints/default/default";
|
||||
import { BlockResponse } from "@/app/api/__generated__/models/blockResponse";
|
||||
|
||||
interface UsePaginatedBlocksProps {
|
||||
type?: "all" | "input" | "action" | "output" | null;
|
||||
@@ -8,7 +8,7 @@ interface UsePaginatedBlocksProps {
|
||||
const PAGE_SIZE = 10;
|
||||
export const usePaginatedBlocks = ({ type }: UsePaginatedBlocksProps) => {
|
||||
const {
|
||||
data: blocks,
|
||||
data: blocksQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -22,26 +22,14 @@ export const usePaginatedBlocks = ({ type }: UsePaginatedBlocksProps) => {
|
||||
type,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
const pagination = (lastPage.data as BlockResponse).pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
const allBlocks =
|
||||
blocks?.pages?.flatMap((page) => {
|
||||
const response = page.data as BlockResponse;
|
||||
return response.blocks;
|
||||
}) ?? [];
|
||||
|
||||
const status = blocks?.pages[0]?.status;
|
||||
const allBlocks = blocksQueryData
|
||||
? unpaginate(blocksQueryData, "blocks")
|
||||
: [];
|
||||
const status = blocksQueryData?.pages[0]?.status;
|
||||
|
||||
return {
|
||||
allBlocks,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getPaginationNextPageNumber, unpaginate } from "@/app/api/helpers";
|
||||
import { useGetV2GetBuilderIntegrationProvidersInfinite } from "@/app/api/__generated__/endpoints/default/default";
|
||||
import { ProviderResponse } from "@/app/api/__generated__/models/providerResponse";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
export const usePaginatedIntegrationList = () => {
|
||||
const {
|
||||
data: providers,
|
||||
data: providersQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -18,26 +18,14 @@ export const usePaginatedIntegrationList = () => {
|
||||
page_size: PAGE_SIZE,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage: any) => {
|
||||
const pagination = (lastPage.data as ProviderResponse).pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
const allProviders =
|
||||
providers?.pages?.flatMap((page: any) => {
|
||||
const response = page.data as ProviderResponse;
|
||||
return response.providers;
|
||||
}) ?? [];
|
||||
|
||||
const status = providers?.pages[0]?.status;
|
||||
const allProviders = providersQueryData
|
||||
? unpaginate(providersQueryData, "providers")
|
||||
: [];
|
||||
const status = providersQueryData?.pages[0]?.status;
|
||||
|
||||
return {
|
||||
allProviders,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import type { SessionDetailResponse } from "@/app/api/__generated__/models/sessionDetailResponse";
|
||||
import { storage, Key } from "@/services/storage/local-storage";
|
||||
import { isValidUUID } from "@/app/(platform)/chat/helpers";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
interface UseChatSessionArgs {
|
||||
urlSessionId?: string | null;
|
||||
@@ -70,6 +71,7 @@ export function useChatSession({
|
||||
} = useGetV2GetSession(sessionId || "", {
|
||||
query: {
|
||||
enabled: !!sessionId,
|
||||
select: okData,
|
||||
staleTime: Infinity, // Never mark as stale
|
||||
refetchOnMount: false, // Don't refetch on component mount
|
||||
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
||||
@@ -81,9 +83,8 @@ export function useChatSession({
|
||||
const { mutateAsync: claimSessionMutation } = usePatchV2SessionAssignUser();
|
||||
|
||||
const session = useMemo(() => {
|
||||
if (sessionData?.status === 200) {
|
||||
return sessionData.data;
|
||||
}
|
||||
if (sessionData) return sessionData;
|
||||
|
||||
if (sessionId && justCreatedSessionIdRef.current === sessionId) {
|
||||
return {
|
||||
id: sessionId,
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import { useUserTimezone } from "@/lib/hooks/useUserTimezone";
|
||||
import { getTimezoneDisplayName } from "@/lib/timezone-utils";
|
||||
import { InfoIcon } from "@phosphor-icons/react";
|
||||
|
||||
export function TimezoneNotice() {
|
||||
const { data: userTimezone, isSuccess } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : undefined),
|
||||
},
|
||||
});
|
||||
const userTimezone = useUserTimezone();
|
||||
|
||||
if (!isSuccess) {
|
||||
if (!userTimezone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions,
|
||||
getGetV1ListGraphExecutionsQueryKey,
|
||||
getV1GetGraphVersion,
|
||||
useDeleteV1DeleteGraphExecution,
|
||||
} from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
@@ -127,9 +127,7 @@ export function AgentActionsDropdown({
|
||||
toast({ title: "Task deleted" });
|
||||
|
||||
await queryClient.refetchQueries({
|
||||
queryKey:
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions(agentGraphId)
|
||||
.queryKey,
|
||||
queryKey: getGetV1ListGraphExecutionsQueryKey(agentGraphId),
|
||||
});
|
||||
|
||||
if (onClearSelectedRun) onClearSelectedRun();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions,
|
||||
getGetV1ListGraphExecutionsQueryKey,
|
||||
usePostV1ExecuteGraphAgent,
|
||||
usePostV1StopGraphExecution,
|
||||
} from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import type { GraphExecution } from "@/app/api/__generated__/models/graphExecution";
|
||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
@@ -58,9 +59,7 @@ export function useSelectedRunActions({
|
||||
toast({ title: "Run stopped" });
|
||||
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey:
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions(agentGraphId)
|
||||
.queryKey,
|
||||
queryKey: getGetV1ListGraphExecutionsQueryKey(agentGraphId),
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
@@ -97,12 +96,10 @@ export function useSelectedRunActions({
|
||||
},
|
||||
});
|
||||
|
||||
const newRunId = res?.status === 200 ? (res?.data?.id ?? "") : "";
|
||||
const newRunId = okData(res)?.id;
|
||||
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey:
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions(agentGraphId)
|
||||
.queryKey,
|
||||
queryKey: getGetV1ListGraphExecutionsQueryKey(agentGraphId),
|
||||
});
|
||||
|
||||
if (newRunId && onSelectRun) onSelectRun(newRunId);
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
import { useGetV1GetExecutionDetails } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { useGetV2GetASpecificPreset } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
|
||||
import type { GetV1GetExecutionDetails200 } from "@/app/api/__generated__/models/getV1GetExecutionDetails200";
|
||||
import type { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
export function useSelectedRunView(graphId: string, runId: string) {
|
||||
const query = useGetV1GetExecutionDetails(graphId, runId, {
|
||||
const executionQuery = useGetV1GetExecutionDetails(graphId, runId, {
|
||||
query: {
|
||||
refetchInterval: (q: any) => {
|
||||
refetchInterval: (q) => {
|
||||
const isSuccess = q.state.data?.status === 200;
|
||||
|
||||
if (!isSuccess) return false;
|
||||
@@ -33,22 +31,15 @@ export function useSelectedRunView(graphId: string, runId: string) {
|
||||
},
|
||||
});
|
||||
|
||||
const status = query.data?.status;
|
||||
const run = okData(executionQuery.data);
|
||||
const status = executionQuery.data?.status;
|
||||
|
||||
const run: GetV1GetExecutionDetails200 | undefined =
|
||||
status === 200
|
||||
? (query.data?.data as GetV1GetExecutionDetails200)
|
||||
: undefined;
|
||||
|
||||
const presetId =
|
||||
run && "preset_id" in run && run.preset_id
|
||||
? (run.preset_id as string)
|
||||
: undefined;
|
||||
const presetId = run?.preset_id || undefined;
|
||||
|
||||
const presetQuery = useGetV2GetASpecificPreset(presetId || "", {
|
||||
query: {
|
||||
enabled: !!presetId,
|
||||
select: (res) => okData<LibraryAgentPreset>(res),
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -60,8 +51,8 @@ export function useSelectedRunView(graphId: string, runId: string) {
|
||||
return {
|
||||
run,
|
||||
preset: presetQuery.data,
|
||||
isLoading: query.isLoading || presetQuery.isLoading,
|
||||
responseError: query.error || presetQuery.error,
|
||||
isLoading: executionQuery.isLoading || presetQuery.isLoading,
|
||||
responseError: executionQuery.error || presetQuery.error,
|
||||
httpError,
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
||||
import { humanizeCronExpression } from "@/lib/cron-expression-utils";
|
||||
import { isLargeScreen, useBreakpoint } from "@/lib/hooks/useBreakpoint";
|
||||
import { useUserTimezone } from "@/lib/hooks/useUserTimezone";
|
||||
import { formatInTimezone, getTimezoneDisplayName } from "@/lib/timezone-utils";
|
||||
import { AgentInputsReadOnly } from "../../modals/AgentInputsReadOnly/AgentInputsReadOnly";
|
||||
import { LoadingSelectedContent } from "../LoadingSelectedContent";
|
||||
@@ -36,11 +36,7 @@ export function SelectedScheduleView({
|
||||
scheduleId,
|
||||
);
|
||||
|
||||
const { data: userTzRes } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : undefined),
|
||||
},
|
||||
});
|
||||
const userTimezone = useUserTimezone();
|
||||
|
||||
const breakpoint = useBreakpoint();
|
||||
const isLgScreenUp = isLargeScreen(breakpoint);
|
||||
@@ -90,7 +86,7 @@ export function SelectedScheduleView({
|
||||
run={undefined}
|
||||
scheduleRecurrence={
|
||||
schedule
|
||||
? `${humanizeCronExpression(schedule.cron || "")} · ${getTimezoneDisplayName(schedule.timezone || userTzRes || "UTC")}`
|
||||
? `${humanizeCronExpression(schedule.cron || "")} · ${getTimezoneDisplayName(schedule.timezone || userTimezone || "UTC")}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
@@ -125,7 +121,7 @@ export function SelectedScheduleView({
|
||||
<span className="text-zinc-500">•</span>{" "}
|
||||
<span className="text-zinc-500">
|
||||
{getTimezoneDisplayName(
|
||||
schedule.timezone || userTzRes || "UTC",
|
||||
schedule.timezone || userTimezone || "UTC",
|
||||
)}
|
||||
</span>
|
||||
</Text>
|
||||
@@ -135,7 +131,7 @@ export function SelectedScheduleView({
|
||||
<Text variant="body" className="flex items-center gap-3">
|
||||
{formatInTimezone(
|
||||
schedule.next_run_time,
|
||||
userTzRes || "UTC",
|
||||
userTimezone || "UTC",
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
@@ -148,7 +144,7 @@ export function SelectedScheduleView({
|
||||
<span className="text-zinc-500">•</span>{" "}
|
||||
<span className="text-zinc-500">
|
||||
{getTimezoneDisplayName(
|
||||
schedule.timezone || userTzRes || "UTC",
|
||||
schedule.timezone || userTimezone || "UTC",
|
||||
)}
|
||||
</span>
|
||||
</Text>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { getGetV1ListGraphExecutionsInfiniteQueryOptions } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { getGetV1ListExecutionSchedulesForAGraphQueryKey } from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
import { getGetV1ListGraphExecutionsQueryKey } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import type { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
@@ -94,8 +94,7 @@ export function useEditScheduleModal(
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getGetV1ListExecutionSchedulesForAGraphQueryKey(graphId),
|
||||
});
|
||||
const runsKey = getGetV1ListGraphExecutionsInfiniteQueryOptions(graphId)
|
||||
.queryKey as any;
|
||||
const runsKey = getGetV1ListGraphExecutionsQueryKey(graphId);
|
||||
await queryClient.invalidateQueries({ queryKey: runsKey });
|
||||
setIsOpen(false);
|
||||
},
|
||||
|
||||
@@ -2,30 +2,29 @@
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useGetV1ListExecutionSchedulesForAGraph } from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
import type { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
export function useSelectedScheduleView(graphId: string, scheduleId: string) {
|
||||
const query = useGetV1ListExecutionSchedulesForAGraph(graphId, {
|
||||
const schedulesQuery = useGetV1ListExecutionSchedulesForAGraph(graphId, {
|
||||
query: {
|
||||
enabled: !!graphId,
|
||||
select: (res) =>
|
||||
res.status === 200 ? (res.data as GraphExecutionJobInfo[]) : [],
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
const schedule = useMemo(
|
||||
() => query.data?.find((s) => s.id === scheduleId),
|
||||
[query.data, scheduleId],
|
||||
() => schedulesQuery.data?.find((s) => s.id === scheduleId),
|
||||
[schedulesQuery.data, scheduleId],
|
||||
);
|
||||
|
||||
const httpError =
|
||||
query.isSuccess && !schedule
|
||||
schedulesQuery.isSuccess && !schedule
|
||||
? { status: 404, statusText: "Not found" }
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
schedule,
|
||||
isLoading: query.isLoading,
|
||||
error: query.error || httpError,
|
||||
isLoading: schedulesQuery.isLoading,
|
||||
error: schedulesQuery.error || httpError,
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import {
|
||||
getGetV2ListPresetsQueryKey,
|
||||
getV2ListPresets,
|
||||
useDeleteV2DeleteAPreset,
|
||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import type { LibraryAgentPresetResponse } from "@/app/api/__generated__/models/libraryAgentPresetResponse";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner";
|
||||
@@ -56,15 +56,13 @@ export function SelectedTemplateActions({
|
||||
queryKey,
|
||||
});
|
||||
|
||||
const queryData = queryClient.getQueryData<{
|
||||
data: LibraryAgentPresetResponse;
|
||||
}>(queryKey);
|
||||
const queryData =
|
||||
queryClient.getQueryData<
|
||||
Awaited<ReturnType<typeof getV2ListPresets>>
|
||||
>(queryKey);
|
||||
|
||||
const presets =
|
||||
okData<LibraryAgentPresetResponse>(queryData)?.presets ?? [];
|
||||
const templates = presets.filter(
|
||||
(preset) => !preset.webhook_id || !preset.webhook,
|
||||
);
|
||||
const presets = okData(queryData)?.presets ?? [];
|
||||
const templates = presets.filter((preset) => !preset.webhook_id);
|
||||
|
||||
setShowDeleteDialog(false);
|
||||
onDeleted?.();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { getGetV1ListGraphExecutionsInfiniteQueryOptions } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { getGetV1ListGraphExecutionsQueryKey } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import {
|
||||
getGetV2GetASpecificPresetQueryKey,
|
||||
getGetV2ListPresetsQueryKey,
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
usePostV2ExecuteAPreset,
|
||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import type { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
import type { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import type { LibraryAgentPresetUpdatable } from "@/app/api/__generated__/models/libraryAgentPresetUpdatable";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
@@ -34,7 +33,7 @@ export function useSelectedTemplateView({
|
||||
const query = useGetV2GetASpecificPreset(templateId, {
|
||||
query: {
|
||||
enabled: !!templateId,
|
||||
select: (res) => okData<LibraryAgentPreset>(res),
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -83,15 +82,13 @@ export function useSelectedTemplateView({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
const execution = okData<GraphExecutionMeta>(response);
|
||||
const execution = okData(response);
|
||||
if (execution) {
|
||||
toast({
|
||||
title: "Task started",
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey:
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions(graphId)
|
||||
.queryKey,
|
||||
queryKey: getGetV1ListGraphExecutionsQueryKey(graphId),
|
||||
});
|
||||
onRunCreated?.(execution);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import {
|
||||
getGetV2ListPresetsQueryKey,
|
||||
getV2ListPresets,
|
||||
useDeleteV2DeleteAPreset,
|
||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import type { LibraryAgentPresetResponse } from "@/app/api/__generated__/models/libraryAgentPresetResponse";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner";
|
||||
@@ -52,15 +52,13 @@ export function SelectedTriggerActions({
|
||||
queryKey,
|
||||
});
|
||||
|
||||
const queryData = queryClient.getQueryData<{
|
||||
data: LibraryAgentPresetResponse;
|
||||
}>(queryKey);
|
||||
const queryData =
|
||||
queryClient.getQueryData<
|
||||
Awaited<ReturnType<typeof getV2ListPresets>>
|
||||
>(queryKey);
|
||||
|
||||
const presets =
|
||||
okData<LibraryAgentPresetResponse>(queryData)?.presets ?? [];
|
||||
const triggers = presets.filter(
|
||||
(preset) => preset.webhook_id && preset.webhook,
|
||||
);
|
||||
const presets = okData(queryData)?.presets ?? [];
|
||||
const triggers = presets.filter((preset) => preset.webhook_id);
|
||||
|
||||
setShowDeleteDialog(false);
|
||||
onDeleted?.();
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
useGetV2GetASpecificPreset,
|
||||
usePatchV2UpdateAnExistingPreset,
|
||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import type { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import type { LibraryAgentPresetUpdatable } from "@/app/api/__generated__/models/libraryAgentPresetUpdatable";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
@@ -26,7 +25,7 @@ export function useSelectedTriggerView({ triggerId, graphId }: Args) {
|
||||
const query = useGetV2GetASpecificPreset(triggerId, {
|
||||
query: {
|
||||
enabled: !!triggerId,
|
||||
select: (res) => okData<LibraryAgentPreset>(res),
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions,
|
||||
getGetV1ListGraphExecutionsQueryKey,
|
||||
useDeleteV1DeleteGraphExecution,
|
||||
} from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import {
|
||||
@@ -51,9 +51,7 @@ export function TaskActionsDropdown({ agent, run, onDeleted }: Props) {
|
||||
toast({ title: "Task deleted" });
|
||||
|
||||
await queryClient.refetchQueries({
|
||||
queryKey: getGetV1ListGraphExecutionsInfiniteQueryOptions(
|
||||
agent.graph_id,
|
||||
).queryKey,
|
||||
queryKey: getGetV1ListGraphExecutionsQueryKey(agent.graph_id),
|
||||
});
|
||||
|
||||
setShowDeleteDialog(false);
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { GraphExecutionsPaginated } from "@/app/api/__generated__/models/graphExecutionsPaginated";
|
||||
import type { InfiniteData } from "@tanstack/react-query";
|
||||
|
||||
function hasValidExecutionsData(
|
||||
page: unknown,
|
||||
): page is { data: GraphExecutionsPaginated } {
|
||||
return (
|
||||
typeof page === "object" &&
|
||||
page !== null &&
|
||||
"data" in page &&
|
||||
typeof (page as { data: unknown }).data === "object" &&
|
||||
(page as { data: unknown }).data !== null &&
|
||||
"executions" in (page as { data: GraphExecutionsPaginated }).data
|
||||
);
|
||||
}
|
||||
|
||||
export function computeRunsCount(
|
||||
infiniteData: InfiniteData<unknown> | undefined,
|
||||
runsLength: number,
|
||||
): number {
|
||||
const lastPage = infiniteData?.pages.at(-1);
|
||||
if (!hasValidExecutionsData(lastPage)) return runsLength;
|
||||
return lastPage.data.pagination?.total_items || runsLength;
|
||||
}
|
||||
|
||||
export function getNextRunsPageParam(lastPage: unknown): number | undefined {
|
||||
if (!hasValidExecutionsData(lastPage)) return undefined;
|
||||
|
||||
const { pagination } = lastPage.data;
|
||||
const hasMore =
|
||||
pagination.current_page * pagination.page_size < pagination.total_items;
|
||||
return hasMore ? pagination.current_page + 1 : undefined;
|
||||
}
|
||||
|
||||
export function extractRunsFromPages(
|
||||
infiniteData: InfiniteData<unknown> | undefined,
|
||||
) {
|
||||
return (
|
||||
infiniteData?.pages.flatMap((page) => {
|
||||
if (!hasValidExecutionsData(page)) return [];
|
||||
return page.data.executions || [];
|
||||
}) || []
|
||||
);
|
||||
}
|
||||
@@ -2,20 +2,18 @@
|
||||
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
import {
|
||||
okData,
|
||||
getPaginationNextPageNumber,
|
||||
getPaginatedTotalCount,
|
||||
unpaginate,
|
||||
} from "@/app/api/helpers";
|
||||
import { useGetV1ListGraphExecutionsInfinite } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { useGetV2ListPresets } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import { useGetV1ListExecutionSchedulesForAGraph } from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
import type { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
import type { LibraryAgentPresetResponse } from "@/app/api/__generated__/models/libraryAgentPresetResponse";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useExecutionEvents } from "@/hooks/useExecutionEvents";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { parseAsString, useQueryStates } from "nuqs";
|
||||
import {
|
||||
computeRunsCount,
|
||||
extractRunsFromPages,
|
||||
getNextRunsPageParam,
|
||||
} from "./helpers";
|
||||
|
||||
function parseTab(
|
||||
value: string | null,
|
||||
@@ -66,7 +64,7 @@ export function useSidebarRunsList({
|
||||
query: {
|
||||
enabled: !!graphId,
|
||||
refetchOnWindowFocus: false,
|
||||
getNextPageParam: getNextRunsPageParam,
|
||||
getNextPageParam: getPaginationNextPageNumber,
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -74,7 +72,7 @@ export function useSidebarRunsList({
|
||||
const schedulesQuery = useGetV1ListExecutionSchedulesForAGraph(graphId, {
|
||||
query: {
|
||||
enabled: !!graphId,
|
||||
select: (r) => okData<GraphExecutionJobInfo[]>(r),
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -83,13 +81,13 @@ export function useSidebarRunsList({
|
||||
{
|
||||
query: {
|
||||
enabled: !!graphId,
|
||||
select: (r) => okData<LibraryAgentPresetResponse>(r)?.presets,
|
||||
select: (r) => okData(r)?.presets,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const runs = useMemo(
|
||||
() => extractRunsFromPages(runsQuery.data),
|
||||
() => (runsQuery.data ? unpaginate(runsQuery.data, "executions") : []),
|
||||
[runsQuery.data],
|
||||
);
|
||||
|
||||
@@ -104,7 +102,7 @@ export function useSidebarRunsList({
|
||||
[allPresets],
|
||||
);
|
||||
|
||||
const runsCount = computeRunsCount(runsQuery.data, runs.length);
|
||||
const runsCount = getPaginatedTotalCount(runsQuery.data, runs.length);
|
||||
const schedulesCount = schedules.length;
|
||||
const templatesCount = templates.length;
|
||||
const triggersCount = triggers.length;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useGetV2GetLibraryAgent } from "@/app/api/__generated__/endpoints/libra
|
||||
import { useGetV2GetASpecificPreset } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useParams } from "next/navigation";
|
||||
@@ -31,11 +30,7 @@ export function useNewAgentLibraryView() {
|
||||
data: agent,
|
||||
isSuccess,
|
||||
error,
|
||||
} = useGetV2GetLibraryAgent(agentId, {
|
||||
query: {
|
||||
select: okData<LibraryAgent>,
|
||||
},
|
||||
});
|
||||
} = useGetV2GetLibraryAgent(agentId, { query: { select: okData } });
|
||||
|
||||
const [{ activeItem, activeTab: activeTabRaw }, setQueryStates] =
|
||||
useQueryStates({
|
||||
@@ -53,7 +48,7 @@ export function useNewAgentLibraryView() {
|
||||
} = useGetV2GetASpecificPreset(activeItem ?? "", {
|
||||
query: {
|
||||
enabled: Boolean(activeTab === "templates" && activeItem),
|
||||
select: okData<LibraryAgentPreset>,
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
const activeTemplate =
|
||||
|
||||
@@ -23,7 +23,7 @@ import LoadingBox from "@/components/__legacy__/ui/loading";
|
||||
import { useToastOnFail } from "@/components/molecules/Toast/use-toast";
|
||||
import { humanizeCronExpression } from "@/lib/cron-expression-utils";
|
||||
import { formatScheduleTime } from "@/lib/timezone-utils";
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import { useUserTimezone } from "@/lib/hooks/useUserTimezone";
|
||||
import { PlayIcon } from "lucide-react";
|
||||
|
||||
import { AgentRunStatus } from "./agent-run-status-chip";
|
||||
@@ -48,11 +48,7 @@ export function AgentScheduleDetailsView({
|
||||
const toastOnFail = useToastOnFail();
|
||||
|
||||
// Get user's timezone for displaying schedule times
|
||||
const { data: userTimezone } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : undefined),
|
||||
},
|
||||
});
|
||||
const userTimezone = useUserTimezone();
|
||||
|
||||
const infoStats: { label: string; value: React.ReactNode }[] = useMemo(() => {
|
||||
return [
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Button } from "@/components/__legacy__/ui/button";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { CronScheduler } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/cron-scheduler";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import { getTimezoneDisplayName } from "@/lib/timezone-utils";
|
||||
import { useUserTimezone } from "@/lib/hooks/useUserTimezone";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
|
||||
// Base type for cron expression only
|
||||
@@ -50,11 +50,7 @@ export function CronSchedulerDialog(props: CronSchedulerDialogProps) {
|
||||
);
|
||||
|
||||
// Get user's timezone
|
||||
const { data: userTimezone } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : undefined),
|
||||
},
|
||||
});
|
||||
const userTimezone = useUserTimezone();
|
||||
const timezoneDisplay = getTimezoneDisplayName(userTimezone || "UTC");
|
||||
|
||||
// Reset state when dialog opens
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import {
|
||||
GraphExecutionMeta as LegacyGraphExecutionMeta,
|
||||
GraphID,
|
||||
GraphExecutionID,
|
||||
} from "@/lib/autogpt-server-api";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import {
|
||||
getPaginatedTotalCount,
|
||||
getPaginationNextPageNumber,
|
||||
unpaginate,
|
||||
} from "@/app/api/helpers";
|
||||
import {
|
||||
getV1ListGraphExecutionsResponse,
|
||||
getV1ListGraphExecutionsResponse200,
|
||||
useGetV1ListGraphExecutionsInfinite,
|
||||
} from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { GraphExecutionsPaginated } from "@/app/api/__generated__/models/graphExecutionsPaginated";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import {
|
||||
GraphExecutionMeta as LegacyGraphExecutionMeta,
|
||||
GraphID,
|
||||
GraphExecutionID,
|
||||
} from "@/lib/autogpt-server-api";
|
||||
import { GraphExecutionMeta as RawGraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
|
||||
export type GraphExecutionMeta = Omit<
|
||||
@@ -44,15 +49,7 @@ export const useAgentRunsInfinite = (graphID?: GraphID) => {
|
||||
{ page: 1, page_size: 20 },
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
const pagination = (lastPage.data as GraphExecutionsPaginated)
|
||||
.pagination;
|
||||
const hasMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return hasMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
getNextPageParam: getPaginationNextPageNumber,
|
||||
|
||||
// Prevent query from running if graphID is not available (yet)
|
||||
...(!graphID
|
||||
@@ -80,15 +77,8 @@ export const useAgentRunsInfinite = (graphID?: GraphID) => {
|
||||
queryClient,
|
||||
);
|
||||
|
||||
const agentRuns =
|
||||
queryResults?.pages.flatMap((page) => {
|
||||
const response = page.data as GraphExecutionsPaginated;
|
||||
return response.executions;
|
||||
}) ?? [];
|
||||
|
||||
const agentRunCount = (
|
||||
queryResults?.pages.at(-1)?.data as GraphExecutionsPaginated | undefined
|
||||
)?.pagination.total_items;
|
||||
const agentRuns = queryResults ? unpaginate(queryResults, "executions") : [];
|
||||
const agentRunCount = getPaginatedTotalCount(queryResults);
|
||||
|
||||
const upsertAgentRun = (newAgentRun: GraphExecutionMeta) => {
|
||||
queryClient.setQueryData(
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getPaginatedTotalCount,
|
||||
getPaginationNextPageNumber,
|
||||
unpaginate,
|
||||
} from "@/app/api/helpers";
|
||||
import { useGetV2ListLibraryAgentsInfinite } from "@/app/api/__generated__/endpoints/library/library";
|
||||
import { LibraryAgentResponse } from "@/app/api/__generated__/models/libraryAgentResponse";
|
||||
import { useLibraryPageContext } from "../state-provider";
|
||||
import { useLibraryAgentsStore } from "@/hooks/useLibraryAgents/store";
|
||||
import { getInitialData } from "./helpers";
|
||||
@@ -11,7 +15,7 @@ export const useLibraryAgentList = () => {
|
||||
const { agents: cachedAgents } = useLibraryAgentsStore();
|
||||
|
||||
const {
|
||||
data: agents,
|
||||
data: agentsQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -26,27 +30,15 @@ export const useLibraryAgentList = () => {
|
||||
{
|
||||
query: {
|
||||
initialData: getInitialData(cachedAgents, searchTerm, 8),
|
||||
getNextPageParam: (lastPage) => {
|
||||
const pagination = (lastPage.data as LibraryAgentResponse).pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
getNextPageParam: getPaginationNextPageNumber,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const allAgents =
|
||||
agents?.pages?.flatMap((page) => {
|
||||
const response = page.data as LibraryAgentResponse;
|
||||
return response.agents;
|
||||
}) ?? [];
|
||||
|
||||
const agentCount = agents?.pages?.[0]
|
||||
? (agents.pages[0].data as LibraryAgentResponse).pagination.total_items
|
||||
: 0;
|
||||
const allAgents = agentsQueryData
|
||||
? unpaginate(agentsQueryData, "agents")
|
||||
: [];
|
||||
const agentCount = getPaginatedTotalCount(agentsQueryData);
|
||||
|
||||
return {
|
||||
allAgents,
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getPaginatedTotalCount,
|
||||
getPaginationNextPageNumber,
|
||||
unpaginate,
|
||||
} from "@/app/api/helpers";
|
||||
import { useGetV2ListFavoriteLibraryAgentsInfinite } from "@/app/api/__generated__/endpoints/library/library";
|
||||
|
||||
export function useFavoriteAgents() {
|
||||
const {
|
||||
data: agents,
|
||||
data: agentsQueryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
@@ -15,36 +20,14 @@ export function useFavoriteAgents() {
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
getNextPageParam: (lastPage) => {
|
||||
// Only paginate on successful responses
|
||||
if (!lastPage || lastPage.status !== 200) return undefined;
|
||||
|
||||
const pagination = lastPage.data.pagination;
|
||||
const isMore =
|
||||
pagination.current_page * pagination.page_size <
|
||||
pagination.total_items;
|
||||
|
||||
return isMore ? pagination.current_page + 1 : undefined;
|
||||
},
|
||||
},
|
||||
query: { getNextPageParam: getPaginationNextPageNumber },
|
||||
},
|
||||
);
|
||||
|
||||
const allAgents =
|
||||
agents?.pages?.flatMap((page) => {
|
||||
// Only process successful responses
|
||||
if (!page || page.status !== 200) return [];
|
||||
const response = page.data;
|
||||
return response?.agents || [];
|
||||
}) ?? [];
|
||||
|
||||
const agentCount = (() => {
|
||||
const firstPage = agents?.pages?.[0];
|
||||
// Only count from successful responses
|
||||
if (!firstPage || firstPage.status !== 200) return 0;
|
||||
return firstPage.data?.pagination?.total_items || 0;
|
||||
})();
|
||||
const allAgents = agentsQueryData
|
||||
? unpaginate(agentsQueryData, "agents")
|
||||
: [];
|
||||
const agentCount = getPaginatedTotalCount(agentsQueryData);
|
||||
|
||||
return {
|
||||
allAgents,
|
||||
|
||||
@@ -15,11 +15,11 @@ import { ScrollArea } from "@/components/__legacy__/ui/scroll-area";
|
||||
import { ClockIcon, Loader2 } from "lucide-react";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { humanizeCronExpression } from "@/lib/cron-expression-utils";
|
||||
import { useUserTimezone } from "@/lib/hooks/useUserTimezone";
|
||||
import {
|
||||
formatScheduleTime,
|
||||
getTimezoneAbbreviation,
|
||||
} from "@/lib/timezone-utils";
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -66,11 +66,7 @@ export const SchedulesTable = ({
|
||||
const [selectedFilter, setSelectedFilter] = useState<string>(""); // Graph ID
|
||||
|
||||
// Get user's timezone for displaying schedule times
|
||||
const { data: userTimezone } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : "UTC"),
|
||||
},
|
||||
});
|
||||
const userTimezone = useUserTimezone() ?? "UTC";
|
||||
|
||||
const filteredAndSortedSchedules = [...schedules]
|
||||
.filter(
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useGetV1ListExecutionSchedulesForAUser,
|
||||
useDeleteV1DeleteExecutionSchedule,
|
||||
} from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
import { Card } from "@/components/__legacy__/ui/card";
|
||||
import { SchedulesTable } from "@/app/(platform)/monitoring/components/SchedulesTable";
|
||||
@@ -34,8 +35,7 @@ const Monitor = () => {
|
||||
useGetV1ListExecutionSchedulesForAUser();
|
||||
const deleteScheduleMutation = useDeleteV1DeleteExecutionSchedule();
|
||||
|
||||
const schedules =
|
||||
schedulesResponse?.status === 200 ? schedulesResponse.data : [];
|
||||
const schedules = okData(schedulesResponse) ?? [];
|
||||
|
||||
const removeSchedule = useCallback(
|
||||
async (scheduleId: string) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
useDeleteV1RevokeApiKey,
|
||||
useGetV1ListUserApiKeys,
|
||||
} from "@/app/api/__generated__/endpoints/api-keys/api-keys";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
|
||||
@@ -13,11 +14,7 @@ export const useAPISection = () => {
|
||||
|
||||
const { data: apiKeys, isLoading } = useGetV1ListUserApiKeys({
|
||||
query: {
|
||||
select: (res) => {
|
||||
if (res.status !== 200) return undefined;
|
||||
|
||||
return res.data.filter((key) => key.status === "ACTIVE");
|
||||
},
|
||||
select: (res) => okData(res)?.filter((key) => key.status === "ACTIVE"),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
usePostOauthUploadAppLogo,
|
||||
getGetOauthListMyOauthAppsQueryKey,
|
||||
} from "@/app/api/__generated__/endpoints/oauth/oauth";
|
||||
import { OAuthApplicationInfo } from "@/app/api/__generated__/models/oAuthApplicationInfo";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
@@ -19,7 +18,7 @@ export const useOAuthApps = () => {
|
||||
const [uploadingAppId, setUploadingAppId] = useState<string | null>(null);
|
||||
|
||||
const { data: oauthAppsResponse, isLoading } = useGetOauthListMyOauthApps({
|
||||
query: { select: okData<OAuthApplicationInfo[]> },
|
||||
query: { select: okData },
|
||||
});
|
||||
|
||||
const { mutateAsync: updateStatus } = usePatchOauthUpdateAppStatus({
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
useGetV1GetNotificationPreferences,
|
||||
useGetV1GetUserTimezone,
|
||||
} from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
@@ -24,7 +25,7 @@ export default function SettingsPage() {
|
||||
} = useGetV1GetNotificationPreferences({
|
||||
query: {
|
||||
enabled: !!user,
|
||||
select: (res) => (res.status === 200 ? res.data : null),
|
||||
select: okData,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,9 +33,7 @@ export default function SettingsPage() {
|
||||
useGetV1GetUserTimezone({
|
||||
query: {
|
||||
enabled: !!user,
|
||||
select: (res) => {
|
||||
return res.status === 200 ? String(res.data.timezone) : "not-set";
|
||||
},
|
||||
select: (res) => okData(res)?.timezone ?? "not-set",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import type { InfiniteData } from "@tanstack/react-query";
|
||||
import {
|
||||
getV1IsOnboardingEnabled,
|
||||
getV1OnboardingState,
|
||||
} from "./__generated__/endpoints/onboarding/onboarding";
|
||||
import { Pagination } from "./__generated__/models/pagination";
|
||||
|
||||
export type OKData<TResponse extends { status: number; data?: any }> =
|
||||
(TResponse & { status: 200 })["data"];
|
||||
|
||||
/**
|
||||
* Narrow an orval response to its success payload if and only if it is a `200` status with OK shape.
|
||||
@@ -9,13 +14,15 @@ import {
|
||||
* Usage with React Query select:
|
||||
* ```ts
|
||||
* const { data: agent } = useGetV2GetLibraryAgent(agentId, {
|
||||
* query: { select: okData<LibraryAgent> },
|
||||
* query: { select: okData },
|
||||
* });
|
||||
*
|
||||
* data // is now properly typed as LibraryAgent | undefined
|
||||
* ```
|
||||
*/
|
||||
export function okData<T>(res: unknown): T | undefined {
|
||||
export function okData<TResponse extends { status: number; data?: any }>(
|
||||
res: TResponse | undefined,
|
||||
): OKData<TResponse> | undefined {
|
||||
if (!res || typeof res !== "object") return undefined;
|
||||
|
||||
// status must exist and be exactly 200
|
||||
@@ -26,7 +33,88 @@ export function okData<T>(res: unknown): T | undefined {
|
||||
// check presence to safely return it as T; the generic T is enforced at call sites.
|
||||
if (!("data" in (res as Record<string, unknown>))) return undefined;
|
||||
|
||||
return (res as { data: T }).data;
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export function getPaginatedTotalCount(
|
||||
infiniteData: InfiniteData<unknown> | undefined,
|
||||
fallbackCount?: number,
|
||||
): number {
|
||||
const lastPage = infiniteData?.pages.at(-1);
|
||||
if (!hasValidPaginationInfo(lastPage)) return fallbackCount ?? 0;
|
||||
return lastPage.data.pagination.total_items ?? fallbackCount ?? 0;
|
||||
}
|
||||
|
||||
export function getPaginationNextPageNumber(
|
||||
lastPage:
|
||||
| { data: { pagination?: Pagination; [key: string]: any } }
|
||||
| undefined,
|
||||
): number | undefined {
|
||||
if (!hasValidPaginationInfo(lastPage)) return undefined;
|
||||
|
||||
const { pagination } = lastPage.data;
|
||||
const hasMore =
|
||||
pagination.current_page * pagination.page_size < pagination.total_items;
|
||||
return hasMore ? pagination.current_page + 1 : undefined;
|
||||
}
|
||||
|
||||
/** Make one list from a paginated infinite query result. */
|
||||
export function unpaginate<
|
||||
TResponse extends { status: number; data: any },
|
||||
TPageDataKey extends {
|
||||
// Only allow keys for which the value is an array:
|
||||
[K in keyof OKData<TResponse>]: OKData<TResponse>[K] extends any[]
|
||||
? K
|
||||
: never;
|
||||
}[keyof OKData<TResponse>] &
|
||||
string,
|
||||
TItemData extends OKData<TResponse>[TPageDataKey][number],
|
||||
>(
|
||||
infiniteData: InfiniteData<TResponse>,
|
||||
pageListKey: TPageDataKey,
|
||||
): TItemData[] {
|
||||
return (
|
||||
infiniteData?.pages.flatMap<TItemData>((page) => {
|
||||
if (!hasValidListPage(page, pageListKey)) return [];
|
||||
return page.data[pageListKey] || [];
|
||||
}) || []
|
||||
);
|
||||
}
|
||||
|
||||
function hasValidListPage<TKey extends string>(
|
||||
page: unknown,
|
||||
pageListKey: TKey,
|
||||
): page is { status: 200; data: { [key in TKey]: any[] } } {
|
||||
return (
|
||||
typeof page === "object" &&
|
||||
page !== null &&
|
||||
"status" in page &&
|
||||
page.status === 200 &&
|
||||
"data" in page &&
|
||||
typeof page.data === "object" &&
|
||||
page.data !== null &&
|
||||
pageListKey in page.data &&
|
||||
Array.isArray((page.data as Record<string, unknown>)[pageListKey])
|
||||
);
|
||||
}
|
||||
|
||||
function hasValidPaginationInfo(
|
||||
page: unknown,
|
||||
): page is { data: { pagination: Pagination; [key: string]: any } } {
|
||||
return (
|
||||
typeof page === "object" &&
|
||||
page !== null &&
|
||||
"data" in page &&
|
||||
typeof page.data === "object" &&
|
||||
page.data !== null &&
|
||||
"pagination" in page.data &&
|
||||
typeof page.data.pagination === "object" &&
|
||||
page.data.pagination !== null &&
|
||||
"total_items" in page.data.pagination &&
|
||||
"total_pages" in page.data.pagination &&
|
||||
"current_page" in page.data.pagination &&
|
||||
"page_size" in page.data.pagination
|
||||
);
|
||||
}
|
||||
|
||||
type ResponseWithData = { status: number; data: unknown };
|
||||
|
||||
@@ -4624,7 +4624,7 @@
|
||||
"get": {
|
||||
"tags": ["v2", "executions", "review", "v2", "executions", "review"],
|
||||
"summary": "Get Pending Reviews for Execution",
|
||||
"description": "Get all pending reviews for a specific graph execution.\n\nRetrieves all reviews with status \"WAITING\" for the specified graph execution\nthat belong to the authenticated user. Results are ordered by creation time\n(oldest first) to preserve review order within the execution.\n\nArgs:\n graph_exec_id: ID of the graph execution to get reviews for\n user_id: Authenticated user ID from security dependency\n\nReturns:\n List of pending review objects for the specified execution\n\nRaises:\n HTTPException:\n - 403: If user doesn't own the graph execution\n - 500: If authentication fails or database error occurs\n\nNote:\n Only returns reviews owned by the authenticated user for security.\n Reviews with invalid status are excluded with warning logs.",
|
||||
"description": "Get all pending reviews for a specific graph execution.\n\nRetrieves all reviews with status \"WAITING\" for the specified graph execution\nthat belong to the authenticated user. Results are ordered by creation time\n(oldest first) to preserve review order within the execution.\n\nArgs:\n graph_exec_id: ID of the graph execution to get reviews for\n user_id: Authenticated user ID from security dependency\n\nReturns:\n List of pending review objects for the specified execution\n\nRaises:\n HTTPException:\n - 404: If the graph execution doesn't exist or isn't owned by this user\n - 500: If authentication fails or database error occurs\n\nNote:\n Only returns reviews owned by the authenticated user for security.\n Reviews with invalid status are excluded with warning logs.",
|
||||
"operationId": "getV2Get pending reviews for execution",
|
||||
"security": [{ "HTTPBearerJWT": [] }],
|
||||
"parameters": [
|
||||
@@ -4650,11 +4650,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "description": "Invalid graph execution ID" },
|
||||
"401": {
|
||||
"$ref": "#/components/responses/HTTP401NotAuthenticatedError"
|
||||
},
|
||||
"403": { "description": "Access denied to graph execution" },
|
||||
"404": { "description": "Graph execution not found" },
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
@@ -5349,7 +5348,11 @@
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": { "application/json": { "schema": {} } }
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/GraphMeta" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/HTTP401NotAuthenticatedError"
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
normalizePickerResponse,
|
||||
scopesIncludeDrive,
|
||||
} from "./helpers";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
const defaultScopes = ["https://www.googleapis.com/auth/drive.file"];
|
||||
|
||||
@@ -126,9 +127,9 @@ export function useGoogleDrivePicker(options: Props) {
|
||||
);
|
||||
|
||||
const response = await queryClient.fetchQuery(queryOptions);
|
||||
const cred = okData(response);
|
||||
|
||||
if (response.status === 200 && response.data) {
|
||||
const cred = response.data;
|
||||
if (cred) {
|
||||
if (cred.type === "oauth2") {
|
||||
const oauthCred = cred as OAuth2Credentials;
|
||||
if (oauthCred.access_token) {
|
||||
|
||||
@@ -34,7 +34,7 @@ export function AgentSelectStep({
|
||||
}: Props) {
|
||||
const {
|
||||
// Data
|
||||
agents,
|
||||
myAgents,
|
||||
isLoading,
|
||||
error,
|
||||
// State
|
||||
@@ -99,7 +99,7 @@ export function AgentSelectStep({
|
||||
description="Select your project that you'd like to publish"
|
||||
/>
|
||||
|
||||
{agents.length === 0 ? (
|
||||
{myAgents.length === 0 ? (
|
||||
<div className="inline-flex h-[370px] flex-col items-center justify-center gap-[29px] px-4 py-5 sm:px-6">
|
||||
<Text variant="lead" className="text-center">
|
||||
Uh-oh.. It seems like you don't have any agents in your
|
||||
@@ -130,7 +130,7 @@ export function AgentSelectStep({
|
||||
</div>
|
||||
<div className="p-2">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{agents.map((agent) => (
|
||||
{myAgents.map((agent) => (
|
||||
<div
|
||||
key={agent.id}
|
||||
data-testid="agent-card"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { useGetV2GetMyAgents } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
export interface Agent {
|
||||
name: string;
|
||||
@@ -36,27 +37,33 @@ export function useAgentSelectStep({
|
||||
number | null
|
||||
>(null);
|
||||
|
||||
const { data: myAgents, isLoading, error } = useGetV2GetMyAgents();
|
||||
|
||||
const agents: Agent[] =
|
||||
(myAgents?.status === 200 &&
|
||||
myAgents.data.agents
|
||||
.map(
|
||||
(agent): Agent => ({
|
||||
name: agent.agent_name,
|
||||
id: agent.agent_id,
|
||||
version: agent.agent_version,
|
||||
lastEdited: agent.last_edited.toLocaleDateString(),
|
||||
imageSrc: agent.agent_image || "https://picsum.photos/300/200",
|
||||
description: agent.description || "",
|
||||
recommendedScheduleCron: agent.recommended_schedule_cron ?? null,
|
||||
}),
|
||||
)
|
||||
.sort(
|
||||
(a: Agent, b: Agent) =>
|
||||
new Date(b.lastEdited).getTime() - new Date(a.lastEdited).getTime(),
|
||||
)) ||
|
||||
[];
|
||||
const {
|
||||
data: _myAgents,
|
||||
isLoading,
|
||||
error,
|
||||
} = useGetV2GetMyAgents(undefined, {
|
||||
query: {
|
||||
select: (res) =>
|
||||
okData(res)
|
||||
?.agents.map(
|
||||
(agent): Agent => ({
|
||||
name: agent.agent_name,
|
||||
id: agent.agent_id,
|
||||
version: agent.agent_version,
|
||||
lastEdited: agent.last_edited.toLocaleDateString(),
|
||||
imageSrc: agent.agent_image || "https://picsum.photos/300/200",
|
||||
description: agent.description || "",
|
||||
recommendedScheduleCron: agent.recommended_schedule_cron ?? null,
|
||||
}),
|
||||
)
|
||||
.sort(
|
||||
(a: Agent, b: Agent) =>
|
||||
new Date(b.lastEdited).getTime() -
|
||||
new Date(a.lastEdited).getTime(),
|
||||
),
|
||||
},
|
||||
});
|
||||
const myAgents = _myAgents ?? [];
|
||||
|
||||
const handleAgentClick = (
|
||||
_: string,
|
||||
@@ -70,7 +77,7 @@ export function useAgentSelectStep({
|
||||
|
||||
const handleNext = () => {
|
||||
if (selectedAgentId && selectedAgentVersion) {
|
||||
const selectedAgent = agents.find(
|
||||
const selectedAgent = myAgents.find(
|
||||
(agent) => agent.id === selectedAgentId,
|
||||
);
|
||||
if (selectedAgent) {
|
||||
@@ -86,7 +93,7 @@ export function useAgentSelectStep({
|
||||
|
||||
return {
|
||||
// Data
|
||||
agents,
|
||||
myAgents,
|
||||
isLoading,
|
||||
error,
|
||||
// State
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useExecutionEvents } from "@/hooks/useExecutionEvents";
|
||||
import { useLibraryAgents } from "@/hooks/useLibraryAgents/useLibraryAgents";
|
||||
import type { GraphExecution } from "@/lib/autogpt-server-api/types";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import {
|
||||
NotificationState,
|
||||
categorizeExecutions,
|
||||
@@ -26,7 +27,7 @@ export function useAgentActivityDropdown() {
|
||||
isSuccess: executionsSuccess,
|
||||
error: executionsError,
|
||||
} = useGetV1ListAllExecutions({
|
||||
query: { select: (res) => (res.status === 200 ? res.data : null) },
|
||||
query: { select: okData },
|
||||
});
|
||||
|
||||
// Get all graph IDs from agentInfoMap
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useBreakpoint } from "@/lib/hooks/useBreakpoint";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
import { useMemo } from "react";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "../helpers";
|
||||
import { AccountMenu } from "./AccountMenu/AccountMenu";
|
||||
import { AgentActivityDropdown } from "./AgentActivityDropdown/AgentActivityDropdown";
|
||||
@@ -29,7 +30,7 @@ export function NavbarView({ isLoggedIn, previewBranchName }: NavbarViewProps) {
|
||||
const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile(
|
||||
{
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data : null),
|
||||
select: okData,
|
||||
enabled: isLoggedIn && !!user,
|
||||
// Include user ID in query key to ensure cache invalidation when user changes
|
||||
queryKey: ["/api/store/profile", user?.id],
|
||||
|
||||
@@ -7,6 +7,7 @@ import { cn } from "@/lib/utils";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { useGetV1GetExecutionDetails } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useGraphStore } from "@/app/(platform)/build/stores/graphStore";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
|
||||
@@ -29,13 +30,11 @@ export function FloatingReviewsPanel({
|
||||
{
|
||||
query: {
|
||||
enabled: !!(graphId && executionId),
|
||||
select: okData,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const executionStatus =
|
||||
executionDetails?.status === 200 ? executionDetails.data.status : undefined;
|
||||
|
||||
// Get graph execution status from the store (updated via WebSocket)
|
||||
const graphExecutionStatus = useGraphStore(
|
||||
useShallow((state) => state.graphExecutionStatus),
|
||||
@@ -49,7 +48,7 @@ export function FloatingReviewsPanel({
|
||||
if (executionId) {
|
||||
refetch();
|
||||
}
|
||||
}, [executionStatus, executionId, refetch]);
|
||||
}, [executionDetails?.status, executionId, refetch]);
|
||||
|
||||
// Refetch when graph execution status changes to REVIEW
|
||||
useEffect(() => {
|
||||
@@ -62,7 +61,7 @@ export function FloatingReviewsPanel({
|
||||
!executionId ||
|
||||
(!isLoading &&
|
||||
pendingReviews.length === 0 &&
|
||||
executionStatus !== AgentExecutionStatus.REVIEW)
|
||||
executionDetails?.status !== AgentExecutionStatus.REVIEW)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ export function PendingReviewsList({
|
||||
|
||||
const reviewActionMutation = usePostV2ProcessReviewAction({
|
||||
mutation: {
|
||||
onSuccess: (data: any) => {
|
||||
if (data.status !== 200) {
|
||||
onSuccess: (res) => {
|
||||
if (res.status !== 200) {
|
||||
toast({
|
||||
title: "Failed to process reviews",
|
||||
description: "Unexpected response from server",
|
||||
@@ -54,18 +54,18 @@ export function PendingReviewsList({
|
||||
return;
|
||||
}
|
||||
|
||||
const response = data.data;
|
||||
const result = res.data;
|
||||
|
||||
if (response.failed_count > 0) {
|
||||
if (result.failed_count > 0) {
|
||||
toast({
|
||||
title: "Reviews partially processed",
|
||||
description: `${response.approved_count + response.rejected_count} succeeded, ${response.failed_count} failed. ${response.error || "Some reviews could not be processed."}`,
|
||||
description: `${result.approved_count + result.rejected_count} succeeded, ${result.failed_count} failed. ${result.error || "Some reviews could not be processed."}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Reviews processed successfully",
|
||||
description: `${response.approved_count} approved, ${response.rejected_count} rejected`,
|
||||
description: `${result.approved_count} approved, ${result.rejected_count} rejected`,
|
||||
variant: "default",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { GraphModel } from "@/app/api/__generated__/models/graphModel";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Graph } from "@/lib/autogpt-server-api/types";
|
||||
|
||||
@@ -47,15 +48,19 @@ export function useAgentSafeMode(graph: GraphModel | LibraryAgent | Graph) {
|
||||
const { data: libraryAgent, isLoading } = useGetV2GetLibraryAgentByGraphId(
|
||||
graphId,
|
||||
{},
|
||||
{ query: { enabled: !isAgent && shouldShowToggle } },
|
||||
{
|
||||
query: {
|
||||
enabled: !isAgent && shouldShowToggle,
|
||||
select: okData,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const [localSafeMode, setLocalSafeMode] = useState<boolean | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAgent && libraryAgent?.status === 200) {
|
||||
const backendValue =
|
||||
libraryAgent.data?.settings?.human_in_the_loop_safe_mode;
|
||||
if (!isAgent && libraryAgent) {
|
||||
const backendValue = libraryAgent.settings?.human_in_the_loop_safe_mode;
|
||||
if (backendValue !== undefined) {
|
||||
setLocalSafeMode(backendValue);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ import {
|
||||
useGetV2GetPendingReviews,
|
||||
useGetV2GetPendingReviewsForExecution,
|
||||
} from "@/app/api/__generated__/endpoints/executions/executions";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
export function usePendingReviews() {
|
||||
const query = useGetV2GetPendingReviews();
|
||||
|
||||
return {
|
||||
pendingReviews: (query.data?.status === 200 ? query.data.data : []) || [],
|
||||
pendingReviews: okData(query.data) || [],
|
||||
isLoading: query.isLoading,
|
||||
error: query.error,
|
||||
refetch: query.refetch,
|
||||
@@ -18,7 +19,7 @@ export function usePendingReviewsForExecution(graphExecId: string) {
|
||||
const query = useGetV2GetPendingReviewsForExecution(graphExecId);
|
||||
|
||||
return {
|
||||
pendingReviews: (query.data?.status === 200 ? query.data.data : []) || [],
|
||||
pendingReviews: okData(query.data) || [],
|
||||
isLoading: query.isLoading,
|
||||
error: query.error,
|
||||
refetch: query.refetch,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
|
||||
export function useUserTimezone(): "not-set" | string | undefined {
|
||||
return useGetV1GetUserTimezone({
|
||||
query: { select: (res) => okData(res)?.timezone },
|
||||
}).data;
|
||||
}
|
||||
@@ -21,6 +21,10 @@ function makeQueryClient() {
|
||||
|
||||
let browserQueryClient: QueryClient | undefined = undefined;
|
||||
|
||||
/** Only for use *outside client component context*
|
||||
* (so in server components, API helpers, etc.).
|
||||
*
|
||||
* In the context of client components, you should always use `useQueryClient()`. */
|
||||
export function getQueryClient() {
|
||||
if (isServer) {
|
||||
// Server: create new client every time (so one user's data doesn't leak to another)
|
||||
|
||||
Reference in New Issue
Block a user